mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-14 01:16:17 +02:00
refactor: add schemas to client application registration (#1746)
This commit is contained in:
parent
286b016b04
commit
5fff523670
@ -79,6 +79,7 @@ import { emailSchema } from './spec/email-schema';
|
|||||||
import { strategySchema } from './spec/strategy-schema';
|
import { strategySchema } from './spec/strategy-schema';
|
||||||
import { strategiesSchema } from './spec/strategies-schema';
|
import { strategiesSchema } from './spec/strategies-schema';
|
||||||
import { upsertStrategySchema } from './spec/upsert-strategy-schema';
|
import { upsertStrategySchema } from './spec/upsert-strategy-schema';
|
||||||
|
import { clientApplicationSchema } from './spec/client-application-schema';
|
||||||
|
|
||||||
// All schemas in `openapi/spec` should be listed here.
|
// All schemas in `openapi/spec` should be listed here.
|
||||||
export const schemas = {
|
export const schemas = {
|
||||||
@ -90,6 +91,7 @@ export const schemas = {
|
|||||||
apiTokensSchema,
|
apiTokensSchema,
|
||||||
applicationSchema,
|
applicationSchema,
|
||||||
applicationsSchema,
|
applicationsSchema,
|
||||||
|
clientApplicationSchema,
|
||||||
cloneFeatureSchema,
|
cloneFeatureSchema,
|
||||||
changePasswordSchema,
|
changePasswordSchema,
|
||||||
constraintSchema,
|
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 ApiUser from '../../types/api-user';
|
||||||
import { ALL } from '../../types/models/api-token';
|
import { ALL } from '../../types/models/api-token';
|
||||||
import { NONE } from '../../types/permissions';
|
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 {
|
export default class RegisterController extends Controller {
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
|
|
||||||
metrics: ClientInstanceService;
|
clientInstanceService: ClientInstanceService;
|
||||||
|
|
||||||
|
openApiService: OpenApiService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
clientInstanceService,
|
clientInstanceService,
|
||||||
}: Pick<IUnleashServices, 'clientInstanceService'>,
|
openApiService,
|
||||||
|
}: Pick<IUnleashServices, 'clientInstanceService' | 'openApiService'>,
|
||||||
config: IUnleashConfig,
|
config: IUnleashConfig,
|
||||||
) {
|
) {
|
||||||
super(config);
|
super(config);
|
||||||
this.logger = config.getLogger('/api/client/register');
|
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.route({
|
||||||
this.post('/', this.handleRegister, NONE);
|
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 instanceof ApiUser) {
|
||||||
if (user.environment !== ALL) {
|
if (user.environment !== ALL) {
|
||||||
return user.environment;
|
return user.environment;
|
||||||
@ -40,10 +60,13 @@ export default class RegisterController extends Controller {
|
|||||||
return 'default';
|
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;
|
const { body: data, ip: clientIp, user } = req;
|
||||||
data.environment = this.resolveEnvironment(user, data);
|
data.environment = RegisterController.resolveEnvironment(user, data);
|
||||||
await this.metrics.registerClient(data, clientIp);
|
await this.clientInstanceService.registerClient(data, clientIp);
|
||||||
return res.status(202).end();
|
return res.status(202).end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,7 +280,7 @@ export interface IClientApp {
|
|||||||
strategies?: string[] | Record<string, string>[];
|
strategies?: string[] | Record<string, string>[];
|
||||||
bucket?: any;
|
bucket?: any;
|
||||||
count?: number;
|
count?: number;
|
||||||
started?: number | Date;
|
started?: string | number | Date;
|
||||||
interval?: number;
|
interval?: number;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
@ -309,6 +309,49 @@ Object {
|
|||||||
],
|
],
|
||||||
"type": "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 {
|
"cloneFeatureSchema": Object {
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
"name": 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 {
|
"/auth/reset/password": Object {
|
||||||
"post": Object {
|
"post": Object {
|
||||||
"operationId": "changePassword",
|
"operationId": "changePassword",
|
||||||
|
Loading…
Reference in New Issue
Block a user