Skip to content

Commit e9120dc

Browse files
committed
567 Added CBBR filtering by cityCouncilDistrictIds
Updated OpenAPI documentation Updated endpoints Added tests Removed duplicate CBBRs Added comment explaining conditionals in CBBR repository
1 parent 0661321 commit e9120dc

12 files changed

Lines changed: 297 additions & 47 deletions

openapi/paths/community-board-budget-requests.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ get:
88
- $ref: ../components/parameters/communityDistrictIdQueryParam.yaml
99
- $ref: ../components/parameters/communityDistrictIdsQueryParam.yaml
1010
- $ref: ../components/parameters/cityCouncilDistrictIdQueryParam.yaml
11+
- $ref: ../components/parameters/cityCouncilDistrictIdsQueryParam.yaml
1112
- $ref: ../components/parameters/cbbrPolicyAreaIdQueryParam.yaml
1213
- $ref: ../components/parameters/cbbrNeedGroupIdQueryParam.yaml
1314
- $ref: ../components/parameters/agencyInitialsQueryParam.yaml

openapi/paths/community-board-budget-requests_csv.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ get:
88
- $ref: ../components/parameters/communityDistrictIdQueryParam.yaml
99
- $ref: ../components/parameters/communityDistrictIdsQueryParam.yaml
1010
- $ref: ../components/parameters/cityCouncilDistrictIdQueryParam.yaml
11+
- $ref: ../components/parameters/cityCouncilDistrictIdsQueryParam.yaml
1112
- $ref: ../components/parameters/cbbrPolicyAreaIdQueryParam.yaml
1213
- $ref: ../components/parameters/cbbrNeedGroupIdQueryParam.yaml
1314
- $ref: ../components/parameters/agencyInitialsQueryParam.yaml

src/community-board-budget-request/community-board-budget-request.controller.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export class CommunityBoardBudgetRequestController {
5656
communityDistrictId,
5757
communityDistrictIds,
5858
cityCouncilDistrictId,
59+
cityCouncilDistrictIds,
5960
cbbrAgencyCategoryResponseIds,
6061
cbbrNeedGroupId,
6162
cbbrPolicyAreaId,
@@ -76,6 +77,7 @@ export class CommunityBoardBudgetRequestController {
7677
communityDistrictCombinedId: communityDistrictId,
7778
communityDistrictIds,
7879
cityCouncilDistrictId,
80+
cityCouncilDistrictIds,
7981
cbbrAgencyCategoryResponseIds,
8082
cbbrNeedGroupId,
8183
cbbrPolicyAreaId,
@@ -103,6 +105,7 @@ export class CommunityBoardBudgetRequestController {
103105
communityDistrictId,
104106
communityDistrictIds,
105107
cityCouncilDistrictId,
108+
cityCouncilDistrictIds,
106109
cbbrAgencyCategoryResponseIds,
107110
cbbrNeedGroupId,
108111
cbbrPolicyAreaId,
@@ -121,6 +124,7 @@ export class CommunityBoardBudgetRequestController {
121124
communityDistrictCombinedId: communityDistrictId,
122125
communityDistrictIds,
123126
cityCouncilDistrictId,
127+
cityCouncilDistrictIds,
124128
cbbrAgencyCategoryResponseIds,
125129
cbbrNeedGroupId,
126130
cbbrPolicyAreaId,

src/community-board-budget-request/community-board-budget-request.repository.ts

Lines changed: 72 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ export class CommunityBoardBudgetRequestRepository {
289289
communityDistrictId,
290290
communityDistrictIds,
291291
cityCouncilDistrictId,
292+
cityCouncilDistrictIds,
292293
cbbrPolicyAreaId,
293294
cbbrNeedGroupId,
294295
agencyInitials,
@@ -306,6 +307,7 @@ export class CommunityBoardBudgetRequestRepository {
306307
communityDistrictId: string | null;
307308
communityDistrictIds: Array<string> | null;
308309
cityCouncilDistrictId: string | null;
310+
cityCouncilDistrictIds: Array<string> | null;
309311
cbbrPolicyAreaId: number | null;
310312
cbbrNeedGroupId: number | null;
311313
agencyInitials: string | null;
@@ -319,18 +321,18 @@ export class CommunityBoardBudgetRequestRepository {
319321
buffer: number;
320322
}): Promise<FindManyRepo> {
321323
try {
322-
return await this.db
323-
.select({
324-
id: communityBoardBudgetRequest.id,
325-
cbbrPolicyAreaId: communityBoardBudgetRequest.policyArea,
326-
title: communityBoardBudgetRequest.title,
327-
communityBoardId: sql<string>`${borough.abbr} || ${communityBoardBudgetRequest.communityDistrictId}`,
328-
isMapped: sql<boolean>`${or(
329-
isNotNull(communityBoardBudgetRequest.liFtMPnt),
330-
isNotNull(communityBoardBudgetRequest.liFtMPoly),
331-
)}`,
332-
isContinuedSupport: communityBoardBudgetRequest.isContinuedSupport,
333-
})
324+
// The query we use to sort by distance in the `orderBy` cannot be used with `selectDistinct`
325+
return await this.db[geom === null ? "selectDistinct" : "select"]({
326+
id: communityBoardBudgetRequest.id,
327+
cbbrPolicyAreaId: communityBoardBudgetRequest.policyArea,
328+
title: communityBoardBudgetRequest.title,
329+
communityBoardId: sql<string>`${borough.abbr} || ${communityBoardBudgetRequest.communityDistrictId}`,
330+
isMapped: sql<boolean>`${or(
331+
isNotNull(communityBoardBudgetRequest.liFtMPnt),
332+
isNotNull(communityBoardBudgetRequest.liFtMPoly),
333+
)}`,
334+
isContinuedSupport: communityBoardBudgetRequest.isContinuedSupport,
335+
})
334336
.from(communityBoardBudgetRequest)
335337
.leftJoin(
336338
borough,
@@ -339,7 +341,10 @@ export class CommunityBoardBudgetRequestRepository {
339341
.leftJoin(
340342
cityCouncilDistrict,
341343
and(
342-
sql`${cityCouncilDistrictId !== null} IS TRUE`,
344+
or(
345+
sql`${cityCouncilDistrictId !== null} IS TRUE`,
346+
sql`${cityCouncilDistrictIds !== null} IS TRUE`,
347+
),
343348
or(
344349
sql`ST_Intersects(${cityCouncilDistrict.liFt}, ${communityBoardBudgetRequest.liFtMPoly})`,
345350
sql`ST_Intersects(${cityCouncilDistrict.liFt}, ${communityBoardBudgetRequest.liFtMPnt})`,
@@ -352,6 +357,9 @@ export class CommunityBoardBudgetRequestRepository {
352357
cityCouncilDistrictId !== null
353358
? eq(cityCouncilDistrict.id, cityCouncilDistrictId)
354359
: undefined,
360+
cityCouncilDistrictIds !== null
361+
? inArray(cityCouncilDistrict.id, cityCouncilDistrictIds)
362+
: undefined,
355363
boroughId !== null && communityDistrictId !== null
356364
? and(
357365
eq(communityBoardBudgetRequest.boroughId, boroughId),
@@ -411,11 +419,12 @@ export class CommunityBoardBudgetRequestRepository {
411419
.limit(limit)
412420
.offset(offset)
413421
.orderBy(
414-
sql`CASE
422+
geom !== null
423+
? sql`CASE
415424
WHEN ${geom !== null && isNotNull(communityBoardBudgetRequest.liFtMPnt)} THEN ${geom} <-> ${communityBoardBudgetRequest.liFtMPnt}
416425
WHEN ${geom !== null && isNotNull(communityBoardBudgetRequest.liFtMPoly)} THEN ${geom} <-> ${communityBoardBudgetRequest.liFtMPoly}
417-
END`,
418-
communityBoardBudgetRequest.id,
426+
END`
427+
: communityBoardBudgetRequest.id,
419428
);
420429
} catch {
421430
throw new DataRetrievalException(
@@ -430,6 +439,7 @@ export class CommunityBoardBudgetRequestRepository {
430439
communityDistrictId,
431440
communityDistrictIds,
432441
cityCouncilDistrictId,
442+
cityCouncilDistrictIds,
433443
cbbrPolicyAreaId,
434444
cbbrNeedGroupId,
435445
agencyInitials,
@@ -445,6 +455,7 @@ export class CommunityBoardBudgetRequestRepository {
445455
communityDistrictId: string | null;
446456
communityDistrictIds: Array<string> | null;
447457
cityCouncilDistrictId: string | null;
458+
cityCouncilDistrictIds: Array<string> | null;
448459
cbbrPolicyAreaId: number | null;
449460
cbbrNeedGroupId: number | null;
450461
agencyInitials: string | null;
@@ -460,6 +471,7 @@ export class CommunityBoardBudgetRequestRepository {
460471
communityDistrictId,
461472
communityDistrictIds,
462473
cityCouncilDistrictId,
474+
cityCouncilDistrictIds,
463475
cbbrPolicyAreaId,
464476
cbbrNeedGroupId,
465477
agencyInitials,
@@ -486,7 +498,10 @@ export class CommunityBoardBudgetRequestRepository {
486498
.leftJoin(
487499
cityCouncilDistrict,
488500
and(
489-
sql`${cityCouncilDistrictId !== null} IS TRUE`,
501+
or(
502+
sql`${cityCouncilDistrictId !== null} IS TRUE`,
503+
sql`${cityCouncilDistrictIds !== null} IS TRUE`,
504+
),
490505
or(
491506
sql`ST_Intersects(${cityCouncilDistrict.liFt}, ${communityBoardBudgetRequest.liFtMPoly})`,
492507
sql`ST_Intersects(${cityCouncilDistrict.liFt}, ${communityBoardBudgetRequest.liFtMPnt})`,
@@ -499,6 +514,9 @@ export class CommunityBoardBudgetRequestRepository {
499514
cityCouncilDistrictId !== null
500515
? eq(cityCouncilDistrict.id, cityCouncilDistrictId)
501516
: undefined,
517+
cityCouncilDistrictIds !== null
518+
? inArray(cityCouncilDistrict.id, cityCouncilDistrictIds)
519+
: undefined,
502520
boroughId !== null && communityDistrictId !== null
503521
? and(
504522
eq(communityBoardBudgetRequest.boroughId, boroughId),
@@ -587,6 +605,7 @@ export class CommunityBoardBudgetRequestRepository {
587605
communityDistrictId,
588606
communityDistrictIds,
589607
cityCouncilDistrictId,
608+
cityCouncilDistrictIds,
590609
cbbrPolicyAreaId,
591610
cbbrNeedGroupId,
592611
agencyInitials,
@@ -602,6 +621,7 @@ export class CommunityBoardBudgetRequestRepository {
602621
communityDistrictId: string | null;
603622
communityDistrictIds: Array<string> | null;
604623
cityCouncilDistrictId: string | null;
624+
cityCouncilDistrictIds: Array<string> | null;
605625
cbbrPolicyAreaId: number | null;
606626
cbbrNeedGroupId: number | null;
607627
agencyInitials: string | null;
@@ -613,30 +633,30 @@ export class CommunityBoardBudgetRequestRepository {
613633
buffer: number;
614634
}): Promise<FindCsvRepo> {
615635
try {
616-
return await this.db
617-
.select({
618-
id: communityBoardBudgetRequest.id,
619-
communityBoardId: sql<string>`${borough.abbr} || ${communityBoardBudgetRequest.communityDistrictId}`,
620-
address: communityBoardBudgetRequest.address,
621-
siteName: communityBoardBudgetRequest.siteName,
622-
segmentOnStreet: communityBoardBudgetRequest.segmentOnStreet,
623-
segmentCrossStreetOne:
624-
communityBoardBudgetRequest.segmentCrossStreetOne,
625-
segmentCrossStreetTwo:
626-
communityBoardBudgetRequest.segmentCrossStreetTwo,
627-
intersectionStreetOne:
628-
communityBoardBudgetRequest.intersectionStreetOne,
629-
intersectionStreetTwo:
630-
communityBoardBudgetRequest.intersectionStreetTwo,
631-
requestType: sql<string>`SUBSTRING(${communityBoardBudgetRequest.requestType}, 1, 1)`,
632-
isContinuedSupport: communityBoardBudgetRequest.isContinuedSupport,
633-
request: cbbrRequest.description,
634-
explanation: communityBoardBudgetRequest.explanation,
635-
agency: communityBoardBudgetRequest.agency,
636-
priority: communityBoardBudgetRequest.priority,
637-
agencyCategoryResponse: cbbrAgencyCategoryResponse.description,
638-
agencyResponse: communityBoardBudgetRequest.agencyResponse,
639-
})
636+
// The query we use to sort by distance in the `orderBy` cannot be used with `selectDistinct`
637+
return await this.db[geom === null ? "selectDistinct" : "select"]({
638+
id: communityBoardBudgetRequest.id,
639+
communityBoardId: sql<string>`${borough.abbr} || ${communityBoardBudgetRequest.communityDistrictId}`,
640+
address: communityBoardBudgetRequest.address,
641+
siteName: communityBoardBudgetRequest.siteName,
642+
segmentOnStreet: communityBoardBudgetRequest.segmentOnStreet,
643+
segmentCrossStreetOne:
644+
communityBoardBudgetRequest.segmentCrossStreetOne,
645+
segmentCrossStreetTwo:
646+
communityBoardBudgetRequest.segmentCrossStreetTwo,
647+
intersectionStreetOne:
648+
communityBoardBudgetRequest.intersectionStreetOne,
649+
intersectionStreetTwo:
650+
communityBoardBudgetRequest.intersectionStreetTwo,
651+
requestType: sql<string>`SUBSTRING(${communityBoardBudgetRequest.requestType}, 1, 1)`,
652+
isContinuedSupport: communityBoardBudgetRequest.isContinuedSupport,
653+
request: cbbrRequest.description,
654+
explanation: communityBoardBudgetRequest.explanation,
655+
agency: communityBoardBudgetRequest.agency,
656+
priority: communityBoardBudgetRequest.priority,
657+
agencyCategoryResponse: cbbrAgencyCategoryResponse.description,
658+
agencyResponse: communityBoardBudgetRequest.agencyResponse,
659+
})
640660
.from(communityBoardBudgetRequest)
641661
.leftJoin(
642662
borough,
@@ -645,7 +665,10 @@ export class CommunityBoardBudgetRequestRepository {
645665
.leftJoin(
646666
cityCouncilDistrict,
647667
and(
648-
sql`${cityCouncilDistrictId !== null} IS TRUE`,
668+
or(
669+
sql`${cityCouncilDistrictId !== null} IS TRUE`,
670+
sql`${cityCouncilDistrictIds !== null} IS TRUE`,
671+
),
649672
or(
650673
sql`ST_Intersects(${cityCouncilDistrict.liFt}, ${communityBoardBudgetRequest.liFtMPoly})`,
651674
sql`ST_Intersects(${cityCouncilDistrict.liFt}, ${communityBoardBudgetRequest.liFtMPnt})`,
@@ -669,6 +692,9 @@ export class CommunityBoardBudgetRequestRepository {
669692
cityCouncilDistrictId !== null
670693
? eq(cityCouncilDistrict.id, cityCouncilDistrictId)
671694
: undefined,
695+
cityCouncilDistrictIds !== null
696+
? inArray(cityCouncilDistrict.id, cityCouncilDistrictIds)
697+
: undefined,
672698
boroughId !== null && communityDistrictId !== null
673699
? and(
674700
eq(communityBoardBudgetRequest.boroughId, boroughId),
@@ -726,11 +752,12 @@ export class CommunityBoardBudgetRequestRepository {
726752
),
727753
)
728754
.orderBy(
729-
sql`CASE
755+
geom !== null
756+
? sql`CASE
730757
WHEN ${geom !== null && isNotNull(communityBoardBudgetRequest.liFtMPnt)} THEN ${geom} <-> ${communityBoardBudgetRequest.liFtMPnt}
731758
WHEN ${geom !== null && isNotNull(communityBoardBudgetRequest.liFtMPoly)} THEN ${geom} <-> ${communityBoardBudgetRequest.liFtMPoly}
732-
END`,
733-
communityBoardBudgetRequest.id,
759+
END`
760+
: communityBoardBudgetRequest.id,
734761
);
735762
} catch {
736763
throw new DataRetrievalException(

src/community-board-budget-request/community-board-budget-request.service.spec.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,31 @@ describe("Community Board Budget Request service unit", () => {
388388
);
389389
});
390390

391+
it("should return a list of community board budget requests filtered by city council districts", async () => {
392+
const cbbrs = await communityBoardBudgetRequestService.findMany({
393+
cityCouncilDistrictIds: [
394+
cityCouncilDistrictRepositoryMock.districts[0].id,
395+
cityCouncilDistrictRepositoryMock.districts[1].id,
396+
],
397+
});
398+
399+
expect(() =>
400+
findCommunityBoardBudgetRequestsQueryResponseSchema.parse(cbbrs),
401+
).not.toThrow();
402+
403+
const parsedBody =
404+
findCommunityBoardBudgetRequestsQueryResponseSchema.parse(cbbrs);
405+
expect(parsedBody.limit).toBe(20);
406+
expect(parsedBody.offset).toBe(0);
407+
expect(parsedBody.communityBoardBudgetRequests.length).toBeGreaterThan(0);
408+
expect(parsedBody.total).toBe(
409+
parsedBody.communityBoardBudgetRequests.length,
410+
);
411+
expect(parsedBody.totalBudgetRequests).toBe(
412+
parsedBody.communityBoardBudgetRequests.length,
413+
);
414+
});
415+
391416
it("should return a list of community board budget requests filtered by city council district", async () => {
392417
const cityCouncilDistrictId =
393418
cityCouncilDistrictRepositoryMock.districts[0].id;
@@ -634,7 +659,7 @@ describe("Community Board Budget Request service unit", () => {
634659
findCommunityBoardBudgetRequestsQueryResponseSchema.parse(allCbbrs);
635660
expect(
636661
continuedSupport.totalBudgetRequests +
637-
nonContinuedSupport.totalBudgetRequests,
662+
nonContinuedSupport.totalBudgetRequests,
638663
).toBe(all.totalBudgetRequests);
639664
});
640665

@@ -879,6 +904,22 @@ describe("Community Board Budget Request service unit", () => {
879904
).not.toThrow();
880905
});
881906

907+
it("should return a list of community board budget requests for download filtered by city council districts", async () => {
908+
const csv = await communityBoardBudgetRequestService.findCsv({
909+
cityCouncilDistrictIds: [
910+
cityCouncilDistrictRepositoryMock.districts[0].id,
911+
cityCouncilDistrictRepositoryMock.districts[1].id,
912+
],
913+
});
914+
915+
expect(() => findCsvRepoSchema.parse(csv)).not.toThrow();
916+
expect(csv.length).toBe(8);
917+
const firstItem = csv[0];
918+
expect(() =>
919+
communityBoardBudgetRequestCsvRepoSchema.strict().parse(firstItem),
920+
).not.toThrow();
921+
});
922+
882923
it("should return a list of community board budget requests for download filtered by policy area", async () => {
883924
const cbbrPolicyAreaId =
884925
communityBoardBudgetRequestRepositoryMock.policyAreaMocks[0].id;

0 commit comments

Comments
 (0)