Skip to content

Commit 507170f

Browse files
authored
feat: migrate teamsettings to v2 endpoint (#699)
* feat: migrate teamsettings to v2 * feat: fix tests * fix: saving team settings file correctly * feat: update api authz test to not load values * feat: add openapi schema * fix: get teams endpoint * fix: get team endpoint * fix: review comments * fix: add ds_store to gitignore * fix: change postman collection run
1 parent 92965a8 commit 507170f

15 files changed

Lines changed: 407 additions & 108 deletions

.github/workflows/postman.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
- name: Run API tests
7272
#The UUID's are the order the requests are executed in. So that the POST are executed first
7373
run: |
74-
postman collection run 44183872-1d1f2dba-6c47-4764-a847-a6bca216ecea -e 43715313-d380e919-5142-44e6-b52c-02f8b04da8fa -i 44183872-be57d2e3-650d-4494-ba65-6486b68082b0 -i 44183872-f50a0c10-89ef-4352-8a56-3093811b3311 -i 44183872-0a01d698-5d93-4ebf-bd52-6cd7b1af9735 -i 44183872-6229c491-250d-49a4-8388-7a2353129925 -i 44183872-07ac4c2a-037a-4002-a5d1-2e990cfcb1b0 -i 44183872-297df31a-4560-4f27-aa06-31437d59dd7c -i 44183872-47db1e8a-9245-49a6-a6ff-7044b95d8ad3 -i 44183872-beeb6115-2e7b-4a7b-bcd8-eb605a5cd3fc -i 44183872-eff09537-0ab0-4914-a15d-a08270360574 -i 44183872-5003d09d-439f-456a-8864-e6e1f62db807 -i 44183872-a25b9ea2-1c64-4bf1-a5e4-dc905f1a9a6a -i 44183872-7082300f-e721-43f3-9828-1f3bbd25ab0a -i 44183872-6631ed83-0ff4-4515-8fa6-2d57c2959e77 -i 44183872-0a483150-4d00-49f1-8f9a-31ab467f79ec -i 44183872-15ce6497-700f-49f9-949a-ec22bf169c3f -i 44183872-c4097a49-9f17-4d6e-8ff2-a1a1fcec2dff -i 44183872-e357451a-9526-4b57-bb5b-1190e0d6788f -i 44183872-9abfae13-8983-453e-85c5-a3951dddd8e6 -i 44183872-3db33396-563e-4f0a-b074-fc74680c72bd -i 44183872-61b903cb-d623-4b6d-85a3-ab2f7b0575d8 -i 44183872-34fb39a5-5bc2-4528-886a-49e4caf2d9b5 -i 44183872-e34ef3b6-f979-4407-8e69-b972be4d5184 -i 44183872-da7d9d0c-3ab1-457a-a56f-2fa49b151f1d -i 44183872-d7d08dac-364f-4f94-9c48-64dbd77fcadd -i 44183872-a6fad9da-3fae-4c7c-9e15-ec6d67a79b78 -i 44183872-79ff962a-4117-4b91-8cb6-2653b6d5a687 -i 44183872-225a21ca-9106-484c-83b5-1257bc6433e7 -i 44183872-7ac118a2-7f55-4996-a2e6-0b34eb7058ae -i 44183872-4d49d6c6-6367-4ec4-8b4c-6c6154118a3a -i 44183872-dd1b76be-26ee-4b5b-913b-8ce465778229 -i 44183872-ee200410-27ac-4c28-a469-21f487dfbaec -i 44183872-c7676daa-2257-47d6-88ca-178d433a5f28 -i 44183872-f162b5b5-1428-4f9e-8ea5-6bb3b7980b48 -i 44183872-048cddb0-5671-4abb-ac9f-8985c8e4ed6d -i 44183872-a9b4f64a-60be-43e5-bb55-6e16c85c9215 -i 44183872-a26a7e26-0df8-4ad9-95ce-ce168010f631 -i 44183872-9feaad53-dac9-475d-bd71-192d0c844e59 -i 44183872-d09732cd-0395-4b25-85c9-01b9c6a7d43c -i 44183872-1c7dca5b-58aa-4bb6-9224-d703e88b17cd -i 44183872-d183edcc-b144-4e80-9e10-72bf49f535d0 -i 44183872-7451bc15-e0cc-44a8-946b-f09bf024c9eb -i 44183872-3d6e845b-8a82-43b4-a6be-e30effc16d46 -i 44183872-d81fa269-56a3-4eef-934e-39a30e9e5dfd -i 44183872-0ed3bf3e-d78b-4818-bb47-ded64e22525f -i 44183872-fe220b54-58ce-427f-bd19-2f2716cfaa65
74+
postman collection run 44183872-1d1f2dba-6c47-4764-a847-a6bca216ecea -e 43715313-d380e919-5142-44e6-b52c-02f8b04da8fa -i 44183872-16af2563-f99c-41c6-81c5-6d68d8aaef42
7575
- name: Upload logs
7676
if: always()
7777
uses: actions/upload-artifact@v4

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ secrets.*.yaml.dec
4949
#intelij
5050
.idea/
5151

52+
# DS_Store
53+
src/.DS_Store
5254
.DS_Store
5355

5456
src/.DS_Store

src/api.authz.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-empty-function */
22

3-
import { Express } from 'express'
43
import { mockDeep } from 'jest-mock-extended'
54
import { initApp, loadSpec } from 'src/app'
65
import getToken from 'src/fixtures/jwt'
@@ -9,8 +8,10 @@ import request, { SuperAgentTest } from 'supertest'
98
import { HttpError } from './error'
109
import { Git } from './git'
1110
import { getSessionStack } from './middleware'
12-
import { App, CodeRepo, SealedSecret } from './otomi-models'
11+
import { App, CodeRepo, Repo, SealedSecret } from './otomi-models'
1312
import * as getValuesSchemaModule from './utils'
13+
import { RepoService } from './services/RepoService'
14+
import { Express } from 'express'
1415

1516
const platformAdminToken = getToken(['platform-admin'])
1617
const teamAdminToken = getToken(['team-admin', 'team-team1'])
@@ -42,14 +43,14 @@ describe('API authz tests', () => {
4243
_otomiStack.git = mockDeep<Git>()
4344
_otomiStack.doDeployment = jest.fn().mockImplementation(() => Promise.resolve())
4445
_otomiStack.transformApps = jest.fn().mockReturnValue([])
45-
await _otomiStack.initRepo()
46+
_otomiStack.repoService = new RepoService({} as Repo)
4647
otomiStack = _otomiStack as jest.Mocked<OtomiStack>
4748

4849
otomiStack.saveTeam = jest.fn().mockResolvedValue(undefined)
4950
otomiStack.doDeployment = jest.fn().mockImplementation(() => Promise.resolve())
5051
otomiStack.doRepoDeployment = jest.fn().mockImplementation(() => Promise.resolve())
5152
otomiStack.doTeamDeployment = jest.fn().mockImplementation(() => Promise.resolve())
52-
await otomiStack.loadValues()
53+
otomiStack.isLoaded = true
5354
await otomiStack.createTeam({ name: 'team1' })
5455
await otomiStack.createTeam({ name: 'team2' })
5556
app = await initApp(otomiStack)
@@ -717,6 +718,7 @@ describe('API authz tests', () => {
717718
})
718719

719720
test('team member can test code repository url', async () => {
721+
jest.spyOn(otomiStack, 'getTestRepoConnect').mockResolvedValue({})
720722
await agent
721723
.get(`/v1/testRepoConnect`)
722724
.query({ url: data.repositoryUrl })

src/api/v2/teams.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Debug from 'debug'
2+
import { Operation, OperationHandlerArray } from 'express-openapi'
3+
import { AplTeamSettingsRequest, OpenApiRequestExt } from 'src/otomi-models'
4+
5+
const debug = Debug('otomi:api:v2:teams')
6+
7+
export default function (): OperationHandlerArray {
8+
const get: Operation = [
9+
({ otomi }: OpenApiRequestExt, res): void => {
10+
debug('getTeams')
11+
// we filter admin team here as it is not for console
12+
const teams = otomi.getAplTeams() || []
13+
14+
res.json(teams)
15+
},
16+
]
17+
const post: Operation = [
18+
async ({ otomi, body }: OpenApiRequestExt, res): Promise<void> => {
19+
debug('createTeam')
20+
const data = await otomi.createAplTeam(body as AplTeamSettingsRequest)
21+
res.json(data)
22+
},
23+
]
24+
const api = {
25+
get,
26+
post,
27+
}
28+
return api
29+
}

src/api/v2/teams/{teamId}.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Debug from 'debug'
2+
import { Operation, OperationHandlerArray } from 'express-openapi'
3+
import { AplTeamSettingsRequest, OpenApiRequestExt } from 'src/otomi-models'
4+
5+
const debug = Debug('otomi:api:v2:teams')
6+
7+
export default function (): OperationHandlerArray {
8+
const del: Operation = [
9+
async ({ otomi, params: { teamId } }: OpenApiRequestExt, res): Promise<void> => {
10+
debug(`deleteTeam(${teamId})`)
11+
await otomi.deleteTeam(teamId)
12+
res.json({})
13+
},
14+
]
15+
const get: Operation = [
16+
({ otomi, params: { teamId } }: OpenApiRequestExt, res): void => {
17+
debug(`getTeam(${teamId})`)
18+
const data = otomi.getAplTeam(teamId)
19+
res.json(data)
20+
},
21+
]
22+
const put: Operation = [
23+
async ({ otomi, params: { teamId }, body }: OpenApiRequestExt, res): Promise<void> => {
24+
debug(`editTeam(${teamId})`)
25+
const data = await otomi.editAplTeam(teamId, body as AplTeamSettingsRequest)
26+
res.json(data)
27+
},
28+
]
29+
const patch: Operation = [
30+
async ({ otomi, params: { teamId }, body }: OpenApiRequestExt, res): Promise<void> => {
31+
debug(`editTeam(${teamId}, patch)`)
32+
const data = await otomi.editAplTeam(teamId, body as AplTeamSettingsRequest, true)
33+
res.json(data)
34+
},
35+
]
36+
const api = {
37+
delete: del,
38+
get,
39+
put,
40+
patch,
41+
}
42+
return api
43+
}

src/app.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export const getAppList = (): string[] => {
148148
return appsSchema.enum as string[]
149149
}
150150

151-
export async function initApp(inOtomiStack?: OtomiStack | undefined) {
151+
export async function initApp(inOtomiStack?: OtomiStack) {
152152
const lightship = createLightship()
153153
const app = express()
154154
const apiRoutesPath = path.resolve(__dirname, 'api')
@@ -166,12 +166,14 @@ export async function initApp(inOtomiStack?: OtomiStack | undefined) {
166166
})
167167
}
168168
// Transforms the interval to minutes
169-
const gitCheckVersionInterval = env.CHECK_LATEST_COMMIT_INTERVAL * 60 * 1000
170-
setInterval(async function () {
171-
await checkAgainstGitea()
172-
}, gitCheckVersionInterval)
169+
if (!env.isTest) {
170+
const gitCheckVersionInterval = env.CHECK_LATEST_COMMIT_INTERVAL * 60 * 1000
171+
setInterval(async function () {
172+
await checkAgainstGitea()
173+
}, gitCheckVersionInterval)
174+
}
173175
let server: Server | undefined
174-
if (!inOtomiStack) {
176+
if (!inOtomiStack && !env.isTest) {
175177
// initialize full server
176178
const { PORT = 8080 } = process.env
177179
server = app
@@ -190,19 +192,20 @@ export async function initApp(inOtomiStack?: OtomiStack | undefined) {
190192
})
191193
}
192194

193-
// emit resource status every 10 seconds
194-
const emitResourceStatusInterval = 10 * 1000
195-
const errorSet = new Set()
196-
setInterval(async function () {
197-
try {
198-
await resourceStatus(errorSet)
199-
} catch (e) {
200-
debug(e)
201-
}
202-
}, emitResourceStatusInterval)
203-
204-
// and register session middleware
195+
if (!env.isTest) {
196+
// emit resource status every 10 seconds
197+
const emitResourceStatusInterval = 10 * 1000
198+
const errorSet = new Set()
199+
setInterval(async function () {
200+
try {
201+
await resourceStatus(errorSet)
202+
} catch (e) {
203+
debug(e)
204+
}
205+
}, emitResourceStatusInterval)
206+
}
205207
app.use(sessionMiddleware(server as Server))
208+
// and register session middleware
206209
// now we can initialize the more specific routes
207210
initialize({
208211
// @ts-ignore

src/openapi/api.yaml

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ paths:
111111
application/json:
112112
schema:
113113
$ref: '#/components/schemas/Team'
114+
114115
'/v1/teams/{teamId}':
115116
parameters:
116117
- $ref: '#/components/parameters/teamParams'
@@ -154,6 +155,83 @@ paths:
154155
'200':
155156
description: Successfully deleted a team
156157

158+
/v2/teams:
159+
get:
160+
operationId: getAplTeams
161+
description: Get teams collection
162+
x-aclSchema: Team
163+
responses:
164+
'200':
165+
description: Successfully obtained teams collection
166+
content:
167+
application/json:
168+
schema:
169+
type: array
170+
items:
171+
$ref: '#/components/schemas/AplTeamSettingsResponse'
172+
post:
173+
operationId: createAplTeam
174+
description: Create a team
175+
x-aclSchema: Team
176+
requestBody:
177+
content:
178+
application/json:
179+
schema:
180+
$ref: '#/components/schemas/AplTeamSettingsRequest'
181+
description: Team object that needs to be added to the collection
182+
required: true
183+
responses:
184+
<<: *DefaultPostResponses
185+
'200':
186+
description: Successfully obtained teams collection
187+
content:
188+
application/json:
189+
schema:
190+
$ref: '#/components/schemas/AplTeamSettingsResponse'
191+
192+
'/v2/teams/{teamId}':
193+
parameters:
194+
- $ref: '#/components/parameters/teamParams'
195+
get:
196+
operationId: getAplTeam
197+
description: Get a specific team
198+
x-aclSchema: Team
199+
responses:
200+
<<: *DefaultGetResponses
201+
'200':
202+
description: Successfully obtained team
203+
content:
204+
application/json:
205+
schema:
206+
$ref: '#/components/schemas/AplTeamSettingsResponse'
207+
put:
208+
operationId: editAplTeam
209+
description: Edit a team
210+
x-aclSchema: Team
211+
requestBody:
212+
content:
213+
application/json:
214+
schema:
215+
$ref: '#/components/schemas/AplTeamSettingsRequest'
216+
description: Team object that contains updated values
217+
required: true
218+
responses:
219+
<<: *DefaultGetResponses
220+
'200':
221+
description: Successfully edited team
222+
content:
223+
application/json:
224+
schema:
225+
$ref: '#/components/schemas/AplTeamSettingsResponse'
226+
delete:
227+
operationId: deleteAplTeam
228+
description: Delete team
229+
x-aclSchema: Team
230+
responses:
231+
<<: *DefaultGetResponses
232+
'200':
233+
description: Successfully deleted a team
234+
157235
/v1/services:
158236
get:
159237
operationId: getAllServices
@@ -2850,7 +2928,7 @@ components:
28502928
properties:
28512929
kind:
28522930
type: string
2853-
enum: [AplTeamSettings]
2931+
enum: [AplTeamSettingSet]
28542932
spec:
28552933
$ref: 'team.yaml#/AplTeamSpec'
28562934
required:
@@ -2859,12 +2937,12 @@ components:
28592937
AplTeamSettingsRequest:
28602938
allOf:
28612939
- $ref: '#/components/schemas/AplTeamSettings'
2862-
- $ref: '#/components/schemas/aplMetadata'
2940+
- $ref: '#/components/schemas/aplTeamMetadata'
28632941
AplTeamSettingsResponse:
28642942
type: object
28652943
allOf:
28662944
- $ref: '#/components/schemas/AplTeamSettings'
2867-
- $ref: '#/components/schemas/aplMetadata'
2945+
- $ref: '#/components/schemas/aplTeamMetadata'
28682946
- $ref: '#/components/schemas/aplStatusResponse'
28692947
AplWorkload:
28702948
type: object

src/otomi-models.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export type RepoBranches = components['schemas']['RepoBranches']
3434
export type TestRepoConnect = components['schemas']['TestRepoConnect']
3535
export type InternalRepoUrls = components['schemas']['InternalRepoUrls']
3636
export type Team = components['schemas']['Team']
37+
export type AplTeamSettingsRequest = components['schemas']['AplTeamSettingsRequest']
38+
export type AplTeamSettingsResponse = components['schemas']['AplTeamSettingsResponse']
3739
export type TeamSelfService = components['schemas']['Team']['selfService']
3840
export type SessionUser = components['schemas']['SessionUser']
3941
export type UserAuthz = components['schemas']['SessionUser']['authz']
@@ -78,6 +80,7 @@ export type AplRequestObject =
7880
| AplSecretRequest
7981
| AplServiceRequest
8082
| AplWorkloadRequest
83+
| AplTeamSettingsRequest
8184
export type AplResponseObject =
8285
| AplBackupResponse
8386
| AplBuildResponse
@@ -88,6 +91,7 @@ export type AplResponseObject =
8891
| AplSecretResponse
8992
| AplServiceResponse
9093
| AplWorkloadResponse
94+
| AplTeamSettingsResponse
9195
export type AplKind =
9296
| 'AplApp'
9397
| 'AplAlertSet'
@@ -254,6 +258,6 @@ export interface TeamConfig {
254258
projects: AplProjectResponse[]
255259
sealedsecrets: AplSecretResponse[]
256260
services: AplServiceResponse[]
257-
settings: Team
261+
settings: AplTeamSettingsResponse
258262
workloads: AplWorkloadResponse[]
259263
}

0 commit comments

Comments
 (0)