func (vc *VaultCollection) storeDataVaultConfiguration(config *models.DataVaultConfiguration, vaultID string) error {
	store, err := vc.provider.OpenStore(edvprovider.VaultConfigurationStoreName)
	if err != nil {
		if errors.Is(err, storage.ErrStoreNotFound) {
			return errors.New(messages.ConfigStoreNotFound)
		}
		return fmt.Errorf("failed to open store for vault configurations: %w", err)
	}
	err = store.StoreDataVaultConfiguration(config, vaultID)
	if err != nil {
		return fmt.Errorf("failed to store data vault configuration in store: %w", err)
	}
	return nil
}
storeDataVaultConfiguration stores a given DataVaultConfiguration and vaultID
package operation
import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"github.com/gorilla/mux"
	"github.com/hyperledger/aries-framework-go/spi/storage"
	"github.com/trustbloc/edge-core/pkg/log"
	"github.com/trustbloc/edv/pkg/edvprovider"
	"github.com/trustbloc/edv/pkg/edvutils"
	"github.com/trustbloc/edv/pkg/internal/common/support"
	"github.com/trustbloc/edv/pkg/restapi/messages"
	"github.com/trustbloc/edv/pkg/restapi/models"
)
const (
	logModuleName = "restapi"
	edvCommonEndpointPathRoot = "/encrypted-data-vaults"
	vaultIDPathVariable       = "vaultID"
	docIDPathVariable         = "docID"
	createVaultEndpoint = edvCommonEndpointPathRoot
	queryVaultEndpoint     = edvCommonEndpointPathRoot + "/{" + vaultIDPathVariable + "}/query"
	createDocumentEndpoint = edvCommonEndpointPathRoot + "/{" + vaultIDPathVariable + "}/documents"
	batchEndpoint          = edvCommonEndpointPathRoot + "/{" + vaultIDPathVariable + "}/batch"
	readDocumentEndpoint   = edvCommonEndpointPathRoot + "/{" + vaultIDPathVariable + "}/documents/{" +
		docIDPathVariable + "}"
	updateDocumentEndpoint = edvCommonEndpointPathRoot + "/{" + vaultIDPathVariable + "}/documents/{" +
		docIDPathVariable + "}"
	deleteDocumentEndpoint = edvCommonEndpointPathRoot + "/{" + vaultIDPathVariable + "}/documents/{" +
		docIDPathVariable + "}"
)
var logger = log.New(logModuleName)
type Operation struct {
	handlers          []Handler
	vaultCollection   VaultCollection
	authEnable        bool
	authService       authService
	enabledExtensions *EnabledExtensions
}
type authService interface {
	Create(resourceID, verificationMethod string) ([]byte, error)
}
type VaultCollection struct {
	provider *edvprovider.Provider
}
type Handler interface {
	Path() string
	Method() string
	Handle() http.HandlerFunc
}
type EnabledExtensions struct {
	ReturnFullDocumentsOnQuery bool
	ReadAllDocumentsEndpoint   bool
	Batch                      bool
}
type Config struct {
	Provider          *edvprovider.Provider
	AuthService       authService
	AuthEnable        bool
	EnabledExtensions *EnabledExtensions
}
func New(config *Config) *Operation {
	svc := &Operation{
		vaultCollection: VaultCollection{
			provider: config.Provider,
		}, authEnable: config.AuthEnable, authService: config.AuthService, enabledExtensions: config.EnabledExtensions,
	}
	svc.registerHandler()
	return svc
}
func (c *Operation) registerHandler() {
	c.handlers = []Handler{
		support.NewHTTPHandler(createVaultEndpoint, http.MethodPost, c.createDataVaultHandler),
		support.NewHTTPHandler(queryVaultEndpoint, http.MethodPost, c.queryVaultHandler),
		support.NewHTTPHandler(createDocumentEndpoint, http.MethodPost, c.createDocumentHandler),
		support.NewHTTPHandler(readDocumentEndpoint, http.MethodGet, c.readDocumentHandler),
		support.NewHTTPHandler(updateDocumentEndpoint, http.MethodPost, c.updateDocumentHandler),
		support.NewHTTPHandler(deleteDocumentEndpoint, http.MethodDelete, c.deleteDocumentHandler),
	}
	if c.enabledExtensions != nil {
		if c.enabledExtensions.Batch {
			c.handlers = append(c.handlers,
				support.NewHTTPHandler(batchEndpoint, http.MethodPost, c.batchHandler))
		}
	}
}
func (c *Operation) GetRESTHandlers() []Handler {
	return c.handlers
}
func (c *Operation) createDataVaultHandler(rw http.ResponseWriter, req *http.Request) {
	requestBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		writeCreateDataVaultRequestReadFailure(rw, err)
		return
	}
	logger.Infof(`Received request to create a new data vault. X-User header: %s,Request body: %s`,
		req.Header.Get("X-User"), string(requestBody))
	var config models.DataVaultConfiguration
	err = json.Unmarshal(requestBody, &config)
	if err != nil {
		writeCreateDataVaultInvalidRequest(rw, err, requestBody)
		return
	}
	err = validateDataVaultConfiguration(&config)
	if err != nil {
		writeCreateDataVaultInvalidRequest(rw, err, requestBody)
		return
	}
	var configBytesForLog []byte
	if debugLogLevelEnabled() {
		var err error
		configBytesForLog, err = json.Marshal(config)
		if err != nil {
			logger.Debugf(messages.DebugLogEventWithReceivedData,
				fmt.Sprintf(messages.MarshalVaultConfigForLogFailure, err),
				requestBody)
		}
	}
	c.createDataVault(rw, &config, req.Host, configBytesForLog)
}
func (c *Operation) createDataVault(rw http.ResponseWriter, config *models.DataVaultConfiguration, hostURL string,
	configBytesForLog []byte) {
	vaultID, err := edvutils.GenerateEDVCompatibleID()
	if err != nil {
		writeCreateDataVaultFailure(rw, err, configBytesForLog)
		return
	}
	err = c.vaultCollection.storeDataVaultConfiguration(config, vaultID)
	if err != nil {
		writeCreateDataVaultFailure(rw, fmt.Errorf(messages.StoreVaultConfigFailure, err), configBytesForLog)
		return
	}
	err = c.vaultCollection.createDataVault(vaultID)
	if err != nil {
		writeCreateDataVaultFailure(rw, err, configBytesForLog)
		return
	}
	var payload []byte
	if c.authEnable {
		payload, err = c.authService.Create(vaultID, config.Controller)
		if err != nil {
			writeCreateDataVaultFailure(rw, err, configBytesForLog)
			return
		}
	}
	writeCreateDataVaultSuccess(rw, vaultID, hostURL, configBytesForLog, payload)
}
func (c *Operation) queryVaultHandler(rw http.ResponseWriter, req *http.Request) {
	vaultID, success := unescapePathVar(vaultIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	requestBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		writeErrorWithVaultIDAndReceivedData(rw, http.StatusInternalServerError, messages.QueryFailReadRequestBody,
			err, vaultID, nil)
		return
	}
	logger.Debugf(messages.DebugLogEventWithReceivedData, fmt.Sprintf(messages.QueryReceiveRequest,
		vaultID), requestBody)
	incomingQuery, err := parseQuery(requestBody)
	if err != nil {
		writeErrorWithVaultIDAndReceivedData(rw, http.StatusBadRequest, messages.InvalidQuery, err, vaultID, requestBody)
		return
	}
	var queryBytesForLog []byte
	if debugLogLevelEnabled() {
		var errMarshal error
		queryBytesForLog, errMarshal = json.Marshal(incomingQuery)
		if errMarshal != nil {
			logger.Errorf(messages.DebugLogEventWithReceivedData,
				fmt.Sprintf(messages.MarshalQueryForLogFailure, errMarshal),
				requestBody)
		}
	}
	matchingDocuments, err := c.vaultCollection.queryVault(vaultID, &incomingQuery)
	if err != nil {
		writeErrorWithVaultIDAndReceivedData(rw, http.StatusBadRequest, messages.QueryFailure, err, vaultID, queryBytesForLog)
		return
	}
	if c.enabledExtensions != nil && c.enabledExtensions.ReturnFullDocumentsOnQuery {
		writeQueryResponse(rw, matchingDocuments, vaultID, queryBytesForLog, incomingQuery.ReturnFullDocuments, req.Host)
	} else {
		writeQueryResponse(rw, matchingDocuments, vaultID, queryBytesForLog, false, req.Host)
	}
}
func (c *Operation) createDocumentHandler(rw http.ResponseWriter, req *http.Request) {
	vaultID, success := unescapePathVar(vaultIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	requestBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		writeErrorWithVaultIDAndReceivedData(rw, http.StatusInternalServerError,
			messages.CreateDocumentFailReadRequestBody, err, vaultID, nil)
		return
	}
	logger.Debugf(messages.DebugLogEventWithReceivedData,
		fmt.Sprintf(messages.CreateDocumentReceiveRequest, vaultID),
		requestBody)
	c.createDocument(rw, requestBody, req.Host, vaultID)
}
func (c *Operation) readDocumentHandler(rw http.ResponseWriter, req *http.Request) {
	vaultID, success := unescapePathVar(vaultIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	docID, success := unescapePathVar(docIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	logger.Debugf(messages.DebugLogEvent, fmt.Sprintf(messages.ReadDocumentReceiveRequest, docID, vaultID))
	documentBytes, err := c.vaultCollection.readDocument(vaultID, docID)
	if err != nil {
		writeReadDocumentFailure(rw, err, docID, vaultID)
		return
	}
	writeReadDocumentSuccess(rw, documentBytes, docID, vaultID)
}
func (c *Operation) updateDocumentHandler(rw http.ResponseWriter, req *http.Request) {
	vaultID, success := unescapePathVar(vaultIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	docID, success := unescapePathVar(docIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	logger.Debugf(messages.DebugLogEvent, fmt.Sprintf(messages.UpdateDocumentReceiveRequest, docID, vaultID))
	requestBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		writeErrorWithVaultIDAndDocID(rw, http.StatusInternalServerError,
			messages.UpdateDocumentFailReadRequestBody, err, docID, vaultID)
		return
	}
	c.updateDocument(rw, requestBody, docID, vaultID)
}
func (c *Operation) deleteDocumentHandler(rw http.ResponseWriter, req *http.Request) {
	vaultID, success := unescapePathVar(vaultIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	docID, success := unescapePathVar(docIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	logger.Debugf(messages.DebugLogEvent, fmt.Sprintf(messages.DeleteDocumentReceiveRequest, docID, vaultID))
	err := c.vaultCollection.deleteDocument(docID, vaultID)
	if err != nil {
		writeDeleteDocumentFailure(rw, err, docID, vaultID)
	}
}
func (c *Operation) batchHandler(rw http.ResponseWriter, req *http.Request) {
	vaultID, success := unescapePathVar(vaultIDPathVariable, mux.Vars(req), rw)
	if !success {
		return
	}
	requestBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		writeErrorWithVaultIDAndReceivedData(rw, http.StatusInternalServerError, messages.BatchFailReadRequestBody,
			err, vaultID, nil)
		return
	}
	logger.Debugf(messages.DebugLogEventWithReceivedData, fmt.Sprintf(messages.BatchReceiveRequest,
		vaultID), requestBody)
	var incomingBatch models.Batch
	err = json.Unmarshal(requestBody, &incomingBatch)
	if err != nil {
		writeErrorWithVaultIDAndReceivedData(rw, http.StatusBadRequest, messages.InvalidBatch, err, vaultID, requestBody)
		return
	}
	responses := createInitialResponses(len(incomingBatch))
	err = validateBatch(incomingBatch, responses)
	if err != nil {
		writeBatchResponse(rw, messages.BatchResponseFailure, vaultID, requestBody, responses)
		return
	}
	c.executeBatchedOperations(rw, req.Host, vaultID, incomingBatch, responses, requestBody)
}
func (c *Operation) executeBatchedOperations(rw http.ResponseWriter, host, vaultID string,
	vaultOperations models.Batch, responses []string, requestBody []byte) {
	var currentUpsertDocumentsBatch []models.EncryptedDocument
	var numOperationsCompleted int
	for vaultOperationIndex, vaultOperation := range vaultOperations {
		switch {
		case strings.EqualFold(vaultOperation.Operation, models.UpsertDocumentVaultOperation):
			currentUpsertDocumentsBatch = append(currentUpsertDocumentsBatch, vaultOperation.EncryptedDocument)
		case strings.EqualFold(vaultOperation.Operation, models.DeleteDocumentVaultOperation):
			if len(currentUpsertDocumentsBatch) > 0 {
				err := c.vaultCollection.upsertDocuments(vaultID, currentUpsertDocumentsBatch)
				if err != nil {
					for i := 0; i < len(currentUpsertDocumentsBatch); i++ {
						responses[i+numOperationsCompleted] = err.Error()
					}
					writeBatchResponse(rw, messages.BatchResponseFailure, vaultID, requestBody, responses)
					return
				}
				for i := 0; i < len(currentUpsertDocumentsBatch); i++ {
					responses[i+numOperationsCompleted] =
						getFullDocumentURL(currentUpsertDocumentsBatch[i].ID, vaultID, host)
				}
				numOperationsCompleted += len(currentUpsertDocumentsBatch)
				currentUpsertDocumentsBatch = nil  
			}
			err := c.vaultCollection.deleteDocument(vaultOperation.DocumentID, vaultID)
			if err == nil {
				responses[vaultOperationIndex] = ""
			} else {
				responses[vaultOperationIndex] = err.Error()
				if !errors.Is(err, messages.ErrDocumentNotFound) {
					writeBatchResponse(rw, messages.BatchResponseFailure, vaultID, requestBody, responses)
					return
				}
			}
			numOperationsCompleted++
		default:  
			err := fmt.Errorf("%s is not a valid vault operation", vaultOperation.Operation)
			responses[vaultOperationIndex] = err.Error()
			writeBatchResponse(rw, messages.BatchResponseFailure, vaultID, requestBody, responses)
			return
		}
	}
	c.upsertRemainingDocuments(rw, host, vaultID, currentUpsertDocumentsBatch, responses, numOperationsCompleted,
		requestBody)
}
func (c *Operation) upsertRemainingDocuments(rw http.ResponseWriter, host, vaultID string,
	currentUpsertDocumentsBatch []models.EncryptedDocument, responses []string, numOperationsCompleted int,
	requestBody []byte) {
	if len(currentUpsertDocumentsBatch) > 0 {
		err := c.vaultCollection.upsertDocuments(vaultID, currentUpsertDocumentsBatch)
		if err != nil {
			for i := 0; i < len(currentUpsertDocumentsBatch); i++ {
				responses[i+numOperationsCompleted] = err.Error()
			}
			writeBatchResponse(rw, messages.BatchResponseFailure, vaultID, requestBody, responses)
			return
		}
		for i := 0; i < len(currentUpsertDocumentsBatch); i++ {
			responses[i+numOperationsCompleted] = getFullDocumentURL(currentUpsertDocumentsBatch[i].ID, vaultID, host)
		}
	}
	writeBatchResponse(rw, messages.BatchResponseSuccess, vaultID, requestBody, responses)
}
func createInitialResponses(numResponses int) []string {
	responses := make([]string, numResponses)
	for i := range responses {
		responses[i] = "not validated or executed"
	}
	return responses
}
func validateBatch(incomingBatch models.Batch, responses []string) error {
	for i, vaultOperation := range incomingBatch {
		switch {
		case strings.EqualFold(vaultOperation.Operation, models.UpsertDocumentVaultOperation):
			if err := validateEncryptedDocument(vaultOperation.EncryptedDocument); err != nil {
				responses[i] = err.Error()
				return err
			}
			responses[i] = "validated but not executed"
		case strings.EqualFold(vaultOperation.Operation, models.DeleteDocumentVaultOperation):
			if vaultOperation.DocumentID == "" {
				err := errors.New("document ID cannot be empty for a delete operation")
				responses[i] = err.Error()
				return err
			}
			responses[i] = "validated but not executed"
		default:
			err := fmt.Errorf("%s is not a valid vault operation", vaultOperation.Operation)
			responses[i] = err.Error()
			return err
		}
	}
	return nil
}
func (vc *VaultCollection) createDataVault(vaultID string) error {
	_, err := vc.provider.OpenStore(vaultID)
	if err != nil {
		return fmt.Errorf("failed to open store for vault: %w", err)
	}
	err = vc.provider.SetStoreConfig(vaultID, storage.StoreConfiguration{TagNames: []string{
		edvprovider.MappingDocumentTagName,
		edvprovider.MappingDocumentMatchingEncryptedDocIDTagName,
	}})
	if err != nil {
		return fmt.Errorf("failed to set store config: %w", err)
	}
	return nil
}