This is an automated email from the ASF dual-hosted git repository. abeizn pushed a commit to branch release-v1.0 in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
commit e0fab64d2f0a970c9fa9b5853579e86e83953b3d Author: abeizn <[email protected]> AuthorDate: Tue Mar 19 18:44:17 2024 +0800 feat: add onboard keyvalue api (#7189) * feat: add onboard keyvalue api * fix: optimize interface * fix: revert poetry lock * fix: get json result * fix: rename onboard storeKey * fix: word * fix: some details --- .../migrationscripts/20240318_add_devlake_store.go | 54 +++++++++++++++ backend/core/models/migrationscripts/register.go | 1 + backend/core/models/project.go | 14 ++++ backend/server/api/router.go | 4 ++ backend/server/api/store/store.go | 78 ++++++++++++++++++++++ backend/server/services/store.go | 57 ++++++++++++++++ 6 files changed, 208 insertions(+) diff --git a/backend/core/models/migrationscripts/20240318_add_devlake_store.go b/backend/core/models/migrationscripts/20240318_add_devlake_store.go new file mode 100644 index 000000000..e338fd896 --- /dev/null +++ b/backend/core/models/migrationscripts/20240318_add_devlake_store.go @@ -0,0 +1,54 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package migrationscripts + +import ( + "encoding/json" + "time" + + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/plugin" +) + +var _ plugin.MigrationScript = (*addStore)(nil) + +type store20240318 struct { + StoreKey string `gorm:"primaryKey;type:varchar(255)"` + StoreValue json.RawMessage `gorm:"type:json;serializer:json"` + CreatedAt time.Time `json:"createdAt" mapstructure:"createdAt"` + UpdatedAt time.Time `json:"updatedAt" mapstructure:"updatedAt"` +} + +func (store20240318) TableName() string { + return "_devlake_store" +} + +type addStore struct{} + +func (*addStore) Up(basicRes context.BasicRes) errors.Error { + return basicRes.GetDal().AutoMigrate(store20240318{}) +} + +func (*addStore) Version() uint64 { + return 20240318111247 +} + +func (*addStore) Name() string { + return "add _devlake_store table" +} diff --git a/backend/core/models/migrationscripts/register.go b/backend/core/models/migrationscripts/register.go index 03319bf92..26b3ec9bb 100644 --- a/backend/core/models/migrationscripts/register.go +++ b/backend/core/models/migrationscripts/register.go @@ -109,5 +109,6 @@ func All() []plugin.MigrationScript { new(modifyRefsIdLength), new(addOriginalEnvironmentToCicdDeploymentsAndCicdDeploymentCommits), new(addSubtabknameToDeployment), + new(addStore), } } diff --git a/backend/core/models/project.go b/backend/core/models/project.go index 6a935e97e..4da6afc67 100644 --- a/backend/core/models/project.go +++ b/backend/core/models/project.go @@ -18,6 +18,9 @@ limitations under the License. package models import ( + "encoding/json" + "time" + "github.com/apache/incubator-devlake/core/models/common" ) @@ -67,3 +70,14 @@ type ApiOutputProject struct { Blueprint *Blueprint `json:"blueprint" mapstructure:"blueprint"` LastPipeline *Pipeline `json:"lastPipeline,omitempty" mapstructure:"lastPipeline"` } + +type Store struct { + StoreKey string `gorm:"primaryKey;type:varchar(255)"` + StoreValue json.RawMessage `gorm:"type:json;serializer:json"` + CreatedAt time.Time `json:"createdAt" mapstructure:"createdAt"` + UpdatedAt time.Time `json:"updatedAt" mapstructure:"updatedAt"` +} + +func (Store) TableName() string { + return "_devlake_store" +} diff --git a/backend/server/api/router.go b/backend/server/api/router.go index 6957044de..188c29e4c 100644 --- a/backend/server/api/router.go +++ b/backend/server/api/router.go @@ -26,6 +26,7 @@ import ( "github.com/apache/incubator-devlake/core/errors" "github.com/apache/incubator-devlake/impls/logruslog" "github.com/apache/incubator-devlake/server/api/apikeys" + "github.com/apache/incubator-devlake/server/api/store" "github.com/apache/incubator-devlake/core/plugin" "github.com/apache/incubator-devlake/server/api/blueprints" @@ -73,6 +74,9 @@ func RegisterRouter(r *gin.Engine, basicRes context.BasicRes) { r.DELETE("/projects/*projectName", project.DeleteProject) r.POST("/projects", project.PostProject) r.GET("/projects", project.GetProjects) + // on board api + r.GET("/store/:storeKey", store.GetStore) + r.PUT("/store/:storeKey", store.PutStore) // api keys api r.GET("/api-keys", apikeys.GetApiKeys) diff --git a/backend/server/api/store/store.go b/backend/server/api/store/store.go new file mode 100644 index 000000000..0c7ab3778 --- /dev/null +++ b/backend/server/api/store/store.go @@ -0,0 +1,78 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package store + +import ( + "fmt" + "net/http" + + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models" + "github.com/apache/incubator-devlake/server/api/shared" + "github.com/apache/incubator-devlake/server/services" + + "github.com/gin-gonic/gin" +) + +// @Summary Get the value by given key +// @Description Get the value by given key +// @Tags framework/projects +// @Param storeKey path string true "storeKey" +// @Success 200 {object} json.RawMessage +// @Failure 400 {string} errcode.Error "Bad Request" +// @Failure 500 {string} errcode.Error "Internal Error" +// @Router /store/{storeKey} [get] +func GetStore(c *gin.Context) { + storeKey := c.Param("storeKey") + result, err := services.GetStore(storeKey) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error fetching %s data", storeKey)}) + return + } + + shared.ApiOutputSuccess(c, result.StoreValue, http.StatusOK) +} + +// @Summary Put the value by given key +// @Description Put the value by given key +// @Tags framework/projects +// @Accept application/json +// @Param storeKey path string true "storeKey" +// @Param project body json.RawMessage false "json" +// @Success 200 {object} json.RawMessage +// @Failure 400 {string} errcode.Error "Bad Request" +// @Failure 500 {string} errcode.Error "Internal Error" +// @Router /store/{storeKey} [PUT] +func PutStore(c *gin.Context) { + storeKey := c.Param("storeKey") + var body models.Store + err := c.ShouldBind(&body.StoreValue) + if err != nil { + shared.ApiOutputError(c, errors.BadInput.Wrap(err, shared.BadRequestBody)) + return + } + body.StoreKey = storeKey + + result, err := services.PutStore(storeKey, &body) + if err != nil { + shared.ApiOutputError(c, errors.Default.Wrap(err, fmt.Sprintf("PutStore: failed to put %s", storeKey))) + return + } + + shared.ApiOutputSuccess(c, result.StoreValue, http.StatusCreated) +} diff --git a/backend/server/services/store.go b/backend/server/services/store.go new file mode 100644 index 000000000..1eb4b717c --- /dev/null +++ b/backend/server/services/store.go @@ -0,0 +1,57 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package services + +import ( + "fmt" + + "github.com/apache/incubator-devlake/core/dal" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models" +) + +// GetProjects returns a paginated list of Projects based on `query` +func GetStore(storeKey string) (*models.Store, errors.Error) { + clauses := []dal.Clause{ + dal.From(&models.Store{}), + dal.Where("store_key = ?", storeKey), + } + + kvstore := &models.Store{} + err := db.All(&kvstore, clauses...) + if err != nil { + return nil, errors.Default.Wrap(err, fmt.Sprintf("error finding %s on _devlake_store table", storeKey)) + } + + return kvstore, nil +} + +// PutOnboard accepts a project instance and insert it to database +func PutStore(storeKey string, storeValue *models.Store) (*models.Store, errors.Error) { + // verify input + if err := VerifyStruct(storeValue); err != nil { + return nil, err + } + + err := db.CreateOrUpdate(storeValue, dal.Where("store_key = ?", storeKey)) + if err != nil { + return nil, err + } + + return storeValue, nil +}
