From 88b9de90a1570ca1fde6f6e862adb3bbb2a27389 Mon Sep 17 00:00:00 2001 From: Nathan Curtis Date: Mon, 16 Dec 2024 11:57:54 -0800 Subject: [PATCH 1/2] [TM-1402] Include taxonId in the previous planting count payload. --- .../src/trees/dto/establishment-trees.dto.ts | 16 +- .../src/trees/tree.service.spec.ts | 254 ++++++++++-------- apps/entity-service/src/trees/tree.service.ts | 15 +- .../src/trees/trees.conroller.spec.ts | 65 +++-- .../src/trees/trees.controller.ts | 5 +- .../src/lib/entities/tree-species.entity.ts | 7 + .../src/lib/factories/tree-species.factory.ts | 1 + 7 files changed, 211 insertions(+), 152 deletions(-) diff --git a/apps/entity-service/src/trees/dto/establishment-trees.dto.ts b/apps/entity-service/src/trees/dto/establishment-trees.dto.ts index a66f513..b50192f 100644 --- a/apps/entity-service/src/trees/dto/establishment-trees.dto.ts +++ b/apps/entity-service/src/trees/dto/establishment-trees.dto.ts @@ -2,6 +2,16 @@ import { JsonApiAttributes } from "@terramatch-microservices/common/dto/json-api import { JsonApiDto } from "@terramatch-microservices/common/decorators"; import { ApiProperty } from "@nestjs/swagger"; +export class PreviousPlantingCountDto { + @ApiProperty({ nullable: true, description: "Taxonomic ID for this tree species row" }) + taxonId?: string; + + @ApiProperty({ + description: "Number of trees of this type that have been planted in all previous reports on this entity." + }) + amount: number; +} + // The ID for this DTO is formed of "entityType|entityUuid". This is a virtual resource, not directly // backed by a single DB table. @JsonApiDto({ type: "establishmentTrees", id: "string" }) @@ -14,10 +24,10 @@ export class EstablishmentsTreesDto extends JsonApiAttributes; + previousPlantingCounts?: Record; } diff --git a/apps/entity-service/src/trees/tree.service.spec.ts b/apps/entity-service/src/trees/tree.service.spec.ts index aca7569..faf9cdf 100644 --- a/apps/entity-service/src/trees/tree.service.spec.ts +++ b/apps/entity-service/src/trees/tree.service.spec.ts @@ -15,6 +15,7 @@ import { } from "@terramatch-microservices/database/factories"; import { BadRequestException, NotFoundException } from "@nestjs/common"; import { DateTime } from "luxon"; +import { PreviousPlantingCountDto } from "./dto/establishment-trees.dto"; describe("TreeService", () => { let service: TreeService; @@ -35,132 +36,153 @@ describe("TreeService", () => { jest.restoreAllMocks(); }); - it("should return an empty array with no matches", async () => { - const result = await service.searchScientificNames("test"); - expect(result.length).toBe(0); - }); + describe("searchScientificName", () => { + it("should return an empty array with no matches", async () => { + const result = await service.searchScientificNames("test"); + expect(result.length).toBe(0); + }); - it("should return the matching entries", async () => { - const tree1 = await TreeSpeciesResearchFactory.create({ scientificName: "Lorem asdfium" }); - const tree2 = await TreeSpeciesResearchFactory.create({ scientificName: "Lorem qasdium" }); - const tree3 = await TreeSpeciesResearchFactory.create({ scientificName: "Ipsum loremium" }); - await TreeSpeciesResearchFactory.create({ scientificName: "Alorem ipsium" }); - await TreeSpeciesResearchFactory.create({ scientificName: "Fakem ipslorem" }); - const result = await service.searchScientificNames("lore"); - expect(result.length).toBe(3); - expect(result).toContainEqual(pick(tree1, ["taxonId", "scientificName"])); - expect(result).toContainEqual(pick(tree2, ["taxonId", "scientificName"])); - expect(result).toContainEqual(pick(tree3, ["taxonId", "scientificName"])); - }); + it("should return the matching entries", async () => { + const tree1 = await TreeSpeciesResearchFactory.create({ scientificName: "Lorem asdfium" }); + const tree2 = await TreeSpeciesResearchFactory.create({ scientificName: "Lorem qasdium" }); + const tree3 = await TreeSpeciesResearchFactory.create({ scientificName: "Ipsum loremium" }); + await TreeSpeciesResearchFactory.create({ scientificName: "Alorem ipsium" }); + await TreeSpeciesResearchFactory.create({ scientificName: "Fakem ipslorem" }); + const result = await service.searchScientificNames("lore"); + expect(result.length).toBe(3); + expect(result).toContainEqual(pick(tree1, ["taxonId", "scientificName"])); + expect(result).toContainEqual(pick(tree2, ["taxonId", "scientificName"])); + expect(result).toContainEqual(pick(tree3, ["taxonId", "scientificName"])); + }); - it("should return 10 entries maximum", async () => { - for (let ii = 0; ii < 12; ii++) { - await TreeSpeciesResearchFactory.create({ scientificName: `Tree${faker.word.words()}` }); - } + it("should return 10 entries maximum", async () => { + for (let ii = 0; ii < 12; ii++) { + await TreeSpeciesResearchFactory.create({ scientificName: `Tree${faker.word.words()}` }); + } - const result = await service.searchScientificNames("tree"); - expect(result.length).toBe(10); + const result = await service.searchScientificNames("tree"); + expect(result.length).toBe(10); + }); }); - it("should return establishment trees", async () => { - const project = await ProjectFactory.create(); - const projectReport = await ProjectReportFactory.create({ projectId: project.id }); - const site = await SiteFactory.create({ projectId: project.id }); - const siteReport = await SiteReportFactory.create({ siteId: site.id }); - const nursery = await NurseryFactory.create({ projectId: project.id }); - const nurseryReport = await NurseryReportFactory.create({ nurseryId: nursery.id }); - - const projectTrees = (await TreeSpeciesFactory.forProject.createMany(3, { speciesableId: project.id })) - .map(({ name }) => name) - .sort(); - const siteTrees = (await TreeSpeciesFactory.forSite.createMany(2, { speciesableId: site.id })) - .map(({ name }) => name) - .sort(); - const nurseryTrees = (await TreeSpeciesFactory.forNursery.createMany(4, { speciesableId: nursery.id })) - .map(({ name }) => name) - .sort(); - - let result = await service.getEstablishmentTrees("project-reports", projectReport.uuid); - expect(result.sort()).toEqual(projectTrees); - result = await service.getEstablishmentTrees("sites", site.uuid); - expect(result.sort()).toEqual(projectTrees); - result = await service.getEstablishmentTrees("nurseries", nursery.uuid); - expect(result.sort()).toEqual(projectTrees); - - result = await service.getEstablishmentTrees("site-reports", siteReport.uuid); - expect(result.sort()).toEqual(uniq([...siteTrees, ...projectTrees]).sort()); - result = await service.getEstablishmentTrees("nursery-reports", nurseryReport.uuid); - expect(result.sort()).toEqual(uniq([...nurseryTrees, ...projectTrees]).sort()); - }); + describe("getEstablishmentTrees", () => { + it("should return establishment trees", async () => { + const project = await ProjectFactory.create(); + const projectReport = await ProjectReportFactory.create({ projectId: project.id }); + const site = await SiteFactory.create({ projectId: project.id }); + const siteReport = await SiteReportFactory.create({ siteId: site.id }); + const nursery = await NurseryFactory.create({ projectId: project.id }); + const nurseryReport = await NurseryReportFactory.create({ nurseryId: nursery.id }); + + const projectTrees = (await TreeSpeciesFactory.forProject.createMany(3, { speciesableId: project.id })) + .map(({ name }) => name) + .sort(); + const siteTrees = (await TreeSpeciesFactory.forSite.createMany(2, { speciesableId: site.id })) + .map(({ name }) => name) + .sort(); + const nurseryTrees = (await TreeSpeciesFactory.forNursery.createMany(4, { speciesableId: nursery.id })) + .map(({ name }) => name) + .sort(); + + let result = await service.getEstablishmentTrees("project-reports", projectReport.uuid); + expect(result.sort()).toEqual(projectTrees); + result = await service.getEstablishmentTrees("sites", site.uuid); + expect(result.sort()).toEqual(projectTrees); + result = await service.getEstablishmentTrees("nurseries", nursery.uuid); + expect(result.sort()).toEqual(projectTrees); + + result = await service.getEstablishmentTrees("site-reports", siteReport.uuid); + expect(result.sort()).toEqual(uniq([...siteTrees, ...projectTrees]).sort()); + result = await service.getEstablishmentTrees("nursery-reports", nurseryReport.uuid); + expect(result.sort()).toEqual(uniq([...nurseryTrees, ...projectTrees]).sort()); + }); - it("throws with bad inputs to establishment trees", async () => { - await expect(service.getEstablishmentTrees("sites", "fakeuuid")).rejects.toThrow(NotFoundException); - await expect(service.getEstablishmentTrees("site-reports", "fakeuuid")).rejects.toThrow(NotFoundException); - // @ts-expect-error intentionally sneaking in a bad entity type - await expect(service.getEstablishmentTrees("nothing-burgers", "fakeuuid")).rejects.toThrow(BadRequestException); + it("throws with bad inputs to establishment trees", async () => { + await expect(service.getEstablishmentTrees("sites", "fakeuuid")).rejects.toThrow(NotFoundException); + await expect(service.getEstablishmentTrees("site-reports", "fakeuuid")).rejects.toThrow(NotFoundException); + // @ts-expect-error intentionally sneaking in a bad entity type + await expect(service.getEstablishmentTrees("nothing-burgers", "fakeuuid")).rejects.toThrow(BadRequestException); + }); }); - it("returns previous planting data", async () => { - const project = await ProjectFactory.create(); - const projectReport1 = await ProjectReportFactory.create({ projectId: project.id }); - const projectReport2 = await ProjectReportFactory.create({ - projectId: project.id, - dueAt: DateTime.fromJSDate(projectReport1.dueAt).plus({ months: 3 }).toJSDate() - }); - const site = await SiteFactory.create({ projectId: project.id }); - const siteReport1 = await SiteReportFactory.create({ siteId: site.id }); - const siteReport2 = await SiteReportFactory.create({ - siteId: site.id, - dueAt: DateTime.fromJSDate(siteReport1.dueAt).plus({ months: 3 }).toJSDate() - }); - const siteReport3 = await SiteReportFactory.create({ - siteId: site.id, - dueAt: DateTime.fromJSDate(siteReport2.dueAt).plus({ months: 3 }).toJSDate() - }); - const nursery = await NurseryFactory.create({ projectId: project.id }); - const nurseryReport1 = await NurseryReportFactory.create({ nurseryId: nursery.id }); - const nurseryReport2 = await NurseryReportFactory.create({ - nurseryId: nursery.id, - dueAt: DateTime.fromJSDate(nurseryReport1.dueAt).plus({ months: 3 }).toJSDate() + describe("getPreviousPlanting", () => { + it("returns previous planting data", async () => { + const project = await ProjectFactory.create(); + const projectReport1 = await ProjectReportFactory.create({ projectId: project.id }); + const projectReport2 = await ProjectReportFactory.create({ + projectId: project.id, + dueAt: DateTime.fromJSDate(projectReport1.dueAt).plus({ months: 3 }).toJSDate() + }); + const site = await SiteFactory.create({ projectId: project.id }); + const siteReport1 = await SiteReportFactory.create({ siteId: site.id }); + const siteReport2 = await SiteReportFactory.create({ + siteId: site.id, + dueAt: DateTime.fromJSDate(siteReport1.dueAt).plus({ months: 3 }).toJSDate() + }); + const siteReport3 = await SiteReportFactory.create({ + siteId: site.id, + dueAt: DateTime.fromJSDate(siteReport2.dueAt).plus({ months: 3 }).toJSDate() + }); + const nursery = await NurseryFactory.create({ projectId: project.id }); + const nurseryReport1 = await NurseryReportFactory.create({ nurseryId: nursery.id }); + const nurseryReport2 = await NurseryReportFactory.create({ + nurseryId: nursery.id, + dueAt: DateTime.fromJSDate(nurseryReport1.dueAt).plus({ months: 3 }).toJSDate() + }); + + const reduceTreeCounts = (counts: Record, tree: TreeSpecies) => ({ + ...counts, + [tree.name]: { + taxonId: counts[tree.name]?.taxonId ?? tree.taxonId, + amount: (counts[tree.name]?.amount ?? 0) + (tree.amount ?? 0) + } + }); + const projectReportTrees = await TreeSpeciesFactory.forProjectReport.createMany(3, { + speciesableId: projectReport1.id + }); + projectReportTrees.push( + await TreeSpeciesFactory.forProjectReport.create({ + speciesableId: projectReport1.id, + taxonId: "wfo-projectreporttree" + }) + ); + let result = await service.getPreviousPlanting("project-reports", projectReport2.uuid); + expect(result).toMatchObject(projectReportTrees.reduce(reduceTreeCounts, {})); + + const siteReport1Trees = await TreeSpeciesFactory.forSiteReport.createMany(3, { speciesableId: siteReport1.id }); + siteReport1Trees.push( + await TreeSpeciesFactory.forSiteReport.create({ + speciesableId: siteReport1.id, + taxonId: "wfo-sitereporttree" + }) + ); + const siteReport2Trees = await TreeSpeciesFactory.forSiteReport.createMany(3, { speciesableId: siteReport2.id }); + result = await service.getPreviousPlanting("site-reports", siteReport1.uuid); + expect(result).toMatchObject({}); + result = await service.getPreviousPlanting("site-reports", siteReport2.uuid); + const siteReport1TreesReduced = siteReport1Trees.reduce(reduceTreeCounts, {}); + expect(result).toMatchObject(siteReport1TreesReduced); + result = await service.getPreviousPlanting("site-reports", siteReport3.uuid); + expect(result).toMatchObject(siteReport2Trees.reduce(reduceTreeCounts, siteReport1TreesReduced)); + + result = await service.getPreviousPlanting("nursery-reports", nurseryReport2.uuid); + expect(result).toMatchObject({}); + const nurseryReportTrees = await TreeSpeciesFactory.forNurseryReport.createMany(5, { + speciesableId: nurseryReport1.id + }); + result = await service.getPreviousPlanting("nursery-reports", nurseryReport2.uuid); + expect(result).toMatchObject(nurseryReportTrees.reduce(reduceTreeCounts, {})); }); - const reduceTreeCounts = (counts: Record, tree: TreeSpecies) => ({ - ...counts, - [tree.name]: (counts[tree.name] ?? 0) + (tree.amount ?? 0) - }); - const projectReportTrees = await TreeSpeciesFactory.forProjectReport.createMany(3, { - speciesableId: projectReport1.id + it("handles bad input to get previous planting with undefined or an exception", async () => { + expect(await service.getPreviousPlanting("sites", "fakeuuid")).toBeUndefined(); + await expect(service.getPreviousPlanting("project-reports", "fakeuuid")).rejects.toThrow(NotFoundException); + await expect(service.getPreviousPlanting("site-reports", "fakeuuid")).rejects.toThrow(NotFoundException); + await expect(service.getPreviousPlanting("nursery-reports", "fakeuuid")).rejects.toThrow(NotFoundException); + // @ts-expect-error intentionally bad report type + await expect(service.getPreviousPlanting("nothing-burger-reports", "fakeuuid")).rejects.toThrow( + BadRequestException + ); }); - let result = await service.getPreviousPlanting("project-reports", projectReport2.uuid); - expect(result).toMatchObject(projectReportTrees.reduce(reduceTreeCounts, {})); - - const siteReport1Trees = await TreeSpeciesFactory.forSiteReport.createMany(3, { speciesableId: siteReport1.id }); - const siteReport2Trees = await TreeSpeciesFactory.forSiteReport.createMany(3, { speciesableId: siteReport2.id }); - result = await service.getPreviousPlanting("site-reports", siteReport1.uuid); - expect(result).toMatchObject({}); - result = await service.getPreviousPlanting("site-reports", siteReport2.uuid); - const siteReport1TreesReduced = siteReport1Trees.reduce(reduceTreeCounts, {}); - expect(result).toMatchObject(siteReport1TreesReduced); - result = await service.getPreviousPlanting("site-reports", siteReport3.uuid); - expect(result).toMatchObject(siteReport2Trees.reduce(reduceTreeCounts, siteReport1TreesReduced)); - - result = await service.getPreviousPlanting("nursery-reports", nurseryReport2.uuid); - expect(result).toMatchObject({}); - const nurseryReportTrees = await TreeSpeciesFactory.forNurseryReport.createMany(5, { - speciesableId: nurseryReport1.id - }); - result = await service.getPreviousPlanting("nursery-reports", nurseryReport2.uuid); - expect(result).toMatchObject(nurseryReportTrees.reduce(reduceTreeCounts, {})); - }); - - it("handles bad input to get previous planting with undefined or an exception", async () => { - expect(await service.getPreviousPlanting("sites", "fakeuuid")).toBeUndefined(); - await expect(service.getPreviousPlanting("project-reports", "fakeuuid")).rejects.toThrow(NotFoundException); - await expect(service.getPreviousPlanting("site-reports", "fakeuuid")).rejects.toThrow(NotFoundException); - await expect(service.getPreviousPlanting("nursery-reports", "fakeuuid")).rejects.toThrow(NotFoundException); - // @ts-expect-error intentionally bad report type - await expect(service.getPreviousPlanting("nothing-burger-reports", "fakeuuid")).rejects.toThrow( - BadRequestException - ); }); }); diff --git a/apps/entity-service/src/trees/tree.service.ts b/apps/entity-service/src/trees/tree.service.ts index bb47d61..55ebc06 100644 --- a/apps/entity-service/src/trees/tree.service.ts +++ b/apps/entity-service/src/trees/tree.service.ts @@ -10,6 +10,7 @@ import { import { Op } from "sequelize"; import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import { filter, flatten, uniq } from "lodash"; +import { PreviousPlantingCountDto } from "./dto/establishment-trees.dto"; export const ESTABLISHMENT_REPORTS = ["project-reports", "site-reports", "nursery-reports"] as const; export type EstablishmentReport = (typeof ESTABLISHMENT_REPORTS)[number]; @@ -103,7 +104,10 @@ export class TreeService { } } - async getPreviousPlanting(entity: EstablishmentEntity, uuid: string): Promise> { + async getPreviousPlanting( + entity: EstablishmentEntity, + uuid: string + ): Promise> { if (!isReport(entity)) return undefined; const treeReportWhere = (parentAttribute: string, report: ProjectReport | SiteReport | NurseryReport) => ({ @@ -115,7 +119,7 @@ export class TreeService { include: [ { association: "treeSpecies", - attributes: ["name", "amount"], + attributes: ["taxonId", "name", "amount"], where: { amount: { [Op.gt]: 0 } } } ] @@ -161,10 +165,13 @@ export class TreeService { } const trees = flatten(records.map(({ treeSpecies }) => treeSpecies)); - return trees.reduce>( + return trees.reduce>( (counts, tree) => ({ ...counts, - [tree.name]: (counts[tree.name] ?? 0) + (tree.amount ?? 0) + [tree.name]: { + taxonId: counts[tree.name]?.taxonId ?? tree.taxonId, + amount: (counts[tree.name]?.amount ?? 0) + (tree.amount ?? 0) + } }), {} ); diff --git a/apps/entity-service/src/trees/trees.conroller.spec.ts b/apps/entity-service/src/trees/trees.conroller.spec.ts index 30ec3cf..4f4c169 100644 --- a/apps/entity-service/src/trees/trees.conroller.spec.ts +++ b/apps/entity-service/src/trees/trees.conroller.spec.ts @@ -22,35 +22,46 @@ describe("TreesController", () => { jest.restoreAllMocks(); }); - it("should throw if the search param is missing", async () => { - await expect(controller.searchScientificNames("")).rejects.toThrow(BadRequestException); - await expect(controller.searchScientificNames(null)).rejects.toThrow(BadRequestException); - }); + describe("searchScientificName", () => { + it("should throw if the search param is missing", async () => { + await expect(controller.searchScientificNames("")).rejects.toThrow(BadRequestException); + await expect(controller.searchScientificNames(null)).rejects.toThrow(BadRequestException); + }); - it("should return the tree species from the service in order", async () => { - treeService.searchScientificNames.mockResolvedValue([ - { taxonId: "wfo-0000002583", scientificName: "Cirsium carolinianum" }, - { taxonId: "wfo-0000003963", scientificName: "Cirsium carniolicum" } - ]); - const result = await controller.searchScientificNames("cirs"); - expect(treeService.searchScientificNames).toHaveBeenCalledWith("cirs"); - const data = result.data as Resource[]; - expect(data.length).toBe(2); - expect(data[0].id).toBe("wfo-0000002583"); - expect(data[0].attributes.scientificName).toBe("Cirsium carolinianum"); - expect(data[1].id).toBe("wfo-0000003963"); - expect(data[1].attributes.scientificName).toBe("Cirsium carniolicum"); + it("should return the tree species from the service in order", async () => { + treeService.searchScientificNames.mockResolvedValue([ + { taxonId: "wfo-0000002583", scientificName: "Cirsium carolinianum" }, + { taxonId: "wfo-0000003963", scientificName: "Cirsium carniolicum" } + ]); + const result = await controller.searchScientificNames("cirs"); + expect(treeService.searchScientificNames).toHaveBeenCalledWith("cirs"); + const data = result.data as Resource[]; + expect(data.length).toBe(2); + expect(data[0].id).toBe("wfo-0000002583"); + expect(data[0].attributes.scientificName).toBe("Cirsium carolinianum"); + expect(data[1].id).toBe("wfo-0000003963"); + expect(data[1].attributes.scientificName).toBe("Cirsium carniolicum"); + }); }); - it("should return establishment data", async () => { - treeService.getEstablishmentTrees.mockResolvedValue(["Coffee", "Banana"]); - treeService.getPreviousPlanting.mockResolvedValue({ Coffee: 123, Banana: 75 }); - const result = await controller.getEstablishmentData({ entity: "site-reports", uuid: "fakeuuid" }); - expect(treeService.getEstablishmentTrees).toHaveBeenCalledWith("site-reports", "fakeuuid"); - expect(treeService.getPreviousPlanting).toHaveBeenCalledWith("site-reports", "fakeuuid"); - const resource = result.data as Resource; - expect(resource.id).toBe("site-reports|fakeuuid"); - expect(resource.attributes.establishmentTrees).toMatchObject(["Coffee", "Banana"]); - expect(resource.attributes.previousPlantingCounts).toMatchObject({ Coffee: 123, Banana: 75 }); + describe("getEstablishmentData", () => { + it("should return establishment data", async () => { + const stubData = { + Coffee: { amount: 123 }, + Banana: { amount: 75, taxonId: "wfo-faketaxonid" } + }; + treeService.getEstablishmentTrees.mockResolvedValue(Object.keys(stubData)); + treeService.getPreviousPlanting.mockResolvedValue(stubData); + const result = await controller.getEstablishmentData({ + entity: "site-reports", + uuid: "fakeuuid" + }); + expect(treeService.getEstablishmentTrees).toHaveBeenCalledWith("site-reports", "fakeuuid"); + expect(treeService.getPreviousPlanting).toHaveBeenCalledWith("site-reports", "fakeuuid"); + const resource = result.data as Resource; + expect(resource.id).toBe("site-reports|fakeuuid"); + expect(resource.attributes.establishmentTrees).toMatchObject(Object.keys(stubData)); + expect(resource.attributes.previousPlantingCounts).toMatchObject(stubData); + }); }); }); diff --git a/apps/entity-service/src/trees/trees.controller.ts b/apps/entity-service/src/trees/trees.controller.ts index 2d86e31..f3e4dc6 100644 --- a/apps/entity-service/src/trees/trees.controller.ts +++ b/apps/entity-service/src/trees/trees.controller.ts @@ -2,14 +2,15 @@ import { BadRequestException, Controller, Get, Param, Query, UnauthorizedExcepti import { TreeService } from "./tree.service"; import { buildJsonApi } from "@terramatch-microservices/common/util"; import { ScientificNameDto } from "./dto/scientific-name.dto"; -import { ApiOperation } from "@nestjs/swagger"; +import { ApiExtraModels, ApiOperation } from "@nestjs/swagger"; import { JsonApiResponse } from "@terramatch-microservices/common/decorators"; import { isEmpty } from "lodash"; import { EstablishmentsTreesParamsDto } from "./dto/establishments-trees-params.dto"; import { ApiException } from "@nanogiants/nestjs-swagger-api-exception-decorator"; -import { EstablishmentsTreesDto } from "./dto/establishment-trees.dto"; +import { EstablishmentsTreesDto, PreviousPlantingCountDto } from "./dto/establishment-trees.dto"; @Controller("trees/v3") +@ApiExtraModels(PreviousPlantingCountDto) export class TreesController { constructor(private readonly treeService: TreeService) {} diff --git a/libs/database/src/lib/entities/tree-species.entity.ts b/libs/database/src/lib/entities/tree-species.entity.ts index ba55008..ba8d606 100644 --- a/libs/database/src/lib/entities/tree-species.entity.ts +++ b/libs/database/src/lib/entities/tree-species.entity.ts @@ -3,6 +3,7 @@ import { AutoIncrement, BelongsTo, Column, + ForeignKey, Index, Model, PrimaryKey, @@ -16,6 +17,7 @@ import { Project } from "./project.entity"; import { ProjectReport } from "./project-report.entity"; import { Nursery } from "./nursery.entity"; import { NurseryReport } from "./nursery-report.entity"; +import { TreeSpeciesResearch } from "./tree-species-research.entity"; @Table({ tableName: "v2_tree_species", @@ -41,6 +43,11 @@ export class TreeSpecies extends Model { @Column(STRING) name: string | null; + @AllowNull + @ForeignKey(() => TreeSpeciesResearch) + @Column(STRING) + taxonId: string | null; + @AllowNull @Column(BIGINT) amount: number | null; diff --git a/libs/database/src/lib/factories/tree-species.factory.ts b/libs/database/src/lib/factories/tree-species.factory.ts index 4358a20..c630a62 100644 --- a/libs/database/src/lib/factories/tree-species.factory.ts +++ b/libs/database/src/lib/factories/tree-species.factory.ts @@ -11,6 +11,7 @@ import { NurseryFactory } from "./nursery.factory"; const defaultAttributesFactory = async () => ({ uuid: crypto.randomUUID(), name: faker.lorem.words(2), + taxonId: null, amount: faker.number.int({ min: 10, max: 1000 }), collection: "tree-planted" }); From 85705f2ac1cc113d1b3f76c611ac36ba61c82d8e Mon Sep 17 00:00:00 2001 From: Nathan Curtis Date: Mon, 16 Dec 2024 12:24:59 -0800 Subject: [PATCH 2/2] [TM-1402] Fix reference name. --- apps/entity-service/src/trees/dto/establishment-trees.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/entity-service/src/trees/dto/establishment-trees.dto.ts b/apps/entity-service/src/trees/dto/establishment-trees.dto.ts index b50192f..036e5de 100644 --- a/apps/entity-service/src/trees/dto/establishment-trees.dto.ts +++ b/apps/entity-service/src/trees/dto/establishment-trees.dto.ts @@ -24,7 +24,7 @@ export class EstablishmentsTreesDto extends JsonApiAttributes