Skip to content

Commit f49fab3

Browse files
fix: Update blueprint request body schema should not specify 'required' (#71)
fix: Update blueprint request body schema should not specify 'required' Sails Models include specification of `required` property for attributes. This should be enforced/specified for create blueprints but **NOT** update blueprints. This change adds two variant schemas for each model: - '{modelIdentity}' - variant containing `required` - '{modelIdentity}-without-required-constraint' - used for updates
1 parent f482891 commit f49fab3

File tree

6 files changed

+95
-168
lines changed

6 files changed

+95
-168
lines changed

README.MD

+1
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ Note that:
585585
- `addSelectQueryParam`
586586
- `addOmitQueryParam`
587587
- `addModelBodyParam`
588+
- `addModelBodyParamUpdate`
588589
- `addResultOfArrayOfModels`
589590
- `addAssociationPathParam`
590591
- `addAssociationFKPathParam`

lib/generators.ts

+48-7
Original file line numberDiff line numberDiff line change
@@ -293,29 +293,44 @@ export const generateSchemas = (models: NameKeyMap<SwaggerSailsModel>): NameKeyM
293293
return schemas;
294294
}
295295

296-
const schema: OpenApi.UpdatedSchema = {
296+
const schemaWithoutRequired: OpenApi.UpdatedSchema = {
297297
description: model.swagger.modelSchema?.description || `Sails ORM Model **${model.globalId}**`,
298298
properties: {},
299-
required: [],
300-
...omit(model.swagger?.modelSchema || {}, 'exclude', 'description', 'tags'),
299+
...omit(model.swagger?.modelSchema || {}, 'exclude', 'description', 'required', 'tags'),
301300
}
302301

302+
let required: string[] = [];
303+
303304
const attributes = model.attributes || {}
304305
defaults(
305-
schema.properties,
306+
schemaWithoutRequired.properties,
306307
Object.keys(attributes).reduce((props, attributeName) => {
307308
const attribute = model.attributes[attributeName];
308309
if (attribute.meta?.swagger?.exclude !== true) {
309310
props[attributeName] = generateAttributeSchema(attribute);
310-
if (attribute.required) schema.required!.push(attributeName);
311+
if (attribute.required) required!.push(attributeName);
311312
}
312313
return props
313314
}, {} as NameKeyMap<OpenApi.UpdatedSchema>)
314315
);
315316

316-
if (schema.required!.length <= 0) delete schema.required;
317+
const withoutRequiredName = `${model.identity}-without-required-constraint`;
318+
const schema: OpenApi.UpdatedSchema = {
319+
allOf: [
320+
{ '$ref': `#/components/schemas/${withoutRequiredName}` },
321+
],
322+
};
323+
324+
if(model.swagger?.modelSchema?.required) {
325+
required = [ ...model.swagger.modelSchema.required ];
326+
}
327+
328+
if(required.length > 0) {
329+
schema.allOf!.push({ required: required });
330+
}
317331

318332
schemas[model.identity] = schema;
333+
schemas[withoutRequiredName] = schemaWithoutRequired;
319334

320335
return schemas
321336
}, {} as NameKeyMap<OpenApi.UpdatedSchema | Reference>)
@@ -478,13 +493,39 @@ export const generatePaths = (routes: SwaggerRouteInfo[], templates: BlueprintAc
478493
required: true,
479494
content: {
480495
'application/json': {
481-
schema: { "$ref": "#/components/schemas/" + route.model!.identity }
496+
schema: { '$ref': `#/components/schemas/${route.model!.identity}` }
482497
},
483498
},
484499
};
485500
}
486501
},
487502

503+
addModelBodyParamUpdate: () => {
504+
if (route.actionType === 'shortcutBlueprint') {
505+
const schema = specification!.components!.schemas?.[route.model!.identity+'-without-required-constraint'];
506+
if (schema) {
507+
const resolvedSchema = resolveRef(specification, schema);
508+
if (resolvedSchema) {
509+
generateSchemaAsQueryParameters(resolvedSchema as OpenApi.UpdatedSchema).map(p => {
510+
if (isParam('query', p.name)) return;
511+
pathEntry.parameters.push(p);
512+
});
513+
}
514+
}
515+
} else {
516+
if (pathEntry.requestBody) return;
517+
pathEntry.requestBody = {
518+
description: subst('JSON dictionary representing the {globalId} instance to update.'),
519+
required: true,
520+
content: {
521+
'application/json': {
522+
schema: { '$ref': `#/components/schemas/${route.model!.identity}-without-required-constraint` }
523+
},
524+
},
525+
}
526+
}
527+
},
528+
488529
addResultOfArrayOfModels: () => {
489530
assign(pathEntry.responses['200'], {
490531
description: subst(template.resultDescription),

lib/interfaces.ts

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export enum Modifiers {
3131
ADD_SELECT_QUERY_PARAM = 'addSelectQueryParam',
3232
ADD_OMIT_QUERY_PARAM = 'addOmitQueryParam',
3333
ADD_MODEL_BODY_PARAM = 'addModelBodyParam',
34+
ADD_MODEL_BODY_PARAM_UPDATE = 'addModelBodyParamUpdate',
3435
ADD_RESULT_OF_ARRAY_OF_MODELS = 'addResultOfArrayOfModels',
3536
ADD_ASSOCIATION_PATH_PARAM = 'addAssociationPathParam',
3637
ADD_ASSOCIATION_FK_PATH_PARAM = 'addAssociationFKPathParam',

lib/type-formatter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export const blueprintActionTemplates: BlueprintActionTemplates = {
124124
],
125125
resultDescription: 'Responds with the newly updated **{globalId}** record as a JSON dictionary',
126126
notFoundDescription: 'Cannot update, **{globalId}** record with specified ID **NOT** found',
127-
modifiers: [Modifiers.ADD_MODEL_BODY_PARAM, Modifiers.ADD_RESULT_OF_MODEL, Modifiers.ADD_RESULT_VALIDATION_ERROR, Modifiers.ADD_RESULT_NOT_FOUND, Modifiers.ADD_SHORTCUT_BLUEPRINT_ROUTE_NOTE]
127+
modifiers: [Modifiers.ADD_MODEL_BODY_PARAM_UPDATE, Modifiers.ADD_RESULT_OF_MODEL, Modifiers.ADD_RESULT_VALIDATION_ERROR, Modifiers.ADD_RESULT_NOT_FOUND, Modifiers.ADD_SHORTCUT_BLUEPRINT_ROUTE_NOTE]
128128
},
129129
destroy: {
130130
summary: 'Delete {globalId} (destroy)',

0 commit comments

Comments
 (0)