Skip to content
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

[api] adopt ESM throughout #3464

Merged
merged 8 commits into from
Aug 9, 2024
1 change: 1 addition & 0 deletions api.planx.uk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Development notes:

- if you need to test or pull new changes from @opensystemslab/planx-document-templates or @opensystemslab/planx-core, make sure to update the commit hash in package.json first
- you can also use `pnpm link {{local relative path to @opensystemslab/planx-document-templates or @opensystemslab/planx-core}}` to manage local development changes these packages without having to reinstall. If you do this, remember to also run `pnpm unlink` to unlink the local directory and then also update the commit hash to point to the most recent version of the package.
- If you want to test particular files directly (e.g. `pnpm jest server.test.js`), note that since we now treat all files as ES modules, you'll need to export `NODE_OPTIONS="--experimental-vm-modules"` (or include it in every such command).

## Prior art

Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/airbrake.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Notifier } from "@airbrake/node";
import { isLiveEnv } from "./helpers";
import { isLiveEnv } from "./helpers.js";

const airbrake =
isLiveEnv() &&
Expand Down
6 changes: 3 additions & 3 deletions api.planx.uk/client/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CoreDomainClient } from "@opensystemslab/planx-core";
import { getClient } from ".";
import { userContext } from "../modules/auth/middleware";
import { getJWT } from "../tests/mockJWT";
import { getClient } from "./index.js";
import { userContext } from "../modules/auth/middleware.js";
import { getJWT } from "../tests/mockJWT.js";

test("getClient() throws an error if a store is not set", () => {
expect(() => getClient()).toThrow();
Expand Down
6 changes: 3 additions & 3 deletions api.planx.uk/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CoreDomainClient } from "@opensystemslab/planx-core";
import { userContext } from "../modules/auth/middleware";
import { ServerError } from "../errors";
import { buildJWTForAPIRole } from "../modules/auth/service";
import { userContext } from "../modules/auth/middleware.js";
import { ServerError } from "../errors/index.js";
import { buildJWTForAPIRole } from "../modules/auth/service.js";

/**
* Connects to Hasura using the "api" role
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { ServerError } from "./ServerError";
export { ServerError } from "./serverError.js";
File renamed without changes.
6 changes: 3 additions & 3 deletions api.planx.uk/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import {
getFlowData,
getFormattedEnvironment,
isLiveEnv,
} from "./helpers";
import { queryMock } from "./tests/graphqlQueryMock";
} from "./helpers.js";
import { queryMock } from "./tests/graphqlQueryMock.js";
import {
childFlow,
draftParentFlow,
flattenedParentFlow,
} from "./tests/mocks/validateAndPublishMocks";
} from "./tests/mocks/validateAndPublishMocks.js";

describe("getFormattedEnvironment() function", () => {
const OLD_ENV = process.env;
Expand Down
6 changes: 3 additions & 3 deletions api.planx.uk/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { gql } from "graphql-request";
import { capitalize } from "lodash";
import { Flow, Node } from "./types";
import capitalize from "lodash/capitalize.js";
import { Flow, Node } from "./types.js";
import { ComponentType, FlowGraph } from "@opensystemslab/planx-core/types";
import { $public, getClient } from "./client";
import { $public, getClient } from "./client/index.js";

export interface FlowData {
slug: string;
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import server from "./server";
import server from "./server.js";

const PORT = process.env.PORT || 8001;

Expand Down
27 changes: 22 additions & 5 deletions api.planx.uk/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
export default {
import type { JestConfigWithTsJest } from "ts-jest";

const config: JestConfigWithTsJest = {
// ts-jest presets are deprecated, so we prefer to give an explicit manual config
testEnvironment: "node",
preset: "ts-jest",
transform: {
"^.+\\.js$": [
"esbuild-jest",
// esbuild-jest transformer is unmaintained and can't handle ts-with-esm, so we stick to ts-jest
// TODO: if tests are too slow, consider swapping out for @swc/jest
"^.+\\.[jt]s$": [
"ts-jest",
{
sourcemap: true,
useESM: true,
// we need a separate module/moduleResolution config for tests (jest v30 may fix this)
tsconfig: "tsconfig.test.json",
},
],
},
// mime v4 (which moves to pure ESM) may still have commonJS traces, so we transform it
transformIgnorePatterns: ["node_modules\\/.pnpm\\/(?!(mime))"],
testPathIgnorePatterns: ["dist/*"],
setupFilesAfterEnv: ["./jest.setup.js"],
// handle .ts files first, as ESM modules, and remove .js from imports for jest
moduleFileExtensions: ["ts", "js", "json"],
extensionsToTreatAsEsm: [".ts"],
moduleNameMapper: {
"^(\\.{1,2}/.*)\\.js$": "$1",
},
// set up coverage collection
collectCoverage: true,
coverageThreshold: {
global: {
Expand All @@ -20,3 +35,5 @@ export default {
coverageReporters: ["lcov", "text-summary"],
coverageDirectory: "./coverage",
};

export default config;
2 changes: 1 addition & 1 deletion api.planx.uk/jest.setup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dotenv from "dotenv";
import { queryMock } from "./tests/graphqlQueryMock";
import { queryMock } from "./tests/graphqlQueryMock.js";

dotenv.config({
path: "./.env.test",
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/lib/hasura/metadata/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createScheduledEvent, RequiredScheduledEventArgs } from ".";
import { createScheduledEvent, RequiredScheduledEventArgs } from "./index.js";
import Axios, { AxiosError } from "axios";

jest.mock("axios", () => ({
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/lib/hasura/metadata/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ const createScheduledEvent = async (args: RequiredScheduledEventArgs) => {
}
};

export { createScheduledEvent, RequiredScheduledEventArgs };
export { createScheduledEvent, type RequiredScheduledEventArgs };
2 changes: 1 addition & 1 deletion api.planx.uk/lib/hasura/schema/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { runSQL } from ".";
import { runSQL } from "./index.js";
import Axios, { AxiosError } from "axios";

jest.mock("axios", () => ({
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/lib/notify/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { sendEmail } from ".";
import { sendEmail } from "./index.js";
import { NotifyClient } from "notifications-node-client";
import { NotifyConfig } from "../../types";
import { NotifyConfig } from "../../types.js";

jest.mock("notifications-node-client");

Expand Down
6 changes: 3 additions & 3 deletions api.planx.uk/lib/notify/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NotifyClient } from "notifications-node-client";
import { softDeleteSession } from "../../modules/saveAndReturn/service/utils";
import { NotifyConfig } from "../../types";
import { $api, $public } from "../../client";
import { softDeleteSession } from "../../modules/saveAndReturn/service/utils.js";
import { NotifyConfig } from "../../types.js";
import { $api, $public } from "../../client/index.js";

const notifyClient = new NotifyClient(process.env.GOVUK_NOTIFY_API_KEY);

Expand Down Expand Up @@ -70,7 +70,7 @@
if (template === "save")
returnValue.expiryDate = config.personalisation.expiryDate;
return returnValue;
} catch (error: any) {

Check warning on line 73 in api.planx.uk/lib/notify/index.ts

View workflow job for this annotation

GitHub Actions / Run API Tests

Unexpected any. Specify a different type
const notifyError = error?.response?.data?.errors?.length
? JSON.stringify(error?.response?.data?.errors?.[0])
: error?.message;
Expand Down
14 changes: 7 additions & 7 deletions api.planx.uk/modules/admin/routes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Router } from "express";
import { usePlatformAdminAuth } from "../auth/middleware";
import { getOneAppXML } from "./session/oneAppXML";
import { getCSVData, getRedactedCSVData } from "./session/csv";
import { getHTMLExport, getRedactedHTMLExport } from "./session/html";
import { generateZip } from "./session/zip";
import { getSessionSummary } from "./session/summary";
import { getDigitalPlanningApplicationPayload } from "./session/digitalPlanningData";
import { usePlatformAdminAuth } from "../auth/middleware.js";
import { getOneAppXML } from "./session/oneAppXML.js";
import { getCSVData, getRedactedCSVData } from "./session/csv.js";
import { getHTMLExport, getRedactedHTMLExport } from "./session/html.js";
import { generateZip } from "./session/zip.js";
import { getSessionSummary } from "./session/summary.js";
import { getDigitalPlanningApplicationPayload } from "./session/digitalPlanningData.js";

const router = Router();

Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/admin/session/csv.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import supertest from "supertest";
import app from "../../../server";
import { authHeader } from "../../../tests/mockJWT";
import app from "../../../server.js";
import { authHeader } from "../../../tests/mockJWT.js";

const endpoint = (strings: TemplateStringsArray) =>
`/admin/session/${strings[0]}/csv`;
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/admin/session/csv.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { stringify } from "csv-stringify";
import { NextFunction, Request, Response } from "express";
import { $api } from "../../../client";
import { $api } from "../../../client/index.js";

/**
* @swagger
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import supertest from "supertest";
import app from "../../../server";
import { authHeader } from "../../../tests/mockJWT";
import { expectedPlanningPermissionPayload } from "../../../tests/mocks/digitalPlanningDataMocks";
import app from "../../../server.js";
import { authHeader } from "../../../tests/mockJWT.js";
import { expectedPlanningPermissionPayload } from "../../../tests/mocks/digitalPlanningDataMocks.js";

const endpoint = (strings: TemplateStringsArray) =>
`/admin/session/${strings[0]}/digital-planning-application`;
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/admin/session/digitalPlanningData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextFunction, Request, Response } from "express";
import { $api } from "../../../client";
import { $api } from "../../../client/index.js";

/**
* @swagger
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/admin/session/html.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import supertest from "supertest";
import app from "../../../server";
import { authHeader } from "../../../tests/mockJWT";
import app from "../../../server.js";
import { authHeader } from "../../../tests/mockJWT.js";

const endpoint = (strings: TemplateStringsArray) =>
`/admin/session/${strings[0]}/html`;
Expand Down Expand Up @@ -46,7 +46,7 @@
.expect(403);
});

it.skip("returns a HTML-formatted payload", () => {

Check warning on line 49 in api.planx.uk/modules/admin/session/html.test.ts

View workflow job for this annotation

GitHub Actions / Run API Tests

Disabled test
return supertest(app)
.get(endpoint`123`)
.set(authHeader({ role: "platformAdmin" }))
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/admin/session/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type {
DrawBoundaryUserAction,
PlanXExportData,
} from "@opensystemslab/planx-core/types";
import { $api } from "../../../client/index.js";
import type { RequestHandler } from "express";
import { $api } from "../../../client";

type HTMLExportHandler = RequestHandler<{ sessionId: string }, string>;

Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/admin/session/oneAppXML.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import supertest from "supertest";
import app from "../../../server";
import { authHeader } from "../../../tests/mockJWT";
import app from "../../../server.js";
import { authHeader } from "../../../tests/mockJWT.js";

const endpoint = (strings: TemplateStringsArray) =>
`/admin/session/${strings[0]}/xml`;
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/admin/session/oneAppXML.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Request, Response, NextFunction } from "express";
import { $api } from "../../../client";
import { $api } from "../../../client/index.js";

/**
* @swagger
Expand Down
6 changes: 3 additions & 3 deletions api.planx.uk/modules/admin/session/summary.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import supertest from "supertest";
import app from "../../../server";
import { queryMock } from "../../../tests/graphqlQueryMock";
import { authHeader } from "../../../tests/mockJWT";
import app from "../../../server.js";
import { queryMock } from "../../../tests/graphqlQueryMock.js";
import { authHeader } from "../../../tests/mockJWT.js";

const endpoint = (strings: TemplateStringsArray) =>
`/admin/session/${strings[0]}/summary`;
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/admin/session/summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
import { NextFunction, Request, Response } from "express";
import { gql } from "graphql-request";

import { Breadcrumb, Flow, LowCalSession, Passport } from "../../../types";
import { $api } from "../../../client";
import { Breadcrumb, Flow, LowCalSession, Passport } from "../../../types.js";
import { $api } from "../../../client/index.js";

/**
* @swagger
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/admin/session/zip.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import supertest from "supertest";
import app from "../../../server";
import { authHeader } from "../../../tests/mockJWT";
import app from "../../../server.js";
import { authHeader } from "../../../tests/mockJWT.js";

jest.mock("../../send/utils/exportZip", () => ({
buildSubmissionExportZip: jest.fn().mockResolvedValue({
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/admin/session/zip.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextFunction, Request, Response } from "express";
import { buildSubmissionExportZip } from "../../send/utils/exportZip";
import { buildSubmissionExportZip } from "../../send/utils/exportZip.js";

/**
* @swagger
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/analytics/controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";
import { trackAnalyticsLogExit } from "./service";
import { ValidatedRequestHandler } from "../../shared/middleware/validate";
import { trackAnalyticsLogExit } from "./service.js";
import { ValidatedRequestHandler } from "../../shared/middleware/validate.js";

export const logAnalyticsSchema = z.object({
query: z.object({
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/analytics/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import supertest from "supertest";
import app from "../../server";
import { queryMock } from "../../tests/graphqlQueryMock";
import app from "../../server.js";
import { queryMock } from "../../tests/graphqlQueryMock.js";

describe("Logging analytics", () => {
beforeEach(() => {
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/analytics/routes.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { validate } from "./../../shared/middleware/validate";
import { validate } from "./../../shared/middleware/validate.js";
import { Router } from "express";
import {
logAnalyticsSchema,
logUserExitController,
logUserResumeController,
} from "./controller";
} from "./controller.js";

const router = Router();

Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/analytics/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { gql } from "graphql-request";
import { $public } from "../../client";
import { $public } from "../../client/index.js";

interface UpdateAnalyticsLogUserExit {
analyticsLog: {
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/auth/middleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isEqual } from "./middleware";
import { isEqual } from "./middleware.js";

describe("isEqual() helper function", () => {
it("handles undefined secrets", () => {
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/auth/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import crypto from "crypto";
import assert from "assert";
import { ServerError } from "../../errors";
import { Template } from "../../lib/notify";
import { ServerError } from "../../errors/index.js";
import { Template } from "../../lib/notify/index.js";
import { expressjwt } from "express-jwt";

import passport from "passport";
Expand Down
4 changes: 2 additions & 2 deletions api.planx.uk/modules/auth/routes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Router } from "express";
import * as Middleware from "./middleware";
import * as Controller from "./controller";
import * as Middleware from "./middleware.js";
import * as Controller from "./controller.js";

const router = Router();

Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/auth/service.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { checkUserCanAccessEnv } from "./service";
import { checkUserCanAccessEnv } from "./service.js";

const mockIsStagingOnly = jest.fn();

Expand Down
9 changes: 4 additions & 5 deletions api.planx.uk/modules/auth/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { sign } from "jsonwebtoken";
import { $api } from "../../client";
import jwt from "jsonwebtoken";
import { $api } from "../../client/index.js";
import { User, Role } from "@opensystemslab/planx-core/types";

export const buildJWT = async (email: string): Promise<string | undefined> => {
Expand All @@ -13,12 +13,11 @@ export const buildJWT = async (email: string): Promise<string | undefined> => {
"https://hasura.io/jwt/claims": generateHasuraClaimsForUser(user),
};

const jwt = sign(data, process.env.JWT_SECRET!);
return jwt;
return jwt.sign(data, process.env.JWT_SECRET!);
};

export const buildJWTForAPIRole = () =>
sign(
jwt.sign(
{
"https://hasura.io/jwt/claims": {
"x-hasura-allowed-roles": ["api"],
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/auth/strategy/google.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
import { buildJWT } from "../service";
import { buildJWT } from "../service.js";

export const googleStrategy = new GoogleStrategy(
{
Expand All @@ -17,7 +17,7 @@
return done({
status: 404,
message: `User (${email}) not found. Do you need to log in to a different Google Account?`,
} as any);

Check warning on line 20 in api.planx.uk/modules/auth/strategy/google.ts

View workflow job for this annotation

GitHub Actions / Run API Tests

Unexpected any. Specify a different type
}

done(null, { jwt });
Expand Down
Loading
Loading