mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-27 00:19:39 +01:00
refactor: add schemas to client application registration (#1746)
This commit is contained in:
parent
286b016b04
commit
5fff523670
src
@ -79,6 +79,7 @@ import { emailSchema } from './spec/email-schema';
|
||||
import { strategySchema } from './spec/strategy-schema';
|
||||
import { strategiesSchema } from './spec/strategies-schema';
|
||||
import { upsertStrategySchema } from './spec/upsert-strategy-schema';
|
||||
import { clientApplicationSchema } from './spec/client-application-schema';
|
||||
|
||||
// All schemas in `openapi/spec` should be listed here.
|
||||
export const schemas = {
|
||||
@ -90,6 +91,7 @@ export const schemas = {
|
||||
apiTokensSchema,
|
||||
applicationSchema,
|
||||
applicationsSchema,
|
||||
clientApplicationSchema,
|
||||
cloneFeatureSchema,
|
||||
changePasswordSchema,
|
||||
constraintSchema,
|
||||
|
@ -0,0 +1,18 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`clientApplicationSchema no fields 1`] = `
|
||||
Object {
|
||||
"errors": Array [
|
||||
Object {
|
||||
"instancePath": "",
|
||||
"keyword": "required",
|
||||
"message": "must have required property 'appName'",
|
||||
"params": Object {
|
||||
"missingProperty": "appName",
|
||||
},
|
||||
"schemaPath": "#/required",
|
||||
},
|
||||
],
|
||||
"schema": "#/components/schemas/clientApplicationSchema",
|
||||
}
|
||||
`;
|
92
src/lib/openapi/spec/client-application-schema.test.ts
Normal file
92
src/lib/openapi/spec/client-application-schema.test.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { validateSchema } from '../validate';
|
||||
import { ClientApplicationSchema } from './client-application-schema';
|
||||
|
||||
test('clientApplicationSchema no fields', () => {
|
||||
expect(
|
||||
validateSchema('#/components/schemas/clientApplicationSchema', {}),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('clientApplicationSchema required fields', () => {
|
||||
const data: ClientApplicationSchema = {
|
||||
appName: '',
|
||||
interval: 0,
|
||||
started: 0,
|
||||
strategies: [''],
|
||||
};
|
||||
|
||||
expect(
|
||||
validateSchema('#/components/schemas/clientApplicationSchema', data),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test('clientApplicationSchema all fields', () => {
|
||||
const data: ClientApplicationSchema = {
|
||||
appName: '',
|
||||
instanceId: '',
|
||||
sdkVersion: '',
|
||||
environment: '',
|
||||
interval: 0,
|
||||
started: 0,
|
||||
strategies: [''],
|
||||
};
|
||||
|
||||
expect(
|
||||
validateSchema('#/components/schemas/clientApplicationSchema', data),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test('clientApplicationSchema go-sdk request', () => {
|
||||
const json = `{
|
||||
"appName": "x",
|
||||
"instanceId": "y",
|
||||
"sdkVersion": "unleash-client-go:3.3.1",
|
||||
"strategies": [
|
||||
"default",
|
||||
"applicationHostname",
|
||||
"gradualRolloutRandom",
|
||||
"gradualRolloutSessionId",
|
||||
"gradualRolloutUserId",
|
||||
"remoteAddress",
|
||||
"userWithId",
|
||||
"flexibleRollout"
|
||||
],
|
||||
"started": "2022-06-24T09:59:12.822607943+02:00",
|
||||
"interval": 1
|
||||
}`;
|
||||
|
||||
expect(
|
||||
validateSchema(
|
||||
'#/components/schemas/clientApplicationSchema',
|
||||
JSON.parse(json),
|
||||
),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test('clientApplicationSchema node-sdk request', () => {
|
||||
const json = `{
|
||||
"appName": "unleash-test-node-appName2",
|
||||
"instanceId": "unleash-test-node-instanceId",
|
||||
"sdkVersion": "unleash-client-node:3.11.0",
|
||||
"strategies": [
|
||||
"p",
|
||||
"default",
|
||||
"applicationHostname",
|
||||
"gradualRolloutRandom",
|
||||
"gradualRolloutUserId",
|
||||
"gradualRolloutSessionId",
|
||||
"userWithId",
|
||||
"remoteAddress",
|
||||
"flexibleRollout"
|
||||
],
|
||||
"started": "2022-06-24T09:54:03.649Z",
|
||||
"interval": 1000
|
||||
}`;
|
||||
|
||||
expect(
|
||||
validateSchema(
|
||||
'#/components/schemas/clientApplicationSchema',
|
||||
JSON.parse(json),
|
||||
),
|
||||
).toBeUndefined();
|
||||
});
|
41
src/lib/openapi/spec/client-application-schema.ts
Normal file
41
src/lib/openapi/spec/client-application-schema.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { FromSchema } from 'json-schema-to-ts';
|
||||
|
||||
export const clientApplicationSchema = {
|
||||
$id: '#/components/schemas/clientApplicationSchema',
|
||||
type: 'object',
|
||||
required: ['appName', 'interval', 'started', 'strategies'],
|
||||
properties: {
|
||||
appName: {
|
||||
type: 'string',
|
||||
},
|
||||
instanceId: {
|
||||
type: 'string',
|
||||
},
|
||||
sdkVersion: {
|
||||
type: 'string',
|
||||
},
|
||||
environment: {
|
||||
type: 'string',
|
||||
},
|
||||
interval: {
|
||||
type: 'number',
|
||||
},
|
||||
started: {
|
||||
oneOf: [
|
||||
{ type: 'string', format: 'date-time' },
|
||||
{ type: 'number' },
|
||||
],
|
||||
},
|
||||
strategies: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
} as const;
|
||||
|
||||
export type ClientApplicationSchema = FromSchema<
|
||||
typeof clientApplicationSchema
|
||||
>;
|
@ -9,27 +9,47 @@ import { IClientApp } from '../../types/model';
|
||||
import ApiUser from '../../types/api-user';
|
||||
import { ALL } from '../../types/models/api-token';
|
||||
import { NONE } from '../../types/permissions';
|
||||
import { OpenApiService } from '../../services/openapi-service';
|
||||
import { emptyResponse } from '../../openapi/spec/empty-response';
|
||||
import { createRequestSchema } from '../../openapi';
|
||||
import { ClientApplicationSchema } from '../../openapi/spec/client-application-schema';
|
||||
|
||||
export default class RegisterController extends Controller {
|
||||
logger: Logger;
|
||||
|
||||
metrics: ClientInstanceService;
|
||||
clientInstanceService: ClientInstanceService;
|
||||
|
||||
openApiService: OpenApiService;
|
||||
|
||||
constructor(
|
||||
{
|
||||
clientInstanceService,
|
||||
}: Pick<IUnleashServices, 'clientInstanceService'>,
|
||||
openApiService,
|
||||
}: Pick<IUnleashServices, 'clientInstanceService' | 'openApiService'>,
|
||||
config: IUnleashConfig,
|
||||
) {
|
||||
super(config);
|
||||
this.logger = config.getLogger('/api/client/register');
|
||||
this.metrics = clientInstanceService;
|
||||
this.clientInstanceService = clientInstanceService;
|
||||
this.openApiService = openApiService;
|
||||
|
||||
// NONE permission is not optimal here in terms of readability.
|
||||
this.post('/', this.handleRegister, NONE);
|
||||
this.route({
|
||||
method: 'post',
|
||||
path: '',
|
||||
handler: this.registerClientApplication,
|
||||
permission: NONE,
|
||||
middleware: [
|
||||
openApiService.validPath({
|
||||
tags: ['client'],
|
||||
operationId: 'registerClientApplication',
|
||||
requestBody: createRequestSchema('clientApplicationSchema'),
|
||||
responses: { 202: emptyResponse },
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
private resolveEnvironment(user: User, data: IClientApp) {
|
||||
private static resolveEnvironment(user: User, data: Partial<IClientApp>) {
|
||||
if (user instanceof ApiUser) {
|
||||
if (user.environment !== ALL) {
|
||||
return user.environment;
|
||||
@ -40,10 +60,13 @@ export default class RegisterController extends Controller {
|
||||
return 'default';
|
||||
}
|
||||
|
||||
async handleRegister(req: IAuthRequest, res: Response): Promise<void> {
|
||||
async registerClientApplication(
|
||||
req: IAuthRequest<unknown, void, ClientApplicationSchema>,
|
||||
res: Response<void>,
|
||||
): Promise<void> {
|
||||
const { body: data, ip: clientIp, user } = req;
|
||||
data.environment = this.resolveEnvironment(user, data);
|
||||
await this.metrics.registerClient(data, clientIp);
|
||||
data.environment = RegisterController.resolveEnvironment(user, data);
|
||||
await this.clientInstanceService.registerClient(data, clientIp);
|
||||
return res.status(202).end();
|
||||
}
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ export interface IClientApp {
|
||||
strategies?: string[] | Record<string, string>[];
|
||||
bucket?: any;
|
||||
count?: number;
|
||||
started?: number | Date;
|
||||
started?: string | number | Date;
|
||||
interval?: number;
|
||||
icon?: string;
|
||||
description?: string;
|
||||
|
@ -309,6 +309,49 @@ Object {
|
||||
],
|
||||
"type": "object",
|
||||
},
|
||||
"clientApplicationSchema": Object {
|
||||
"properties": Object {
|
||||
"appName": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"environment": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"instanceId": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"interval": Object {
|
||||
"type": "number",
|
||||
},
|
||||
"sdkVersion": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"started": Object {
|
||||
"oneOf": Array [
|
||||
Object {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"type": "number",
|
||||
},
|
||||
],
|
||||
},
|
||||
"strategies": Object {
|
||||
"items": Object {
|
||||
"type": "string",
|
||||
},
|
||||
"type": "array",
|
||||
},
|
||||
},
|
||||
"required": Array [
|
||||
"appName",
|
||||
"interval",
|
||||
"started",
|
||||
"strategies",
|
||||
],
|
||||
"type": "object",
|
||||
},
|
||||
"cloneFeatureSchema": Object {
|
||||
"properties": Object {
|
||||
"name": Object {
|
||||
@ -4908,6 +4951,30 @@ Object {
|
||||
],
|
||||
},
|
||||
},
|
||||
"/api/client/register": Object {
|
||||
"post": Object {
|
||||
"operationId": "registerClientApplication",
|
||||
"requestBody": Object {
|
||||
"content": Object {
|
||||
"application/json": Object {
|
||||
"schema": Object {
|
||||
"$ref": "#/components/schemas/clientApplicationSchema",
|
||||
},
|
||||
},
|
||||
},
|
||||
"description": "clientApplicationSchema",
|
||||
"required": true,
|
||||
},
|
||||
"responses": Object {
|
||||
"202": Object {
|
||||
"description": "emptyResponse",
|
||||
},
|
||||
},
|
||||
"tags": Array [
|
||||
"client",
|
||||
],
|
||||
},
|
||||
},
|
||||
"/auth/reset/password": Object {
|
||||
"post": Object {
|
||||
"operationId": "changePassword",
|
||||
|
Loading…
Reference in New Issue
Block a user