diff --git a/momentum-core/.gitignore b/momentum-core/.gitignore index 4993363..3484ca2 100644 --- a/momentum-core/.gitignore +++ b/momentum-core/.gitignore @@ -1,2 +1,2 @@ -dev-setup.sh +./dev-setup.sh momentum-core \ No newline at end of file diff --git a/momentum-core/config/config.go b/momentum-core/config/config.go index ecd528b..bef810d 100644 --- a/momentum-core/config/config.go +++ b/momentum-core/config/config.go @@ -99,6 +99,10 @@ func (m *MomentumConfig) checkMandatoryTemplates() error { func (m *MomentumConfig) initializeGitAccessToken() error { + if TRANSACTION_MODE == gittransaction.DEBUG { + return nil + } + m.transactionToken = new(gittransaction.Token) m.transactionToken.Username = os.Getenv(MOMENTUM_GIT_USER) diff --git a/momentum-core/dispatcher.go b/momentum-core/dispatcher.go index dd5e9be..dbe2771 100644 --- a/momentum-core/dispatcher.go +++ b/momentum-core/dispatcher.go @@ -47,6 +47,7 @@ func NewDispatcher(config *config.MomentumConfig, dispatcher.applicationRouter.RegisterApplicationRoutes(dispatcher.server) dispatcher.stageRouter.RegisterStageRoutes(dispatcher.server) dispatcher.deploymentRouter.RegisterDeploymentRoutes(dispatcher.server) + dispatcher.valueRouter.RegisterValueRoutes(dispatcher.server) dispatcher.server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/momentum-core/docs/docs.go b/momentum-core/docs/docs.go index 5f98176..4e66efb 100644 --- a/momentum-core/docs/docs.go +++ b/momentum-core/docs/docs.go @@ -367,6 +367,62 @@ const docTemplate = `{ } } }, + "/repository/{repositoryName}/application/values/{applicationId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "values" + ], + "summary": "get all values of an application by the applications id", + "parameters": [ + { + "type": "string", + "description": "Repository Name", + "name": "repositoryName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Application ID", + "name": "applicationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ValueWrapper" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + } + } + } + }, "/repository/{repositoryName}/applications": { "get": { "consumes": [ @@ -419,6 +475,62 @@ const docTemplate = `{ } } }, + "/repository/{repositoryName}/deployment/values/{deploymentId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "values" + ], + "summary": "get all values of a deployment by the deployments id", + "parameters": [ + { + "type": "string", + "description": "Repository Name", + "name": "repositoryName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Deployment ID", + "name": "deploymentId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ValueWrapper" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + } + } + } + }, "/repository/{repositoryName}/deployments": { "get": { "produces": [ @@ -468,6 +580,62 @@ const docTemplate = `{ } } }, + "/repository/{repositoryName}/stage/values/{stageId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "values" + ], + "summary": "get all values of an stage by the stages id", + "parameters": [ + { + "type": "string", + "description": "Repository Name", + "name": "repositoryName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Stage ID", + "name": "stageId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ValueWrapper" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + } + } + } + }, "/repository/{repositoryName}/stages": { "get": { "produces": [ @@ -517,6 +685,59 @@ const docTemplate = `{ } } }, + "/repository/{repositoryName}/value/{valueId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "values" + ], + "summary": "get a value of a repository by id", + "parameters": [ + { + "type": "string", + "description": "Repository Name", + "name": "repositoryName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Value ID", + "name": "valueId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Value" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + } + } + } + }, "/repository/{repositoryName}/{applicationId}": { "get": { "produces": [ @@ -776,6 +997,64 @@ const docTemplate = `{ "type": "string" } } + }, + "models.Value": { + "type": "object", + "properties": { + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "key": { + "type": "string" + }, + "parentDeploymentId": { + "type": "string" + }, + "parentStageId": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "models.ValueType": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "REPOSITORY", + "APPLICATION", + "STAGE", + "DEPLOYMENT" + ] + }, + "models.ValueWrapper": { + "type": "object", + "properties": { + "parentFileId": { + "type": "string" + }, + "parentFileName": { + "type": "string" + }, + "valueType": { + "$ref": "#/definitions/models.ValueType" + }, + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Value" + } + } + } } } }` diff --git a/momentum-core/docs/swagger.json b/momentum-core/docs/swagger.json index ca07c93..7836826 100644 --- a/momentum-core/docs/swagger.json +++ b/momentum-core/docs/swagger.json @@ -364,6 +364,62 @@ } } }, + "/repository/{repositoryName}/application/values/{applicationId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "values" + ], + "summary": "get all values of an application by the applications id", + "parameters": [ + { + "type": "string", + "description": "Repository Name", + "name": "repositoryName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Application ID", + "name": "applicationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ValueWrapper" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + } + } + } + }, "/repository/{repositoryName}/applications": { "get": { "consumes": [ @@ -416,6 +472,62 @@ } } }, + "/repository/{repositoryName}/deployment/values/{deploymentId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "values" + ], + "summary": "get all values of a deployment by the deployments id", + "parameters": [ + { + "type": "string", + "description": "Repository Name", + "name": "repositoryName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Deployment ID", + "name": "deploymentId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ValueWrapper" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + } + } + } + }, "/repository/{repositoryName}/deployments": { "get": { "produces": [ @@ -465,6 +577,62 @@ } } }, + "/repository/{repositoryName}/stage/values/{stageId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "values" + ], + "summary": "get all values of an stage by the stages id", + "parameters": [ + { + "type": "string", + "description": "Repository Name", + "name": "repositoryName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Stage ID", + "name": "stageId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ValueWrapper" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + } + } + } + }, "/repository/{repositoryName}/stages": { "get": { "produces": [ @@ -514,6 +682,59 @@ } } }, + "/repository/{repositoryName}/value/{valueId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "values" + ], + "summary": "get a value of a repository by id", + "parameters": [ + { + "type": "string", + "description": "Repository Name", + "name": "repositoryName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Value ID", + "name": "valueId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Value" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ApiError" + } + } + } + } + }, "/repository/{repositoryName}/{applicationId}": { "get": { "produces": [ @@ -773,6 +994,64 @@ "type": "string" } } + }, + "models.Value": { + "type": "object", + "properties": { + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "key": { + "type": "string" + }, + "parentDeploymentId": { + "type": "string" + }, + "parentStageId": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "models.ValueType": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "REPOSITORY", + "APPLICATION", + "STAGE", + "DEPLOYMENT" + ] + }, + "models.ValueWrapper": { + "type": "object", + "properties": { + "parentFileId": { + "type": "string" + }, + "parentFileName": { + "type": "string" + }, + "valueType": { + "$ref": "#/definitions/models.ValueType" + }, + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Value" + } + } + } } } } \ No newline at end of file diff --git a/momentum-core/docs/swagger.yaml b/momentum-core/docs/swagger.yaml index 64b93ae..04d9d25 100644 --- a/momentum-core/docs/swagger.yaml +++ b/momentum-core/docs/swagger.yaml @@ -100,6 +100,46 @@ definitions: repositoryName: type: string type: object + models.Value: + properties: + displayName: + type: string + id: + type: string + key: + type: string + parentDeploymentId: + type: string + parentStageId: + type: string + value: + type: string + type: object + models.ValueType: + enum: + - 0 + - 1 + - 2 + - 3 + type: integer + x-enum-varnames: + - REPOSITORY + - APPLICATION + - STAGE + - DEPLOYMENT + models.ValueWrapper: + properties: + parentFileId: + type: string + parentFileName: + type: string + valueType: + $ref: '#/definitions/models.ValueType' + values: + items: + $ref: '#/definitions/models.Value' + type: array + type: object host: localhost:8080 info: contact: {} @@ -372,6 +412,43 @@ paths: summary: get a deployment of a repository by id tags: - deployments + /repository/{repositoryName}/application/values/{applicationId}: + get: + parameters: + - description: Repository Name + in: path + name: repositoryName + required: true + type: string + - description: Application ID + in: path + name: applicationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.ValueWrapper' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ApiError' + summary: get all values of an application by the applications id + tags: + - values /repository/{repositoryName}/applications: get: consumes: @@ -406,6 +483,43 @@ paths: summary: get all applications of a repository tags: - applications + /repository/{repositoryName}/deployment/values/{deploymentId}: + get: + parameters: + - description: Repository Name + in: path + name: repositoryName + required: true + type: string + - description: Deployment ID + in: path + name: deploymentId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.ValueWrapper' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ApiError' + summary: get all values of a deployment by the deployments id + tags: + - values /repository/{repositoryName}/deployments: get: parameters: @@ -438,6 +552,43 @@ paths: summary: get deployments tags: - deployments + /repository/{repositoryName}/stage/values/{stageId}: + get: + parameters: + - description: Repository Name + in: path + name: repositoryName + required: true + type: string + - description: Stage ID + in: path + name: stageId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.ValueWrapper' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ApiError' + summary: get all values of an stage by the stages id + tags: + - values /repository/{repositoryName}/stages: get: parameters: @@ -470,6 +621,41 @@ paths: summary: get stages tags: - stages + /repository/{repositoryName}/value/{valueId}: + get: + parameters: + - description: Repository Name + in: path + name: repositoryName + required: true + type: string + - description: Value ID + in: path + name: valueId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.Value' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ApiError' + summary: get a value of a repository by id + tags: + - values /stage: post: consumes: diff --git a/momentum-core/main.go b/momentum-core/main.go index 3fa959c..28ca829 100644 --- a/momentum-core/main.go +++ b/momentum-core/main.go @@ -38,10 +38,10 @@ func main() { applicationService := services.NewApplicationService(config, treeService, templateService) stageService := services.NewStageService(config, treeService, templateService) deploymentService := services.NewDeploymentService(config, stageService, templateService, treeService) - // valueService := services.NewValueService() + valueService := services.NewValueService(treeService) templateRouter := routers.NewTemplateRouter() - valueRouter := routers.NewValueRouter() + valueRouter := routers.NewValueRouter(valueService) deploymentRouter := routers.NewDeploymentRouter(deploymentService, repositoryService, config) stageRouter := routers.NewStageRouter(stageService, repositoryService, config) applicationRouter := routers.NewApplicationRouter(applicationService, repositoryService, config) diff --git a/momentum-core/models/keyvalue.go b/momentum-core/models/keyvalue.go index 0c9e8fa..1f5a011 100644 --- a/momentum-core/models/keyvalue.go +++ b/momentum-core/models/keyvalue.go @@ -1,6 +1,7 @@ package models import ( + "errors" "momentum-core/clients" "momentum-core/tree" "momentum-core/utils" @@ -8,6 +9,15 @@ import ( "github.com/gin-gonic/gin" ) +type ValueType int + +const ( + REPOSITORY ValueType = iota + APPLICATION + STAGE + DEPLOYMENT +) + type KeyValueCreateRequest struct { Key string `json:"key"` Value string `json:"value"` @@ -17,7 +27,7 @@ type KeyValueCreateRequest struct { ParentKeyValueId string `json:"parentKeyValueId"` } -type KeyValue struct { +type Value struct { Id string `json:"id"` Key string `json:"key"` Value string `json:"value"` @@ -27,24 +37,60 @@ type KeyValue struct { ParentDeploymentId string `json:"parentDeploymentId"` } +type ValueWrapper struct { + FileId string `json:"parentFileId"` + FileName string `json:"parentFileName"` + ValueType ValueType `json:"valueType"` + Values []*Value `json:"values"` +} + func ExtractKeyValueCreateRequest(c *gin.Context) (*KeyValueCreateRequest, error) { return utils.Extract[KeyValueCreateRequest](c) } -func ExtractKeyValue(c *gin.Context) (*KeyValue, error) { - return utils.Extract[KeyValue](c) +func ExtractKeyValue(c *gin.Context) (*Value, error) { + return utils.Extract[Value](c) } -func ToKeyValueFromNode(n *tree.Node) (*KeyValue, error) { +func ToValueWrapperFromNode(n *tree.Node, valType ValueType) (*ValueWrapper, error) { - keyValue := new(KeyValue) + if n.Kind != tree.File { + return nil, errors.New("only files can be converted to a value wrapper") + } + + valueWrapper := new(ValueWrapper) + + valueWrapper.FileId = n.Id + valueWrapper.FileName = n.NormalizedPath() + mappedValues := make([]*Value, 0) + for _, v := range n.Values() { + value, err := ToValueFromNode(v) + if err != nil { + return nil, errors.New("unable to map value from node") + } + mappedValues = append(mappedValues, value) + } + valueWrapper.Values = mappedValues + valueWrapper.ValueType = valType + + return valueWrapper, nil +} + +func ToValueFromNode(n *tree.Node) (*Value, error) { + + if n == nil { + return nil, errors.New("expected a tree node but was nil") + } + + keyValue := new(Value) keyValue.Id = n.Id keyValue.Key = n.FullPath() keyValue.Value = n.Value parentStage := n - for !parentStage.IsStage() { + + for !parentStage.IsStage() && parentStage.Parent != nil { if parentStage.Kind == tree.File { keyValue.ParentDeploymentId = parentStage.Id } @@ -57,16 +103,16 @@ func ToKeyValueFromNode(n *tree.Node) (*KeyValue, error) { return keyValue, nil } -func ToKeyValue(data []byte) (*KeyValue, error) { +func ToKeyValue(data []byte) (*Value, error) { - keyValue, err := clients.UnmarshallJson[KeyValue](data) + keyValue, err := clients.UnmarshallJson[Value](data) if err != nil { return nil, err } return keyValue, nil } -func (kv *KeyValue) ToJson() ([]byte, error) { +func (kv *Value) ToJson() ([]byte, error) { data, err := clients.MarshallJson(kv) if err != nil { diff --git a/momentum-core/routers/application-router.go b/momentum-core/routers/application-router.go index 1dc2847..bb5d37b 100644 --- a/momentum-core/routers/application-router.go +++ b/momentum-core/routers/application-router.go @@ -39,7 +39,7 @@ func (a *ApplicationRouter) RegisterApplicationRoutes(server *gin.Engine) { server.POST(ROUTING_PATH_APPLICATION, a.addApplication) } -// GetApplication godoc +// getApplication godoc // // @Summary get an application of a repository by id // @Tags applications @@ -85,7 +85,7 @@ func (a *ApplicationRouter) getApplication(c *gin.Context) { c.JSON(http.StatusOK, result) } -// AddApplication godoc +// addApplication godoc // // @Summary add an application // @Tags applications diff --git a/momentum-core/routers/value-router.go b/momentum-core/routers/value-router.go index 4c444ca..9fd8446 100644 --- a/momentum-core/routers/value-router.go +++ b/momentum-core/routers/value-router.go @@ -1,7 +1,153 @@ package routers -type ValueRouter struct{} +import ( + "momentum-core/config" + "momentum-core/models" + "momentum-core/services" + "net/http" -func NewValueRouter() *ValueRouter { - return new(ValueRouter) + "github.com/gin-gonic/gin" +) + +const ROUTING_PATH_VALUE_BY_ID = VERSION + "/repository/:repositoryName/value/:valueId" +const ROUTING_PATH_VALUE = VERSION + "/value" +const ROUTING_PATH_VALUE_BY_APPLICATION = VERSION + "/repository/:repositoryName/application/values/:applicationId" +const ROUTING_PATH_VALUE_BY_STAGE = VERSION + "/repository/:repositoryName/stage/values/:stageId" +const ROUTING_PATH_VALUE_BY_DEPLOYMENT = VERSION + "/repository/:repositoryName/deployment/values/:deploymentId" + +type ValueRouter struct { + valueService *services.ValueService +} + +func NewValueRouter(valueService *services.ValueService) *ValueRouter { + + vs := new(ValueRouter) + + vs.valueService = valueService + + return vs +} + +func (vr *ValueRouter) RegisterValueRoutes(server *gin.Engine) { + + server.GET(ROUTING_PATH_VALUE_BY_ID, vr.valueById) + server.GET(ROUTING_PATH_VALUE_BY_APPLICATION, vr.valuesByApplication) + server.GET(ROUTING_PATH_VALUE_BY_STAGE, vr.valuesByStage) + server.GET(ROUTING_PATH_VALUE_BY_DEPLOYMENT, vr.valuesByDeployment) +} + +// valueById godoc +// +// @Summary get a value of a repository by id +// @Tags values +// @Produce json +// @Param repositoryName path string true "Repository Name" +// @Param valueId path string true "Value ID" +// @Success 200 {object} models.Value +// @Failure 400 {object} models.ApiError +// @Failure 404 {object} models.ApiError +// @Failure 500 {object} models.ApiError +// @Router /repository/{repositoryName}/value/{valueId} [get] +func (vr *ValueRouter) valueById(c *gin.Context) { + + traceId := config.LOGGER.TraceId() + + repoName := c.Param("repositoryName") + valueId := c.Param("valueId") + + result, err := vr.valueService.ValueById(repoName, valueId, traceId) + if err != nil { + c.JSON(http.StatusNotFound, models.NewApiError(err, http.StatusNotFound, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + c.JSON(http.StatusOK, result) +} + +// valuesByApplication godoc +// +// @Summary get all values of an application by the applications id +// @Tags values +// @Produce json +// @Param repositoryName path string true "Repository Name" +// @Param applicationId path string true "Application ID" +// @Success 200 {array} models.ValueWrapper +// @Failure 400 {object} models.ApiError +// @Failure 404 {object} models.ApiError +// @Failure 500 {object} models.ApiError +// @Router /repository/{repositoryName}/application/values/{applicationId} [get] +func (vr *ValueRouter) valuesByApplication(c *gin.Context) { + + traceId := config.LOGGER.TraceId() + + repoName := c.Param("repositoryName") + applicationId := c.Param("applicationId") + + result, err := vr.valueService.ValuesByApplication(repoName, applicationId, traceId) + if err != nil { + c.JSON(http.StatusNotFound, models.NewApiError(err, http.StatusNotFound, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + c.JSON(http.StatusOK, result) +} + +// valuesByStage godoc +// +// @Summary get all values of an stage by the stages id +// @Tags values +// @Produce json +// @Param repositoryName path string true "Repository Name" +// @Param stageId path string true "Stage ID" +// @Success 200 {array} models.ValueWrapper +// @Failure 400 {object} models.ApiError +// @Failure 404 {object} models.ApiError +// @Failure 500 {object} models.ApiError +// @Router /repository/{repositoryName}/stage/values/{stageId} [get] +func (vr *ValueRouter) valuesByStage(c *gin.Context) { + + traceId := config.LOGGER.TraceId() + + repoName := c.Param("repositoryName") + stageId := c.Param("stageId") + + result, err := vr.valueService.ValuesByStage(repoName, stageId, traceId) + if err != nil { + c.JSON(http.StatusNotFound, models.NewApiError(err, http.StatusNotFound, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + c.JSON(http.StatusOK, result) +} + +// valuesByDeployment godoc +// +// @Summary get all values of a deployment by the deployments id +// @Tags values +// @Produce json +// @Param repositoryName path string true "Repository Name" +// @Param deploymentId path string true "Deployment ID" +// @Success 200 {array} models.ValueWrapper +// @Failure 400 {object} models.ApiError +// @Failure 404 {object} models.ApiError +// @Failure 500 {object} models.ApiError +// @Router /repository/{repositoryName}/deployment/values/{deploymentId} [get] +func (vr *ValueRouter) valuesByDeployment(c *gin.Context) { + + traceId := config.LOGGER.TraceId() + + repoName := c.Param("repositoryName") + deploymentId := c.Param("deploymentId") + + result, err := vr.valueService.ValuesByDeployment(repoName, deploymentId, traceId) + if err != nil { + c.JSON(http.StatusNotFound, models.NewApiError(err, http.StatusNotFound, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + c.JSON(http.StatusOK, result) } diff --git a/momentum-core/services/keyvalue-service.go b/momentum-core/services/keyvalue-service.go deleted file mode 100644 index 2d3b435..0000000 --- a/momentum-core/services/keyvalue-service.go +++ /dev/null @@ -1,10 +0,0 @@ -package services - -type ValueService struct{} - -func NewValueService() *ValueService { - - valueService := new(ValueService) - - return valueService -} diff --git a/momentum-core/services/tree-service.go b/momentum-core/services/tree-service.go index ad8caf4..be8a0f2 100644 --- a/momentum-core/services/tree-service.go +++ b/momentum-core/services/tree-service.go @@ -79,6 +79,24 @@ func (ts *TreeService) deployment(repositoryName string, deploymentId string, tr return nil, errors.New("no deployment with id " + deploymentId) } +func (ts *TreeService) value(repositoryName string, valueId string, traceId string) (*tree.Node, error) { + + repo, err := ts.repository(repositoryName, traceId) + if err != nil { + return nil, err + } + + // TODO -> AllValues / ValueById etc. on Momentum-Tree abstraction. + + values := repo.AllDeployments() + for _, value := range values { + if value.Id == valueId { + return value, nil + } + } + return nil, errors.New("no value with id " + valueId) +} + func (ts *TreeService) find(traceId string, repositoryName string, terms ...string) (*tree.Node, error) { repo, err := ts.repository(repositoryName, traceId) diff --git a/momentum-core/services/value-service.go b/momentum-core/services/value-service.go new file mode 100644 index 0000000..b7ed484 --- /dev/null +++ b/momentum-core/services/value-service.go @@ -0,0 +1,120 @@ +package services + +import ( + "errors" + "fmt" + "momentum-core/config" + "momentum-core/models" + "strings" +) + +type ValueService struct { + treeService *TreeService +} + +func NewValueService(treeService *TreeService) *ValueService { + + valueService := new(ValueService) + + valueService.treeService = treeService + + return valueService +} + +func (vs *ValueService) ValueById(repositoryName string, valueId string, traceId string) (*models.Value, error) { + + value, err := vs.treeService.value(repositoryName, valueId, traceId) + if err != nil { + config.LOGGER.LogWarning("unable to find value "+valueId, err, traceId) + return nil, err + } + if value == nil { + return nil, errors.New("no value with id " + valueId) + } + + return models.ToValueFromNode(value) +} + +func (vs *ValueService) ValuesByApplication(repositoryName string, applicationId string, traceId string) ([]*models.ValueWrapper, error) { + + application, err := vs.treeService.application(repositoryName, applicationId, traceId) + if err != nil { + return nil, err + } + + wrappedValues := make([]*models.ValueWrapper, 0) + files := application.Files() + for _, f := range files { + fmt.Println("File:", f.NormalizedPath()) + if strings.EqualFold(f.NormalizedPath(), KUSTOMIZATION_FILE_NAME) { + wrappedKustomization, err := models.ToValueWrapperFromNode(f, models.APPLICATION) + if err != nil { + return nil, err + } + wrappedValues = append(wrappedValues, wrappedKustomization) + } + if strings.EqualFold(f.PathWithoutEnding(), "ns") { + wrappedNamespace, err := models.ToValueWrapperFromNode(f, models.APPLICATION) + if err != nil { + return nil, err + } + wrappedValues = append(wrappedValues, wrappedNamespace) + } + if strings.EqualFold(f.PathWithoutEnding(), "repository") { + wrappedRepository, err := models.ToValueWrapperFromNode(f, models.APPLICATION) + if err != nil { + return nil, err + } + wrappedValues = append(wrappedValues, wrappedRepository) + } + } + + return wrappedValues, nil +} + +func (vs *ValueService) ValuesByStage(repositoryName string, stageId string, traceId string) ([]*models.ValueWrapper, error) { + + stage, err := vs.treeService.stage(repositoryName, stageId, traceId) + if err != nil { + return nil, err + } + + files := stage.Files() + for _, f := range files { + if strings.EqualFold(f.NormalizedPath(), KUSTOMIZATION_FILE_NAME) { + wrappedKustomization, err := models.ToValueWrapperFromNode(f, models.STAGE) + if err != nil { + return nil, err + } + return []*models.ValueWrapper{wrappedKustomization}, nil + } + } + + return make([]*models.ValueWrapper, 0), nil +} + +func (vs *ValueService) ValuesByDeployment(repositoryName string, deploymentId string, traceId string) ([]*models.ValueWrapper, error) { + + deployment, err := vs.treeService.deployment(repositoryName, deploymentId, traceId) + if err != nil { + return nil, err + } + + wrappers := make([]*models.ValueWrapper, 0) + + wrappedDeploymentFile, err := models.ToValueWrapperFromNode(deployment, models.DEPLOYMENT) + if err != nil { + return nil, err + } + wrappers = append(wrappers, wrappedDeploymentFile) + + for _, f := range deployment.DeploymentFolderFiles() { + wrappedFile, err := models.ToValueWrapperFromNode(f, models.DEPLOYMENT) + if err != nil { + return nil, err + } + wrappers = append(wrappers, wrappedFile) + } + + return wrappers, nil +} diff --git a/momentum-core/tree/momentum-tree.go b/momentum-core/tree/momentum-tree.go index b5bbba1..630b17e 100644 --- a/momentum-core/tree/momentum-tree.go +++ b/momentum-core/tree/momentum-tree.go @@ -102,6 +102,45 @@ func (n *Node) Deployment(deploymentId string) *Node { return nil } +func (n *Node) DeploymentFolderFiles() []*Node { + + deploymentFolders := childrenWithName(n.Parent, "_deploy") + for _, depl := range deploymentFolders { + + if strings.EqualFold(n.PathWithoutEnding(), depl.PathWithoutEnding()) { + return depl.Files() + } + } + + return make([]*Node, 0) +} + +func (n *Node) Values() []*Node { + + if n == nil || n.Kind != File { + return make([]*Node, 0) + } + + return n.flatPreorder(make([]*Node, 0)) +} + +func (n *Node) flatPreorder(result []*Node) []*Node { + + if n == nil { + return result + } + + result = append(result, n) + + if len(n.Children) > 0 { + for _, child := range n.Children { + result = child.flatPreorder(result) + } + } + + return result +} + func deployments(stage *Node) []*Node { files := stage.Files() @@ -142,3 +181,15 @@ func deployments(stage *Node) []*Node { return depls } + +func childrenWithName(n *Node, name string) []*Node { + + matches := make([]*Node, 0) + for _, child := range n.Children { + if strings.EqualFold(child.Path, name) { + matches = append(matches, child) + } + } + + return matches +} diff --git a/momentum-core/tree/tree.go b/momentum-core/tree/tree.go index 5b16b90..f58a2b3 100644 --- a/momentum-core/tree/tree.go +++ b/momentum-core/tree/tree.go @@ -80,6 +80,9 @@ func (n *Node) Remove() { } } n.Parent.Children = newChilds + + n.Parent.RemoveYamlChild(n.Path) + n.Parent = nil } diff --git a/momentum-core/tree/yaml-node-factory.go b/momentum-core/tree/yaml-node-factory.go index cd9ed12..6521830 100644 --- a/momentum-core/tree/yaml-node-factory.go +++ b/momentum-core/tree/yaml-node-factory.go @@ -2,6 +2,7 @@ package tree import ( "errors" + "momentum-core/utils" "momentum-core/yaml" ) @@ -19,6 +20,46 @@ const ( mergeTag = "!!merge" ) +func (n *Node) RemoveYamlChildren() error { + + errs := make([]error, 0) + for _, chld := range n.Children { + errs = append(errs, chld.RemoveYamlChild(chld.Path)) + } + + return errors.Join(errs...) +} + +func (n *Node) RemoveYamlChild(path string) error { + + updated := make([]*Node, 0) + for _, child := range n.Children { + if child.Path != path { + updated = append(updated, child) + } + + updatedYaml := make([]*yaml.Node, 0) + if child.Path == path { + if child.Parent != nil && child.Parent.YamlNode != nil { + if child.Parent.YamlNode != nil { + for _, yamlChild := range child.Parent.YamlNode.Content { + if yamlChild.Value != utils.LastPartOfPath(path) { + updatedYaml = append(updatedYaml, yamlChild) + } + } + child.Parent.YamlNode.Content = updatedYaml + } + } + } + + return nil + } + + n.Children = updated + + return nil +} + func (n *Node) AddYamlSequence(key string, values []string, style yaml.Style) error { if len(values) < 1 {