Skip to content

Commit

Permalink
feat(rest-crud): introduct id required option
Browse files Browse the repository at this point in the history
  • Loading branch information
jannyHou committed Sep 10, 2019
1 parent 9408a86 commit 03e8048
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ describe('CrudRestController for a simple Product model', () => {
}
}

@model()
class ProductWithId extends Entity {
@property({id: true, required: true})
id: number;

@property({required: true})
name: string;

@property()
description?: string;

constructor(data: Partial<Product>) {
super(data);
}
}

let app: RestApplication;
let repo: EntityCrudRepository<Product, typeof Product.prototype.id>;
let client: Client;
Expand Down Expand Up @@ -84,6 +100,31 @@ describe('CrudRestController for a simple Product model', () => {
.send({id: 1, name: 'a name'})
.expect(422);
});

it('creates a new Product - with id required', async () => {
const response = await client
.post('/productsWithId')
.send({id: 1, name: 'A pen'})
// FIXME: POST should return 201
// See https://github.com/strongloop/loopback-next/issues/788
.expect(200);

const created = response.body;
expect(created).to.containEql({id: 1, name: 'A pen'});
expect(created)
.to.have.property('id')
.of.type('number');

const found = (await repo.find())[0];
expect(toJSON(found)).to.deepEqual(created);
});

it('rejects request without `id` value', async () => {
await client
.post('/productsWithId')
.send({name: 'a name'})
.expect(422);
});
});

describe('find', () => {
Expand Down Expand Up @@ -294,14 +335,27 @@ describe('CrudRestController for a simple Product model', () => {
'id'
>(Product, {basePath: '/products'});

const CrudRestWithIdRequiredController = defineCrudRestController<
ProductWithId,
typeof Product.prototype.id,
'id'
>(ProductWithId, {basePath: '/productsWithId', idRequired: true});

class ProductController extends CrudRestController {
constructor() {
super(repo);
}
}

class ProductWithIdController extends CrudRestWithIdRequiredController {
constructor() {
super(repo);
}
}

app = new RestApplication({rest: givenHttpServerConfig()});
app.controller(ProductController);
app.controller(ProductWithIdController);

await app.start();
client = createRestAppClient(app);
Expand Down
66 changes: 44 additions & 22 deletions packages/rest-crud/src/crud-rest.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ export interface CrudRestController<
*/
readonly repository: EntityCrudRepository<T, IdType>;

/**
* Implementation of the endpoint `POST /`.
* @param data Model data
*/
create(data: Omit<T, IdName>): Promise<T>;
// /**
// * Implementation of the endpoint `POST /`.
// * @param data Model data
// */
// create(data: Omit<T, IdName>): Promise<T>;
}

/**
Expand All @@ -99,6 +99,7 @@ export interface CrudRestControllerOptions {
* The base path where to "mount" the controller.
*/
basePath: string;
idRequired?: boolean;
}

/**
Expand Down Expand Up @@ -142,26 +143,12 @@ export function defineCrudRestController<
};

@api({basePath: options.basePath, paths: {}})
class CrudRestControllerImpl
class CrudRestControllerWithoutCreateImpl
implements CrudRestController<T, IdType, IdName> {
constructor(
public readonly repository: EntityCrudRepository<T, IdType, Relations>,
) {}

@post('/', {
...response.model(200, `${modelName} instance created`, modelCtor),
})
async create(
@body(modelCtor, {exclude: modelCtor.getIdProperties() as (keyof T)[]})
data: Omit<T, IdName>,
): Promise<T> {
return this.repository.create(
// FIXME(bajtos) Improve repository API to support this use case
// with no explicit type-casts required
data as DataObject<T>,
);
}

@get('/', {
...response.array(200, `Array of ${modelName} instances`, modelCtor, {
includeRelations: true,
Expand Down Expand Up @@ -254,8 +241,43 @@ export function defineCrudRestController<
}
}

// See https://github.com/microsoft/TypeScript/issues/14607
return CrudRestControllerImpl;
if (options && options.idRequired) {
class CrudRestControllerImpl extends CrudRestControllerWithoutCreateImpl {
@post('/', {
...response.model(200, `${modelName} instance created`, modelCtor),
})
async create(
@body(modelCtor)
data: T,
): Promise<T> {
return this.repository.create(
// FIXME(bajtos) Improve repository API to support this use case
// with no explicit type-casts required
data as DataObject<T>,
);
}
}
// See https://github.com/microsoft/TypeScript/issues/14607
return CrudRestControllerImpl;
} else {
class CrudRestControllerImpl extends CrudRestControllerWithoutCreateImpl {
@post('/', {
...response.model(200, `${modelName} instance created`, modelCtor),
})
async create(
@body(modelCtor, {exclude: modelCtor.getIdProperties() as (keyof T)[]})
data: Omit<T, IdName>,
): Promise<T> {
return this.repository.create(
// FIXME(bajtos) Improve repository API to support this use case
// with no explicit type-casts required
data as DataObject<T>,
);
}
}
// See https://github.com/microsoft/TypeScript/issues/14607
return CrudRestControllerImpl;
}
}

function getIdSchema<T extends Entity>(
Expand Down

0 comments on commit 03e8048

Please sign in to comment.