Skip to content

[TM-2084] Move demographics new controller and support querying #194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4f2c676
[TM-2084] feat: add demographics controller and service to app module
Scriptmatico Jun 3, 2025
e02f183
[TM-2084] feat: implement DemographicDtoV2 and update demographics se…
Scriptmatico Jun 4, 2025
263bb2f
[TM-2084] feat: enhance demographic service and query DTO to support …
Scriptmatico Jun 4, 2025
b1f9b5b
[TM-2084] feat: add setup script and policy for demographics, and con…
Scriptmatico Jun 4, 2025
802b849
[TM-2084] add demographic.service.spec.ts unit test
Scriptmatico Jun 5, 2025
ff5ac96
[TM-2084] feat: update demographic service to support project and sit…
Scriptmatico Jun 5, 2025
d09c44b
[TM-2084] feat: enhance demographic service to validate filters and s…
Scriptmatico Jun 5, 2025
179d289
[TM-2084] Merge branch 'staging' into feat/TM-2084-Move-demographics-…
Scriptmatico Jun 6, 2025
e6e848b
[TM-2084] feat: refactor demographics module to improve structure and…
Scriptmatico Jun 6, 2025
2ab0c23
[TM-2084] feat: refactor demographic DTO and service for improved que…
Scriptmatico Jun 8, 2025
f2de920
[TM-2084] feat: restructure demographic service and query DTO for imp…
Scriptmatico Jun 10, 2025
f057aae
[TM-2084] Merge branch 'staging' into feat/TM-2084-Move-demographics-…
Scriptmatico Jun 11, 2025
cbfbfc6
[TM-2084] Add LARAVEL_MODELS constant and update DemographicDto const…
Scriptmatico Jun 11, 2025
f654add
[TM-2084] Fix syntax error in POLICIES array by adding missing comma …
Scriptmatico Jun 11, 2025
58f267e
[TM-2084] fix laravel types import
Scriptmatico Jun 11, 2025
7a5826d
[TM-2084] Update demographic tests to include demographicalType in mo…
Scriptmatico Jun 12, 2025
484e110
[TM-2084] Update demographic tests to include demographicalId in mock…
Scriptmatico Jun 12, 2025
918faf5
[TM-2084] Merge branch 'staging' into feat/TM-2084-Move-demographics-…
Scriptmatico Jun 17, 2025
47a8360
[TM-2084] Enhance DemographicsController with error handling and logg…
Scriptmatico Jun 17, 2025
0127b24
[TM-2084] Refactor DemographicsController to utilize LARAVEL_MODEL_TY…
Scriptmatico Jun 17, 2025
5058d78
[TM-2084] Remove unused import for MediaOwnerType in DemographicsCont…
Scriptmatico Jun 17, 2025
251e7e8
[TM-2084] Refactor DemographicsController and tests to utilize Projec…
Scriptmatico Jun 18, 2025
e178818
[TM-2084] Refactor association types in DTO and constants to include …
Scriptmatico Jun 18, 2025
1820aee
[TM-2084] Remove unused import for Media in laravel-types.ts
Scriptmatico Jun 18, 2025
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
28 changes: 15 additions & 13 deletions apps/entity-service/src/entities/demographic.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@ import { PaginatedQueryBuilder } from "@terramatch-microservices/common/util/pag
import { DemographicQueryDto } from "./dto/demographic-query.dto";
import { Model, ModelStatic, Op } from "sequelize";

type DemographicFilter<T extends Model = Model> = {
uuidKey: string;
model: ModelStatic<T>;
laravelType: string;
};

@Injectable()
export class DemographicService {
async getDemographics(query: DemographicQueryDto) {
const builder = PaginatedQueryBuilder.forNumberPage(Demographic, query);

type DemographicFilter<T extends Model = Model> = {
uuidKey: string;
model: ModelStatic<T>;
laravelType: string;
};

const demographicFilters: DemographicFilter[] = [
const DEMOGRAPHIC_FILTERS: DemographicFilter[] = [
{
uuidKey: "projectUuid",
model: Project,
Expand All @@ -31,16 +29,20 @@ export class DemographicService {
model: SiteReport,
laravelType: SiteReport.LARAVEL_TYPE
}
];
] as const;

const builder = PaginatedQueryBuilder.forNumberPage(Demographic, query);

const VALID_FILTER_KEYS = DEMOGRAPHIC_FILTERS.map(({ uuidKey }) => uuidKey);

Object.entries(query).forEach(([key]) => {
Object.keys(query).forEach(key => {
if (key === "page" || key === "sort") return;
if (!demographicFilters.map(d => d.uuidKey).includes(key)) {
if (!VALID_FILTER_KEYS.includes(key)) {
throw new BadRequestException(`Invalid filter key: ${key}`);
}
});

for (const { uuidKey, model, laravelType } of demographicFilters) {
for (const { uuidKey, model, laravelType } of DEMOGRAPHIC_FILTERS) {
const uuids = query[uuidKey];
if (uuids != null && uuids.length > 0) {
const records = (await model.findAll({
Expand Down
4 changes: 2 additions & 2 deletions apps/entity-service/src/entities/demographics.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { buildJsonApi, getStableRequestQuery } from "@terramatch-microservices/c
import { ApiOperation } from "@nestjs/swagger";
import { ExceptionResponse, JsonApiResponse } from "@terramatch-microservices/common/decorators";
import { PolicyService } from "@terramatch-microservices/common";
import { DemographicDto, DemographicDtoV2 } from "./dto/demographic.dto";
import { DemographicDto } from "./dto/demographic.dto";
import { DemographicQueryDto } from "./dto/demographic-query.dto";
import { DemographicService } from "./demographic.service";

Expand All @@ -27,7 +27,7 @@ export class DemographicsController {
await this.policyService.authorize("read", data);
for (const demographic of data) {
indexIds.push(demographic.uuid);
const demographicDto = new DemographicDtoV2(demographic);
const demographicDto = new DemographicDto(demographic);
document.addData(demographicDto.uuid, demographicDto);
}
}
Expand Down
24 changes: 3 additions & 21 deletions apps/entity-service/src/entities/dto/demographic-query.dto.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,9 @@
import { ApiProperty, IntersectionType } from "@nestjs/swagger";
import { IsArray, IsEnum, IsOptional, ValidateNested } from "class-validator";
import { IsArray, IsOptional } from "class-validator";
import { NumberPage } from "@terramatch-microservices/common/dto/page.dto";
import { IndexQueryDto } from "./index-query.dto";

class QuerySort {
@ApiProperty({ name: "sort[field]", required: false })
@IsOptional()
field?: string;

@ApiProperty({ name: "sort[direction]", required: false, enum: ["ASC", "DESC"], default: "ASC" })
@IsEnum(["ASC", "DESC"])
@IsOptional()
direction?: "ASC" | "DESC";
}

export class DemographicQueryDto extends IntersectionType(QuerySort, NumberPage) {
@ValidateNested()
@IsOptional()
page?: NumberPage;

@ValidateNested()
@IsOptional()
sort?: QuerySort;

export class DemographicQueryDto extends IntersectionType(IndexQueryDto, NumberPage) {
@ApiProperty({ required: false, isArray: true, description: "project uuid array" })
@IsOptional()
@IsArray()
Expand Down
34 changes: 11 additions & 23 deletions apps/entity-service/src/entities/dto/demographic.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,31 +75,19 @@ export class DemographicEntryDto {

@JsonApiDto({ type: "demographics" })
export class DemographicDto extends AssociationDto {
constructor(demographic: Demographic, additional: AdditionalProps<DemographicDto, Demographic>) {
constructor(demographic: Demographic, additional: AdditionalProps<DemographicDto, Demographic>);
constructor(demographic: Demographic);
constructor(demographic: Demographic, additional?: AdditionalProps<DemographicDto, Demographic>) {
super();
populateDto<DemographicDto, Omit<Demographic, "entries">>(this, demographic, {
...additional,
entries: demographic.entries?.map(entry => new DemographicEntryDto(entry)) ?? []
});
}

@ApiProperty()
uuid: string;

@ApiProperty({ enum: Demographic.VALID_TYPES })
type: string;

@ApiProperty()
collection: string;

@ApiProperty({ type: () => DemographicEntryDto, isArray: true })
entries: DemographicEntryDto[];
}

@JsonApiDto({ type: "demographics" })
export class DemographicDtoV2 {
constructor(data: Demographic) {
populateDto<DemographicDtoV2>(this, data as DemographicDtoV2);
if (additional != null) {
populateDto<DemographicDto, Omit<Demographic, "entries">>(this, demographic, {
...additional,
entries: demographic.entries?.map(entry => new DemographicEntryDto(entry)) ?? []
});
} else {
populateDto<DemographicDto>(this, demographic as unknown as DemographicDto);
}
}

@ApiProperty()
Expand Down