From 8dfaa78a834622ff753b68be6409d253d67dde88 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Tue, 13 May 2025 17:48:02 +0200 Subject: [PATCH] feat: add editMetaByRequest, specifies job by parameters instead of dbid --- api/swagger.json | 96 ++++++++++++++++++++++++++++++++++++++++++- api/swagger.yaml | 65 ++++++++++++++++++++++++++++- internal/api/docs.go | 96 ++++++++++++++++++++++++++++++++++++++++++- internal/api/rest.go | 69 ++++++++++++++++++++++++++++--- internal/auth/auth.go | 1 + 5 files changed, 318 insertions(+), 9 deletions(-) diff --git a/api/swagger.json b/api/swagger.json index c05ec77..c32678e 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -383,8 +383,71 @@ } } }, + "/api/jobs/edit_meta/": { + "patch": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Edit key value pairs in metadata json of job specified by jobID, StartTime and Cluster\nIf a key already exists its content will be overwritten", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Job add and modify" + ], + "summary": "Edit meta-data json by request", + "parameters": [ + { + "description": "Specifies job and payload to add or update", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.JobMetaRequest" + } + } + ], + "responses": { + "200": { + "description": "Updated job resource", + "schema": { + "$ref": "#/definitions/schema.Job" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "404": { + "description": "Job does not exist", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + } + }, "/api/jobs/edit_meta/{id}": { - "post": { + "patch": { "security": [ { "ApiKeyAuth": [] @@ -1177,6 +1240,37 @@ } } }, + "api.JobMetaRequest": { + "type": "object", + "required": [ + "jobId" + ], + "properties": { + "cluster": { + "description": "Cluster of job", + "type": "string", + "example": "fritz" + }, + "jobId": { + "description": "Cluster Job ID of job", + "type": "integer", + "example": 123000 + }, + "payload": { + "description": "Content to Add to Job Meta_Data", + "allOf": [ + { + "$ref": "#/definitions/api.EditMetaRequest" + } + ] + }, + "startTime": { + "description": "Start Time of job as epoch", + "type": "integer", + "example": 1649723812 + } + } + }, "api.JobMetricWithName": { "type": "object", "properties": { diff --git a/api/swagger.yaml b/api/swagger.yaml index 26210be..96ad325 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -102,6 +102,27 @@ definitions: description: Page id returned type: integer type: object + api.JobMetaRequest: + properties: + cluster: + description: Cluster of job + example: fritz + type: string + jobId: + description: Cluster Job ID of job + example: 123000 + type: integer + payload: + allOf: + - $ref: '#/definitions/api.EditMetaRequest' + description: Content to Add to Job Meta_Data + startTime: + description: Start Time of job as epoch + example: 1649723812 + type: integer + required: + - jobId + type: object api.JobMetricWithName: properties: metric: @@ -1025,8 +1046,50 @@ paths: summary: Remove a job from the sql database tags: - Job remove + /api/jobs/edit_meta/: + patch: + consumes: + - application/json + description: |- + Edit key value pairs in metadata json of job specified by jobID, StartTime and Cluster + If a key already exists its content will be overwritten + parameters: + - description: Specifies job and payload to add or update + in: body + name: request + required: true + schema: + $ref: '#/definitions/api.JobMetaRequest' + produces: + - application/json + responses: + "200": + description: Updated job resource + schema: + $ref: '#/definitions/schema.Job' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/api.ErrorResponse' + "404": + description: Job does not exist + schema: + $ref: '#/definitions/api.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Edit meta-data json by request + tags: + - Job add and modify /api/jobs/edit_meta/{id}: - post: + patch: consumes: - application/json description: |- diff --git a/internal/api/docs.go b/internal/api/docs.go index c1cd391..567789c 100644 --- a/internal/api/docs.go +++ b/internal/api/docs.go @@ -390,8 +390,71 @@ const docTemplate = `{ } } }, + "/api/jobs/edit_meta/": { + "patch": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Edit key value pairs in metadata json of job specified by jobID, StartTime and Cluster\nIf a key already exists its content will be overwritten", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Job add and modify" + ], + "summary": "Edit meta-data json by request", + "parameters": [ + { + "description": "Specifies job and payload to add or update", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.JobMetaRequest" + } + } + ], + "responses": { + "200": { + "description": "Updated job resource", + "schema": { + "$ref": "#/definitions/schema.Job" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "404": { + "description": "Job does not exist", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + } + }, "/api/jobs/edit_meta/{id}": { - "post": { + "patch": { "security": [ { "ApiKeyAuth": [] @@ -1184,6 +1247,37 @@ const docTemplate = `{ } } }, + "api.JobMetaRequest": { + "type": "object", + "required": [ + "jobId" + ], + "properties": { + "cluster": { + "description": "Cluster of job", + "type": "string", + "example": "fritz" + }, + "jobId": { + "description": "Cluster Job ID of job", + "type": "integer", + "example": 123000 + }, + "payload": { + "description": "Content to Add to Job Meta_Data", + "allOf": [ + { + "$ref": "#/definitions/api.EditMetaRequest" + } + ] + }, + "startTime": { + "description": "Start Time of job as epoch", + "type": "integer", + "example": 1649723812 + } + } + }, "api.JobMetricWithName": { "type": "object", "properties": { diff --git a/internal/api/rest.go b/internal/api/rest.go index 669768e..23ff618 100644 --- a/internal/api/rest.go +++ b/internal/api/rest.go @@ -82,7 +82,8 @@ func (api *RestApi) MountApiRoutes(r *mux.Router) { r.HandleFunc("/jobs/{id}", api.getCompleteJobById).Methods(http.MethodGet) r.HandleFunc("/jobs/tag_job/{id}", api.tagJob).Methods(http.MethodPost, http.MethodPatch) r.HandleFunc("/jobs/tag_job/{id}", api.removeTagJob).Methods(http.MethodDelete) - r.HandleFunc("/jobs/edit_meta/{id}", api.editMeta).Methods(http.MethodPost, http.MethodPatch) + r.HandleFunc("/jobs/edit_meta/", api.editMetaByRequest).Methods(http.MethodPatch) + r.HandleFunc("/jobs/edit_meta/{id}", api.editMeta).Methods(http.MethodPatch) r.HandleFunc("/jobs/metrics/{id}", api.getJobMetrics).Methods(http.MethodGet) r.HandleFunc("/jobs/delete_job/", api.deleteJobByRequest).Methods(http.MethodDelete) r.HandleFunc("/jobs/delete_job/{id}", api.deleteJobById).Methods(http.MethodDelete) @@ -98,7 +99,7 @@ func (api *RestApi) MountApiRoutes(r *mux.Router) { func (api *RestApi) MountUserApiRoutes(r *mux.Router) { r.StrictSlash(true) - // REST API Uses TokenAuth + // USER REST API Uses TokenAuth r.HandleFunc("/jobs/", api.getJobs).Methods(http.MethodGet) r.HandleFunc("/jobs/{id}", api.getJobById).Methods(http.MethodPost) r.HandleFunc("/jobs/{id}", api.getCompleteJobById).Methods(http.MethodGet) @@ -181,6 +182,14 @@ type EditMetaRequest struct { Value string `json:"value" example:"bash script"` } +// JobMetaRequest model +type JobMetaRequest struct { + JobId *int64 `json:"jobId" validate:"required" example:"123000"` // Cluster Job ID of job + Cluster *string `json:"cluster" example:"fritz"` // Cluster of job + StartTime *int64 `json:"startTime" example:"1649723812"` // Start Time of job as epoch + Payload EditMetaRequest `json:"payload"` // Content to Add to Job Meta_Data +} + type TagJobApiRequest []*ApiTag type GetJobApiRequest []string @@ -609,21 +618,21 @@ func (api *RestApi) getJobById(rw http.ResponseWriter, r *http.Request) { } // editMeta godoc -// @summary Edit meta-data json +// @summary Edit meta-data json of job identified by database id // @tags Job add and modify -// @description Edit key value pairs in job metadata json +// @description Edit key value pairs in job metadata json of job specified by database id // @description If a key already exists its content will be overwritten // @accept json // @produce json // @param id path int true "Job Database ID" -// @param request body api.EditMetaRequest true "Kay value pair to add" +// @param request body api.EditMetaRequest true "Metadata Key value pair to add or update" // @success 200 {object} schema.Job "Updated job resource" // @failure 400 {object} api.ErrorResponse "Bad Request" // @failure 401 {object} api.ErrorResponse "Unauthorized" // @failure 404 {object} api.ErrorResponse "Job does not exist" // @failure 500 {object} api.ErrorResponse "Internal Server Error" // @security ApiKeyAuth -// @router /api/jobs/edit_meta/{id} [post] +// @router /api/jobs/edit_meta/{id} [patch] func (api *RestApi) editMeta(rw http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64) if err != nil { @@ -653,6 +662,54 @@ func (api *RestApi) editMeta(rw http.ResponseWriter, r *http.Request) { json.NewEncoder(rw).Encode(job) } +// editMetaByRequest godoc +// @summary Edit meta-data json of job identified by request +// @tags Job add and modify +// @description Edit key value pairs in metadata json of job specified by jobID, StartTime and Cluster +// @description If a key already exists its content will be overwritten +// @accept json +// @produce json +// @param request body api.JobMetaRequest true "Specifies job and payload to add or update" +// @success 200 {object} schema.Job "Updated job resource" +// @failure 400 {object} api.ErrorResponse "Bad Request" +// @failure 401 {object} api.ErrorResponse "Unauthorized" +// @failure 404 {object} api.ErrorResponse "Job does not exist" +// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @security ApiKeyAuth +// @router /api/jobs/edit_meta/ [patch] +func (api *RestApi) editMetaByRequest(rw http.ResponseWriter, r *http.Request) { + // Parse request body + req := JobMetaRequest{} + if err := decode(r.Body, &req); err != nil { + handleError(fmt.Errorf("parsing request body failed: %w", err), http.StatusBadRequest, rw) + return + } + + // Fetch job (that will have its meta_data edited) from db + var job *schema.Job + var err error + if req.JobId == nil { + handleError(errors.New("the field 'jobId' is required"), http.StatusBadRequest, rw) + return + } + + // log.Printf("loading db job for editMetaByRequest... : JobMetaRequest=%v", req) + job, err = api.JobRepository.Find(req.JobId, req.Cluster, req.StartTime) + if err != nil { + handleError(fmt.Errorf("finding job failed: %w", err), http.StatusUnprocessableEntity, rw) + return + } + + if err := api.JobRepository.UpdateMetadata(job, req.Payload.Key, req.Payload.Value); err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + rw.Header().Add("Content-Type", "application/json") + rw.WriteHeader(http.StatusOK) + json.NewEncoder(rw).Encode(job) +} + // tagJob godoc // @summary Adds one or more tags to a job // @tags Job add and modify diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 5f88bbb..a4d4c9f 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -473,6 +473,7 @@ func securedCheck(user *schema.User, r *http.Request) error { IPAddress = r.RemoteAddr } + // Cannot Handle ipv6! (e.g. localhost -> [::1]) if strings.Contains(IPAddress, ":") { IPAddress = strings.Split(IPAddress, ":")[0] }