Skip to content

Commit

Permalink
Backend - OpenMaps api path added to values, proj4 dependency added, …
Browse files Browse the repository at this point in the history
…migration added to add geoJSON and locationPIDSAuto columns, models, types, validators updated
  • Loading branch information
sanjaytkbabu authored and kyle1morel committed Feb 26, 2025
1 parent 51200d3 commit 9253ec1
Show file tree
Hide file tree
Showing 28 changed files with 252 additions and 22 deletions.
1 change: 1 addition & 0 deletions .github/environments/values.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ config:
SERVER_OBJECTSTORAGE_KEY: dev
SERVER_OIDC_AUTHORITY: https://dev.loginproxy.gov.bc.ca/auth/realms/standard
SERVER_OIDC_PUBLICKEY: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuy7zfh2ZgpDV5mH/aXyLDTddZK81rGakJcTy4KvCNOkDDxt1KAhW02lmbCo8YhHCOzjNZBp1+Vi6QiMRgBqAe2GTPZYEiV70aXfROGZe3Nvwcjbtki6HoyRte3SpqLJEIPL2F+hjJkw1UPGnjPTWZkEx9p74b9i3BjuE8RnjJ0Sza2MWw83zoQUZEJRGiopSL0yuVej6t2LO2btVdVf7QuZfPt9ehkcQYlPKpVvJA+pfeqPAdnNt7OjEIeYxinjurZr8Z04hz8UhkRefcWlSbFzFQYmL7O7iArjW0bsSvq8yNUd5r0KCOQkFduwZy26yTzTxj8OLFT91fEmbBBl4rQIDAQAB
SERVER_OPENMAPS_APIPATH: https://openmaps.gov.bc.ca
SERVER_PORT: "8080"
SERVER_SSO_APIPATH: https://api.loginproxy.gov.bc.ca/api/v1
SERVER_SSO_TOKENURL: https://loginproxy.gov.bc.ca/auth/realms/standard/protocol/openid-connect/token
Expand Down
1 change: 1 addition & 0 deletions .github/environments/values.prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ config:
SERVER_OBJECTSTORAGE_KEY: shas
SERVER_OIDC_AUTHORITY: https://loginproxy.gov.bc.ca/auth/realms/standard
SERVER_OIDC_PUBLICKEY: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHiuPKOkpkq4GXN1ktr23rJtDl6Vdu/Y37ZAd3PnQ8/IDfAODvy1Y81aAUZicKe9egolv+OTRANN3yOg+TAbRhkeXLE5p/473EK0aQ0NazTCuWo6Am3oDQ7Yt8x0pw56/qcLtkTuXNyo5EnVV2Z2BzCnnaL31JOhyitolku0DNT6GDoRBmT4o2ItqEVHk5nM25cf1t2zbwI2790W6if1B2qVRkxxivS8tbH7nYC61Is3XCPockKptkH22cm2ZQJmtYd5sZKuXaGsvtyzHmn8/l0Kd1xnHmUu4JNuQ67YiNZGu3hOkrF0Js3BzAk1Qm4kvYRaxbJFCs/qokLZ4Z0W9wIDAQAB
SERVER_OPENMAPS_APIPATH: https://openmaps.gov.bc.ca
SERVER_PORT: "8080"
SERVER_SSO_APIPATH: https://api.loginproxy.gov.bc.ca/api/v1
SERVER_SSO_TOKENURL: https://loginproxy.gov.bc.ca/auth/realms/standard/protocol/openid-connect/token
Expand Down
1 change: 1 addition & 0 deletions .github/environments/values.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ config:
SERVER_OBJECTSTORAGE_KEY: shas
SERVER_OIDC_AUTHORITY: https://test.loginproxy.gov.bc.ca/auth/realms/standard
SERVER_OIDC_PUBLICKEY: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiFdv9GA83uHuy8Eu9yiZHGGF9j6J8t7FkbcpaN81GDjwbjsIJ0OJO9dKRAx6BAtTC4ubJTBJMPvQER5ikOhIeBi4o25fg61jpgsU6oRZHkCXc9gX6mrjMjbsPaf3/bjjYxP5jicBDJQeD1oRa24+tiGggoQ7k6gDEN+cRYqqNpzC/GQbkUPk8YsgroncEgu8ChMh/3ERsLV2zorchMANUq76max16mHrhtWIQxrb/STpSt4JuSlUzzBV/dcXjJe5gywZHe0jAutFhNqjHzHdgyaC4RAd3eYQo+Kl/JOgy2AZrnx+CiPmvOJKe9tAW4k4H087ng8aVE40v4HW/FEbnwIDAQAB
SERVER_OPENMAPS_APIPATH: https://openmaps.gov.bc.ca
SERVER_PORT: "8080"
SERVER_SSO_APIPATH: https://api.loginproxy.gov.bc.ca/api/v1
SERVER_SSO_TOKENURL: https://loginproxy.gov.bc.ca/auth/realms/standard/protocol/openid-connect/token
Expand Down
3 changes: 3 additions & 0 deletions app/config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
"clientSecret": "SERVER_OIDC_CLIENTSECRET",
"publicKey": "SERVER_OIDC_PUBLICKEY"
},
"openMaps": {
"apiPath": "SERVER_OPENMAPS_APIPATH"
},
"port": "SERVER_PORT",
"sso": {
"apiPath": "SERVER_SSO_APIPATH",
Expand Down
26 changes: 26 additions & 0 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
},
"dependencies": {
"@prisma/client": "^6.0.1",
"@types/proj4": "^2.5.5",
"api-problem": "^9.0.2",
"axios": "^1.7.9",
"compression": "^1.7.5",
Expand All @@ -67,6 +68,7 @@
"jsonwebtoken": "^9.0.2",
"knex": "^3.1.0",
"pg": "^8.13.1",
"proj4": "^2.15.0",
"ts-node": "^10.9.2",
"uuid": "^11.0.3",
"winston": "^3.17.0",
Expand Down
1 change: 1 addition & 0 deletions app/src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { default as atsController } from './ats';
export { default as contactController } from './contact';
export { default as documentController } from './document';
export { default as enquiryController } from './enquiry';
export { default as mapController } from './map';
export { default as noteController } from './note';
export { default as permitController } from './permit';
export { default as permitNoteController } from './permitNote';
Expand Down
19 changes: 19 additions & 0 deletions app/src/controllers/map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { mapService, submissionService } from '../services';
import type { NextFunction, Request, Response } from 'express';

const controller = {
getPIDs: async (req: Request<{ submissionId: string }>, res: Response, next: NextFunction) => {
try {
const submission = await submissionService.getSubmission(req.params.submissionId);

let response;
if (submission?.geoJSON) response = await mapService.getPIDs(submission?.geoJSON);

res.status(200).json(response);
} catch (e: unknown) {
next(e);
}
}
};

export default controller;
1 change: 1 addition & 0 deletions app/src/controllers/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const controller = {
naturalDisaster: data.location.naturalDisaster,
projectLocation: data.location.projectLocation,
projectLocationDescription: data.location.projectLocationDescription,
geoJSON: data.location.geoJSON,
locationPIDs: data.location.ltsaPIDLookup,
latitude: data.location.latitude,
longitude: data.location.longitude,
Expand Down
18 changes: 18 additions & 0 deletions app/src/db/migrations/20250130000000_021-advanced-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-disable max-len */
import type { Knex } from 'knex';

export async function up(knex: Knex): Promise<void> {
return Promise.resolve().then(() =>
knex.schema.alterTable('submission', function (table) {
table.json('geo_json');
})
);
}

export async function down(knex: Knex): Promise<void> {
return Promise.resolve().then(() =>
knex.schema.alterTable('submission', function (table) {
table.dropColumn('geo_json');
})
);
}
3 changes: 3 additions & 0 deletions app/src/db/models/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export default {
consent_to_feedback: input.consentToFeedback,
location_pids: input.locationPIDs,
company_name_registered: input.companyNameRegistered,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
geo_json: input.geoJSON as any,
single_family_units: input.singleFamilyUnits,
has_rental_units: input.hasRentalUnits,
street_address: input.streetAddress,
Expand Down Expand Up @@ -110,6 +112,7 @@ export default {
projectName: input.project_name,
projectDescription: input.project_description,
companyNameRegistered: input.company_name_registered,
geoJSON: input.geo_json,
singleFamilyUnits: input.single_family_units,
hasRentalUnits: input.has_rental_units,
streetAddress: input.street_address,
Expand Down
1 change: 1 addition & 0 deletions app/src/db/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ model submission {
housing_coop_description String?
submission_type String?
consent_to_feedback Boolean @default(false)
geo_json Json? @db.Json
activity activity @relation(fields: [activity_id], references: [activity_id], onDelete: Cascade, map: "submission_activity_id_foreign")
user user? @relation(fields: [assigned_user_id], references: [user_id], onDelete: Cascade, map: "submission_assigned_user_id_foreign")
Expand Down
3 changes: 3 additions & 0 deletions app/src/routes/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import contact from './contact';
import docs from './docs';
import document from './document';
import enquiry from './enquiry';
import map from './map';
import note from './note';
import permit from './permit';
import reporting from './reporting';
Expand All @@ -32,6 +33,7 @@ router.get('/', (_req, res) => {
'/docs',
'/document',
'/enquiry',
'/map',
'/note',
'/permit',
'/reporting',
Expand All @@ -50,6 +52,7 @@ router.use('/ats', ats);
router.use('/contact', contact);
router.use('/document', document);
router.use('/enquiry', enquiry);
router.use('/map', map);
router.use('/note', note);
router.use('/permit', permit);
router.use('/reporting', reporting);
Expand Down
23 changes: 23 additions & 0 deletions app/src/routes/v1/map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import express from 'express';

import { mapController } from '../../controllers';
import { hasAuthorization } from '../../middleware/authorization';
import { requireSomeAuth } from '../../middleware/requireSomeAuth';
import { requireSomeGroup } from '../../middleware/requireSomeGroup';
import { Action, Resource } from '../../utils/enums/application';

import type { NextFunction, Request, Response } from 'express';

const router = express.Router();
router.use(requireSomeAuth);
router.use(requireSomeGroup);

router.get(
'/pids/:submissionId',
hasAuthorization(Resource.SUBMISSION, Action.READ),
(req: Request<{ submissionId: string }>, res: Response, next: NextFunction): void => {
mapController.getPIDs(req, res, next);
}
);

export default router;
1 change: 1 addition & 0 deletions app/src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { default as draftService } from './draft';
export { default as emailService } from './email';
export { default as enquiryService } from './enquiry';
export { default as initiativeService } from './initiative';
export { default as mapService } from './map';
export { default as noteService } from './note';
export { default as permitService } from './permit';
export { default as permitNoteService } from './permitNote';
Expand Down
89 changes: 89 additions & 0 deletions app/src/services/map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import axios from 'axios';
import config from 'config';
import proj4 from 'proj4';

import type { AxiosInstance, AxiosRequestConfig } from 'axios';

/**
* @function openMapsAxios
* Returns an Axios instance for the CHEFS API
* @param {AxiosRequestConfig} options Axios request config options
* @returns {AxiosInstance} An axios instance
*/
function openMapsAxios(options: AxiosRequestConfig = {}): AxiosInstance {
return axios.create({
baseURL: config.get('server.openMaps.apiPath'),
timeout: 10000,
...options
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getPolygonArray(geoJSON: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const polygonArray = geoJSON?.geometry?.coordinates[0]?.map((c: any) => {
return { lat: c[1], lng: c[0] };
});
return polygonArray;
}

const service = {
/**
* @function getParcelDataFromPMBC
* DataBC’s Open Web Services
* Accessing geographic data via WMS/WFS
* Services Provided by OCIO - Digital Platforms & Data - Data Systems & Services
* ref: https://docs.geoserver.org/main/en/user/services/wfs/reference.html#getfeature
* ref: https://catalogue.data.gov.bc.ca/dataset/parcelmap-bc-parcel-fabric
* @returns parcel data in JSON
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getPIDs: async (geoJSON: any) => {
const polygon = getPolygonArray(geoJSON);
// close polygon by re-adding first point to end of array
// define the source and destination layer types
// leaflet map layer
const source = proj4.Proj('EPSG:4326'); // gps format of leaflet map
// projection (BC Parcel data layer)
proj4.defs(
'EPSG:3005',
// eslint-disable-next-line max-len
'PROJCS["NAD83 / BC Albers", GEOGCS["NAD83", DATUM["North_American_Datum_1983", SPHEROID["GRS 1980",6378137,298.257222101, AUTHORITY["EPSG","7019"]], TOWGS84[0,0,0,0,0,0,0], AUTHORITY["EPSG","6269"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.0174532925199433, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4269"]], PROJECTION["Albers_Conic_Equal_Area"], PARAMETER["standard_parallel_1",50], PARAMETER["standard_parallel_2",58.5], PARAMETER["latitude_of_center",45], PARAMETER["longitude_of_center",-126], PARAMETER["false_easting",1000000], PARAMETER["false_northing",0], UNIT["metre",1, AUTHORITY["EPSG","9001"]], AXIS["Easting",EAST], AXIS["Northing",NORTH], AUTHORITY["EPSG","3005"]]'
);
const dest = proj4.Proj('EPSG:3005');

// convert lat/long for WFS query
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = polygon.map((point: any) => {
//@ts-expect-error insufficient type definitions
return proj4(source, dest, { x: point.lng, y: point.lat });
});

// build query string for WFS request
let query = '';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
result.forEach((point: any, index: any, array: any) => {
query = query.concat(point.x, ' ', point.y);
if (index < array.length - 1) query = query.concat(', ');
});

let params =
// eslint-disable-next-line max-len
'/geo/pub/wfs?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&outputFormat=json&typeName=WHSE_CADASTRE.PMBC_PARCEL_FABRIC_POLY_SVW&CQL_FILTER=INTERSECTS(SHAPE, POLYGON ((query)))';

params = params.replace('query', query);

const response = await openMapsAxios().get(params);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const parcelData = response.data.features?.map((f: any) => f.properties);

const PIDs = parcelData
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.map((p: any) => p.PID_FORMATTED)
.filter((pid: string) => pid && pid.trim().length > 0)
.join(', ');

return PIDs;
}
};

export default service;
6 changes: 4 additions & 2 deletions app/src/services/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const service = {
*/
createSubmission: async (data: Partial<Submission>) => {
const response = await prisma.submission.create({
// @ts-expect-error 2322
data: { ...submission.toPrismaModel(data as Submission), created_at: data.createdAt, created_by: data.createdBy },
include: {
activity: {
Expand All @@ -48,7 +49,7 @@ const service = {
}
}
});

// @ts-expect-error 2322
return submission.fromPrismaModelWithContact(response);
},

Expand Down Expand Up @@ -357,6 +358,7 @@ const service = {
updateSubmission: async (data: Submission) => {
try {
const result = await prisma.submission.update({
// @ts-expect-error 2322
data: { ...submission.toPrismaModel(data), updated_at: data.updatedAt, updated_by: data.updatedBy },
where: {
submission_id: data.submissionId
Expand All @@ -373,7 +375,7 @@ const service = {
}
}
});

// @ts-expect-error 2322
return submission.fromPrismaModelWithContact(result);
} catch (e: unknown) {
throw e;
Expand Down
2 changes: 2 additions & 0 deletions app/src/types/Submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export type Submission = {
locationPIDs: string | null;
companyNameRegistered: string | null;
consentToFeedback: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
geoJSON: any;
projectName: string | null;
projectDescription: string | null;
singleFamilyUnits: string | null;
Expand Down
1 change: 1 addition & 0 deletions app/src/types/SubmissionIntake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type SubmissionIntake = {
naturalDisaster?: string;
projectLocation?: string;
projectLocationDescription?: string;
geoJSON?: string | null;
ltsaPIDLookup?: string;
latitude?: number | null;
longitude?: number | null;
Expand Down
Loading

0 comments on commit 9253ec1

Please sign in to comment.