Skip to content

Commit 5f7cf24

Browse files
committed
LF-4765 Make types work for the routes
1 parent db951fb commit 5f7cf24

File tree

6 files changed

+94
-35
lines changed

6 files changed

+94
-35
lines changed

packages/api/src/controllers/irrigationPrescriptionController.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
*/
1515

1616
import { Response } from 'express';
17-
import { LiteFarmRequest, HttpError } from '../types.js';
17+
import { HttpError, ScopeCheckedLiteFarmRequest } from '../types.js';
1818
import ESciAddon from '../util/ensembleService.js';
1919
import { fakeIrrigationPrescriptions } from '../../tests/utils/ensembleUtils.js';
2020
import FarmAddonModel from '../models/farmAddonModel.js';
2121
import { AddonFunctions, IrrigationPrescription } from '../util/ensembleService.types.js';
22+
import { customError } from '../util/customErrors.js';
2223

23-
interface IrrigationPrescriptionQueryParams {
24+
export interface IrrigationPrescriptionQueryParams {
2425
startTime?: string;
2526
endTime?: string;
2627
shouldSend?: string;
@@ -34,16 +35,29 @@ const PARTNER_ID_MAP: Record<string, Partial<AddonFunctions>> = {
3435

3536
const irrigationPrescriptionController = {
3637
getPrescriptions() {
37-
return async (req: LiteFarmRequest<IrrigationPrescriptionQueryParams>, res: Response) => {
38+
return async (
39+
req: ScopeCheckedLiteFarmRequest<IrrigationPrescriptionQueryParams>,
40+
res: Response,
41+
) => {
3842
try {
3943
const { farm_id } = req.headers;
4044
const { startTime, endTime, shouldSend } = req.query;
45+
if (
46+
typeof startTime != 'string' ||
47+
startTime != undefined ||
48+
typeof endTime != 'string' ||
49+
endTime != undefined ||
50+
typeof shouldSend != 'string' ||
51+
shouldSend != undefined
52+
) {
53+
throw customError('Bad query param');
54+
}
55+
4156
const irrigationPrescriptions: IrrigationPrescription[] = [];
4257
const partnerErrors: unknown[] = [];
4358

4459
if (shouldSend === 'true') {
4560
// Check for registered farm addons (only esci for now)
46-
// @ts-expect-error - farm_id is guaranteed here by the checkScope middleware with single argument
4761
const farmAddonPartnerIds = await FarmAddonModel.getDistinctFarmAddonPartnerIds(farm_id);
4862

4963
// Return empty array if no addons
@@ -62,7 +76,6 @@ const irrigationPrescriptionController = {
6276
}
6377

6478
irrigationPrescriptions.push(
65-
// @ts-expect-error - farm_id is guaranteed here by the checkScope middleware with single argument
6679
...(await addonPartner.getIrrigationPrescriptions(farm_id, startTime, endTime)),
6780
);
6881
} catch (error) {
@@ -78,7 +91,6 @@ const irrigationPrescriptionController = {
7891
} else {
7992
// Return data for dev purposes + QA
8093
const mockData = await fakeIrrigationPrescriptions({
81-
// @ts-expect-error - farm_id is guaranteed here by the checkScope middleware with single argument
8294
farmId: farm_id,
8395
startTime,
8496
endTime,

packages/api/src/controllers/irrigationPrescriptionRequestController.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,22 @@
1515

1616
import { Response } from 'express';
1717
import { getOrgLocationAndCropData, sendFieldAndCropDataToEsci } from '../util/ensembleService.js';
18-
import { LiteFarmRequest } from '../types.js';
18+
import { ScopeCheckedLiteFarmRequest } from '../types.js';
1919

2020
interface HttpError extends Error {
2121
status?: number;
2222
code?: number; // LF custom error
2323
}
2424

25-
interface InitiateFarmIrrigationPrescriptionQueryParams {
25+
export interface InitiateFarmIrrigationPrescriptionQueryParams {
2626
allOrgs?: string;
2727
shouldSend?: string;
2828
}
2929

3030
const irrigationPrescriptionRequestController = {
3131
initiateFarmIrrigationPrescription() {
3232
return async (
33-
req: LiteFarmRequest<InitiateFarmIrrigationPrescriptionQueryParams>,
33+
req: ScopeCheckedLiteFarmRequest<InitiateFarmIrrigationPrescriptionQueryParams>,
3434
res: Response,
3535
) => {
3636
const { farm_id } = req.headers;

packages/api/src/middleware/acl/checkScope.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,27 @@ const checkScope = (
6565
return next();
6666
}
6767

68-
// Consider making this a separate middleware with checkJwt
68+
// Check auth
69+
// NOTE: Consider making this a separate middleware with checkJwt
6970
if (!req.auth) {
7071
return res.status(400).send('No Auth provided');
7172
}
72-
7373
const { user_id } = req.auth;
7474
if (!user_id || user_id === 'undefined') {
7575
return res.status(400).send('Missing user_id in auth');
7676
}
7777

78-
const { headers } = req;
79-
const { farm_id } = headers; // these are the minimum props needed for most endpoints' authorization
78+
// Check headers
79+
if (!req.headers) {
80+
return res.status(400).send('Missing headers');
81+
}
82+
83+
const { farm_id } = req.headers; // these are the minimum props needed for most endpoints' authorization
8084

81-
if (!farm_id || farm_id === 'undefined')
85+
if (!farm_id || farm_id === 'undefined') {
8286
return res.status(400).send('Missing farm_id in headers');
87+
}
88+
8389
try {
8490
const scopes = await getScopes(user_id, farm_id, { checkConsent });
8591

packages/api/src/routes/irrigationPrescriptionRequestRoute.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@
1515

1616
import express from 'express';
1717
import checkScope from '../middleware/acl/checkScope.js';
18-
import IrrigationPrescriptionRequestController from '../controllers/irrigationPrescriptionRequestController.js';
18+
import IrrigationPrescriptionRequestController, {
19+
InitiateFarmIrrigationPrescriptionQueryParams,
20+
} from '../controllers/irrigationPrescriptionRequestController.js';
21+
import { ScopeCheckedLiteFarmRequest } from '../types.js';
1922

2023
const router = express.Router();
2124

22-
router.post(
23-
'/',
24-
checkScope(['get:smart_irrigation']),
25-
IrrigationPrescriptionRequestController.initiateFarmIrrigationPrescription(),
26-
);
25+
router.post('/', checkScope(['get:smart_irrigation']), (req, res) => {
26+
const typedReq =
27+
req as ScopeCheckedLiteFarmRequest<InitiateFarmIrrigationPrescriptionQueryParams>;
28+
IrrigationPrescriptionRequestController.initiateFarmIrrigationPrescription()(typedReq, res);
29+
});
2730

2831
export default router;

packages/api/src/routes/irrigationPrescriptionRoute.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
1515

1616
import express from 'express';
1717
import checkScope from '../middleware/acl/checkScope.js';
18-
import IrrigationPrescriptionController from '../controllers/irrigationPrescriptionController.js';
18+
import IrrigationPrescriptionController, {
19+
IrrigationPrescriptionQueryParams,
20+
} from '../controllers/irrigationPrescriptionController.js';
21+
import { ScopeCheckedLiteFarmRequest } from '../types.js';
1922

2023
const router = express.Router();
2124

22-
router.get(
23-
'/',
24-
checkScope(['get:smart_irrigation']),
25-
IrrigationPrescriptionController.getPrescriptions(),
26-
);
25+
router.get('/', checkScope(['get:smart_irrigation']), (req, res) => {
26+
const typedReq = req as ScopeCheckedLiteFarmRequest<IrrigationPrescriptionQueryParams>;
27+
IrrigationPrescriptionController.getPrescriptions()(typedReq, res);
28+
});
2729

2830
export default router;

packages/api/src/types.ts

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,60 @@
1313
* GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
1414
*/
1515

16-
import { NextFunction, Request, Response } from 'express';
16+
import { Request } from 'express';
17+
import { Farm, Point, Role, Task, TaskType, User } from './models/types.js';
1718

1819
export interface HttpError extends Error {
1920
status?: number;
2021
code?: number; // LF custom error
2122
}
2223

23-
// TODO: Remove farm_id conditional and cast this in a checkScope() that takes the function and casts this to req
24+
/**
25+
* For use with unchecked requests
26+
*
27+
* All possible shapes of a litefarm 'req' object
28+
*/
2429
export interface LiteFarmRequest<QueryParams = unknown>
2530
extends Request<unknown, unknown, unknown, QueryParams> {
31+
auth?: {
32+
user_id?: User['user_id'];
33+
farm_id?: Farm['farm_id'];
34+
sub?: User['user_id'];
35+
email?: User['email'];
36+
given_name?: User['first_name'];
37+
family_name?: User['last_name'];
38+
first_name?: User['first_name'];
39+
language_preference?: string;
40+
};
2641
headers: Request['headers'] & {
27-
farm_id?: string;
42+
user_id?: User['user_id'];
43+
farm_id?: Farm['farm_id'];
44+
};
45+
role?: Role['role_id'];
46+
isMinimized?: boolean;
47+
isTextDocument?: boolean;
48+
isNotMinimized?: boolean;
49+
field?: { fieldId?: string | number; point?: Point };
50+
file?: unknown;
51+
checkTaskStatus?: {
52+
complete_date?: Task['complete_date'];
53+
abandon_date?: Task['abandon_date'];
54+
assignee_user_id?: Task['assignee_user_id'];
55+
task_translation_key?: TaskType['task_translation_key'];
2856
};
2957
}
3058

31-
// Can be used to cast after checkScope() succeeds
32-
export type LiteFarmHandler<QueryParams = unknown> = (
33-
req: LiteFarmRequest<QueryParams>,
34-
res: Response,
35-
next: NextFunction,
36-
) => void | Promise<void>;
59+
/**
60+
* For use after checkScope() middleware.
61+
*
62+
* DO NOT add more required props unless it is auth related, make a new type
63+
*/
64+
65+
export interface ScopeCheckedLiteFarmRequest<ReqQuery = unknown> extends LiteFarmRequest<ReqQuery> {
66+
auth: {
67+
user_id: User['user_id'];
68+
};
69+
headers: Request['headers'] & {
70+
farm_id: string;
71+
};
72+
}

0 commit comments

Comments
 (0)