/
scope.go
239 lines (225 loc) · 8.15 KB
/
scope.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
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 (
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"github.com/apache/incubator-devlake/errors"
"github.com/apache/incubator-devlake/plugins/core"
"github.com/apache/incubator-devlake/plugins/core/dal"
"github.com/apache/incubator-devlake/plugins/helper"
"github.com/apache/incubator-devlake/plugins/jira/models"
"github.com/apache/incubator-devlake/plugins/jira/tasks"
"github.com/apache/incubator-devlake/plugins/jira/tasks/apiv2models"
"github.com/mitchellh/mapstructure"
)
type apiBoard struct {
models.JiraBoard
TransformationRuleName string `json:"transformationRuleName,omitempty"`
}
type req struct {
Data []*models.JiraBoard `json:"data"`
}
// PutScope create or update jira board
// @Summary create or update jira board
// @Description Create or update Jira board
// @Tags plugins/jira
// @Accept application/json
// @Param connectionId path int false "connection ID"
// @Param scope body req true "json"
// @Success 200 {object} []models.JiraBoard
// @Failure 400 {object} shared.ApiBody "Bad Request"
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/jira/connections/{connectionId}/scopes [PUT]
func PutScope(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) {
connectionId, _ := extractParam(input.Params)
if connectionId == 0 {
return nil, errors.BadInput.New("invalid connectionId")
}
var boards req
err := errors.Convert(mapstructure.Decode(input.Body, &boards))
if err != nil {
return nil, errors.BadInput.Wrap(err, "decoding Jira board error")
}
keeper := make(map[uint64]struct{})
for _, board := range boards.Data {
if _, ok := keeper[board.BoardId]; ok {
return nil, errors.BadInput.New("duplicated item")
} else {
keeper[board.BoardId] = struct{}{}
}
board.ConnectionId = connectionId
err = verifyBoard(board)
if err != nil {
return nil, err
}
}
err = basicRes.GetDal().CreateOrUpdate(boards.Data)
if err != nil {
return nil, errors.Default.Wrap(err, "error on saving JiraBoard")
}
return &core.ApiResourceOutput{Body: boards.Data, Status: http.StatusOK}, nil
}
// UpdateScope patch to jira board
// @Summary patch to jira board
// @Description patch to jira board
// @Tags plugins/jira
// @Accept application/json
// @Param connectionId path int false "connection ID"
// @Param boardId path int false "board ID"
// @Param scope body models.JiraBoard true "json"
// @Success 200 {object} models.JiraBoard
// @Failure 400 {object} shared.ApiBody "Bad Request"
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/jira/connections/{connectionId}/scopes/{boardId} [PATCH]
func UpdateScope(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) {
connectionId, boardId := extractParam(input.Params)
if connectionId*boardId == 0 {
return nil, errors.BadInput.New("invalid connectionId or boardId")
}
var board models.JiraBoard
err := basicRes.GetDal().First(&board, dal.Where("connection_id = ? AND board_id = ?", connectionId, boardId))
if err != nil {
return nil, errors.Default.Wrap(err, "getting JiraBoard error")
}
err = helper.DecodeMapStruct(input.Body, &board)
if err != nil {
return nil, errors.Default.Wrap(err, "patch jira board error")
}
err = verifyBoard(&board)
if err != nil {
return nil, err
}
err = basicRes.GetDal().Update(board)
if err != nil {
return nil, errors.Default.Wrap(err, "error on saving JiraBoard")
}
return &core.ApiResourceOutput{Body: board, Status: http.StatusOK}, nil
}
// GetScopeList get Jira boards
// @Summary get Jira boards
// @Description get Jira boards
// @Tags plugins/jira
// @Param connectionId path int false "connection ID"
// @Param pageSize query int false "page size, default 50"
// @Param page query int false "page size, default 1"
// @Success 200 {object} []apiBoard
// @Failure 400 {object} shared.ApiBody "Bad Request"
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/jira/connections/{connectionId}/scopes/ [GET]
func GetScopeList(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) {
var boards []models.JiraBoard
connectionId, _ := extractParam(input.Params)
if connectionId == 0 {
return nil, errors.BadInput.New("invalid path params")
}
limit, offset := helper.GetLimitOffset(input.Query, "pageSize", "page")
err := basicRes.GetDal().All(&boards, dal.Where("connection_id = ?", connectionId), dal.Limit(limit), dal.Offset(offset))
if err != nil {
return nil, err
}
var ruleIds []uint64
for _, board := range boards {
if board.TransformationRuleId > 0 {
ruleIds = append(ruleIds, board.TransformationRuleId)
}
}
var rules []models.JiraTransformationRule
if len(ruleIds) > 0 {
err = basicRes.GetDal().All(&rules, dal.Where("id IN (?)", ruleIds))
if err != nil {
return nil, err
}
}
names := make(map[uint64]string)
for _, rule := range rules {
names[rule.ID] = rule.Name
}
var apiBoards []apiBoard
for _, board := range boards {
apiBoards = append(apiBoards, apiBoard{board, names[board.TransformationRuleId]})
}
return &core.ApiResourceOutput{Body: apiBoards, Status: http.StatusOK}, nil
}
// GetScope get one Jira board
// @Summary get one Jira board
// @Description get one Jira board
// @Tags plugins/jira
// @Param connectionId path int false "connection ID"
// @Param boardId path int false "board ID"
// @Success 200 {object} models.JiraBoard
// @Failure 400 {object} shared.ApiBody "Bad Request"
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/jira/connections/{connectionId}/scopes/{boardId} [GET]
func GetScope(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) {
var board models.JiraBoard
connectionId, boardId := extractParam(input.Params)
if connectionId*boardId == 0 {
return nil, errors.BadInput.New("invalid path params")
}
db := basicRes.GetDal()
err := db.First(&board, dal.Where("connection_id = ? AND board_id = ?", connectionId, boardId))
if db.IsErrorNotFound(err) {
return nil, errors.NotFound.New("record not found")
}
if err != nil {
return nil, err
}
var rule models.JiraTransformationRule
if board.TransformationRuleId > 0 {
err = basicRes.GetDal().First(&rule, dal.Where("id = ?", board.TransformationRuleId))
if err != nil {
return nil, err
}
}
return &core.ApiResourceOutput{Body: apiBoard{board, rule.Name}, Status: http.StatusOK}, nil
}
func extractParam(params map[string]string) (uint64, uint64) {
connectionId, _ := strconv.ParseUint(params["connectionId"], 10, 64)
boardId, _ := strconv.ParseUint(params["boardId"], 10, 64)
return connectionId, boardId
}
func verifyBoard(board *models.JiraBoard) errors.Error {
if board.ConnectionId == 0 {
return errors.BadInput.New("invalid connectionId")
}
if board.BoardId == 0 {
return errors.BadInput.New("invalid boardId")
}
return nil
}
func GetApiJira(op *tasks.JiraOptions, apiClient helper.ApiClientGetter) (*apiv2models.Board, errors.Error) {
boardRes := &apiv2models.Board{}
res, err := apiClient.Get(fmt.Sprintf("agile/1.0/board/%d", op.BoardId), nil, nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("unexpected status code when requesting repo detail from %s", res.Request.URL.String()))
}
body, err := errors.Convert01(io.ReadAll(res.Body))
if err != nil {
return nil, err
}
err = errors.Convert(json.Unmarshal(body, boardRes))
if err != nil {
return nil, err
}
return boardRes, nil
}