Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/site/Controller-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export class TodoController {
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Todo, {exclude: ['id']}),
schema: getModelSchemaRef(Todo, {title: 'NewTodo', exclude: ['id']}),
},
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export class TodoListTodoController {
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Todo, {exclude: ['id']}),
schema: getModelSchemaRef(Todo, {title: 'NewTodo', exclude: ['id']}),
},
},
})
Expand Down Expand Up @@ -237,7 +237,7 @@ export class TodoListTodoController {
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Todo, {exclude: ['id']}),
schema: getModelSchemaRef(Todo, {title: 'NewTodo', exclude: ['id']}),
},
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class NoteController {
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Note, {exclude: ['id']}),
schema: getModelSchemaRef(Note, {title: 'NewNote', exclude: ['id']}),
},
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class TodoListTodoController {
content: {
'application/json': {
schema: getModelSchemaRef(Todo, {
title: 'NewTodoInTodoList',
exclude: ['id'],
optional: ['todoListId'],
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export class TodoListController {
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(TodoList, {exclude: ['id']}),
schema: getModelSchemaRef(TodoList, {
title: 'NewTodoList',
exclude: ['id'],
}),
},
},
})
Expand Down
2 changes: 1 addition & 1 deletion examples/todo-list/src/controllers/todo.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class TodoController {
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Todo, {exclude: ['id']}),
schema: getModelSchemaRef(Todo, {title: 'NewTodo', exclude: ['id']}),
},
},
})
Expand Down
2 changes: 1 addition & 1 deletion examples/todo/src/controllers/todo.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class TodoController {
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Todo, {exclude: ['id']}),
schema: getModelSchemaRef(Todo, {title: 'NewTodo', exclude: ['id']}),
},
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export class <%= className %>Controller {
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(<%= modelName %>, {exclude: ['<%= id %>']}),
schema: getModelSchemaRef(<%= modelName %>, {
title: 'New<%= modelName %>',
exclude: ['<%= id %>'],
}),
},
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class <%= controllerClassName %> {
content: {
'application/json': {
schema: getModelSchemaRef(<%= targetModelClassName %>, {
title: 'New <%= targetModelClassName %>In<%= sourceModelClassName %>',
exclude: ['<%= targetModelPrimaryKey %>'],
optional: ['<%= foreignKeyName %>']
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ function checkRestCrudContents() {
/'200': {/,
/description: 'ProductReview model instance'/,
/content: {'application\/json': {schema: getModelSchemaRef\(ProductReview\)}},\s{1,}},\s{1,}},\s{1,}}\)/,
/async create\(\s+\@requestBody\({\s+content: {\s+'application\/json': {\s+schema: getModelSchemaRef\(ProductReview, {exclude: \['productId'\]}\),\s+},\s+},\s+}\)\s+productReview: Omit<ProductReview, 'productId'>,\s+\)/,
/async create\(\s+\@requestBody\({\s+content: {\s+'application\/json': {\s+schema: getModelSchemaRef\(ProductReview, {\s+title: 'NewProductReview',\s+exclude: \['productId'\],\s+}\),\s+},\s+},\s+}\)\s+productReview: Omit<ProductReview, 'productId'>,\s+\)/,
];
postCreateRegEx.forEach(regex => {
assert.fileContent(expectedFile, regex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,34 @@ describe('build-schema', () => {
expect(schema).to.deepEqual(expectedSchema);
});
});

it('uses title from model metadata instead of model name', () => {
@model({title: 'MyCustomer'})
class Customer {}

const schema = modelToJsonSchema(Customer, {
// trigger build of a custom title
partial: true,
});

expect(schema.title).to.equal('MyCustomerPartial');
});

it('uses title from options instead of model name and computed suffix', () => {
@model({title: 'ShouldBeIgnored'})
class TestModel {
@property()
id: string;
}

const schema = modelToJsonSchema(TestModel, {
title: 'NewTestModel',
partial: true,
exclude: ['id'],
});

expect(schema.title).to.equal('NewTestModel');
});
});

describe('getJsonSchema', () => {
Expand Down Expand Up @@ -1082,5 +1110,21 @@ describe('build-schema', () => {
expect(optionalNameSchema.title).to.equal('ProductPartial');
});
});

it('creates new cache entry for each custom title', () => {
@model()
class TestModel {}

// populate the cache
getJsonSchema(TestModel, {title: 'First'});
getJsonSchema(TestModel, {title: 'Second'});

// obtain cached instances & verify the title
const schema1 = getJsonSchema(TestModel, {title: 'First'});
expect(schema1.title).to.equal('First');

const schema2 = getJsonSchema(TestModel, {title: 'Second'});
expect(schema2.title).to.equal('Second');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -325,5 +325,10 @@ describe('build-schema', () => {
'modelOptional[name]Excluding[id,_rev]WithRelations',
);
});

it('includes custom title', () => {
const key = buildModelCacheKey({title: 'NewProduct', partial: true});
expect(key).to.equal('modelNewProductPartial');
});
});
});
23 changes: 21 additions & 2 deletions packages/repository-json-schema/src/build-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ import {JSON_SCHEMA_KEY, MODEL_TYPE_KEYS} from './keys';
const debug = debugFactory('loopback:repository-json-schema:build-schema');

export interface JsonSchemaOptions<T extends object> {
/**
* The title to use in the generated schema.
*
* When using options like `exclude`, the auto-generated title can be
* difficult to read for humans. Use this option to change the title to
* a more meaningful value.
*/
title?: string;

/**
* Set this flag if you want the schema to define navigational properties
* for model relations.
Expand Down Expand Up @@ -62,7 +71,7 @@ export function buildModelCacheKey<T extends object>(
// New key schema: use the same suffix as we use for schema title
// For example: "modelPartialWithRelations"
// Note this new key schema preserves the old key "modelWithRelations"
return 'model' + getTitleSuffix(options);
return 'model' + (options.title || '') + getTitleSuffix(options);
}

/**
Expand Down Expand Up @@ -271,6 +280,16 @@ export function getNavigationalPropertyForRelation(
}
}

function buildSchemaTitle<T extends object>(
ctor: Function & {prototype: T},
meta: ModelDefinition,
options: JsonSchemaOptions<T>,
) {
if (options.title) return options.title;
const title = meta.title || ctor.name;
return title + getTitleSuffix(options);
}

function getTitleSuffix<T extends object>(options: JsonSchemaOptions<T> = {}) {
let suffix = '';
if (options.optional && options.optional.length) {
Expand Down Expand Up @@ -319,7 +338,7 @@ export function modelToJsonSchema<T extends object>(
return {};
}

const title = (meta.title || ctor.name) + getTitleSuffix(options);
const title = buildSchemaTitle(ctor, meta, options);

if (options.visited[title]) return options.visited[title];

Expand Down
5 changes: 4 additions & 1 deletion packages/rest-crud/src/crud-rest.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ export function defineCrudRestController<
...response.model(200, `${modelName} instance created`, modelCtor),
})
async create(
@body(modelCtor, {exclude: modelCtor.getIdProperties() as (keyof T)[]})
@body(modelCtor, {
title: `New${modelName}`,
exclude: modelCtor.getIdProperties() as (keyof T)[],
})
data: Omit<T, IdName>,
): Promise<T> {
return this.repository.create(
Expand Down