diff --git a/internal/platform/persistence/database.go b/internal/platform/persistence/database.go index 143d359..6cac3d2 100644 --- a/internal/platform/persistence/database.go +++ b/internal/platform/persistence/database.go @@ -77,6 +77,10 @@ func (n noOpDatabase) FindAllTenants(ctx context.Context) ([]string, error) { return nil, errNoDatabase } +func (n noOpDatabase) CreateMapping(ctx context.Context, tenantID string, mappingsName string, url string) error { + return errNoDatabase +} + func (n noOpDatabase) SetMapping(ctx context.Context, tenantID string, mappingsName string, url string) error { return errNoDatabase } diff --git a/internal/platform/persistence/mappings.go b/internal/platform/persistence/mappings.go index e3d598b..c719379 100644 --- a/internal/platform/persistence/mappings.go +++ b/internal/platform/persistence/mappings.go @@ -144,8 +144,13 @@ func NewMappingWriter(log restql.Logger, env domain.EnvSource, local map[string] return MappingsWriter{log: log, env: envMappings, local: localMappings, db: db} } -// Write sets a URL to a resource name under the given tenant -func (mw *MappingsWriter) Write(ctx context.Context, tenant string, resource string, url string) error { +// Create makes a new mapping from the name to the URL on the given tenant +func (mw *MappingsWriter) Create(ctx context.Context, tenant string, resource string, url string) error { + return mw.db.CreateMapping(ctx, tenant, resource, url) +} + +// Update sets a URL to a resource name under the given tenant +func (mw *MappingsWriter) Update(ctx context.Context, tenant string, resource string, url string) error { if !mw.allowWrite(tenant, resource) { log := restql.GetLogger(ctx) log.Error("write operation on resource mapping not allowed", ErrSetResourceMappingNotAllowed, "tenant", tenant, "resource", resource) diff --git a/internal/platform/persistence/mappings_test.go b/internal/platform/persistence/mappings_test.go index ba24c16..fa490b8 100644 --- a/internal/platform/persistence/mappings_test.go +++ b/internal/platform/persistence/mappings_test.go @@ -176,6 +176,10 @@ func (s stubDatabase) FindAllTenants(ctx context.Context) ([]string, error) { panic("implement me") } +func (s stubDatabase) CreateMapping(ctx context.Context, tenantID string, mappingsName string, url string) error { + panic("implement me") +} + func (s stubDatabase) SetMapping(ctx context.Context, tenantID string, mappingsName string, url string) error { panic("implement me") } diff --git a/internal/platform/web/admin.go b/internal/platform/web/admin.go index 50896bc..9bf8de6 100644 --- a/internal/platform/web/admin.go +++ b/internal/platform/web/admin.go @@ -234,7 +234,39 @@ type mapResourceBody struct { Url string `json:"url"` } -func (adm *administrator) MapResource(reqCtx *fasthttp.RequestCtx) error { +func (adm *administrator) CreateResource(reqCtx *fasthttp.RequestCtx) error { + ctx := middleware.GetNativeContext(reqCtx) + ctx = restql.WithLogger(ctx, adm.log) + + tenantName, err := pathParamString(reqCtx, "tenantName") + if err != nil { + adm.log.Error("failed to load tenant name path param", err) + return err + } + + resourceName, err := pathParamString(reqCtx, "resource") + if err != nil { + adm.log.Error("failed to load resource name path param", err) + return err + } + + var mrb mapResourceBody + + bytesBody := reqCtx.PostBody() + err = json.Unmarshal(bytesBody, &mrb) + if err != nil { + return err + } + + err = adm.mw.Create(ctx, tenantName, resourceName, mrb.Url) + if err != nil { + return RespondError(reqCtx, err, errToStatusCode) + } + + return Respond(reqCtx, nil, fasthttp.StatusCreated, nil) +} + +func (adm *administrator) UpdateResource(reqCtx *fasthttp.RequestCtx) error { ctx := middleware.GetNativeContext(reqCtx) ctx = restql.WithLogger(ctx, adm.log) @@ -263,12 +295,12 @@ func (adm *administrator) MapResource(reqCtx *fasthttp.RequestCtx) error { return err } - err = adm.mw.Write(ctx, tenantName, resourceName, mrb.Url) + err = adm.mw.Update(ctx, tenantName, resourceName, mrb.Url) if err != nil { return RespondError(reqCtx, err, errToStatusCode) } - return Respond(reqCtx, nil, fasthttp.StatusCreated, nil) + return Respond(reqCtx, nil, fasthttp.StatusNoContent, nil) } func isAuthorized(ctx *fasthttp.RequestCtx, authorizationCode []byte) bool { diff --git a/internal/platform/web/response.go b/internal/platform/web/response.go index 6b23b53..046dbc5 100644 --- a/internal/platform/web/response.go +++ b/internal/platform/web/response.go @@ -21,6 +21,7 @@ var errToStatusCode = map[error]int{ restql.ErrNamespaceNotFound: fasthttp.StatusNotFound, restql.ErrQueryNotFoundInDatabase: fasthttp.StatusNotFound, restql.ErrMappingsNotFoundInDatabase: fasthttp.StatusNotFound, + restql.ErrMappingAlreadyExistsInDatabase: fasthttp.StatusBadRequest, restql.ErrDatabaseCommunicationFailed: fasthttp.StatusInsufficientStorage, eval.ErrValidation: fasthttp.StatusUnprocessableEntity, eval.ErrParser: fasthttp.StatusInternalServerError, diff --git a/internal/platform/web/routes.go b/internal/platform/web/routes.go index 976c36a..2d7b3e5 100644 --- a/internal/platform/web/routes.go +++ b/internal/platform/web/routes.go @@ -87,7 +87,8 @@ func API(log restql.Logger, cfg *conf.Config) (fasthttp.RequestHandler, error) { func registerAdminEndpoints(adm *administrator, apiApp app) app { apiApp.Handle(http.MethodGet, "/admin/tenant", adm.AllTenants) apiApp.Handle(http.MethodGet, "/admin/tenant/{tenantName}/mapping", adm.TenantMappings) - apiApp.Handle(http.MethodPost, "/admin/tenant/{tenantName}/mapping/{resource}", adm.MapResource) + apiApp.Handle(http.MethodPost, "/admin/tenant/{tenantName}/mapping/{resource}", adm.CreateResource) + apiApp.Handle(http.MethodPut, "/admin/tenant/{tenantName}/mapping/{resource}", adm.UpdateResource) apiApp.Handle(http.MethodGet, "/admin/namespace", adm.AllNamespaces) apiApp.Handle(http.MethodGet, "/admin/namespace/{namespace}/query", adm.NamespaceQueries) diff --git a/pkg/restql/plugins.go b/pkg/restql/plugins.go index 9b7034e..721fc20 100644 --- a/pkg/restql/plugins.go +++ b/pkg/restql/plugins.go @@ -143,14 +143,16 @@ type DatabasePlugin interface { FindAllTenants(ctx context.Context) ([]string, error) FindMappingsForTenant(ctx context.Context, tenantID string) ([]Mapping, error) + CreateMapping(ctx context.Context, tenantID string, mappingsName string, url string) error SetMapping(ctx context.Context, tenantID string, mappingsName string, url string) error } // Errors returned by Database plugin var ( - ErrMappingsNotFoundInDatabase = errors.New("mappings not found in database") - ErrQueryNotFoundInDatabase = errors.New("query not found in database") - ErrDatabaseCommunicationFailed = errors.New("failed to communicate with the database") + ErrMappingsNotFoundInDatabase = errors.New("mappings not found in database") + ErrQueryNotFoundInDatabase = errors.New("query not found in database") + ErrDatabaseCommunicationFailed = errors.New("failed to communicate with the database") + ErrMappingAlreadyExistsInDatabase = errors.New("mapping already exist in database, create operation not allowed") ) // ErrMappingsNotFound is the error returned when