diff --git a/api.yaml b/api.yaml index 44cb2f5..03737e2 100644 --- a/api.yaml +++ b/api.yaml @@ -572,6 +572,52 @@ paths: text/plain: schema: type: string + patch: + summary: Update a Tier + description: Update mutable fields of a Tier + operationId: updateTier + parameters: + - name: id + in: path + description: ID of Tier to update + required: true + schema: + type: string + format: uuid + requestBody: + description: Updated fields + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TierPatch' + responses: + '204': + description: Tier updated + '400': + description: Bad request + content: + text/plain: + schema: + type: string + '401': + description: Access denied + content: + text/plain: + schema: + type: string + '404': + description: Not found + content: + text/plain: + schema: + type: string + '500': + description: Internal server error + content: + text/plain: + schema: + type: string delete: summary: Delete Tier by ID operationId: deleteTier @@ -732,6 +778,52 @@ paths: text/plain: schema: type: string + patch: + summary: Update a Network + description: Update mutable fields of a Network + operationId: updateNetwork + parameters: + - name: id + in: path + description: ID of Network to update + required: true + schema: + type: string + format: uuid + requestBody: + description: Updated fields + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NetworkPatch' + responses: + '204': + description: Network updated + '400': + description: Bad request + content: + text/plain: + schema: + type: string + '401': + description: Access denied + content: + text/plain: + schema: + type: string + '404': + description: Not found + content: + text/plain: + schema: + type: string + '500': + description: Internal server error + content: + text/plain: + schema: + type: string delete: summary: Delete Network by ID operationId: deleteNetwork @@ -1853,6 +1945,25 @@ components: type: number format: double default: 0.0 + NetworkPatch: + type: object + properties: + id: + type: string + format: uuid + nullable: true + description: Unique identifier; cannot be changed + consortium: + type: string + format: uuid + nullable: true + name: + type: string + nullable: true + priority: + type: number + format: double + nullable: true Tier: type: object @@ -1882,17 +1993,72 @@ components: - secondarymail - standard - urgent + x-enum-varnames: + - Express + - Normal + - Rush + - Secondarymail + - Standard + - Urgent default: standard type: type: string enum: - loan - copy + x-enum-varnames: + - Loan + - Copy default: loan cost: type: number format: double default: 0.0 + TierPatch: + type: object + properties: + id: + type: string + format: uuid + nullable: true + description: Unique identifier; cannot be changed + consortium: + type: string + format: uuid + nullable: true + name: + type: string + nullable: true + level: + type: string + nullable: true + enum: + - express + - normal + - rush + - secondarymail + - standard + - urgent + x-enum-varnames: + - TierPatchLevelExpress + - TierPatchLevelNormal + - TierPatchLevelRush + - TierPatchLevelSecondarymail + - TierPatchLevelStandard + - TierPatchLevelUrgent + type: + type: string + nullable: true + enum: + - loan + - copy + x-enum-varnames: + - TierPatchTypeLoan + - TierPatchTypeCopy + cost: + type: number + format: double + nullable: true EntryNetwork: type: object diff --git a/api/directory.gen.go b/api/directory.gen.go index 641be2c..47fa9fc 100644 --- a/api/directory.gen.go +++ b/api/directory.gen.go @@ -88,6 +88,22 @@ const ( Loan TierType = "loan" ) +// Defines values for TierPatchLevel. +const ( + TierPatchLevelExpress TierPatchLevel = "express" + TierPatchLevelNormal TierPatchLevel = "normal" + TierPatchLevelRush TierPatchLevel = "rush" + TierPatchLevelSecondarymail TierPatchLevel = "secondarymail" + TierPatchLevelStandard TierPatchLevel = "standard" + TierPatchLevelUrgent TierPatchLevel = "urgent" +) + +// Defines values for TierPatchType. +const ( + TierPatchTypeCopy TierPatchType = "copy" + TierPatchTypeLoan TierPatchType = "loan" +) + // Defines values for EntryLookupKey. const ( EntryLookupKeyById EntryLookupKey = "by-id" @@ -432,6 +448,16 @@ type Network struct { Priority float64 `json:"priority"` } +// NetworkPatch defines model for NetworkPatch. +type NetworkPatch struct { + Consortium nullable.Nullable[openapi_types.UUID] `json:"consortium,omitempty"` + + // Id Unique identifier; cannot be changed + Id nullable.Nullable[openapi_types.UUID] `json:"id,omitempty"` + Name nullable.Nullable[string] `json:"name,omitempty"` + Priority nullable.Nullable[float64] `json:"priority,omitempty"` +} + // NetworksResponse defines model for NetworksResponse. type NetworksResponse struct { About About `json:"about"` @@ -516,6 +542,24 @@ type TierLevel string // TierType defines model for Tier.Type. type TierType string +// TierPatch defines model for TierPatch. +type TierPatch struct { + Consortium nullable.Nullable[openapi_types.UUID] `json:"consortium,omitempty"` + Cost nullable.Nullable[float64] `json:"cost,omitempty"` + + // Id Unique identifier; cannot be changed + Id nullable.Nullable[openapi_types.UUID] `json:"id,omitempty"` + Level nullable.Nullable[TierPatchLevel] `json:"level,omitempty"` + Name nullable.Nullable[string] `json:"name,omitempty"` + Type nullable.Nullable[TierPatchType] `json:"type,omitempty"` +} + +// TierPatchLevel defines model for TierPatch.Level. +type TierPatchLevel string + +// TierPatchType defines model for TierPatch.Type. +type TierPatchType string + // TiersResponse defines model for TiersResponse. type TiersResponse struct { About About `json:"about"` @@ -660,9 +704,15 @@ type AddEntryTierJSONRequestBody = EntryTier // AddNetworkJSONRequestBody defines body for AddNetwork for application/json ContentType. type AddNetworkJSONRequestBody = Network +// UpdateNetworkJSONRequestBody defines body for UpdateNetwork for application/json ContentType. +type UpdateNetworkJSONRequestBody = NetworkPatch + // AddTierJSONRequestBody defines body for AddTier for application/json ContentType. type AddTierJSONRequestBody = Tier +// UpdateTierJSONRequestBody defines body for UpdateTier for application/json ContentType. +type UpdateTierJSONRequestBody = TierPatch + // ServerInterface represents all server handlers. type ServerInterface interface { // Get all Closures @@ -749,6 +799,9 @@ type ServerInterface interface { // Returns a Network by ID // (GET /networks/{id}) GetNetwork(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) + // Update a Network + // (PATCH /networks/{id}) + UpdateNetwork(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) // Get all tiers // (GET /tiers) GetTiers(w http.ResponseWriter, r *http.Request, params GetTiersParams) @@ -761,6 +814,9 @@ type ServerInterface interface { // Returns a Tier by ID // (GET /tiers/{id}) GetTier(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) + // Update a Tier + // (PATCH /tiers/{id}) + UpdateTier(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) } // ServerInterfaceWrapper converts contexts to parameters. @@ -1606,6 +1662,31 @@ func (siw *ServerInterfaceWrapper) GetNetwork(w http.ResponseWriter, r *http.Req handler.ServeHTTP(w, r) } +// UpdateNetwork operation middleware +func (siw *ServerInterfaceWrapper) UpdateNetwork(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "id" ------------- + var id openapi_types.UUID + + err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.UpdateNetwork(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // GetTiers operation middleware func (siw *ServerInterfaceWrapper) GetTiers(w http.ResponseWriter, r *http.Request) { @@ -1713,6 +1794,31 @@ func (siw *ServerInterfaceWrapper) GetTier(w http.ResponseWriter, r *http.Reques handler.ServeHTTP(w, r) } +// UpdateTier operation middleware +func (siw *ServerInterfaceWrapper) UpdateTier(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "id" ------------- + var id openapi_types.UUID + + err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.UpdateTier(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + type UnescapedCookieParamError struct { ParamName string Err error @@ -1861,10 +1967,12 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc("POST "+options.BaseURL+"/networks", wrapper.AddNetwork) m.HandleFunc("DELETE "+options.BaseURL+"/networks/{id}", wrapper.DeleteNetwork) m.HandleFunc("GET "+options.BaseURL+"/networks/{id}", wrapper.GetNetwork) + m.HandleFunc("PATCH "+options.BaseURL+"/networks/{id}", wrapper.UpdateNetwork) m.HandleFunc("GET "+options.BaseURL+"/tiers", wrapper.GetTiers) m.HandleFunc("POST "+options.BaseURL+"/tiers", wrapper.AddTier) m.HandleFunc("DELETE "+options.BaseURL+"/tiers/{id}", wrapper.DeleteTier) m.HandleFunc("GET "+options.BaseURL+"/tiers/{id}", wrapper.GetTier) + m.HandleFunc("PATCH "+options.BaseURL+"/tiers/{id}", wrapper.UpdateTier) return m } @@ -3321,6 +3429,63 @@ func (response GetNetwork500TextResponse) VisitGetNetworkResponse(w http.Respons return err } +type UpdateNetworkRequestObject struct { + Id openapi_types.UUID `json:"id"` + Body *UpdateNetworkJSONRequestBody +} + +type UpdateNetworkResponseObject interface { + VisitUpdateNetworkResponse(w http.ResponseWriter) error +} + +type UpdateNetwork204Response struct { +} + +func (response UpdateNetwork204Response) VisitUpdateNetworkResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type UpdateNetwork400TextResponse string + +func (response UpdateNetwork400TextResponse) VisitUpdateNetworkResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(400) + + _, err := w.Write([]byte(response)) + return err +} + +type UpdateNetwork401TextResponse string + +func (response UpdateNetwork401TextResponse) VisitUpdateNetworkResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(401) + + _, err := w.Write([]byte(response)) + return err +} + +type UpdateNetwork404TextResponse string + +func (response UpdateNetwork404TextResponse) VisitUpdateNetworkResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(404) + + _, err := w.Write([]byte(response)) + return err +} + +type UpdateNetwork500TextResponse string + +func (response UpdateNetwork500TextResponse) VisitUpdateNetworkResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(500) + + _, err := w.Write([]byte(response)) + return err +} + type GetTiersRequestObject struct { Params GetTiersParams } @@ -3518,6 +3683,63 @@ func (response GetTier500TextResponse) VisitGetTierResponse(w http.ResponseWrite return err } +type UpdateTierRequestObject struct { + Id openapi_types.UUID `json:"id"` + Body *UpdateTierJSONRequestBody +} + +type UpdateTierResponseObject interface { + VisitUpdateTierResponse(w http.ResponseWriter) error +} + +type UpdateTier204Response struct { +} + +func (response UpdateTier204Response) VisitUpdateTierResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type UpdateTier400TextResponse string + +func (response UpdateTier400TextResponse) VisitUpdateTierResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(400) + + _, err := w.Write([]byte(response)) + return err +} + +type UpdateTier401TextResponse string + +func (response UpdateTier401TextResponse) VisitUpdateTierResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(401) + + _, err := w.Write([]byte(response)) + return err +} + +type UpdateTier404TextResponse string + +func (response UpdateTier404TextResponse) VisitUpdateTierResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(404) + + _, err := w.Write([]byte(response)) + return err +} + +type UpdateTier500TextResponse string + +func (response UpdateTier500TextResponse) VisitUpdateTierResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(500) + + _, err := w.Write([]byte(response)) + return err +} + // StrictServerInterface represents all server handlers. type StrictServerInterface interface { // Get all Closures @@ -3604,6 +3826,9 @@ type StrictServerInterface interface { // Returns a Network by ID // (GET /networks/{id}) GetNetwork(ctx context.Context, request GetNetworkRequestObject) (GetNetworkResponseObject, error) + // Update a Network + // (PATCH /networks/{id}) + UpdateNetwork(ctx context.Context, request UpdateNetworkRequestObject) (UpdateNetworkResponseObject, error) // Get all tiers // (GET /tiers) GetTiers(ctx context.Context, request GetTiersRequestObject) (GetTiersResponseObject, error) @@ -3616,6 +3841,9 @@ type StrictServerInterface interface { // Returns a Tier by ID // (GET /tiers/{id}) GetTier(ctx context.Context, request GetTierRequestObject) (GetTierResponseObject, error) + // Update a Tier + // (PATCH /tiers/{id}) + UpdateTier(ctx context.Context, request UpdateTierRequestObject) (UpdateTierResponseObject, error) } type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc @@ -4439,6 +4667,39 @@ func (sh *strictHandler) GetNetwork(w http.ResponseWriter, r *http.Request, id o } } +// UpdateNetwork operation middleware +func (sh *strictHandler) UpdateNetwork(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + var request UpdateNetworkRequestObject + + request.Id = id + + var body UpdateNetworkJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.UpdateNetwork(ctx, request.(UpdateNetworkRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UpdateNetwork") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(UpdateNetworkResponseObject); ok { + if err := validResponse.VisitUpdateNetworkResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // GetTiers operation middleware func (sh *strictHandler) GetTiers(w http.ResponseWriter, r *http.Request, params GetTiersParams) { var request GetTiersRequestObject @@ -4548,69 +4809,106 @@ func (sh *strictHandler) GetTier(w http.ResponseWriter, r *http.Request, id open } } +// UpdateTier operation middleware +func (sh *strictHandler) UpdateTier(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + var request UpdateTierRequestObject + + request.Id = id + + var body UpdateTierJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.UpdateTier(ctx, request.(UpdateTierRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UpdateTier") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(UpdateTierResponseObject); ok { + if err := validResponse.VisitUpdateTierResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/networks.go b/api/networks.go index b5ed12b..a3bf26c 100644 --- a/api/networks.go +++ b/api/networks.go @@ -161,3 +161,78 @@ func (a ApiImpl) DeleteNetwork(ctx context.Context, request DeleteNetworkRequest return DeleteNetwork204Response{}, nil } + +func (a ApiImpl) UpdateNetwork(ctx context.Context, request UpdateNetworkRequestObject) (UpdateNetworkResponseObject, error) { + authData := auth.GetAuthData(ctx) + + if !authData.HasRole(auth.ConsortialAdminRole) { + slog.ErrorContext(ctx, "permission denied") + return UpdateNetwork401TextResponse("Access denied"), nil + } + + if request.Body.Id.IsSpecified() { + return UpdateNetwork400TextResponse("'id' cannot be changed"), nil + } + if request.Body.Consortium.IsSpecified() && request.Body.Consortium.IsNull() { + return UpdateNetwork400TextResponse("'consortium' cannot be set to null"), nil + } + if request.Body.Priority.IsSpecified() && request.Body.Priority.IsNull() { + return UpdateNetwork400TextResponse("'priority' cannot be set to null"), nil + } + + tx, err := a.pool.Begin(ctx) + if err != nil { + slog.ErrorContext(ctx, "failed to begin transaction", "error", err, "operation", "UpdateNetwork") + return UpdateNetwork500TextResponse("Internal server error"), nil + } + defer func() { _ = tx.Rollback(ctx) }() + qtx := a.queries.WithTx(tx) + + orig, err := qtx.GetNetworkByIdForUpdate(ctx, request.Id) + if errors.Is(err, pgx.ErrNoRows) { + return UpdateNetwork404TextResponse("Network not found"), nil + } else if err != nil { + slog.ErrorContext(ctx, "failed to get network for update", "error", err, "id", request.Id) + return UpdateNetwork500TextResponse("Internal server error"), nil + } + + consortiumID := orig.Consortium + if request.Body.Consortium.IsSpecified() { + consortiumID = request.Body.Consortium.MustGet() + } + if consortiumID != orig.Consortium { + consortium, err := qtx.EntryByIdForUpdate(ctx, consortiumID) + if errors.Is(err, pgx.ErrNoRows) { + return UpdateNetwork400TextResponse("Consortium not found"), nil + } else if err != nil { + slog.ErrorContext(ctx, "failed to get consortium", "error", err, "consortium", consortiumID) + return UpdateNetwork500TextResponse("Internal server error"), nil + } + if consortium.Type != string(EntryTypeConsortium) { + return UpdateNetwork400TextResponse("Entry is not a consortium"), nil + } + } + + priority := orig.Priority + if request.Body.Priority.IsSpecified() { + priority = request.Body.Priority.MustGet() + } + + err = qtx.UpdateNetwork(ctx, db.UpdateNetworkParams{ + ID: request.Id, + Consortium: consortiumID, + Name: maybeUpdateCol(orig.Name, request.Body.Name), + Priority: priority, + }) + if err != nil { + slog.ErrorContext(ctx, "failed to update network", "error", err, "id", request.Id) + return UpdateNetwork500TextResponse("Internal server error"), nil + } + + if err := tx.Commit(ctx); err != nil { + slog.ErrorContext(ctx, "failed to commit transaction", "error", err, "operation", "UpdateNetwork") + return UpdateNetwork500TextResponse("Internal server error"), nil + } + + return UpdateNetwork204Response{}, nil +} diff --git a/api/tiers.go b/api/tiers.go index 95dc78a..766eb53 100644 --- a/api/tiers.go +++ b/api/tiers.go @@ -186,3 +186,106 @@ func (a ApiImpl) DeleteTier(ctx context.Context, request DeleteTierRequestObject return DeleteTier204Response{}, nil } + +func (a ApiImpl) UpdateTier(ctx context.Context, request UpdateTierRequestObject) (UpdateTierResponseObject, error) { + authData := auth.GetAuthData(ctx) + + if !authData.HasRole(auth.ConsortialAdminRole) { + slog.ErrorContext(ctx, "permission denied") + return UpdateTier401TextResponse("Access denied"), nil + } + + if request.Body.Id.IsSpecified() { + return UpdateTier400TextResponse("'id' cannot be changed"), nil + } + if request.Body.Consortium.IsSpecified() && request.Body.Consortium.IsNull() { + return UpdateTier400TextResponse("'consortium' cannot be set to null"), nil + } + if request.Body.Level.IsSpecified() && request.Body.Level.IsNull() { + return UpdateTier400TextResponse("'level' cannot be set to null"), nil + } + if request.Body.Type.IsSpecified() && request.Body.Type.IsNull() { + return UpdateTier400TextResponse("'type' cannot be set to null"), nil + } + if request.Body.Cost.IsSpecified() && request.Body.Cost.IsNull() { + return UpdateTier400TextResponse("'cost' cannot be set to null"), nil + } + + tx, err := a.pool.Begin(ctx) + if err != nil { + slog.ErrorContext(ctx, "failed to begin transaction", "error", err, "operation", "UpdateTier") + return UpdateTier500TextResponse("Internal server error"), nil + } + defer func() { _ = tx.Rollback(ctx) }() + qtx := a.queries.WithTx(tx) + + orig, err := qtx.GetTierByIdForUpdate(ctx, request.Id) + if errors.Is(err, pgx.ErrNoRows) { + return UpdateTier404TextResponse("Tier not found"), nil + } else if err != nil { + slog.ErrorContext(ctx, "failed to get tier for update", "error", err, "id", request.Id) + return UpdateTier500TextResponse("Internal server error"), nil + } + + consortiumID := orig.Consortium + if request.Body.Consortium.IsSpecified() { + consortiumID = request.Body.Consortium.MustGet() + } + if consortiumID != orig.Consortium { + consortium, err := qtx.EntryByIdForUpdate(ctx, consortiumID) + if errors.Is(err, pgx.ErrNoRows) { + return UpdateTier400TextResponse("Consortium not found"), nil + } else if err != nil { + slog.ErrorContext(ctx, "failed to get consortium", "error", err, "consortium", consortiumID) + return UpdateTier500TextResponse("Internal server error"), nil + } + if consortium.Type != string(EntryTypeConsortium) { + return UpdateTier400TextResponse("Entry is not a consortium"), nil + } + } + + level := orig.Level + if request.Body.Level.IsSpecified() { + level = string(request.Body.Level.MustGet()) + } + switch TierLevel(level) { + case Express, Normal, Rush, Secondarymail, Standard, Urgent: + default: + return UpdateTier400TextResponse("Invalid tier level"), nil + } + + tierType := orig.Type + if request.Body.Type.IsSpecified() { + tierType = string(request.Body.Type.MustGet()) + } + switch TierType(tierType) { + case Loan, Copy: + default: + return UpdateTier400TextResponse("Invalid tier type"), nil + } + + cost := orig.Cost + if request.Body.Cost.IsSpecified() { + cost = request.Body.Cost.MustGet() + } + + err = qtx.UpdateTier(ctx, db.UpdateTierParams{ + ID: request.Id, + Consortium: consortiumID, + Name: maybeUpdateCol(orig.Name, request.Body.Name), + Level: level, + Type: tierType, + Cost: cost, + }) + if err != nil { + slog.ErrorContext(ctx, "failed to update tier", "error", err, "id", request.Id) + return UpdateTier500TextResponse("Internal server error"), nil + } + + if err := tx.Commit(ctx); err != nil { + slog.ErrorContext(ctx, "failed to commit transaction", "error", err, "operation", "UpdateTier") + return UpdateTier500TextResponse("Internal server error"), nil + } + + return UpdateTier204Response{}, nil +} diff --git a/db/query.sql.go b/db/query.sql.go index fdbbe63..ae59839 100644 --- a/db/query.sql.go +++ b/db/query.sql.go @@ -707,6 +707,22 @@ func (q *Queries) GetNetworkById(ctx context.Context, id uuid.UUID) (Network, er return i, err } +const getNetworkByIdForUpdate = `-- name: GetNetworkByIdForUpdate :one +SELECT id, consortium, name, priority FROM networks WHERE id = $1 LIMIT 1 FOR UPDATE +` + +func (q *Queries) GetNetworkByIdForUpdate(ctx context.Context, id uuid.UUID) (Network, error) { + row := q.db.QueryRow(ctx, getNetworkByIdForUpdate, id) + var i Network + err := row.Scan( + &i.ID, + &i.Consortium, + &i.Name, + &i.Priority, + ) + return i, err +} + const getTierById = `-- name: GetTierById :one SELECT id, consortium, name, level, type, cost FROM tiers WHERE id = $1 LIMIT 1 ` @@ -725,6 +741,24 @@ func (q *Queries) GetTierById(ctx context.Context, id uuid.UUID) (Tier, error) { return i, err } +const getTierByIdForUpdate = `-- name: GetTierByIdForUpdate :one +SELECT id, consortium, name, level, type, cost FROM tiers WHERE id = $1 LIMIT 1 FOR UPDATE +` + +func (q *Queries) GetTierByIdForUpdate(ctx context.Context, id uuid.UUID) (Tier, error) { + row := q.db.QueryRow(ctx, getTierByIdForUpdate, id) + var i Tier + err := row.Scan( + &i.ID, + &i.Consortium, + &i.Name, + &i.Level, + &i.Type, + &i.Cost, + ) + return i, err +} + const listClosures = `-- name: ListClosures :many SELECT id, entry, start_date, end_date, reason FROM closures LIMIT $2 @@ -1173,6 +1207,64 @@ func (q *Queries) UpdateEntry(ctx context.Context, arg UpdateEntryParams) error return err } +const updateNetwork = `-- name: UpdateNetwork :exec +UPDATE networks +SET + consortium = $1, + name = $2, + priority = $3 +WHERE id = $4 +` + +type UpdateNetworkParams struct { + Consortium uuid.UUID + Name *string + Priority float64 + ID uuid.UUID +} + +func (q *Queries) UpdateNetwork(ctx context.Context, arg UpdateNetworkParams) error { + _, err := q.db.Exec(ctx, updateNetwork, + arg.Consortium, + arg.Name, + arg.Priority, + arg.ID, + ) + return err +} + +const updateTier = `-- name: UpdateTier :exec +UPDATE tiers +SET + consortium = $1, + name = $2, + level = $3, + type = $4, + cost = $5 +WHERE id = $6 +` + +type UpdateTierParams struct { + Consortium uuid.UUID + Name *string + Level string + Type string + Cost float64 + ID uuid.UUID +} + +func (q *Queries) UpdateTier(ctx context.Context, arg UpdateTierParams) error { + _, err := q.db.Exec(ctx, updateTier, + arg.Consortium, + arg.Name, + arg.Level, + arg.Type, + arg.Cost, + arg.ID, + ) + return err +} + const upsertAddress = `-- name: UpsertAddress :one INSERT INTO addresses ( id, entry, type diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 4624c60..6911352 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -166,6 +166,17 @@ "directory.public.all" ] }, + { + "methods": ["PATCH"], + "pathPattern": "/rsdir/tiers/{id}", + "permissionsRequired" : [], + "permissionsDesired": [ + "directory.consortium.all", + "directory.institution.all", + "directory.system.all", + "directory.public.all" + ] + }, { "methods": ["DELETE"], "pathPattern": "/rsdir/tiers/{id}", @@ -216,6 +227,17 @@ "directory.public.all" ] }, + { + "methods": ["PATCH"], + "pathPattern": "/rsdir/networks/{id}", + "permissionsRequired" : [], + "permissionsDesired": [ + "directory.consortium.all", + "directory.institution.all", + "directory.system.all", + "directory.public.all" + ] + }, { "methods": ["DELETE"], "pathPattern": "/rsdir/networks/{id}", diff --git a/query.sql b/query.sql index f0f93f9..569c656 100644 --- a/query.sql +++ b/query.sql @@ -153,6 +153,24 @@ INSERT INTO networks ( ) RETURNING *; +-- name: UpdateNetwork :exec +UPDATE networks +SET + consortium = @consortium, + name = @name, + priority = @priority +WHERE id = @id; + +-- name: UpdateTier :exec +UPDATE tiers +SET + consortium = @consortium, + name = @name, + level = @level, + type = @type, + cost = @cost +WHERE id = @id; + -- name: CreateEntryNetwork :one INSERT INTO entry_networks ( @@ -233,9 +251,15 @@ RETURNING *; -- name: GetNetworkById :one SELECT * FROM networks WHERE id = $1 LIMIT 1; +-- name: GetNetworkByIdForUpdate :one +SELECT * FROM networks WHERE id = $1 LIMIT 1 FOR UPDATE; + -- name: GetTierById :one SELECT * FROM tiers WHERE id = $1 LIMIT 1; +-- name: GetTierByIdForUpdate :one +SELECT * FROM tiers WHERE id = $1 LIMIT 1 FOR UPDATE; + -- name: GetEntryNetworkById :one SELECT * FROM entry_networks WHERE id = $1 LIMIT 1; diff --git a/test/apifixtures/network.patch.refetch.json b/test/apifixtures/network.patch.refetch.json new file mode 100644 index 0000000..61420f2 --- /dev/null +++ b/test/apifixtures/network.patch.refetch.json @@ -0,0 +1,5 @@ +{ + "id": "20000000-0000-0000-0000-000000000001", + "consortium": "00000000-0000-0000-0000-000000000004", + "priority": 1.5 +} diff --git a/test/apifixtures/network.patch.req.json b/test/apifixtures/network.patch.req.json new file mode 100644 index 0000000..b2b8408 --- /dev/null +++ b/test/apifixtures/network.patch.req.json @@ -0,0 +1,4 @@ +{ + "name": null, + "priority": 1.5 +} diff --git a/test/apifixtures/tier.patch.refetch.json b/test/apifixtures/tier.patch.refetch.json new file mode 100644 index 0000000..db5e7d5 --- /dev/null +++ b/test/apifixtures/tier.patch.refetch.json @@ -0,0 +1,7 @@ +{ + "id": "30000000-0000-0000-0000-000000000001", + "consortium": "00000000-0000-0000-0000-000000000004", + "level": "urgent", + "type": "copy", + "cost": 1.5 +} diff --git a/test/apifixtures/tier.patch.req.json b/test/apifixtures/tier.patch.req.json new file mode 100644 index 0000000..dce540c --- /dev/null +++ b/test/apifixtures/tier.patch.req.json @@ -0,0 +1,6 @@ +{ + "name": null, + "level": "urgent", + "type": "copy", + "cost": 1.5 +} diff --git a/test/networks_test.go b/test/networks_test.go index 0bb2e6a..31229a8 100644 --- a/test/networks_test.go +++ b/test/networks_test.go @@ -59,6 +59,72 @@ func TestNetworkCases(t *testing.T) { body: `{"name":"Institution Network","consortium":"00000000-0000-0000-0000-000000000002"}`, addlHeaders: consortiumPermissionHeaders, }, + { + name: "PATCH network", + method: http.MethodPatch, + endpoint: "/networks/20000000-0000-0000-0000-000000000001", + status: http.StatusNoContent, + bodyFile: "network.patch.req.json", + refetchFile: "network.patch.refetch.json", + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH network cannot change id", + method: http.MethodPatch, + endpoint: "/networks/20000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"id":"20000000-0000-0000-0000-000000000003","priority":99}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH network with no fields", + method: http.MethodPatch, + endpoint: "/networks/20000000-0000-0000-0000-000000000001", + status: http.StatusNoContent, + body: `{}`, + refetchFile: "network.get.res.json", + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH network rejects null consortium", + method: http.MethodPatch, + endpoint: "/networks/20000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"consortium":null}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH network rejects null priority", + method: http.MethodPatch, + endpoint: "/networks/20000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"priority":null}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH network rejects non-consortium entry", + method: http.MethodPatch, + endpoint: "/networks/20000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"consortium":"00000000-0000-0000-0000-000000000002"}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH network not found", + method: http.MethodPatch, + endpoint: "/networks/20000000-0000-0000-0000-000000000099", + status: http.StatusNotFound, + body: `{}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH network requires consortial admin", + method: http.MethodPatch, + endpoint: "/networks/20000000-0000-0000-0000-000000000001", + status: http.StatusUnauthorized, + body: `{"priority":2}`, + addlHeaders: institutionPermissionHeaders, + }, { name: "DELETE network", method: http.MethodDelete, diff --git a/test/tiers_test.go b/test/tiers_test.go index f0a9209..804d21c 100644 --- a/test/tiers_test.go +++ b/test/tiers_test.go @@ -59,6 +59,104 @@ func TestTierCases(t *testing.T) { body: `{"name":"Institution Tier","consortium":"00000000-0000-0000-0000-000000000002","level":"standard","type":"loan"}`, addlHeaders: consortiumPermissionHeaders, }, + { + name: "PATCH tier", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusNoContent, + bodyFile: "tier.patch.req.json", + refetchFile: "tier.patch.refetch.json", + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier cannot change id", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"id":"30000000-0000-0000-0000-000000000003","cost":99}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier with no fields", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusNoContent, + body: `{}`, + refetchFile: "tier.get.res.json", + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier rejects null consortium", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"consortium":null}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier rejects null level", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"level":null}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier rejects null type", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"type":null}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier rejects null cost", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"cost":null}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier rejects non-consortium entry", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"consortium":"00000000-0000-0000-0000-000000000002"}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier rejects invalid level", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"level":"overnight"}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier rejects invalid type", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusBadRequest, + body: `{"type":"returnable"}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier not found", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000099", + status: http.StatusNotFound, + body: `{}`, + addlHeaders: consortiumPermissionHeaders, + }, + { + name: "PATCH tier requires consortial admin", + method: http.MethodPatch, + endpoint: "/tiers/30000000-0000-0000-0000-000000000001", + status: http.StatusUnauthorized, + body: `{"cost":2}`, + addlHeaders: institutionPermissionHeaders, + }, { name: "DELETE tier", method: http.MethodDelete,