Skip to content

Commit

Permalink
[TM-1401] Create the scientific names search endpoint.
Browse files Browse the repository at this point in the history
  • Loading branch information
roguenet committed Dec 4, 2024
1 parent db6956d commit 7d898ac
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 11 deletions.
6 changes: 4 additions & 2 deletions apps/entity-service/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { Module } from "@nestjs/common";
import { DatabaseModule } from "@terramatch-microservices/database";
import { CommonModule } from "@terramatch-microservices/common";
import { HealthModule } from "./health/health.module";
import { TreesController } from "./trees/trees.controller";
import { ResearchService } from "./trees/research.service";

@Module({
imports: [DatabaseModule, CommonModule, HealthModule],
controllers: [],
providers: []
controllers: [TreesController],
providers: [ResearchService]
})
export class AppModule {}
12 changes: 12 additions & 0 deletions apps/entity-service/src/trees/dto/scientific-name.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { JsonApiAttributes } from "@terramatch-microservices/common/dto/json-api-attributes";
import { ApiProperty } from "@nestjs/swagger";
import { JsonApiDto } from "@terramatch-microservices/common/decorators";

@JsonApiDto({ type: "treeSpeciesScientificNames", id: "string" })
export class ScientificNameDto extends JsonApiAttributes<ScientificNameDto> {
@ApiProperty({
description: "The scientific name for this tree species",
example: "Abelia uniflora"
})
scientificName: string;
}
21 changes: 21 additions & 0 deletions apps/entity-service/src/trees/research.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { TreeSpeciesResearch } from "@terramatch-microservices/database/entities";
import { Op } from "sequelize";
import { Injectable } from "@nestjs/common";

@Injectable()
export class ResearchService {
async searchScientificNames(search: string) {
return await TreeSpeciesResearch.findAll({
where: {
[Op.or]: [
// By checking these two, we're limiting the search term to only occurrences at the
// beginning of a word in the scientific name, which tends to lead to better results.
{ scientificName: { [Op.like]: `${search}%` } },
{ scientificName: { [Op.like]: `% ${search}%` } }
]
},
attributes: ["taxonId", "scientificName"],
limit: 10
});
}
}
28 changes: 28 additions & 0 deletions apps/entity-service/src/trees/trees.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { BadRequestException, Controller, Get, Query } from "@nestjs/common";
import { ResearchService } from "./research.service";
import { buildJsonApi } from "@terramatch-microservices/common/util";
import { ScientificNameDto } from "./dto/scientific-name.dto";
import { ApiOperation } from "@nestjs/swagger";
import { JsonApiResponse } from "@terramatch-microservices/common/decorators";

@Controller("trees/v3")
export class TreesController {
constructor(private readonly researchService: ResearchService) {}

@Get("scientific-names")
@ApiOperation({
operationId: "treeScientificNames",
description: "Search scientific names of tree species. Returns up to 10 entries"
})
@JsonApiResponse({ data: { type: ScientificNameDto } })
async searchScientificNames(@Query("search") search: string) {
if (search == null) throw new BadRequestException("search query param is required");

const document = buildJsonApi();
for (const treeSpecies of await this.researchService.searchScientificNames(search)) {
document.addData(`${treeSpecies.taxonId}`, new ScientificNameDto(treeSpecies));
}

return document.serialize();
}
}
16 changes: 7 additions & 9 deletions libs/common/src/lib/decorators/json-api-dto.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { applyDecorators, SetMetadata } from '@nestjs/common';
import { applyDecorators, SetMetadata } from "@nestjs/common";

export const DTO_TYPE_METADATA = 'DTO_TYPE_METADATA';
export const DTO_ID_METADATA = 'DTO_ID_METADATA';
export const DTO_TYPE_METADATA = "DTO_TYPE_METADATA";
export const DTO_ID_METADATA = "DTO_ID_METADATA";

export type IdType = 'uuid' | 'number';
export type IdType = "uuid" | "number" | "string";

export type DtoOptions = {
type: string;
Expand All @@ -12,9 +12,7 @@ export type DtoOptions = {
* The type of the id for this DTO. Defaults to UUID
*/
id?: IdType;
}
};

export const JsonApiDto = (options: DtoOptions) => applyDecorators(
SetMetadata(DTO_TYPE_METADATA, options.type),
SetMetadata(DTO_ID_METADATA, options.id ?? 'uuid')
);
export const JsonApiDto = (options: DtoOptions) =>
applyDecorators(SetMetadata(DTO_TYPE_METADATA, options.type), SetMetadata(DTO_ID_METADATA, options.id ?? "uuid"));

0 comments on commit 7d898ac

Please sign in to comment.