From 3cab451963fe42f3935e2eb59af4fffb3050413e Mon Sep 17 00:00:00 2001 From: Alex Goth <64845621+GAlexIHU@users.noreply.github.com> Date: Thu, 27 Jun 2024 14:11:02 +0200 Subject: [PATCH] feat: implement get and delete (#1077) * refactor: unify error handling for entitlements * feat: implement get and delete * chore: add new errorencoder * fix: expose new handlers --------- Signed-off-by: Alex Goth <64845621+GAlexIHU@users.noreply.github.com> --- internal/entitlement/connector.go | 23 ++++-- .../entitlement/httpdriver/entitlement.go | 78 +++++++++++++++++++ .../postgresadapter/entitlement.go | 15 ++++ internal/entitlement/repository.go | 1 + internal/server/router/entitlement.go | 24 ++++++ internal/server/router/noop.go | 16 ---- openmeter/entitlement/httpdriver/handlers.go | 2 + openmeter/entitlement/httpdriver/types.go | 3 + 8 files changed, 140 insertions(+), 22 deletions(-) diff --git a/internal/entitlement/connector.go b/internal/entitlement/connector.go index e70a6c4f8..ed8426b2d 100644 --- a/internal/entitlement/connector.go +++ b/internal/entitlement/connector.go @@ -18,18 +18,21 @@ const ( ) type ListEntitlementsParams struct { - Namespace string - Limit int - Offset int - OrderBy ListEntitlementsOrderBy + Namespace string + Limit int + Offset int + OrderBy ListEntitlementsOrderBy + IncludeDeleted bool } type Connector interface { - // Entitlement Management CreateEntitlement(ctx context.Context, input CreateEntitlementInputs) (*Entitlement, error) - GetEntitlementsOfSubject(ctx context.Context, namespace string, subjectKey models.SubjectKey) ([]Entitlement, error) + GetEntitlement(ctx context.Context, namespace string, id string) (*Entitlement, error) + DeleteEntitlement(ctx context.Context, namespace string, id string) error + GetEntitlementValue(ctx context.Context, namespace string, subjectKey string, id string, at time.Time) (EntitlementValue, error) + GetEntitlementsOfSubject(ctx context.Context, namespace string, subjectKey models.SubjectKey) ([]Entitlement, error) ListEntitlements(ctx context.Context, params ListEntitlementsParams) ([]Entitlement, error) } @@ -122,6 +125,14 @@ func (c *entitlementConnector) CreateEntitlement(ctx context.Context, input Crea return ent, nil } +func (c *entitlementConnector) GetEntitlement(ctx context.Context, namespace string, id string) (*Entitlement, error) { + return c.entitlementRepo.GetEntitlement(ctx, models.NamespacedID{Namespace: namespace, ID: id}) +} + +func (c *entitlementConnector) DeleteEntitlement(ctx context.Context, namespace string, id string) error { + return c.entitlementRepo.DeleteEntitlement(ctx, models.NamespacedID{Namespace: namespace, ID: id}) +} + func (c *entitlementConnector) GetEntitlementsOfSubject(ctx context.Context, namespace string, subjectKey models.SubjectKey) ([]Entitlement, error) { return c.entitlementRepo.GetEntitlementsOfSubject(ctx, namespace, subjectKey) } diff --git a/internal/entitlement/httpdriver/entitlement.go b/internal/entitlement/httpdriver/entitlement.go index fbe8d99b1..e909c3541 100644 --- a/internal/entitlement/httpdriver/entitlement.go +++ b/internal/entitlement/httpdriver/entitlement.go @@ -22,6 +22,8 @@ import ( type EntitlementHandler interface { CreateEntitlement() CreateEntitlementHandler + GetEntitlement() GetEntitlementHandler + DeleteEntitlement() DeleteEntitlementHandler GetEntitlementValue() GetEntitlementValueHandler GetEntitlementsOfSubjectHandler() GetEntitlementsOfSubjectHandler ListEntitlements() ListEntitlementsHandler @@ -313,6 +315,82 @@ func (h *entitlementHandler) ListEntitlements() ListEntitlementsHandler { ) } +type GetEntitlementHandlerRequest struct { + EntitlementId string + Namespace string +} +type GetEntitlementHandlerResponse = *api.Entitlement +type GetEntitlementHandlerParams struct { + EntitlementId string +} +type GetEntitlementHandler httptransport.HandlerWithArgs[GetEntitlementHandlerRequest, GetEntitlementHandlerResponse, GetEntitlementHandlerParams] + +func (h *entitlementHandler) GetEntitlement() GetEntitlementHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, params GetEntitlementHandlerParams) (GetEntitlementHandlerRequest, error) { + ns, err := h.resolveNamespace(ctx) + if err != nil { + return GetEntitlementHandlerRequest{}, err + } + + return GetEntitlementHandlerRequest{ + EntitlementId: params.EntitlementId, + Namespace: ns, + }, nil + }, + func(ctx context.Context, request GetEntitlementHandlerRequest) (GetEntitlementHandlerResponse, error) { + entitlement, err := h.connector.GetEntitlement(ctx, request.Namespace, request.EntitlementId) + if err != nil { + return nil, err + } + + return Parser.ToAPIGeneric(entitlement) + }, + commonhttp.JSONResponseEncoder[GetEntitlementHandlerResponse], + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("getEntitlement"), + httptransport.WithErrorEncoder(getErrorEncoder()), + )..., + ) +} + +type DeleteEntitlementHandlerRequest struct { + EntitlementId string + Namespace string +} +type DeleteEntitlementHandlerResponse = interface{} +type DeleteEntitlementHandlerParams struct { + EntitlementId string +} +type DeleteEntitlementHandler httptransport.HandlerWithArgs[DeleteEntitlementHandlerRequest, DeleteEntitlementHandlerResponse, DeleteEntitlementHandlerParams] + +func (h *entitlementHandler) DeleteEntitlement() DeleteEntitlementHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, params DeleteEntitlementHandlerParams) (DeleteEntitlementHandlerRequest, error) { + ns, err := h.resolveNamespace(ctx) + if err != nil { + return DeleteEntitlementHandlerRequest{}, err + } + + return DeleteEntitlementHandlerRequest{ + EntitlementId: params.EntitlementId, + Namespace: ns, + }, nil + }, + func(ctx context.Context, request DeleteEntitlementHandlerRequest) (DeleteEntitlementHandlerResponse, error) { + err := h.connector.DeleteEntitlement(ctx, request.Namespace, request.EntitlementId) + return nil, err + }, + commonhttp.EmptyResponseEncoder[DeleteEntitlementHandlerResponse](http.StatusNoContent), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("deleteEntitlement"), + httptransport.WithErrorEncoder(getErrorEncoder()), + )..., + ) +} + func (h *entitlementHandler) resolveNamespace(ctx context.Context) (string, error) { ns, ok := h.namespaceDecoder.GetNamespace(ctx) if !ok { diff --git a/internal/entitlement/postgresadapter/entitlement.go b/internal/entitlement/postgresadapter/entitlement.go index f40454f46..c3b6c84da 100644 --- a/internal/entitlement/postgresadapter/entitlement.go +++ b/internal/entitlement/postgresadapter/entitlement.go @@ -32,6 +32,7 @@ func (a *entitlementDBAdapter) GetEntitlement(ctx context.Context, entitlementID Where( db_entitlement.ID(entitlementID.ID), db_entitlement.Namespace(entitlementID.Namespace), + db_entitlement.Or(db_entitlement.DeletedAtGT(time.Now()), db_entitlement.DeletedAtIsNil()), ). First(ctx) @@ -48,6 +49,7 @@ func (a *entitlementDBAdapter) GetEntitlement(ctx context.Context, entitlementID func (a *entitlementDBAdapter) GetEntitlementOfSubject(ctx context.Context, namespace string, subjectKey string, id string) (*entitlement.Entitlement, error) { res, err := withLatestUsageReset(a.db.Entitlement.Query()). Where( + db_entitlement.Or(db_entitlement.DeletedAtGT(time.Now()), db_entitlement.DeletedAtIsNil()), db_entitlement.SubjectKey(string(subjectKey)), db_entitlement.Namespace(namespace), db_entitlement.ID(id), @@ -109,9 +111,18 @@ func (a *entitlementDBAdapter) CreateEntitlement(ctx context.Context, entitlemen return mapEntitlementEntity(res), nil } +func (a *entitlementDBAdapter) DeleteEntitlement(ctx context.Context, entitlementID models.NamespacedID) error { + _, err := a.db.Entitlement.Update(). + Where(db_entitlement.ID(entitlementID.ID), db_entitlement.Namespace(entitlementID.Namespace)). + SetDeletedAt(time.Now()). + Save(ctx) + return err +} + func (a *entitlementDBAdapter) GetEntitlementsOfSubject(ctx context.Context, namespace string, subjectKey models.SubjectKey) ([]entitlement.Entitlement, error) { res, err := withLatestUsageReset(a.db.Entitlement.Query()). Where( + db_entitlement.Or(db_entitlement.DeletedAtGT(time.Now()), db_entitlement.DeletedAtIsNil()), db_entitlement.SubjectKey(string(subjectKey)), db_entitlement.Namespace(namespace), ). @@ -134,6 +145,10 @@ func (a *entitlementDBAdapter) ListEntitlements(ctx context.Context, params enti query := withLatestUsageReset(a.db.Entitlement.Query(). Where(db_entitlement.Namespace(params.Namespace))) + if !params.IncludeDeleted { + query = query.Where(db_entitlement.Or(db_entitlement.DeletedAtGT(time.Now()), db_entitlement.DeletedAtIsNil())) + } + if params.Limit > 0 { query = query.Limit(params.Limit) } diff --git a/internal/entitlement/repository.go b/internal/entitlement/repository.go index e5884a6e1..16162b590 100644 --- a/internal/entitlement/repository.go +++ b/internal/entitlement/repository.go @@ -20,6 +20,7 @@ type EntitlementRepo interface { CreateEntitlement(ctx context.Context, entitlement CreateEntitlementRepoInputs) (*Entitlement, error) GetEntitlement(ctx context.Context, entitlementID models.NamespacedID) (*Entitlement, error) GetEntitlementOfSubject(ctx context.Context, namespace string, subjectKey string, id string) (*Entitlement, error) + DeleteEntitlement(ctx context.Context, entitlementID models.NamespacedID) error ListEntitlements(ctx context.Context, params ListEntitlementsParams) ([]Entitlement, error) diff --git a/internal/server/router/entitlement.go b/internal/server/router/entitlement.go index 5c1fc64fb..a65e2db83 100644 --- a/internal/server/router/entitlement.go +++ b/internal/server/router/entitlement.go @@ -106,3 +106,27 @@ func (a *Router) ListEntitlements(w http.ResponseWriter, r *http.Request, params } a.entitlementHandler.ListEntitlements().With(params).ServeHTTP(w, r) } + +// Delete entitlement +// (DELETE /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementId}) +func (a *Router) DeleteEntitlement(w http.ResponseWriter, r *http.Request, subjectIdOrKey api.SubjectIdOrKey, entitlementId api.EntitlementId) { + if !a.config.EntitlementsEnabled { + unimplemented.DeleteEntitlement(w, r, subjectIdOrKey, entitlementId) + return + } + a.entitlementHandler.DeleteEntitlement().With(httpdriver.DeleteEntitlementHandlerParams{ + EntitlementId: entitlementId, + }).ServeHTTP(w, r) +} + +// Get entitlement +// (GET /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementId}) +func (a *Router) GetEntitlement(w http.ResponseWriter, r *http.Request, subjectIdOrKey api.SubjectIdOrKey, entitlementId api.EntitlementId) { + if !a.config.EntitlementsEnabled { + unimplemented.GetEntitlement(w, r, subjectIdOrKey, entitlementId) + return + } + a.entitlementHandler.GetEntitlement().With(httpdriver.GetEntitlementHandlerParams{ + EntitlementId: entitlementId, + }).ServeHTTP(w, r) +} diff --git a/internal/server/router/noop.go b/internal/server/router/noop.go index 1286fa7fb..92fbdefb5 100644 --- a/internal/server/router/noop.go +++ b/internal/server/router/noop.go @@ -3,23 +3,7 @@ package router // We explicitly define no-op implementations for future APIs instead of just using the codegen version. import ( - "fmt" - "net/http" - "github.com/openmeterio/openmeter/api" - "github.com/openmeterio/openmeter/pkg/framework/commonhttp" ) var unimplemented api.ServerInterface = api.Unimplemented{} - -// Delete entitlement -// (DELETE /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementId}) -func (a *Router) DeleteEntitlement(w http.ResponseWriter, r *http.Request, subjectIdOrKey api.SubjectIdOrKey, entitlementId api.EntitlementId) { - commonhttp.NewHTTPError(http.StatusNotImplemented, fmt.Errorf("not implemented")).EncodeError(r.Context(), w) -} - -// Get entitlement -// (GET /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementId}) -func (a *Router) GetEntitlement(w http.ResponseWriter, r *http.Request, subjectIdOrKey api.SubjectIdOrKey, entitlementId api.EntitlementId) { - commonhttp.NewHTTPError(http.StatusNotImplemented, fmt.Errorf("not implemented")).EncodeError(r.Context(), w) -} diff --git a/openmeter/entitlement/httpdriver/handlers.go b/openmeter/entitlement/httpdriver/handlers.go index 1b913df44..bcc939718 100644 --- a/openmeter/entitlement/httpdriver/handlers.go +++ b/openmeter/entitlement/httpdriver/handlers.go @@ -19,6 +19,8 @@ type GetEntitlementsOfSubjectHandler = httpdriver.GetEntitlementsOfSubjectHandle type ListEntitlementGrantsHandler = httpdriver.ListEntitlementGrantsHandler type ResetEntitlementUsageHandler = httpdriver.ResetEntitlementUsageHandler type ListEntitlementsHandler = httpdriver.ListEntitlementsHandler +type GetEntitlementHandler = httpdriver.GetEntitlementHandler +type DeleteEntitlementHandler = httpdriver.DeleteEntitlementHandler func NewEntitlementHandler( connector entitlement.EntitlementConnector, diff --git a/openmeter/entitlement/httpdriver/types.go b/openmeter/entitlement/httpdriver/types.go index 94dc4c4fc..edb5c1e91 100644 --- a/openmeter/entitlement/httpdriver/types.go +++ b/openmeter/entitlement/httpdriver/types.go @@ -11,6 +11,7 @@ type GetEntitlementsOfSubjectHandlerRequest = httpdriver.GetEntitlementsOfSubjec type ListEntitlementGrantHandlerRequest = httpdriver.ListEntitlementGrantHandlerRequest type ResetEntitlementUsageHandlerRequest = httpdriver.ResetEntitlementUsageHandlerRequest type ListEntitlementsHandlerRequest = httpdriver.ListEntitlementsHandlerRequest +type GetEntitlementHandlerRequest = httpdriver.GetEntitlementHandlerRequest // responses type CreateEntitlementHandlerResponse = httpdriver.CreateEntitlementHandlerResponse @@ -21,6 +22,7 @@ type GetEntitlementsOfSubjectHandlerResponse = httpdriver.GetEntitlementsOfSubje type ListEntitlementGrantHandlerResponse = httpdriver.ListEntitlementGrantHandlerResponse type ResetEntitlementUsageHandlerResponse = httpdriver.ResetEntitlementUsageHandlerResponse type ListEntitlementsHandlerResponse = httpdriver.ListEntitlementsHandlerResponse +type GetEntitlementHandlerResponse = httpdriver.GetEntitlementHandlerResponse // params type CreateEntitlementHandlerParams = httpdriver.CreateEntitlementHandlerParams @@ -31,3 +33,4 @@ type GetEntitlementsOfSubjectHandlerParams = httpdriver.GetEntitlementsOfSubject type ListEntitlementGrantsHandlerParams = httpdriver.ListEntitlementGrantsHandlerParams type ResetEntitlementUsageHandlerParams = httpdriver.ResetEntitlementUsageHandlerParams type ListEntitlementsHandlerParams = httpdriver.ListEntitlementsHandlerParams +type GetEntitlementHandlerParams = httpdriver.GetEntitlementHandlerParams