d4x1 commented on code in PR #6689: URL: https://github.com/apache/incubator-devlake/pull/6689#discussion_r1436035525
########## backend/plugins/bitbucket_server/api/init.go: ########## @@ -0,0 +1,69 @@ +/* +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 api + +import ( + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/pluginhelper/api" + "github.com/apache/incubator-devlake/plugins/bitbucket_server/models" + "github.com/go-playground/validator/v10" +) + +var vld *validator.Validate Review Comment: I think you'd better to use the latest helper. The code you are using can work, but the latest helper will be more clear and simple. Examples: https://github.com/apache/incubator-devlake/blob/main/backend/plugins/github/api/init.go#L31 For more help about new helper, @klesh will help you. ########## backend/plugins/bitbucket_server/api/remote.go: ########## @@ -0,0 +1,178 @@ +/* +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 api + +import ( + gocontext "context" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/pluginhelper/api" + "github.com/apache/incubator-devlake/plugins/bitbucket_server/models" +) + +// RemoteScopes list all available scope for users +// @Summary list all available scope for users +// @Description list all available scope for users +// @Tags plugins/bitbucket_server +// @Accept application/json +// @Param connectionId path int false "connection ID" +// @Param groupId query string false "group ID" +// @Param pageToken query string false "page Token" +// @Success 200 {object} api.RemoteScopesOutput +// @Failure 400 {object} shared.ApiBody "Bad Request" +// @Failure 500 {object} shared.ApiBody "Internal Error" +// @Router /plugins/bitbucket_server/connections/{connectionId}/remote-scopes [GET] +func RemoteScopes(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) { + return remoteHelper.GetScopesFromRemote(input, + func(basicRes context.BasicRes, gid string, queryData *api.RemoteQueryData, connection models.BitbucketServerConnection) ([]models.ProjectItem, errors.Error) { + if gid != "" { + return nil, nil + } + query := initialQuery(queryData) + + apiClient, err := api.NewApiClientFromConnection(gocontext.TODO(), basicRes, &connection) + if err != nil { + return nil, errors.BadInput.Wrap(err, "failed to get create apiClient") + } + var res *http.Response + // query.Set("sort", "values.key") + // query.Set("fields", "values.slug,values.name,limit,start,size") + res, err = apiClient.Get("rest/api/1.0/projects", query, nil) + if err != nil { + return nil, err + } + + resBody := &models.ProjectsResponse{} + err = api.UnmarshalResponse(res, resBody) + if err != nil { + return nil, err + } + + return resBody.Values, err + }, + func(basicRes context.BasicRes, gid string, queryData *api.RemoteQueryData, connection models.BitbucketServerConnection) ([]models.BitbucketApiRepo, errors.Error) { + if gid == "" { + return nil, nil + } + query := initialQuery(queryData) + + apiClient, err := api.NewApiClientFromConnection(gocontext.TODO(), basicRes, &connection) + if err != nil { + return nil, errors.BadInput.Wrap(err, "failed to get create apiClient") + } + var res *http.Response + // query.Set("fields", "values.name,values.full_name,values.language,values.description,values.owner.display_name,values.created_on,values.updated_on,values.links.clone,values.links.html,pagelen,page,size") + // list projects part + res, err = apiClient.Get(fmt.Sprintf("rest/api/1.0/projects/%s/repos", gid), query, nil) + if err != nil { + return nil, err + } + var resBody models.ReposResponse + err = api.UnmarshalResponse(res, &resBody) + if err != nil { + return nil, err + } + return resBody.Values, err + }, + ) +} + +// SearchRemoteScopes use the Search API and only return project +// @Summary use the Search API and only return project +// @Description use the Search API and only return project +// @Tags plugins/bitbucket_server +// @Accept application/json +// @Param connectionId path int false "connection ID" +// @Param search query string false "search" +// @Param page query int false "page number" +// @Param pageSize query int false "page size per page" +// @Success 200 {object} api.SearchRemoteScopesOutput +// @Failure 400 {object} shared.ApiBody "Bad Request" +// @Failure 500 {object} shared.ApiBody "Internal Error" +// @Router /plugins/bitbucket_server/connections/{connectionId}/search-remote-scopes [GET] +func SearchRemoteScopes(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) { + return remoteHelper.SearchRemoteScopes(input, + func(basicRes context.BasicRes, queryData *api.RemoteQueryData, connection models.BitbucketServerConnection) ([]models.BitbucketApiRepo, errors.Error) { + // create api client + apiClient, err := api.NewApiClientFromConnection(gocontext.TODO(), basicRes, &connection) + if err != nil { + return nil, errors.BadInput.Wrap(err, "failed to get create apiClient") + } + + // request search + query := initialQuery(queryData) + if len(queryData.Search) == 0 { + return nil, errors.BadInput.New("empty search query") + } + s := queryData.Search[0] + gid, searchName := getSearch(s) + // query.Set("sort", "name") + // query.Set("fields", "values.name,values.full_name,values.language,values.description,values.owner.display_name,values.created_on,values.updated_on,values.links.clone,values.links.html,pagelen,page,size") Review Comment: These commented codes can be deleted directly. ########## backend/plugins/bitbucket_server/api/connection.go: ########## @@ -0,0 +1,151 @@ +/* +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 api + +import ( + "context" + "net/http" + + "github.com/apache/incubator-devlake/server/api/shared" + + "github.com/apache/incubator-devlake/core/errors" + plugin "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/pluginhelper/api" + "github.com/apache/incubator-devlake/plugins/bitbucket_server/models" +) + +type BitBucketServerTestConnResponse struct { + shared.ApiBody + Connection *models.BitbucketServerConn +} + +// @Summary test bitbucket connection +// @Description Test bitbucket Connection +// @Tags plugins/bitbucket_server +// @Param body body models.BitbucketServerConn true "json body" +// @Success 200 {object} BitBucketServerTestConnResponse "Success" +// @Failure 400 {string} errcode.Error "Bad Request" +// @Failure 500 {string} errcode.Error "Internal Error" +// @Router /plugins/bitbucket_server/test [POST] +func TestConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) { Review Comment: A new handle for testing connection is required. Example: https://github.com/apache/incubator-devlake/blob/main/backend/plugins/github/impl/impl.go#L190 ########## backend/plugins/bitbucket_server/api/connection.go: ########## @@ -0,0 +1,151 @@ +/* +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 api + +import ( + "context" + "net/http" + + "github.com/apache/incubator-devlake/server/api/shared" + + "github.com/apache/incubator-devlake/core/errors" + plugin "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/pluginhelper/api" + "github.com/apache/incubator-devlake/plugins/bitbucket_server/models" +) + +type BitBucketServerTestConnResponse struct { + shared.ApiBody + Connection *models.BitbucketServerConn +} + +// @Summary test bitbucket connection +// @Description Test bitbucket Connection +// @Tags plugins/bitbucket_server +// @Param body body models.BitbucketServerConn true "json body" +// @Success 200 {object} BitBucketServerTestConnResponse "Success" +// @Failure 400 {string} errcode.Error "Bad Request" +// @Failure 500 {string} errcode.Error "Internal Error" +// @Router /plugins/bitbucket_server/test [POST] +func TestConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) { + // decode + var err errors.Error + var connection models.BitbucketServerConn + if err := api.Decode(input.Body, &connection, vld); err != nil { + return nil, errors.BadInput.Wrap(err, "could not decode request parameters") + } + // test connection + apiClient, err := api.NewApiClientFromConnection(context.TODO(), basicRes, &connection) + if err != nil { + return nil, err + } + res, err := apiClient.Get("rest/api/1.0/projects", nil, nil) + if err != nil { + return nil, err + } + + if res.StatusCode == http.StatusUnauthorized { + return nil, errors.HttpStatus(http.StatusBadRequest).New("StatusUnauthorized error when testing connection") + } + + if res.StatusCode != http.StatusOK { + return nil, errors.HttpStatus(res.StatusCode).New("unexpected status code when testing connection") + } + body := BitBucketServerTestConnResponse{} + body.Success = true + body.Message = "success" + body.Connection = &connection + // output + return &plugin.ApiResourceOutput{Body: body, Status: 200}, nil +} + +// @Summary create bitbucket connection +// @Description Create bitbucket connection +// @Tags plugins/bitbucket_server +// @Param body body models.BitbucketServerConnection true "json body" +// @Success 200 {object} models.BitbucketServerConnection +// @Failure 400 {string} errcode.Error "Bad Request" +// @Failure 500 {string} errcode.Error "Internal Error" +// @Router /plugins/bitbucket_server/connections [POST] +func PostConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) { + // update from request and save to database + connection := &models.BitbucketServerConnection{} + err := connectionHelper.Create(connection, input) + if err != nil { + return nil, err + } + return &plugin.ApiResourceOutput{Body: connection, Status: http.StatusOK}, nil +} + +// @Summary patch bitbucket connection +// @Description Patch bitbucket connection +// @Tags plugins/bitbucket_server +// @Param body body models.BitbucketServerConnection true "json body" +// @Success 200 {object} models.BitbucketServerConnection +// @Failure 400 {string} errcode.Error "Bad Request" +// @Failure 500 {string} errcode.Error "Internal Error" +// @Router /plugins/bitbucket_server/connections/{connectionId} [PATCH] +func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) { + connection := &models.BitbucketServerConnection{} + err := connectionHelper.Patch(connection, input) + if err != nil { + return nil, err + } + return &plugin.ApiResourceOutput{Body: connection}, nil +} + +// @Summary delete a bitbucket connection +// @Description Delete a bitbucket connection +// @Tags plugins/bitbucket_server +// @Success 200 {object} models.BitbucketServerConnection +// @Failure 400 {string} errcode.Error "Bad Request" +// @Failure 409 {object} services.BlueprintProjectPairs "References exist to this connection" +// @Failure 500 {string} errcode.Error "Internal Error" +// @Router /plugins/bitbucket_server/connections/{connectionId} [DELETE] +func DeleteConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) { + return connectionHelper.Delete(&models.BitbucketServerConnection{}, input) +} + +// @Summary get all bitbucket connections +// @Description Get all bitbucket connections +// @Tags plugins/bitbucket_server +// @Success 200 {object} []models.BitbucketServerConnection +// @Failure 400 {string} errcode.Error "Bad Request" +// @Failure 500 {string} errcode.Error "Internal Error" +// @Router /plugins/bitbucket_server/connections [GET] +func ListConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) { + var connections []models.BitbucketServerConnection + err := connectionHelper.List(&connections) + if err != nil { + return nil, err + } + return &plugin.ApiResourceOutput{Body: connections, Status: http.StatusOK}, nil Review Comment: To avoid sensitive tokens leakage, a `Sanitize` function is expected. Example: https://github.com/apache/incubator-devlake/blob/main/backend/plugins/github/models/connection.go#L253 And this function should be inited to `dsHelper`(Example: https://github.com/apache/incubator-devlake/blob/main/backend/plugins/github/api/init.go#L47). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
