From 920e7812cd9d405df921f9ae9ce52ba0a9743bea Mon Sep 17 00:00:00 2001 From: James Ward Date: Thu, 15 Oct 2020 22:54:57 -0400 Subject: [PATCH] feat: Add SelectQueryBuilder.getOneOrFail() (#6885) This adds a `getOneOrFail` which which is to `getOne` as `findOneOrFail` is to `findOne` - it never returns `undefined`, it will instead throw an `EntityNotFoundError` closes #6246 --- docs/select-query-builder.md | 10 +++++ src/query-builder/SelectQueryBuilder.ts | 14 ++++++ .../select/query-builder-select.ts | 43 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/docs/select-query-builder.md b/docs/select-query-builder.md index ba590c2d39..5b2352d51b 100644 --- a/docs/select-query-builder.md +++ b/docs/select-query-builder.md @@ -174,6 +174,16 @@ const timber = await getRepository(User) .getOne(); ``` +`getOneOrFail` will get a single result from the database, but if +no result exists it will throw an `EntityNotFoundError`: + +```typescript +const timber = await getRepository(User) + .createQueryBuilder("user") + .where("user.id = :id OR user.name = :name", { id: 1, name: "Timber" }) + .getOneOrFail(); +``` + To get multiple results from the database, for example, to get all users from the database, use `getMany`: diff --git a/src/query-builder/SelectQueryBuilder.ts b/src/query-builder/SelectQueryBuilder.ts index 7f95fd7d61..50f7592183 100644 --- a/src/query-builder/SelectQueryBuilder.ts +++ b/src/query-builder/SelectQueryBuilder.ts @@ -37,6 +37,7 @@ import {ObjectUtils} from "../util/ObjectUtils"; import {DriverUtils} from "../driver/DriverUtils"; import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver"; +import {EntityNotFoundError} from "../error/EntityNotFoundError"; /** * Allows to build complex sql queries in a fashion way and execute those queries. @@ -1105,6 +1106,19 @@ export class SelectQueryBuilder extends QueryBuilder implements return result; } + /** + * Gets the first entity returned by execution of generated query builder sql or rejects the returned promise on error. + */ + async getOneOrFail(): Promise { + const entity = await this.getOne(); + + if (!entity) { + throw new EntityNotFoundError(this.expressionMap.mainAlias!.target, this); + } + + return entity; + } + /** * Gets entities returned by execution of generated query builder sql. */ diff --git a/test/functional/query-builder/select/query-builder-select.ts b/test/functional/query-builder/select/query-builder-select.ts index 6b1ba78e89..c2883263a9 100644 --- a/test/functional/query-builder/select/query-builder-select.ts +++ b/test/functional/query-builder/select/query-builder-select.ts @@ -3,6 +3,7 @@ import {closeTestingConnections, createTestingConnections, reloadTestingDatabase import {Connection} from "../../../../src/connection/Connection"; import {Post} from "./entity/Post"; import {expect} from "chai"; +import {EntityNotFoundError} from "../../../../src/error/EntityNotFoundError"; describe("query builder > select", () => { @@ -110,4 +111,46 @@ describe("query builder > select", () => { expect(sql).to.equal("SELECT post.name FROM post post"); }))); + it("should return a single entity for getOne when found", () => Promise.all(connections.map(async connection => { + await connection.getRepository(Post).save({ id: 1, title: "Hello", description: 'World', rating: 0 }); + + const entity = await connection.createQueryBuilder(Post, "post") + .where("post.id = :id", { id: 1 }) + .getOne(); + + expect(entity).not.to.be.undefined; + expect(entity!.id).to.equal(1); + expect(entity!.title).to.equal("Hello"); + }))); + + it("should return undefined for getOne when not found", () => Promise.all(connections.map(async connection => { + await connection.getRepository(Post).save({ id: 1, title: "Hello", description: 'World', rating: 0 }); + + const entity = await connection.createQueryBuilder(Post, "post") + .where("post.id = :id", { id: 2 }) + .getOne(); + + expect(entity).to.be.undefined; + }))); + + it("should return a single entity for getOneOrFail when found", () => Promise.all(connections.map(async connection => { + await connection.getRepository(Post).save({ id: 1, title: "Hello", description: 'World', rating: 0 }); + + const entity = await connection.createQueryBuilder(Post, "post") + .where("post.id = :id", { id: 1 }) + .getOneOrFail(); + + expect(entity.id).to.equal(1); + expect(entity.title).to.equal("Hello"); + }))); + + it("should throw an Error for getOneOrFail when not found", () => Promise.all(connections.map(async connection => { + await connection.getRepository(Post).save({ id: 1, title: "Hello", description: 'World', rating: 0 }); + + await expect( + connection.createQueryBuilder(Post, "post") + .where("post.id = :id", { id: 2 }) + .getOneOrFail() + ).to.be.rejectedWith(EntityNotFoundError); + }))); });