Merge pull request #15755 from Budibase/BUDI-9127/edit-delete-configs
Implement edit and delete config endpoints
This commit is contained in:
commit
e4eb09b203
|
@ -1,3 +1,3 @@
|
|||
import { CreateOAuth2ConfigRequest } from "@budibase/types"
|
||||
import { UpsertOAuth2ConfigRequest } from "@budibase/types"
|
||||
|
||||
export interface CreateOAuth2Config extends CreateOAuth2ConfigRequest {}
|
||||
export interface CreateOAuth2Config extends UpsertOAuth2ConfigRequest {}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import {
|
||||
FetchOAuth2ConfigsResponse,
|
||||
CreateOAuth2ConfigResponse,
|
||||
OAuth2ConfigResponse,
|
||||
CreateOAuth2ConfigRequest,
|
||||
UpsertOAuth2ConfigRequest,
|
||||
UpsertOAuth2ConfigResponse,
|
||||
} from "@budibase/types"
|
||||
import { BaseAPIClient } from "./types"
|
||||
|
||||
export interface OAuth2Endpoints {
|
||||
fetch: () => Promise<OAuth2ConfigResponse[]>
|
||||
create: (
|
||||
config: CreateOAuth2ConfigRequest
|
||||
) => Promise<CreateOAuth2ConfigResponse>
|
||||
config: UpsertOAuth2ConfigRequest
|
||||
) => Promise<UpsertOAuth2ConfigResponse>
|
||||
}
|
||||
|
||||
export const buildOAuth2Endpoints = (API: BaseAPIClient): OAuth2Endpoints => ({
|
||||
|
@ -33,8 +33,8 @@ export const buildOAuth2Endpoints = (API: BaseAPIClient): OAuth2Endpoints => ({
|
|||
*/
|
||||
create: async config => {
|
||||
return await API.post<
|
||||
CreateOAuth2ConfigRequest,
|
||||
CreateOAuth2ConfigResponse
|
||||
UpsertOAuth2ConfigRequest,
|
||||
UpsertOAuth2ConfigResponse
|
||||
>({
|
||||
url: `/api/oauth2`,
|
||||
body: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {
|
||||
CreateOAuth2ConfigRequest,
|
||||
CreateOAuth2ConfigResponse,
|
||||
UpsertOAuth2ConfigRequest,
|
||||
UpsertOAuth2ConfigResponse,
|
||||
Ctx,
|
||||
FetchOAuth2ConfigsResponse,
|
||||
OAuth2Config,
|
||||
|
@ -22,7 +22,7 @@ export async function fetch(ctx: Ctx<void, FetchOAuth2ConfigsResponse>) {
|
|||
}
|
||||
|
||||
export async function create(
|
||||
ctx: Ctx<CreateOAuth2ConfigRequest, CreateOAuth2ConfigResponse>
|
||||
ctx: Ctx<UpsertOAuth2ConfigRequest, UpsertOAuth2ConfigResponse>
|
||||
) {
|
||||
const { body } = ctx.request
|
||||
const newConfig: RequiredKeys<Omit<OAuth2Config, "id">> = {
|
||||
|
@ -36,3 +36,28 @@ export async function create(
|
|||
ctx.status = 201
|
||||
ctx.body = { config }
|
||||
}
|
||||
|
||||
export async function edit(
|
||||
ctx: Ctx<UpsertOAuth2ConfigRequest, UpsertOAuth2ConfigResponse>
|
||||
) {
|
||||
const { body } = ctx.request
|
||||
const toUpdate: RequiredKeys<OAuth2Config> = {
|
||||
id: ctx.params.id,
|
||||
name: body.name,
|
||||
url: body.url,
|
||||
clientId: ctx.clientId,
|
||||
clientSecret: ctx.clientSecret,
|
||||
}
|
||||
|
||||
const config = await sdk.oauth2.update(toUpdate)
|
||||
ctx.body = { config }
|
||||
}
|
||||
|
||||
export async function remove(
|
||||
ctx: Ctx<UpsertOAuth2ConfigRequest, UpsertOAuth2ConfigResponse>
|
||||
) {
|
||||
const configToRemove = ctx.params.id
|
||||
|
||||
await sdk.oauth2.remove(configToRemove)
|
||||
ctx.status = 204
|
||||
}
|
||||
|
|
|
@ -1,8 +1,22 @@
|
|||
import Router from "@koa/router"
|
||||
import { PermissionType } from "@budibase/types"
|
||||
import { middleware } from "@budibase/backend-core"
|
||||
import authorized from "../../middleware/authorized"
|
||||
|
||||
import * as controller from "../controllers/oauth2"
|
||||
import Joi from "joi"
|
||||
|
||||
function oAuth2ConfigValidator() {
|
||||
return middleware.joiValidator.body(
|
||||
Joi.object({
|
||||
name: Joi.string().required(),
|
||||
url: Joi.string().required(),
|
||||
clientId: Joi.string().required(),
|
||||
clientSecret: Joi.string().required(),
|
||||
}),
|
||||
{ allowUnknown: false }
|
||||
)
|
||||
}
|
||||
|
||||
const router: Router = new Router()
|
||||
|
||||
|
@ -10,7 +24,19 @@ router.get("/api/oauth2", authorized(PermissionType.BUILDER), controller.fetch)
|
|||
router.post(
|
||||
"/api/oauth2",
|
||||
authorized(PermissionType.BUILDER),
|
||||
oAuth2ConfigValidator(),
|
||||
controller.create
|
||||
)
|
||||
router.put(
|
||||
"/api/oauth2/:id",
|
||||
authorized(PermissionType.BUILDER),
|
||||
oAuth2ConfigValidator(),
|
||||
controller.edit
|
||||
)
|
||||
router.delete(
|
||||
"/api/oauth2/:id",
|
||||
authorized(PermissionType.BUILDER),
|
||||
controller.remove
|
||||
)
|
||||
|
||||
export default router
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import { CreateOAuth2ConfigRequest, VirtualDocumentType } from "@budibase/types"
|
||||
import {
|
||||
OAuth2Config,
|
||||
UpsertOAuth2ConfigRequest,
|
||||
VirtualDocumentType,
|
||||
} from "@budibase/types"
|
||||
import * as setup from "./utilities"
|
||||
import { generator } from "@budibase/backend-core/tests"
|
||||
import _ from "lodash/fp"
|
||||
|
||||
describe("/oauth2", () => {
|
||||
let config = setup.getConfig()
|
||||
|
||||
function makeOAuth2Config(): CreateOAuth2ConfigRequest {
|
||||
function makeOAuth2Config(): UpsertOAuth2ConfigRequest {
|
||||
return {
|
||||
name: generator.guid(),
|
||||
url: generator.url(),
|
||||
|
@ -92,4 +97,96 @@ describe("/oauth2", () => {
|
|||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
let existingConfigs: OAuth2Config[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
existingConfigs = []
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const oauth2Config = makeOAuth2Config()
|
||||
const result = await config.api.oauth2.create(oauth2Config)
|
||||
|
||||
existingConfigs.push({ ...oauth2Config, id: result.config.id })
|
||||
}
|
||||
})
|
||||
|
||||
it("can update an existing configuration", async () => {
|
||||
const { id: configId, ...configData } = _.sample(existingConfigs)!
|
||||
|
||||
await config.api.oauth2.update(configId, {
|
||||
...configData,
|
||||
name: "updated name",
|
||||
})
|
||||
|
||||
const response = await config.api.oauth2.fetch()
|
||||
expect(response.configs).toHaveLength(existingConfigs.length)
|
||||
expect(response.configs).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
id: configId,
|
||||
name: "updated name",
|
||||
url: configData.url,
|
||||
},
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("throw if config not found", async () => {
|
||||
await config.api.oauth2.update("unexisting", makeOAuth2Config(), {
|
||||
status: 404,
|
||||
body: { message: "OAuth2 config with id 'unexisting' not found." },
|
||||
})
|
||||
})
|
||||
|
||||
it("throws if trying to use an existing name", async () => {
|
||||
const [config1, config2] = _.sampleSize(2, existingConfigs)
|
||||
const { id: configId, ...configData } = config1
|
||||
|
||||
await config.api.oauth2.update(
|
||||
configId,
|
||||
{
|
||||
...configData,
|
||||
name: config2.name,
|
||||
},
|
||||
{
|
||||
status: 400,
|
||||
body: {
|
||||
message: `OAuth2 config with name '${config2.name}' is already taken.`,
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("delete", () => {
|
||||
let existingConfigs: OAuth2Config[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
existingConfigs = []
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const oauth2Config = makeOAuth2Config()
|
||||
const result = await config.api.oauth2.create(oauth2Config)
|
||||
|
||||
existingConfigs.push({ ...oauth2Config, id: result.config.id })
|
||||
}
|
||||
})
|
||||
|
||||
it("can delete an existing configuration", async () => {
|
||||
const { id: configId } = _.sample(existingConfigs)!
|
||||
|
||||
await config.api.oauth2.delete(configId, { status: 204 })
|
||||
|
||||
const response = await config.api.oauth2.fetch()
|
||||
expect(response.configs).toHaveLength(existingConfigs.length - 1)
|
||||
expect(response.configs.find(c => c.id === configId)).toBeUndefined()
|
||||
})
|
||||
|
||||
it("throw if config not found", async () => {
|
||||
await config.api.oauth2.delete("unexisting", {
|
||||
status: 404,
|
||||
body: { message: "OAuth2 config with id 'unexisting' not found." },
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -48,3 +48,49 @@ export async function get(id: string): Promise<OAuth2Config | undefined> {
|
|||
const doc = await getDocument()
|
||||
return doc?.configs?.[id]
|
||||
}
|
||||
|
||||
export async function update(config: OAuth2Config): Promise<OAuth2Config> {
|
||||
const db = context.getAppDB()
|
||||
const doc: OAuth2Configs = (await getDocument(db)) ?? {
|
||||
_id: DocumentType.OAUTH2_CONFIG,
|
||||
configs: {},
|
||||
}
|
||||
|
||||
if (!doc.configs[config.id]) {
|
||||
throw new HTTPError(`OAuth2 config with id '${config.id}' not found.`, 404)
|
||||
}
|
||||
|
||||
if (
|
||||
Object.values(doc.configs).find(
|
||||
c => c.name === config.name && c.id !== config.id
|
||||
)
|
||||
) {
|
||||
throw new HTTPError(
|
||||
`OAuth2 config with name '${config.name}' is already taken.`,
|
||||
400
|
||||
)
|
||||
}
|
||||
|
||||
doc.configs[config.id] = {
|
||||
...config,
|
||||
}
|
||||
|
||||
await db.put(doc)
|
||||
return doc.configs[config.id]
|
||||
}
|
||||
|
||||
export async function remove(configId: string): Promise<void> {
|
||||
const db = context.getAppDB()
|
||||
const doc: OAuth2Configs = (await getDocument(db)) ?? {
|
||||
_id: DocumentType.OAUTH2_CONFIG,
|
||||
configs: {},
|
||||
}
|
||||
|
||||
if (!doc.configs[configId]) {
|
||||
throw new HTTPError(`OAuth2 config with id '${configId}' not found.`, 404)
|
||||
}
|
||||
|
||||
delete doc.configs[configId]
|
||||
|
||||
await db.put(doc)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {
|
||||
CreateOAuth2ConfigRequest,
|
||||
CreateOAuth2ConfigResponse,
|
||||
UpsertOAuth2ConfigRequest,
|
||||
UpsertOAuth2ConfigResponse,
|
||||
FetchOAuth2ConfigsResponse,
|
||||
} from "@budibase/types"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
|
@ -13,10 +13,10 @@ export class OAuth2API extends TestAPI {
|
|||
}
|
||||
|
||||
create = async (
|
||||
body: CreateOAuth2ConfigRequest,
|
||||
body: UpsertOAuth2ConfigRequest,
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
return await this._post<CreateOAuth2ConfigResponse>("/api/oauth2", {
|
||||
return await this._post<UpsertOAuth2ConfigResponse>("/api/oauth2", {
|
||||
body,
|
||||
expectations: {
|
||||
status: expectations?.status ?? 201,
|
||||
|
@ -24,4 +24,21 @@ export class OAuth2API extends TestAPI {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
update = async (
|
||||
id: string,
|
||||
body: UpsertOAuth2ConfigRequest,
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
return await this._put<UpsertOAuth2ConfigResponse>(`/api/oauth2/${id}`, {
|
||||
body,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
delete = async (id: string, expectations?: Expectations) => {
|
||||
return await this._delete<void>(`/api/oauth2/${id}`, {
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ export interface FetchOAuth2ConfigsResponse {
|
|||
configs: OAuth2ConfigResponse[]
|
||||
}
|
||||
|
||||
export interface CreateOAuth2ConfigRequest {
|
||||
export interface UpsertOAuth2ConfigRequest {
|
||||
name: string
|
||||
url: string
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
}
|
||||
|
||||
export interface CreateOAuth2ConfigResponse {
|
||||
export interface UpsertOAuth2ConfigResponse {
|
||||
config: OAuth2ConfigResponse
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue