Skip to content

Commit

Permalink
feat: Opsgenie plugin implementation (#6154)
Browse files Browse the repository at this point in the history
* feat: Opsgenie plugin implementation

* fix(lint): lint with gofmt

* refactor(lint): removing unused config-ui files

* feat(task): changed incident collector to accept incremental collection

* fix(config-ui): adjust the params for bp add connection (#6241)

* refactor: removed unused code (#6243)

* refactor(config-ui): remove content about user info (#6245)

* fix(azure): fix 400 error when cloing azure repos (#6246)

* fix: normalize_bp_settings throws error (#6249)

* fix: normalize_bp_settings throws error

* fix: pg ci

* fix: pg ci (#6252)

* fix(gitlab): fix incremental deployment collector (#6254)

* fix(gitlab): fix incremental deployment collector

* fix(gitlab): rollback to full collector

* refactor(gitlab): remove unused codes

* fix(gitlab): collect deployment incrementally

* fix: modify _tool_jira_issue_relationships primary key (#6253)

* fix: modify _tool_jira_issue_relationships primary key

* fix: ci lint

* feat(pagerduty): search remote scope by keyword (#6255)

* feat(pagerduty): search remote scope by keyword

* feat(pagerduty): add `createdAt` field

* refactor(impl): refactoring impl.go and service model

* refactor(comment): removing comment from incident collector

* feat(task): added GetCreated function

* feat(zentao): check db url when testing connections (#6258)

* feat(zentao): check db url when testing connections

* fix(zentao): remove test codes

* fix(zentao): remove test codes

* fix(configui): tagsLimit should be a number (#6261)

* fix(check): fixing lint in config-ui

* refactor(newline): adding removed newline

* fix(config-ui): removed old plugin type

* refactor(config-ui): remove type inheritance from opsgenieconfig

* style(config-ui): update icon with icon.svg

---------

Co-authored-by: 青湛 <[email protected]>
Co-authored-by: Klesh Wong <[email protected]>
Co-authored-by: Lynwee <[email protected]>
Co-authored-by: abeizn <[email protected]>
  • Loading branch information
5 people authored Nov 24, 2023
1 parent 3efad6a commit 4453aa5
Show file tree
Hide file tree
Showing 70 changed files with 5,030 additions and 0 deletions.
119 changes: 119 additions & 0 deletions backend/plugins/opsgenie/api/blueprint_v200.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
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/errors"
coreModels "github.com/apache/incubator-devlake/core/models"
"github.com/apache/incubator-devlake/core/models/domainlayer"
"github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
"github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/core/utils"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/opsgenie/models"
"github.com/apache/incubator-devlake/plugins/opsgenie/tasks"
)

func MakeDataSourcePipelinePlanV200(
subtaskMetas []plugin.SubTaskMeta,
connectionId uint64,
bpScopes []*coreModels.BlueprintScope,
) (coreModels.PipelinePlan, []plugin.Scope, errors.Error) {
// get the connection info for url
connection := &models.OpsgenieConnection{}
err := connectionHelper.FirstById(connection, connectionId)
if err != nil {
return nil, nil, err
}

plan := make(coreModels.PipelinePlan, len(bpScopes))
plan, err = makeDataSourcePipelinePlanV200(subtaskMetas, plan, bpScopes, connection)
if err != nil {
return nil, nil, err
}
scopes, err := makeScopesV200(bpScopes, connection)
if err != nil {
return nil, nil, err
}

return plan, scopes, nil
}

func makeDataSourcePipelinePlanV200(
subtaskMetas []plugin.SubTaskMeta,
plan coreModels.PipelinePlan,
bpScopes []*coreModels.BlueprintScope,
connection *models.OpsgenieConnection,
) (coreModels.PipelinePlan, errors.Error) {
for i, bpScope := range bpScopes {
// get board and scope config from db
service, scopeConfig, err := scopeHelper.DbHelper().GetScopeAndConfig(connection.ID, bpScope.ScopeId)
if err != nil {
return nil, err
}
// construct task options for opsgenie
op := &tasks.OpsgenieOptions{
ConnectionId: service.ConnectionId,
ServiceId: service.Id,
ServiceName: service.Name,
}

var options map[string]any
options, err = tasks.EncodeTaskOptions(op)
if err != nil {
return nil, err
}
var subtasks []string
subtasks, err = api.MakePipelinePlanSubtasks(subtaskMetas, scopeConfig.Entities)
if err != nil {
return nil, err
}
stage := []*coreModels.PipelineTask{
{
Plugin: "opsgenie",
Subtasks: subtasks,
Options: options,
},
}
plan[i] = stage
}
return plan, nil
}

func makeScopesV200(bpScopes []*coreModels.BlueprintScope, connection *models.OpsgenieConnection) ([]plugin.Scope, errors.Error) {
scopes := make([]plugin.Scope, 0)
for _, bpScope := range bpScopes {
// get board and scope config from db
service, scopeConfig, err := scopeHelper.DbHelper().GetScopeAndConfig(connection.ID, bpScope.ScopeId)
if err != nil {
return nil, err
}
// add board to scopes
if utils.StringsContains(scopeConfig.Entities, plugin.DOMAIN_TYPE_TICKET) {
scopeTicket := &ticket.Board{
DomainEntity: domainlayer.DomainEntity{
Id: didgen.NewDomainIdGenerator(&models.Service{}).Generate(connection.ID, service.Id),
},
Name: service.Name,
}
scopes = append(scopes, scopeTicket)
}
}
return scopes, nil
}
153 changes: 153 additions & 0 deletions backend/plugins/opsgenie/api/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
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/core/errors"
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/opsgenie/models"
)

// @Summary test opsgenie connection
// @Description Test Opsgenie Connection
// @Tags plugins/opsgenie
// @Param body body models.OpsgenieConn true "json body"
// @Success 200 {object} shared.ApiBody "Success"
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/opsgenie/test [POST]
func TestConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
var connection models.OpsgenieConn

err := api.Decode(input.Body, &connection, vld)
if err != nil {
return nil, err
}

apiClient, err := api.NewApiClientFromConnection(context.TODO(), basicRes, &connection)
if err != nil {
return nil, err
}

// check API permissions
response, err := apiClient.Get("v2/heartbeats/HeartbeatName/ping", nil, nil)
if err != nil {
return nil, err
}

if response.StatusCode == http.StatusUnauthorized {
return nil, errors.HttpStatus(http.StatusUnauthorized).New("StatusUnauthorized error when testing api or read_api permissions")
}

if response.StatusCode == http.StatusUnprocessableEntity {
return nil, errors.HttpStatus(http.StatusUnprocessableEntity).New("StatusUnprocessableEntity error when testing api")
}

if response.StatusCode == http.StatusForbidden {
return nil, errors.HttpStatus(http.StatusForbidden).New("API Key need 'Read' and 'Configuration access' Access rights")
}

if response.StatusCode == http.StatusOK {
return &plugin.ApiResourceOutput{Body: nil, Status: http.StatusOK}, nil
}

return &plugin.ApiResourceOutput{Body: nil, Status: response.StatusCode}, errors.HttpStatus(response.StatusCode).Wrap(err, "could not validate connection")
}

// @Summary create opsgenie connection
// @Description Create Opsgenie connection
// @Tags plugins/opsgenie
// @Param body body models.OpsgenieConnection true "json body"
// @Success 200 {object} models.OpsgenieConnection
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/opsgenie/connections [POST]
func PostConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.OpsgenieConnection{}
err := connectionHelper.Create(connection, input)
if err != nil {
return nil, err
}
return &plugin.ApiResourceOutput{Body: connection, Status: http.StatusOK}, nil
}

// @Summary patch opsgenie connection
// @Description Patch Opsgenie connection
// @Tags plugins/opsgenie
// @Param body body models.OpsgenieConnection true "json body"
// @Success 200 {object} models.OpsgenieConnection
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/opsgenie/connections/{connectionId} [PATCH]
func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.OpsgenieConnection{}
err := connectionHelper.Patch(connection, input)
if err != nil {
return nil, err
}
return &plugin.ApiResourceOutput{Body: connection, Status: http.StatusOK}, nil
}

// @Summary delete opsgenie connection
// @Description Delete Opsgenie connection
// @Tags plugins/opsgenie
// @Success 200 {object} models.OpsgenieConnection
// @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/opsgenie/connections/{connectionId} [DELETE]
func DeleteConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
return connectionHelper.Delete(&models.OpsgenieConnection{}, input)
}

// @Summary list opsgenie connections
// @Description List Opsgenie connections
// @Tags plugins/opsgenie
// @Success 200 {object} models.OpsgenieConnection
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/opsgenie/connections [GET]
func ListConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
var connections []models.OpsgenieConnection
err := connectionHelper.List(&connections)
if err != nil {
return nil, err
}

return &plugin.ApiResourceOutput{Body: connections}, nil
}

// @Summary get opsgenie connection
// @Description Get Opsgenie connection
// @Tags plugins/opsgenie
// @Success 200 {object} models.OpsgenieConnection
// @Failure 400 {string} errcode.Error "Bad Request"
// @Failure 500 {string} errcode.Error "Internal Error"
// @Router /plugins/opsgenie/connections/{connectionId} [GET]
func GetConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.OpsgenieConnection{}
err := connectionHelper.First(connection, input.Params)
if err != nil {
return nil, err
}
return &plugin.ApiResourceOutput{Body: connection}, nil
}
59 changes: 59 additions & 0 deletions backend/plugins/opsgenie/api/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
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/opsgenie/models"
"github.com/go-playground/validator/v10"
)

var vld *validator.Validate
var connectionHelper *api.ConnectionApiHelper

var scopeHelper *api.ScopeApiHelper[models.OpsgenieConnection, models.Service, models.OpsenieScopeConfig]

var basicRes context.BasicRes

func Init(br context.BasicRes, p plugin.PluginMeta) {

basicRes = br
vld = validator.New()
connectionHelper = api.NewConnectionHelper(
basicRes,
vld,
p.Name(),
)
params := &api.ReflectionParameters{
ScopeIdFieldName: "Id",
ScopeIdColumnName: "id",
RawScopeParamName: "ScopeId",
SearchScopeParamName: "name",
}
scopeHelper = api.NewScopeHelper[models.OpsgenieConnection, models.Service, models.OpsenieScopeConfig](
basicRes,
vld,
connectionHelper,
api.NewScopeDatabaseHelperImpl[models.OpsgenieConnection, models.Service, models.OpsenieScopeConfig](
basicRes, connectionHelper, params),
params,
&api.ScopeHelperOptions{},
)
}
Loading

0 comments on commit 4453aa5

Please sign in to comment.