mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-12 01:17:04 +02:00
validation: fix _some_ 201 created location header endpoints
This commit is contained in:
parent
d0500b6c1a
commit
fe07191c63
@ -4,6 +4,7 @@ import {
|
||||
IContextField,
|
||||
IContextFieldDto,
|
||||
IContextFieldStore,
|
||||
ILegalValue,
|
||||
} from '../types/stores/context-field-store';
|
||||
|
||||
const COLUMNS = [
|
||||
@ -16,7 +17,16 @@ const COLUMNS = [
|
||||
];
|
||||
const TABLE = 'context_fields';
|
||||
|
||||
const mapRow: (object) => IContextField = (row) => ({
|
||||
type ContextFieldDB = {
|
||||
name: string;
|
||||
description: string;
|
||||
stickiness: boolean;
|
||||
sort_order: number;
|
||||
legal_values: ILegalValue[];
|
||||
created_at: Date;
|
||||
};
|
||||
|
||||
const mapRow = (row: ContextFieldDB): IContextField => ({
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
stickiness: row.stickiness,
|
||||
@ -88,15 +98,17 @@ class ContextFieldStore implements IContextFieldStore {
|
||||
return present;
|
||||
}
|
||||
|
||||
// TODO: write tests for the changes you made here?
|
||||
async create(contextField: IContextFieldDto): Promise<IContextField> {
|
||||
const row = await this.db(TABLE)
|
||||
const [row] = await this.db(TABLE)
|
||||
.insert(this.fieldToRow(contextField))
|
||||
.returning('*');
|
||||
|
||||
return mapRow(row);
|
||||
}
|
||||
|
||||
async update(data: IContextFieldDto): Promise<IContextField> {
|
||||
const row = await this.db(TABLE)
|
||||
const [row] = await this.db(TABLE)
|
||||
.where({ name: data.name })
|
||||
.update(this.fieldToRow(data))
|
||||
.returning('*');
|
||||
|
@ -14,3 +14,27 @@ export const createResponseSchema = (
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const resourceCreatedResponseSchema = (
|
||||
schemaName: string,
|
||||
): OpenAPIV3.ResponseObject => {
|
||||
return {
|
||||
headers: {
|
||||
location: {
|
||||
description: 'The location of the newly created resource.',
|
||||
schema: {
|
||||
type: 'string',
|
||||
format: 'uri',
|
||||
},
|
||||
},
|
||||
},
|
||||
description: `The resource was successfully created.`,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: `#/components/schemas/${schemaName}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ const ajv = new Ajv({
|
||||
),
|
||||
});
|
||||
|
||||
addFormats(ajv, ['date-time']);
|
||||
addFormats(ajv, ['date-time', 'uri', 'uri-reference']);
|
||||
|
||||
// example was superseded by examples in openapi 3.1, but we're still on 3.0, so
|
||||
// let's add it back in!
|
||||
|
@ -19,7 +19,10 @@ import { createApiToken } from '../../schema/api-token-schema';
|
||||
import { OpenApiService } from '../../services/openapi-service';
|
||||
import { IUnleashServices } from '../../types';
|
||||
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
createResponseSchema,
|
||||
resourceCreatedResponseSchema,
|
||||
} from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
apiTokensSchema,
|
||||
ApiTokensSchema,
|
||||
@ -96,7 +99,7 @@ export class ApiTokenController extends Controller {
|
||||
operationId: 'createApiToken',
|
||||
requestBody: createRequestSchema('createApiTokenSchema'),
|
||||
responses: {
|
||||
201: createResponseSchema('apiTokenSchema'),
|
||||
201: resourceCreatedResponseSchema('apiTokenSchema'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
@ -24,7 +24,10 @@ import {
|
||||
import { ContextFieldsSchema } from '../../openapi/spec/context-fields-schema';
|
||||
import { UpsertContextFieldSchema } from '../../openapi/spec/upsert-context-field-schema';
|
||||
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
createResponseSchema,
|
||||
resourceCreatedResponseSchema,
|
||||
} from '../../openapi/util/create-response-schema';
|
||||
import { serializeDates } from '../../types/serialize-dates';
|
||||
import NotFoundError from '../../error/notfound-error';
|
||||
import { NameSchema } from '../../openapi/spec/name-schema';
|
||||
@ -98,7 +101,9 @@ export class ContextController extends Controller {
|
||||
'upsertContextFieldSchema',
|
||||
),
|
||||
responses: {
|
||||
201: emptyResponse,
|
||||
201: resourceCreatedResponseSchema(
|
||||
'contextFieldSchema',
|
||||
),
|
||||
},
|
||||
}),
|
||||
],
|
||||
@ -189,13 +194,19 @@ export class ContextController extends Controller {
|
||||
|
||||
async createContextField(
|
||||
req: IAuthRequest<void, void, UpsertContextFieldSchema>,
|
||||
res: Response,
|
||||
res: Response<ContextFieldSchema>,
|
||||
): Promise<void> {
|
||||
const value = req.body;
|
||||
const userName = extractUsername(req);
|
||||
|
||||
await this.contextService.createContextField(value, userName);
|
||||
res.status(201).end();
|
||||
const result = await this.contextService.createContextField(
|
||||
value,
|
||||
userName,
|
||||
);
|
||||
res.status(201)
|
||||
.header('location', `context/${result.name}`) // todo: how to ensure that the location is (and stays) correct?
|
||||
.json(serializeDates(result))
|
||||
.end();
|
||||
}
|
||||
|
||||
async updateContextField(
|
||||
|
@ -25,7 +25,10 @@ import { TagsSchema } from '../../openapi/spec/tags-schema';
|
||||
import { serializeDates } from '../../types/serialize-dates';
|
||||
import { OpenApiService } from '../../services/openapi-service';
|
||||
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
createResponseSchema,
|
||||
resourceCreatedResponseSchema,
|
||||
} from '../../openapi/util/create-response-schema';
|
||||
import { emptyResponse } from '../../openapi/util/standard-responses';
|
||||
|
||||
const version = 1;
|
||||
@ -123,7 +126,9 @@ class FeatureController extends Controller {
|
||||
tags: ['Features'],
|
||||
operationId: 'addTag',
|
||||
requestBody: createRequestSchema('tagSchema'),
|
||||
responses: { 201: createResponseSchema('tagSchema') },
|
||||
responses: {
|
||||
201: resourceCreatedResponseSchema('tagSchema'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
@ -221,7 +226,7 @@ class FeatureController extends Controller {
|
||||
req.body,
|
||||
userName,
|
||||
);
|
||||
res.status(201).json(tag);
|
||||
res.status(201).header('location', `${featureName}/tags`).json(tag);
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
@ -15,7 +15,10 @@ import { IAuthRequest } from '../unleash-types';
|
||||
import { OpenApiService } from '../../services/openapi-service';
|
||||
import { emptyResponse } from '../../openapi/util/standard-responses';
|
||||
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
createResponseSchema,
|
||||
resourceCreatedResponseSchema,
|
||||
} from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
strategySchema,
|
||||
StrategySchema,
|
||||
@ -102,7 +105,9 @@ class StrategyController extends Controller {
|
||||
tags: ['Strategies'],
|
||||
operationId: 'createStrategy',
|
||||
requestBody: createRequestSchema('upsertStrategySchema'),
|
||||
responses: { 201: emptyResponse },
|
||||
responses: {
|
||||
201: resourceCreatedResponseSchema('strategySchema'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
@ -193,12 +198,18 @@ class StrategyController extends Controller {
|
||||
|
||||
async createStrategy(
|
||||
req: IAuthRequest<unknown, UpsertStrategySchema>,
|
||||
res: Response<void>,
|
||||
res: Response<StrategySchema>,
|
||||
): Promise<void> {
|
||||
const userName = extractUsername(req);
|
||||
|
||||
await this.strategyService.createStrategy(req.body, userName);
|
||||
res.status(201).end();
|
||||
const strategy = await this.strategyService.createStrategy(
|
||||
req.body,
|
||||
userName,
|
||||
);
|
||||
res.header('location', `strategies/${strategy.name}`)
|
||||
.status(201)
|
||||
.json(strategy)
|
||||
.end();
|
||||
}
|
||||
|
||||
async updateStrategy(
|
||||
|
@ -13,7 +13,10 @@ import TagTypeService from '../../services/tag-type-service';
|
||||
import { Logger } from '../../logger';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
createResponseSchema,
|
||||
resourceCreatedResponseSchema,
|
||||
} from '../../openapi/util/create-response-schema';
|
||||
import { TagTypesSchema } from '../../openapi/spec/tag-types-schema';
|
||||
import { ValidateTagTypeSchema } from '../../openapi/spec/validate-tag-type-schema';
|
||||
import {
|
||||
@ -66,7 +69,9 @@ class TagTypeController extends Controller {
|
||||
openApiService.validPath({
|
||||
tags: ['Tags'],
|
||||
operationId: 'createTagType',
|
||||
responses: { 201: createResponseSchema('tagTypeSchema') },
|
||||
responses: {
|
||||
201: resourceCreatedResponseSchema('tagTypeSchema'),
|
||||
},
|
||||
requestBody: createRequestSchema('tagTypeSchema'),
|
||||
}),
|
||||
],
|
||||
|
@ -10,7 +10,10 @@ import { NONE, UPDATE_FEATURE } from '../../types/permissions';
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
createResponseSchema,
|
||||
resourceCreatedResponseSchema,
|
||||
} from '../../openapi/util/create-response-schema';
|
||||
import { tagsSchema, TagsSchema } from '../../openapi/spec/tags-schema';
|
||||
import { TagSchema } from '../../openapi/spec/tag-schema';
|
||||
import { OpenApiService } from '../../services/openapi-service';
|
||||
@ -64,7 +67,9 @@ class TagController extends Controller {
|
||||
tags: ['Tags'],
|
||||
operationId: 'createTag',
|
||||
responses: {
|
||||
201: emptyResponse,
|
||||
201: resourceCreatedResponseSchema(
|
||||
'tagWithVersionSchema',
|
||||
),
|
||||
},
|
||||
requestBody: createRequestSchema('tagSchema'),
|
||||
}),
|
||||
@ -157,11 +162,14 @@ class TagController extends Controller {
|
||||
|
||||
async createTag(
|
||||
req: IAuthRequest<unknown, unknown, TagSchema>,
|
||||
res: Response,
|
||||
res: Response<TagWithVersionSchema>,
|
||||
): Promise<void> {
|
||||
const userName = extractUsername(req);
|
||||
await this.tagService.createTag(req.body, userName);
|
||||
res.status(201).end();
|
||||
const tag = await this.tagService.createTag(req.body, userName);
|
||||
res.status(201)
|
||||
.header('location', `tags/${tag.type}/${tag.value}`)
|
||||
.json({ version, tag })
|
||||
.end();
|
||||
}
|
||||
|
||||
async deleteTag(
|
||||
|
@ -55,18 +55,20 @@ class ContextService {
|
||||
async createContextField(
|
||||
value: IContextFieldDto,
|
||||
userName: string,
|
||||
): Promise<void> {
|
||||
): Promise<IContextField> {
|
||||
// validations
|
||||
await this.validateUniqueName(value);
|
||||
const contextField = await contextSchema.validateAsync(value);
|
||||
|
||||
// creations
|
||||
await this.contextFieldStore.create(value);
|
||||
const createdField = await this.contextFieldStore.create(value);
|
||||
await this.eventStore.store({
|
||||
type: CONTEXT_FIELD_CREATED,
|
||||
createdBy: userName,
|
||||
data: contextField,
|
||||
});
|
||||
|
||||
return createdField;
|
||||
}
|
||||
|
||||
async updateContextField(
|
||||
|
@ -101,7 +101,7 @@ class StrategyService {
|
||||
async createStrategy(
|
||||
value: IMinimalStrategy,
|
||||
userName: string,
|
||||
): Promise<void> {
|
||||
): Promise<IStrategy> {
|
||||
const strategy = await strategySchema.validateAsync(value);
|
||||
strategy.deprecated = false;
|
||||
await this._validateStrategyName(strategy);
|
||||
@ -111,6 +111,7 @@ class StrategyService {
|
||||
createdBy: userName,
|
||||
data: strategy,
|
||||
});
|
||||
return this.strategyStore.get(strategy.name);
|
||||
}
|
||||
|
||||
async updateStrategy(
|
||||
|
@ -52,7 +52,7 @@ export default class TagService {
|
||||
return data;
|
||||
}
|
||||
|
||||
async createTag(tag: ITag, userName: string): Promise<void> {
|
||||
async createTag(tag: ITag, userName: string): Promise<ITag> {
|
||||
const data = await this.validate(tag);
|
||||
await this.tagStore.createTag(data);
|
||||
await this.eventStore.store({
|
||||
@ -60,6 +60,8 @@ export default class TagService {
|
||||
createdBy: userName,
|
||||
data,
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async deleteTag(tag: ITag, userName: string): Promise<void> {
|
||||
|
Loading…
Reference in New Issue
Block a user