Skip to content

Commit

Permalink
OpenAPI Implementation findCityCouncilDistricts endpoint
Browse files Browse the repository at this point in the history
implement city council district (ccd) endpoint:
 - ccd controller, module, repository, service
 - cd unit and e2e tests
 - indentation fixes on openapi yaml
 - tweaks to README
 - regenerate ts and zod schemas

Closes #250
  • Loading branch information
horatiorosa committed Jun 4, 2024
1 parent 9b50671 commit a3a4995
Show file tree
Hide file tree
Showing 24 changed files with 281 additions and 53 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ Finally, to run the api locally:
npm run dev
```

- View the documentation locally via `localhost:3000`
- To use the API locally, append `/api/{endpoint_route}` to the localhost port (ex: `localhost:3000/api/boroughs`)

(This command will also create a static site of the OpenAPI documentation at the root of the API.
This site reflects the documentation at the point where the command was written. Viewing changes to
the OpenAPI documentation requires restarting the development server).
Expand Down
4 changes: 4 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@ services:
- POSTGRES_DB=${DATABASE_NAME}
ports:
- "8010:5432"
networks:
- data
volumes:
- ./db-volume:/var/lib/postgresql/data
networks:
data:
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { DbConfig, FeatureFlagConfig, StorageConfig } from "./config";
import { GlobalModule } from "./global/global.module";
import { ZoningDistrictClassModule } from "./zoning-district-class/zoning-district-class.module";
import { AgencyModule } from "./agency/agency.module";
import { CityCouncilDistrictModule } from "./city-council-district/city-council-district.module";

@Module({
imports: [
Expand All @@ -39,6 +40,7 @@ import { AgencyModule } from "./agency/agency.module";
GlobalModule,
AgencyModule,
BoroughModule,
CityCouncilDistrictModule,
LandUseModule,
TaxLotModule,
ZoningDistrictModule,
Expand Down
16 changes: 16 additions & 0 deletions src/city-council-district/city-council-district.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Controller, Get, UseFilters } from "@nestjs/common";
import { CityCouncilDistrictService } from "./city-council-district.service";
import { InternalServerErrorExceptionFilter } from "src/filter";

@UseFilters(InternalServerErrorExceptionFilter)
@Controller("city-council-districts")
export class CityCouncilDistrictController {
constructor(
private readonly cityCouncilDistrictService: CityCouncilDistrictService,
) {}

@Get()
async findMany() {
return this.cityCouncilDistrictService.findMany();
}
}
11 changes: 11 additions & 0 deletions src/city-council-district/city-council-district.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from "@nestjs/common";
import { CityCouncilDistrictService } from "./city-council-district.service";
import { CityCouncilDistrictController } from "./city-council-district.controller";
import { CityCouncilDistrictRepository } from "./city-council-district.repository";

@Module({
exports: [CityCouncilDistrictService],
providers: [CityCouncilDistrictService, CityCouncilDistrictRepository],
controllers: [CityCouncilDistrictController],
})
export class CityCouncilDistrictModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { cityCouncilDistrictEntitySchema } from "src/schema";
import { z } from "zod";

export const findManyRepoSchema = z.array(cityCouncilDistrictEntitySchema);

export type FindManyRepo = z.infer<typeof findManyRepoSchema>;
23 changes: 23 additions & 0 deletions src/city-council-district/city-council-district.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Inject } from "@nestjs/common";
import { DB, DbType } from "src/global/providers/db.provider";
import { DataRetrievalException } from "src/exception";
import { FindManyRepo } from "./city-council-district.repository.schema";

export class CityCouncilDistrictRepository {
constructor(
@Inject(DB)
private readonly db: DbType,
) {}

async findMany(): Promise<FindManyRepo> {
try {
return await this.db.query.cityCouncilDistrict.findMany({
columns: {
id: true,
},
});
} catch {
throw new DataRetrievalException();
}
}
}
32 changes: 32 additions & 0 deletions src/city-council-district/city-council-district.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { CityCouncilDistrictRepositoryMock } from "test/city-council-district/city-council-district.repository.mock";
import { Test } from "@nestjs/testing";
import { CityCouncilDistrictRepository } from "./city-council-district.repository";
import { findCityCouncilDistrictsQueryResponseSchema } from "src/gen";
import { CityCouncilDistrictService } from "./city-council-district.service";

describe("City Council District service unit", () => {
let cityCouncilDistrictService: CityCouncilDistrictService;

beforeEach(async () => {
const cityCouncilDistrictRepositoryMock =
new CityCouncilDistrictRepositoryMock();

const moduleRef = await Test.createTestingModule({
providers: [CityCouncilDistrictService, CityCouncilDistrictRepository],
})
.overrideProvider(CityCouncilDistrictRepository)
.useValue(cityCouncilDistrictRepositoryMock)
.compile();

cityCouncilDistrictService = moduleRef.get<CityCouncilDistrictService>(
CityCouncilDistrictService,
);
});

it("service should return a findCityCouncilDistrictsQueryResponseSchema compliant object", async () => {
const cityCouncilDistricts = await cityCouncilDistrictService.findMany();
expect(() =>
findCityCouncilDistrictsQueryResponseSchema.parse(cityCouncilDistricts),
).not.toThrow();
});
});
19 changes: 19 additions & 0 deletions src/city-council-district/city-council-district.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Inject, Injectable } from "@nestjs/common";
import { CityCouncilDistrictRepository } from "./city-council-district.repository";

@Injectable()
export class CityCouncilDistrictService {
constructor(
@Inject(CityCouncilDistrictRepository)
private readonly cityCouncilDistrictRepository: CityCouncilDistrictRepository,
) {}

async findMany() {
const cityCouncilDistricts =
await this.cityCouncilDistrictRepository.findMany();

return {
cityCouncilDistricts,
};
}
}
33 changes: 16 additions & 17 deletions src/gen/types/CapitalCommitment.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,49 @@
export type CapitalCommitment = {
/**
* @description A uuid used to refer to the capital commitment.
* @type string | undefined uuid
* @type string uuid
*/
id?: string;
id: string;
/**
* @description A four character string used to refer to the commitment type.
* @type string | undefined
* @type string
* @example DSGN
*/
type?: string;
type: string;
/**
* @description A string used to refer to the date when the commitment is projected to be committed.
* @type string | undefined date
* @type string date
* @example 2012-04-23
*/
plannedDate?: string;
plannedDate: string;
/**
* @description A string used to refer to the budget line.
* @type string | undefined
* @type string
* @example HW
*/
budgetLineCode?: string;
budgetLineCode: string;
/**
* @description A string used to refer to the budget line.
* @type string | undefined
* @type string
* @example 0002Q
*/
budgetLineId?: string;
budgetLineId: string;
/**
* @description A string of variable length containing the initials of the sponsoring agency.
* @type string | undefined
* @type string
* @example DOT
*/
sponsoringAgencyInitials?: string;
sponsoringAgencyInitials: string;
/**
* @description A string of variable length denoting the type of budget.
* @type string | undefined
* @type string
* @example Highways
*/
budgetType?: string;
budgetType: string;
/**
* @description A numeric string used to refer to the amount of total planned commitments.
* @type number | undefined
* @type number
* @example 1600000
*/
totalValue?: number;
required?: any;
totalValue: number;
};
12 changes: 6 additions & 6 deletions src/gen/types/CapitalProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ export type CapitalProject = {
managingAgencyInitials: string;
/**
* @description The starting date of the capital project
* @type date
* @example 2024-05-15T14:20:03.842Z
* @type string date
* @example 2024-05-15
*/
minDate: any;
minDate: string;
/**
* @description The ending date of the capital project
* @type date
* @example 2024-05-15T14:20:03.842Z
* @type string date
* @example 2024-05-15
*/
maxDate: any;
maxDate: string;
category?: CapitalProjectCategory;
};
9 changes: 9 additions & 0 deletions src/gen/types/CapitalProjectPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Page } from "./Page";
import type { CapitalProject } from "./CapitalProject";

export type CapitalProjectPage = Page & {
/**
* @type array
*/
capitalProjects: CapitalProject[];
};
9 changes: 4 additions & 5 deletions src/gen/types/CommunityDistrict.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
export type CommunityDistrict = {
/**
* @description The two character numeric string containing the number used to refer to the community district.
* @type string | undefined
* @type string
* @example 1
*/
id?: string;
id: string;
/**
* @description A single character numeric string containing the common number used to refer to the borough. Possible values are 1-5.
* @type string | undefined
* @type string
* @example 1
*/
boroughId?: string;
required?: any;
boroughId: string;
};
16 changes: 16 additions & 0 deletions src/gen/types/FindCityCouncilDistricts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Error } from "./Error";
import type { CityCouncilDistrict } from "./CityCouncilDistrict";

export type FindCityCouncilDistricts400 = Error;

export type FindCityCouncilDistricts500 = Error;

/**
* @description an object of city council districts
*/
export type FindCityCouncilDistrictsQueryResponse = {
/**
* @type array
*/
cityCouncilDistricts: CityCouncilDistrict[];
};
2 changes: 2 additions & 0 deletions src/gen/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ export * from "./CapitalCommitment";
export * from "./CapitalProject";
export * from "./CapitalProjectBudgeted";
export * from "./CapitalProjectCategory";
export * from "./CapitalProjectPage";
export * from "./CityCouncilDistrict";
export * from "./CommunityDistrict";
export * from "./Error";
export * from "./FindAgencies";
export * from "./FindBoroughs";
export * from "./FindCityCouncilDistricts";
export * from "./FindCommunityDistrictsByBoroughId";
export * from "./FindLandUses";
export * from "./FindTaxLotByBbl";
Expand Down
25 changes: 8 additions & 17 deletions src/gen/zod/capitalCommitmentSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,33 @@ export const capitalCommitmentSchema = z.object({
id: z
.string()
.describe(`A uuid used to refer to the capital commitment.`)
.uuid()
.optional(),
.uuid(),
type: z
.string()
.describe(`A four character string used to refer to the commitment type.`)
.regex(new RegExp("^([A-z]{4})$"))
.optional(),
.regex(new RegExp("^([A-z]{4})$")),
plannedDate: z
.string()
.describe(
`A string used to refer to the date when the commitment is projected to be committed.`,
)
.optional(),
),
budgetLineCode: z
.string()
.describe(`A string used to refer to the budget line.`)
.optional(),
.describe(`A string used to refer to the budget line.`),
budgetLineId: z
.string()
.describe(`A string used to refer to the budget line.`)
.optional(),
.describe(`A string used to refer to the budget line.`),
sponsoringAgencyInitials: z
.string()
.describe(
`A string of variable length containing the initials of the sponsoring agency.`,
)
.optional(),
),
budgetType: z
.string()
.describe(`A string of variable length denoting the type of budget.`)
.optional(),
.describe(`A string of variable length denoting the type of budget.`),
totalValue: z
.number()
.describe(
`A numeric string used to refer to the amount of total planned commitments.`,
)
.optional(),
required: z.any().optional(),
),
});
12 changes: 12 additions & 0 deletions src/gen/zod/capitalProjectPageSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { z } from "zod";

import { pageSchema } from "./pageSchema";
import { capitalProjectSchema } from "./capitalProjectSchema";

export const capitalProjectPageSchema = z
.lazy(() => pageSchema)
.schema.and(
z.object({
capitalProjects: z.array(z.lazy(() => capitalProjectSchema).schema),
}),
);
4 changes: 2 additions & 2 deletions src/gen/zod/capitalProjectSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const capitalProjectSchema = z.object({
managingAgencyInitials: z
.string()
.describe(`The managing agency name abbreviation or acronym`),
minDate: z.any().describe(`The starting date of the capital project`),
maxDate: z.any().describe(`The ending date of the capital project`),
minDate: z.string().describe(`The starting date of the capital project`),
maxDate: z.string().describe(`The ending date of the capital project`),
category: z.lazy(() => capitalProjectCategorySchema).schema.optional(),
});
7 changes: 2 additions & 5 deletions src/gen/zod/communityDistrictSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@ export const communityDistrictSchema = z.object({
.describe(
`The two character numeric string containing the number used to refer to the community district.`,
)
.regex(new RegExp("^([0-9]{2})$"))
.optional(),
.regex(new RegExp("^([0-9]{2})$")),
boroughId: z
.string()
.describe(
`A single character numeric string containing the common number used to refer to the borough. Possible values are 1-5.`,
)
.regex(new RegExp("\\b[1-9]\\b"))
.optional(),
required: z.any().optional(),
.regex(new RegExp("\\b[1-9]\\b")),
});
Loading

0 comments on commit a3a4995

Please sign in to comment.