From b9730575111cd0d4fe60c32a6f69a5b27c49a410 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 06:39:44 -0400 Subject: [PATCH 01/18] refactor: better define the api bounderies of the module --- apps/server/src/ServerController.ts | 4 +- libs/web/index.ts | 10 +- libs/web/package.json | 3 + libs/web/src/helpers.ts | 32 +++++ libs/web/src/index.ts | 19 +-- libs/web/src/processors/index.ts | 13 ++ libs/web/src/processors/processLogin.ts | 66 ++++++++++ libs/web/src/services/AuthLogin.ts | 81 +----------- libs/web/test/AuthLogin.test.ts | 156 ------------------------ libs/web/test/errors.test.ts | 129 +------------------- libs/web/test/routes.test.ts | 80 ++++++++++++ nx.json | 2 +- 12 files changed, 218 insertions(+), 377 deletions(-) create mode 100644 libs/web/src/helpers.ts create mode 100644 libs/web/src/processors/index.ts create mode 100644 libs/web/src/processors/processLogin.ts delete mode 100644 libs/web/test/AuthLogin.test.ts create mode 100644 libs/web/test/routes.test.ts diff --git a/apps/server/src/ServerController.ts b/apps/server/src/ServerController.ts index d242c5c..d1ab4f7 100644 --- a/apps/server/src/ServerController.ts +++ b/apps/server/src/ServerController.ts @@ -4,7 +4,7 @@ import { ConsoleThread } from "./ConsoleThread.js"; import { WrappedServer } from "./WrappedServer.js"; import { exit } from "node:process"; import { log } from "@rusty/util"; -import { handleWebRequests } from "@rusty/web"; +import { processWebRequests } from "@rusty/web"; import { headersToRecords } from "./headersToRecords.js"; /** @@ -16,7 +16,7 @@ async function handleIncomingRequest( req: import("node:http").IncomingMessage, res: import("node:http").ServerResponse ) { - const response = await handleWebRequests({ + const response = await processWebRequests({ headers: headersToRecords(req.headers), remoteAddress: req.socket.remoteAddress || "", method: req.method || "", diff --git a/libs/web/index.ts b/libs/web/index.ts index cdf9f2a..8176b15 100644 --- a/libs/web/index.ts +++ b/libs/web/index.ts @@ -1 +1,9 @@ -export { handleWebRequests } from "./src/index.js"; +export { processWebRequests } from "./src/index.js"; +export { RouteHandlers } from "./src/processors/index.js"; +export { + ErrorUserNotFound, + handleCreateUserError, + ErrorMissingCredentials, + ErrorUserExists, +} from "./src/errors.js"; +export { createUser, userLogin, deleteUser } from "./src/services/AuthLogin.js"; diff --git a/libs/web/package.json b/libs/web/package.json index 157afe6..a1712ac 100644 --- a/libs/web/package.json +++ b/libs/web/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "private": true, "type": "module", + "imports": { + "#internal": "./dist/src/index.js" + }, "exports": { ".": "./dist/index.js" }, diff --git a/libs/web/src/helpers.ts b/libs/web/src/helpers.ts new file mode 100644 index 0000000..1513559 --- /dev/null +++ b/libs/web/src/helpers.ts @@ -0,0 +1,32 @@ +import { ErrorMissingCredentials } from "./errors.js"; +import type { parsedHttpRequestData } from "./types.js"; + +/** + * Extracts the username and password from the parsed HTTP request data. + * @param info - The parsed HTTP request data. + * @returns An object containing the extracted username and password. + */ +export function extractCredentials(info: parsedHttpRequestData): { + username: string; + password: string; + } { + const username = (info.searchParams.get("username") as string) || ""; + const password = (info.searchParams.get("password") as string) || ""; + return { username, password }; + } + +/** + * Validates the provided username and password. + * @param {string} username - The username to validate. + * @param {string} password - The password to validate. + * @throws {ErrorMissingCredentials} If either the username or password is empty. + */ + export function validateCredentials(username: string, password: string): void { + if (username === "") { + throw new ErrorMissingCredentials("Username is required"); + } + + if (password === "") { + throw new ErrorMissingCredentials("Password is required"); + } + } \ No newline at end of file diff --git a/libs/web/src/index.ts b/libs/web/src/index.ts index d999f55..2fdec69 100644 --- a/libs/web/src/index.ts +++ b/libs/web/src/index.ts @@ -1,19 +1,10 @@ import { log } from "@rusty/util"; -import type { parsedHttpRequestData, rawHttpRequestData, RequestResponse } from "./types.js"; -import { handleAuthLogin } from "./services/AuthLogin.js"; +import type { rawHttpRequestData, RequestResponse } from "./types.js"; +import { RouteHandlers } from "./processors/index.js"; +export { User } from "./models/User.js"; +export type { UserAttributes } from "./models/User.js"; -const RouteHandlers: Record< - string, - (info: parsedHttpRequestData) => Promise<{ - statusCode: number; - body: string; - headers: Record; - }> -> = { - "/AuthLogin": handleAuthLogin, -}; - -export async function handleWebRequests({ +export async function processWebRequests({ headers, remoteAddress, method, diff --git a/libs/web/src/processors/index.ts b/libs/web/src/processors/index.ts new file mode 100644 index 0000000..346b992 --- /dev/null +++ b/libs/web/src/processors/index.ts @@ -0,0 +1,13 @@ +import type { parsedHttpRequestData } from "../types.js"; +import { authenticateUser } from "./processLogin.js"; + +export const RouteHandlers: Record< + string, + (info: parsedHttpRequestData) => Promise<{ + statusCode: number; + body: string; + headers: Record; + }> +> = { + "/AuthLogin": authenticateUser, +}; diff --git a/libs/web/src/processors/processLogin.ts b/libs/web/src/processors/processLogin.ts new file mode 100644 index 0000000..c4e3db3 --- /dev/null +++ b/libs/web/src/processors/processLogin.ts @@ -0,0 +1,66 @@ +import { log } from "@rusty/util"; +import { extractCredentials, validateCredentials } from "../helpers.js"; +import { userLogin } from "../services/AuthLogin.js"; +import type { parsedHttpRequestData, RequestResponse } from "../types.js"; + +/** + * Handles the authentication login process. + * + * @param info - The parsed HTTP request data. + * @returns A promise that resolves to the request response. + */ +export async function authenticateUser( + info: parsedHttpRequestData +): Promise { + const { username, password } = extractCredentials(info); + + try { + validateCredentials(username, password); + + await userLogin(username, password); + + const token = "abc123"; + + return constructLoginResponse(`Valid=TRUE\nTicket=${token}`); + } catch (error: unknown) { + log.error(`Error validating credentials: ${(error as Error).message}`); + return generateLoginError( + "INV-200", + "Unable to login", + "https://rusty-motors.com" + ); + } +} + +/** + * Constructs a login response object. + * @param body - The response body. + * @returns The constructed login response object. + */ +function constructLoginResponse(body = ""): RequestResponse { + return { + statusCode: 200, + body, + headers: { "Content-Type": "text/plain" }, + }; +} + +/** + * Generates a login error response. + * + * @param errorCode - The error code. Default is "INV-200". + * @param errorText - The error text. Default is "Unable to login". + * @param errorUrl - The error URL. Default is "https://rusty-motors.com". + * @returns The login error response. + */ +function generateLoginError( + errorCode = "INV-200", + errorText = "Unable to login", + errorUrl = "https://rusty-motors.com" +): RequestResponse | PromiseLike { + return { + statusCode: 200, + body: `reasoncode=${errorCode}\nreasontext=${errorText}\nreasonurl=${errorUrl}`, + headers: { "Content-Type": "text/plain" }, + }; +} diff --git a/libs/web/src/services/AuthLogin.ts b/libs/web/src/services/AuthLogin.ts index bc1a4e5..47e9a1e 100644 --- a/libs/web/src/services/AuthLogin.ts +++ b/libs/web/src/services/AuthLogin.ts @@ -1,21 +1,12 @@ import { User, type UserAttributes } from "../models/User.js"; -import type { parsedHttpRequestData, RequestResponse } from "../types.js"; import { log } from "@rusty/util"; import { - ErrorMissingCredentials, ErrorUserNotFound, handleCreateUserError, } from "../errors.js"; +import { validateCredentials } from "../helpers.js"; -function validateCredentials(username: string, password: string): void { - if (username === "") { - throw new ErrorMissingCredentials("Username is required"); - } - if (password === "") { - throw new ErrorMissingCredentials("Password is required"); - } -} export async function createUser( username: string, @@ -65,81 +56,11 @@ export async function userLogin( password: user.password, }; } -/** - * Handles the authentication login process. - * - * @param info - The parsed HTTP request data. - * @returns A promise that resolves to the request response. - */ -export async function handleAuthLogin( - info: parsedHttpRequestData -): Promise { - const { username, password } = extractCredentials(info); - try { - validateCredentials(username, password); - await userLogin(username, password); - const token = "abc123"; - - return constructLoginResponse(`Valid=TRUE\nTicket=${token}`); - } catch (error: unknown) { - log.error(`Error validating credentials: ${(error as Error).message}`); - return generateLoginError( - "INV-200", - "Unable to login", - "https://rusty-motors.com" - ); - } -} - -/** - * Extracts the username and password from the parsed HTTP request data. - * @param info - The parsed HTTP request data. - * @returns An object containing the extracted username and password. - */ -function extractCredentials(info: parsedHttpRequestData): { - username: string; - password: string; -} { - const username = (info.searchParams.get("username") as string) || ""; - const password = (info.searchParams.get("password") as string) || ""; - return { username, password }; -} -/** - * Constructs a login response object. - * @param body - The response body. - * @returns The constructed login response object. - */ -function constructLoginResponse(body = ""): RequestResponse { - return { - statusCode: 200, - body, - headers: { "Content-Type": "text/plain" }, - }; -} -/** - * Generates a login error response. - * - * @param errorCode - The error code. Default is "INV-200". - * @param errorText - The error text. Default is "Unable to login". - * @param errorUrl - The error URL. Default is "https://rusty-motors.com". - * @returns The login error response. - */ -function generateLoginError( - errorCode = "INV-200", - errorText = "Unable to login", - errorUrl = "https://rusty-motors.com" -): RequestResponse | PromiseLike { - return { - statusCode: 200, - body: `reasoncode=${errorCode}\nreasontext=${errorText}\nreasonurl=${errorUrl}`, - headers: { "Content-Type": "text/plain" }, - }; -} /** * Deletes a user from the database. diff --git a/libs/web/test/AuthLogin.test.ts b/libs/web/test/AuthLogin.test.ts deleted file mode 100644 index ab983be..0000000 --- a/libs/web/test/AuthLogin.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { describe, expect, it, test } from "vitest"; - -import { - handleAuthLogin, - createUser, - deleteUser, -} from "../src/services/AuthLogin.js"; -import { ErrorMissingCredentials, ErrorUserExists } from "../src/errors.js"; - -describe("handleAuthLogin -> User Login", () => { - it("When either username or password is not supplied, expect a generic error", async () => { - const searchParams = new URLSearchParams(); - searchParams.set("username", "validuser"); - searchParams.set("password", ""); - const info = { - headers: {}, - remoteAddress: "", - method: "", - pathname: "", - searchParams, - }; - - const response = await handleAuthLogin(info); - - expect(response.statusCode).toBe(200); // Client expects all responses to be 200 - expect(response.body).toBe( - "reasoncode=INV-200\nreasontext=Unable to login\nreasonurl=https://rusty-motors.com" - ); - expect(response.headers).toEqual({ "Content-Type": "text/plain" }); - }); - - it("When user is not found, expect a generic error", async () => { - const searchParams = new URLSearchParams(); - searchParams.set("username", "nonexistent"); - searchParams.set("password", "password"); - const info = { - headers: {}, - remoteAddress: "", - method: "", - pathname: "", - searchParams, - }; - - const response = await handleAuthLogin(info); - - expect(response.statusCode).toBe(200); - expect(response.body).toBe( - "reasoncode=INV-200\nreasontext=Unable to login\nreasonurl=https://rusty-motors.com" - ); - expect(response.headers).toEqual({ "Content-Type": "text/plain" }); - }); - - it("should return 200 with user data if login is successful", async () => { - const searchParams = new URLSearchParams(); - searchParams.set("username", "validuser"); - searchParams.set("password", "password"); - const info = { - headers: {}, - remoteAddress: "", - method: "", - pathname: "", - searchParams, - }; - - await createUser("validuser", "password"); - - const response = await handleAuthLogin(info); - - expect(response.statusCode).toBe(200); - expect(response.body).toContain(`Valid=TRUE\nTicket=`); - expect(response.headers).toEqual({ "Content-Type": "text/plain" }); - }); -}); - -describe("AuthLogin -> createUser", () => { - it("when username is blank, expect an ErrorMissingCredentials to be thrown", async () => { - // Arrange - const user = ""; - const password = "password"; - - // Assert - try { - await createUser(user, password); - } catch (error: unknown) { - expect(error).toBeInstanceOf(ErrorMissingCredentials); - expect((error as Error).message).toBe("Username is required"); - } - }); - - it("when password is blank, expect an ErrorMissingCredentials to be thrown", async () => { - // Arrange - const user = "validuser"; - const password = ""; - - // Assert - try { - await createUser(user, password); - } catch (error: unknown) { - expect(error).toBeInstanceOf(ErrorMissingCredentials); - expect((error as Error).message).toBe("Password is required"); - } - }); - - it("when user already exists, expect an ErrorUserExists to be thrown", async () => { - // Arrange - const user = "validuser"; - const password = "password"; - await deleteUser(user); - await createUser(user, password); - - // Assert - try { - await createUser(user, password); - } catch (error: unknown) { - expect(error).toBeInstanceOf(ErrorUserExists); - } - }); - - test("when user is created, expect a promise to be resolved with the user", async () => { - // Arrange - const user = "validuser"; - const password = "password"; - await deleteUser(user); - - // Act - const result = createUser(user, password); - - // Assert - await expect(result).resolves.toEqual({ username: user, password }); - }); -}); - -describe("AuthLogin -> deleteUser", () => { - it("when user is not found, expect a promise to be resolved with 0", async () => { - // Arrange - const user = "nonexistent"; - - // Assert - const result = await deleteUser(user); - expect(result).toEqual(0); - }); - - test("when user is deleted, expect a promise to be resolved with the number of rows affected (1)", async () => { - // Arrange - const user = "validuser"; - const password = "password"; - await deleteUser(user); - - // Act - await createUser(user, password); - const result = await deleteUser(user); - - // Assert - expect(result).toEqual(1); - }); -}); diff --git a/libs/web/test/errors.test.ts b/libs/web/test/errors.test.ts index 157d5c8..5270fa8 100644 --- a/libs/web/test/errors.test.ts +++ b/libs/web/test/errors.test.ts @@ -1,129 +1,12 @@ import { describe, expect, it } from "vitest"; -import { handleAuthLogin, createUser } from "../src/services/AuthLogin.js"; -import { - ErrorMissingCredentials, - ErrorUserExists, -} from "../src/errors.js"; -import { deleteUser } from "../src/services/AuthLogin.js"; +import { ErrorMissingCredentials, ErrorUserExists } from "@rusty/web"; -describe("handleAuthLogin -> User Login", () => { - it("When either username or password is not supplied, expect a generic error", async () => { - // Arrange - const searchParams = new URLSearchParams(); - searchParams.set("username", "validuser"); - searchParams.set("password", ""); - const info = { - headers: {}, - remoteAddress: "", - method: "", - pathname: "", - searchParams, - }; - - // Act - const response = await handleAuthLogin(info); - - // Assert - expect(response.statusCode).toBe(200); - expect(response.body).toBe( - "reasoncode=INV-200\nreasontext=Unable to login\nreasonurl=https://rusty-motors.com" - ); - expect(response.headers).toEqual({ "Content-Type": "text/plain" }); - }); - - it("When user is not found, expect a generic error", async () => { - // Arrange - const searchParams = new URLSearchParams(); - searchParams.set("username", "nonexistent"); - searchParams.set("password", "password"); - const info = { - headers: {}, - remoteAddress: "", - method: "", - pathname: "", - searchParams, - }; - - // Act - const response = await handleAuthLogin(info); - - // Assert - expect(response.statusCode).toBe(200); - expect(response.body).toBe( - "reasoncode=INV-200\nreasontext=Unable to login\nreasonurl=https://rusty-motors.com" - ); - expect(response.headers).toEqual({ "Content-Type": "text/plain" }); +describe("Errors", () => { + it("should have an ErrorMissingCredentials class", () => { + expect(ErrorMissingCredentials).toBeDefined(); }); - it("should return 200 with user data if login is successful", async () => { - // Arrange - const searchParams = new URLSearchParams(); - searchParams.set("username", "validuser"); - searchParams.set("password", "password"); - const info = { - headers: {}, - remoteAddress: "", - method: "", - pathname: "", - searchParams, - }; - - await createUser("validuser", "password"); - - // Act - const response = await handleAuthLogin(info); - - // Assert - expect(response.statusCode).toBe(200); - expect(response.body).toContain(`Valid=TRUE\nTicket=`); - expect(response.headers).toEqual({ "Content-Type": "text/plain" }); - }); -}); - -describe("AuthLogin -> createUser", () => { - it("when username is blank, expect an ErrorMissingCredentials to be thrown", async () => { - // Arrange - const user = ""; - const password = "password"; - - // Act & Assert - await expect(createUser(user, password)).rejects.toThrow( - ErrorMissingCredentials - ); - }); - - it("when password is blank, expect an ErrorMissingCredentials to be thrown", async () => { - // Arrange - const user = "validuser"; - const password = ""; - - // Act & Assert - await expect(createUser(user, password)).rejects.toThrow( - ErrorMissingCredentials - ); - }); - - it("when user already exists, expect an ErrorUserExists to be thrown", async () => { - // Arrange - const user = "validuser"; - const password = "password"; - await deleteUser(user); - await createUser(user, password); - - // Act & Assert - await expect(createUser(user, password)).rejects.toThrow(ErrorUserExists); - }); - - it("when user is created, expect a promise to be resolved with the user", async () => { - // Arrange - const user = "validuser"; - const password = "password"; - await deleteUser(user); - - // Act - const result = createUser(user, password); - - // Assert - await expect(result).resolves.toEqual({ username: user, password }); + it("should have an ErrorUserExists class", () => { + expect(ErrorUserExists).toBeDefined(); }); }); diff --git a/libs/web/test/routes.test.ts b/libs/web/test/routes.test.ts new file mode 100644 index 0000000..506f5fd --- /dev/null +++ b/libs/web/test/routes.test.ts @@ -0,0 +1,80 @@ +import { describe, expect, it, test } from "vitest"; +import { RouteHandlers } from "@rusty/web"; +import { User } from "#internal"; + +describe("RouteHandlers", () => { + it("should have a handler for /AuthLogin", () => { + expect(RouteHandlers["/AuthLogin"]).toBeDefined(); + }); + + test("when /AuthLogin is called with a missing username, expect a generic error", async () => { + // Arrange + const searchParams = new URLSearchParams(); + searchParams.set("password", "password"); + const info = { + headers: {}, + remoteAddress: "", + method: "", + pathname: "/AuthLogin", + searchParams, + }; + + // Act + const response = await RouteHandlers["/AuthLogin"](info); + + // Assert + expect(response.statusCode).toBe(200); + expect(response.body).toBe( + "reasoncode=INV-200\nreasontext=Unable to login\nreasonurl=https://rusty-motors.com" + ); + expect(response.headers).toEqual({ "Content-Type": "text/plain" }); + }); + + test("when /AuthLogin is called with a missing password, expect a generic error", async () => { + // Arrange + const searchParams = new URLSearchParams(); + searchParams.set("username", "validuser"); + const info = { + headers: {}, + remoteAddress: "", + method: "", + pathname: "/AuthLogin", + searchParams, + }; + await User.findOrCreate({ + where: { username: "validuser", password: "password" }, + }); + + // Act + const response = await RouteHandlers["/AuthLogin"](info); + + // Assert + expect(response.statusCode).toBe(200); + expect(response.body).toBe( + "reasoncode=INV-200\nreasontext=Unable to login\nreasonurl=https://rusty-motors.com" + ); + expect(response.headers).toEqual({ "Content-Type": "text/plain" }); + }); + + test("when /AuthLogin is called with a valid username and password, expect a login response", async () => { + // Arrange + const searchParams = new URLSearchParams(); + searchParams.set("username", "validuser"); + searchParams.set("password", "password"); + const info = { + headers: {}, + remoteAddress: "", + method: "", + pathname: "/AuthLogin", + searchParams, + }; + + // Act + const response = await RouteHandlers["/AuthLogin"](info); + + // Assert + expect(response.statusCode).toBe(200); + expect(response.body).toContain(`Valid=TRUE\nTicket=`); + expect(response.headers).toEqual({ "Content-Type": "text/plain" }); + }); +}); diff --git a/nx.json b/nx.json index 4014854..3fb391a 100644 --- a/nx.json +++ b/nx.json @@ -22,5 +22,5 @@ } }, "defaultBase": "main", - "nxCloudAccessToken": "__NX_CLOUD_AUTH_TOKEN__" + "nxCloudAccessToken": "MmE0NmEwNmEtMzYyNy00Yzk2LTkxYzYtYWY1ZTYwMzZlYmMzfHJlYWQ=" } From 1d8ecbe2ccc9afc797eadb8ea1ab19ddea68ea6b Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 06:51:57 -0400 Subject: [PATCH 02/18] refactor: Add customerId property to User model and AuthLogin service The User model and AuthLogin service have been modified to include a new customerId property. This change allows for the association of users with specific customers. The necessary updates have been made to the User model, UserAttributes interface, and UserCreationAttributes interface. Additionally, the AuthLogin service has been updated to handle the customerId parameter when creating a new user. This change ensures that the customerId is properly stored and retrieved when interacting with user data. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- libs/web/src/models/User.ts | 6 ++++++ libs/web/src/services/AuthLogin.ts | 8 +++++--- libs/web/test/routes.test.ts | 10 +++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/libs/web/src/models/User.ts b/libs/web/src/models/User.ts index e33e390..f19f8e5 100644 --- a/libs/web/src/models/User.ts +++ b/libs/web/src/models/User.ts @@ -5,6 +5,7 @@ export interface UserAttributes { id: number; username: string; password: string; + customerId: number; } export interface UserCreationAttributes @@ -17,6 +18,7 @@ export class User declare id: number; declare username: string; declare password: string; + declare customerId: number; } User.init( @@ -35,6 +37,10 @@ User.init( type: DataTypes.STRING, allowNull: false, }, + customerId: { + type: DataTypes.INTEGER, + allowNull: false, + }, }, { sequelize: db, diff --git a/libs/web/src/services/AuthLogin.ts b/libs/web/src/services/AuthLogin.ts index 47e9a1e..4283362 100644 --- a/libs/web/src/services/AuthLogin.ts +++ b/libs/web/src/services/AuthLogin.ts @@ -10,7 +10,8 @@ import { validateCredentials } from "../helpers.js"; export async function createUser( username: string, - password: string + password: string, + customerId: number ): Promise> { log.debug("AuthLogin.create"); @@ -18,9 +19,9 @@ export async function createUser( log.debug(`Creating user: ${username}`); try { - const user = await User.create({ username, password }); + const user = await User.create({ username, password, customerId }); log.debug(`User created: ${user ? user.username : "null"}`); - return { username: user.username, password: user.password }; + return { username: user.username, password: user.password, customerId: user.customerId }; } catch (error: unknown) { handleCreateUserError(error); } @@ -54,6 +55,7 @@ export async function userLogin( return { username: user.username, password: user.password, + customerId: user.customerId, }; } diff --git a/libs/web/test/routes.test.ts b/libs/web/test/routes.test.ts index 506f5fd..553dea3 100644 --- a/libs/web/test/routes.test.ts +++ b/libs/web/test/routes.test.ts @@ -41,9 +41,6 @@ describe("RouteHandlers", () => { pathname: "/AuthLogin", searchParams, }; - await User.findOrCreate({ - where: { username: "validuser", password: "password" }, - }); // Act const response = await RouteHandlers["/AuthLogin"](info); @@ -68,6 +65,13 @@ describe("RouteHandlers", () => { pathname: "/AuthLogin", searchParams, }; + await User.sync({ force: true }); + await User.findOrCreate({ + where: { username: "validuser", password: "password", customerId: 1 }, + }).catch((error: unknown) => { + console.error((error as Error).message); + expect(true).toBe(false); + }); // Act const response = await RouteHandlers["/AuthLogin"](info); From c59222617a6500526c0e3428da5e9d8a5b4042e0 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 08:11:09 -0400 Subject: [PATCH 03/18] refactor: Add customerId property to User model and AuthLogin service --- .gitignore | 1 + apps/server/.env.example | 2 + apps/server/package.json | 7 +- apps/server/src/index.ts | 27 +- apps/server/src/instrument.ts | 18 + libs/util/package.json | 2 + libs/web/package.json | 2 + libs/web/src/db.ts | 2 +- pnpm-lock.yaml | 658 ++++++++++++++++++++++++++++++++++ 9 files changed, 714 insertions(+), 5 deletions(-) create mode 100644 apps/server/.env.example create mode 100644 apps/server/src/instrument.ts diff --git a/.gitignore b/.gitignore index 535b02e..b41c735 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ coverage #$ git update-index --no-assume-unchanged # Credit: https://web.archive.org/web/20240726013834/https://practicalgit.com/blog/make-git-ignore-local-changes-to-tracked-files.html nx.json +.env diff --git a/apps/server/.env.example b/apps/server/.env.example new file mode 100644 index 0000000..6b7e904 --- /dev/null +++ b/apps/server/.env.example @@ -0,0 +1,2 @@ +NX_CLOUD_AUTH_TOKEN= +SENTRY_DSN= \ No newline at end of file diff --git a/apps/server/package.json b/apps/server/package.json index 74d475f..9adb4d7 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -8,7 +8,8 @@ "build": "tsc --build --verbose", "check-types": "tsc --noEmit", "lint": "eslint src", - "start": "node dist/src/index.js", + "prestart": "nx run @rusty/server:build", + "start": "node --env-file .env dist/src/index.js", "test": "vitest", "dev": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' --watch src/index.ts" }, @@ -30,6 +31,8 @@ }, "dependencies": { "@rusty/util": "workspace:*", - "@rusty/web": "workspace:*" + "@rusty/web": "workspace:*", + "@sentry/node": "^8.20.0", + "@sentry/profiling-node": "^8.20.0" } } diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 232b19f..18854cc 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -1,4 +1,27 @@ +// IMPORTANT: Make sure to import `instrument.js` at the top of your file. +// If you're using ECMAScript Modules (ESM) syntax, use `import "./instrument.js";` +void import ("./instrument.js"); + +// All other imports below +import * as Sentry from "@sentry/node"; import { ServerController } from "./ServerController.js"; -const serverController = new ServerController(); -void serverController.start(); +process.on("unhandledRejection", (reason, promise) => { + console.error("Unhandled Rejection at:", promise, "reason:", reason); + Sentry.captureException(reason as Error); + process.exit(1); + }) + +process.on("uncaughtException", (error) => { + console.error("Uncaught Exception thrown", error); + Sentry.captureException(error); + process.exit(1); + }) + +function main() { + const serverController = new ServerController(); + void serverController.start(); +} + +main(); + diff --git a/apps/server/src/instrument.ts b/apps/server/src/instrument.ts new file mode 100644 index 0000000..ae710ae --- /dev/null +++ b/apps/server/src/instrument.ts @@ -0,0 +1,18 @@ +// Import with `import * as Sentry from "@sentry/node"` if you are using ESM +import * as Sentry from "@sentry/node"; +import { nodeProfilingIntegration } from "@sentry/profiling-node"; + +if (typeof process.env.SENTRY_DSN === "undefined" || process.env.SENTRY_DSN === "") { + console.error("No SENTRY_DSN provided"); + process.exit(1); +} + +Sentry.init({ + dsn: process.env.SENTRY_DSN, + integrations: [nodeProfilingIntegration()], + // Performance Monitoring + tracesSampleRate: 1.0, // Capture 100% of the transactions + + // Set sampling rate for profiling - this is relative to tracesSampleRate + profilesSampleRate: 1.0, +}); diff --git a/libs/util/package.json b/libs/util/package.json index c18b3f7..a1bdb3d 100644 --- a/libs/util/package.json +++ b/libs/util/package.json @@ -31,6 +31,8 @@ "vitest": "^2.0.4" }, "dependencies": { + "@sentry/node": "^8.20.0", + "@sentry/profiling-node": "^8.20.0", "pino": "9.3.1" } } diff --git a/libs/web/package.json b/libs/web/package.json index a1712ac..56b8f07 100644 --- a/libs/web/package.json +++ b/libs/web/package.json @@ -35,6 +35,8 @@ }, "dependencies": { "@rusty/util": "workspace:*", + "@sentry/node": "^8.20.0", + "@sentry/profiling-node": "^8.20.0", "pino": "9.3.1", "sequelize": "6.37.3", "sqlite3": "5.1.7" diff --git a/libs/web/src/db.ts b/libs/web/src/db.ts index 12143ec..831a955 100644 --- a/libs/web/src/db.ts +++ b/libs/web/src/db.ts @@ -5,7 +5,7 @@ const DATABASE_URL = process.env.DATABASE_URL || ":memory:"; export const db = new Sequelize({ dialect: "sqlite", storage: DATABASE_URL, - logging: console.log, // Enable logging + logging: false, }); db.authenticate() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a4481b..1b59ab5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,12 @@ importers: '@rusty/web': specifier: workspace:* version: link:../../libs/web + '@sentry/node': + specifier: ^8.20.0 + version: 8.20.0 + '@sentry/profiling-node': + specifier: ^8.20.0 + version: 8.20.0 devDependencies: '@types/node': specifier: 20.14.12 @@ -90,6 +96,12 @@ importers: libs/util: dependencies: + '@sentry/node': + specifier: ^8.20.0 + version: 8.20.0 + '@sentry/profiling-node': + specifier: ^8.20.0 + version: 8.20.0 pino: specifier: 9.3.1 version: 9.3.1 @@ -133,6 +145,12 @@ importers: '@rusty/util': specifier: workspace:* version: link:../util + '@sentry/node': + specifier: ^8.20.0 + version: 8.20.0 + '@sentry/profiling-node': + specifier: ^8.20.0 + version: 8.20.0 pino: specifier: 9.3.1 version: 9.3.1 @@ -1144,6 +1162,160 @@ packages: '@nx/workspace@19.5.1': resolution: {integrity: sha512-I/O+kDxaLzVXITmddijffNzllioe84oGSQ3q8s3nW7RP2/T1LkmOS7Prnd80sEVteQowHe0kOnvAh+p82kAUjQ==} + '@opentelemetry/api-logs@0.52.1': + resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} + engines: {node: '>=14'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/context-async-hooks@1.25.1': + resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@1.25.1': + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/instrumentation-connect@0.38.0': + resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.41.0': + resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fastify@0.38.0': + resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.42.0': + resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-hapi@0.40.0': + resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.52.1': + resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.42.0': + resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.42.0': + resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.46.0': + resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.40.0': + resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.40.0': + resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.40.0': + resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.39.0': + resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.43.0': + resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis-4@0.41.0': + resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.46.0': + resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.52.1': + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/redis-common@0.36.2': + resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} + engines: {node: '>=14'} + + '@opentelemetry/resources@1.25.1': + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-metrics@1.25.1': + resolution: {integrity: sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@1.25.1': + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.25.1': + resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} + engines: {node: '>=14'} + + '@opentelemetry/sql-common@0.40.1': + resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1151,6 +1323,9 @@ packages: '@polka/url@1.0.0-next.25': resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + '@prisma/instrumentation@5.17.0': + resolution: {integrity: sha512-c1Sle4ji8aasMcYfBBHFM56We4ljfenVtRmS8aY06BllS7SoU6SmJBwG7vil+GHiR0Yrh+t9iBwt4AY0Jr4KNQ==} + '@rollup/rollup-android-arm-eabi@4.19.0': resolution: {integrity: sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==} cpu: [arm] @@ -1231,6 +1406,37 @@ packages: cpu: [x64] os: [win32] + '@sentry/core@8.20.0': + resolution: {integrity: sha512-R81snuw+67VT4aCxr6ShST/s0Y6FlwN2YczhDwaGyzumn5rlvA6A4JtQDeExduNoDDyv4T3LrmW8wlYZn3CJJw==} + engines: {node: '>=14.18'} + + '@sentry/node@8.20.0': + resolution: {integrity: sha512-i4ywT2m0Gw65U3uwI4NwiNcyqp9YF6/RsusfH1pg4YkiL/RYp7FS0MPVgMggfvoue9S3KjCgRVlzTLwFATyPXQ==} + engines: {node: '>=14.18'} + + '@sentry/opentelemetry@8.20.0': + resolution: {integrity: sha512-NFcLK6+t9wUc4HlGKeuDn6W4KjZxZfZmWlrK2/tgC5KzG1cnVeOnWUrJzGHTa+YDDdIijpjiFUcpXGPkX3rmIg==} + engines: {node: '>=14.18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.25.1 + '@opentelemetry/instrumentation': ^0.52.1 + '@opentelemetry/sdk-trace-base': ^1.25.1 + '@opentelemetry/semantic-conventions': ^1.25.1 + + '@sentry/profiling-node@8.20.0': + resolution: {integrity: sha512-vQaMYjPM7o0qvmj4atxXZywIDhnxMwTlc6x24eKqT8zN0OFBuIc1nYIacT7pEmd7R6e/mXdiG04GH1Vg0bHfOQ==} + engines: {node: '>=14.18'} + hasBin: true + + '@sentry/types@8.20.0': + resolution: {integrity: sha512-6IP278KojOpiAA7vrd1hjhUyn26cl0n0nGsShzic5ztCVs92sTeVRnh7MTB9irDVtAbOEyt/YH6go3h+Jia1pA==} + engines: {node: '>=14.18'} + + '@sentry/utils@8.20.0': + resolution: {integrity: sha512-+1I5H8dojURiEUGPliDwheQk8dhjp8uV1sMccR/W/zjFrt4wZyPs+Ttp/V7gzm9LDJoNek9tmELert/jQqWTgg==} + engines: {node: '>=14.18'} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -1253,6 +1459,9 @@ packages: '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + '@types/connect@3.4.36': + resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1262,16 +1471,28 @@ packages: '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + '@types/mysql@2.15.22': + resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} + '@types/node@20.14.12': resolution: {integrity: sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/pg-pool@2.0.4': + resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} + + '@types/pg@8.6.1': + resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + '@types/pino@7.0.5': resolution: {integrity: sha512-wKoab31pknvILkxAF8ss+v9iNyhw5Iu/0jLtRkUD74cNfOOLJNnqfFKAv0r7wVaTQxRZtWrMpGfShwwBjOcgcg==} deprecated: This is a stub types definition. pino provides its own type definitions, so you do not need this installed. + '@types/shimmer@1.2.0': + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + '@types/validator@13.12.0': resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==} @@ -1382,6 +1603,16 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + acorn-import-assertions@1.9.0: + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + peerDependencies: + acorn: ^8 + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1598,6 +1829,9 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -2134,6 +2368,12 @@ packages: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} + import-in-the-middle@1.10.0: + resolution: {integrity: sha512-Z1jumVdF2GwnnYfM0a/y2ts7mZbwFMgt5rRuVmLgobgahC6iKgN5MBuXjzfTIOUpq5LSU10vJIPpVKe0X89fIw==} + + import-in-the-middle@1.7.1: + resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -2458,6 +2698,9 @@ packages: engines: {node: '>=10'} hasBin: true + module-details-from-path@1.0.3: + resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} + moment-timezone@0.5.45: resolution: {integrity: sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==} @@ -2568,6 +2811,12 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + opentelemetry-instrumentation-fetch-node@1.2.3: + resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} + engines: {node: '>18.0.0'} + peerDependencies: + '@opentelemetry/api': ^1.6.0 + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2636,6 +2885,17 @@ packages: pg-connection-string@2.6.4: resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} @@ -2661,6 +2921,22 @@ packages: resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==} engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + prebuild-install@7.1.2: resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} engines: {node: '>=10'} @@ -2761,6 +3037,10 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-in-the-middle@7.4.0: + resolution: {integrity: sha512-X34iHADNbNDfr6OTStIAHWSAvvKQRYgLO6duASaVf7J2VA3lvmNYboAHOuLC2huav1IwgZJtyEcJCKVzFxOSMQ==} + engines: {node: '>=8.6.0'} + requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -2882,6 +3162,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} @@ -3300,6 +3583,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -4530,11 +4817,223 @@ snapshots: - '@swc/core' - debug + '@opentelemetry/api-logs@0.52.1': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.25.1 + + '@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@types/connect': 3.4.36 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.36.2 + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@types/mysql': 2.15.22 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) + '@types/pg': 8.6.1 + '@types/pg-pool': 2.0.4 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.36.2 + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.7.1 + require-in-the-middle: 7.4.0 + semver: 7.6.3 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.10.0 + require-in-the-middle: 7.4.0 + semver: 7.6.3 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/redis-common@0.36.2': {} + + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + + '@opentelemetry/sdk-metrics@1.25.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + lodash.merge: 4.6.2 + + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + + '@opentelemetry/semantic-conventions@1.25.1': {} + + '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@pkgjs/parseargs@0.11.0': optional: true '@polka/url@1.0.0-next.25': {} + '@prisma/instrumentation@5.17.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + '@rollup/rollup-android-arm-eabi@4.19.0': optional: true @@ -4583,6 +5082,74 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.19.0': optional: true + '@sentry/core@8.20.0': + dependencies: + '@sentry/types': 8.20.0 + '@sentry/utils': 8.20.0 + + '@sentry/node@8.20.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.39.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@prisma/instrumentation': 5.17.0 + '@sentry/core': 8.20.0 + '@sentry/opentelemetry': 8.20.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/types': 8.20.0 + '@sentry/utils': 8.20.0 + import-in-the-middle: 1.10.0 + optionalDependencies: + opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@sentry/opentelemetry@8.20.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@sentry/core': 8.20.0 + '@sentry/types': 8.20.0 + '@sentry/utils': 8.20.0 + + '@sentry/profiling-node@8.20.0': + dependencies: + '@sentry/core': 8.20.0 + '@sentry/node': 8.20.0 + '@sentry/types': 8.20.0 + '@sentry/utils': 8.20.0 + detect-libc: 2.0.3 + node-abi: 3.65.0 + transitivePeerDependencies: + - supports-color + + '@sentry/types@8.20.0': {} + + '@sentry/utils@8.20.0': + dependencies: + '@sentry/types': 8.20.0 + '@sinclair/typebox@0.27.8': {} '@tootallnate/once@1.1.2': @@ -4600,6 +5167,10 @@ snapshots: dependencies: tslib: 2.6.3 + '@types/connect@3.4.36': + dependencies: + '@types/node': 20.14.12 + '@types/debug@4.1.12': dependencies: '@types/ms': 0.7.34 @@ -4608,16 +5179,32 @@ snapshots: '@types/ms@0.7.34': {} + '@types/mysql@2.15.22': + dependencies: + '@types/node': 20.14.12 + '@types/node@20.14.12': dependencies: undici-types: 5.26.5 '@types/parse-json@4.0.2': {} + '@types/pg-pool@2.0.4': + dependencies: + '@types/pg': 8.6.1 + + '@types/pg@8.6.1': + dependencies: + '@types/node': 20.14.12 + pg-protocol: 1.6.1 + pg-types: 2.2.0 + '@types/pino@7.0.5': dependencies: pino: 9.3.1 + '@types/shimmer@1.2.0': {} + '@types/validator@13.12.0': {} '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)': @@ -4783,6 +5370,15 @@ snapshots: dependencies: event-target-shim: 5.0.1 + acorn-import-assertions@1.9.0(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + optional: true + + acorn-import-attributes@1.9.5(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 @@ -5036,6 +5632,8 @@ snapshots: chownr@2.0.0: {} + cjs-module-lexer@1.3.1: {} + clean-stack@2.2.0: optional: true @@ -5617,6 +6215,21 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-in-the-middle@1.10.0: + dependencies: + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + cjs-module-lexer: 1.3.1 + module-details-from-path: 1.0.3 + + import-in-the-middle@1.7.1: + dependencies: + acorn: 8.12.1 + acorn-import-assertions: 1.9.0(acorn@8.12.1) + cjs-module-lexer: 1.3.1 + module-details-from-path: 1.0.3 + optional: true + imurmurhash@0.1.4: {} indent-string@4.0.0: @@ -5929,6 +6542,8 @@ snapshots: mkdirp@1.0.4: {} + module-details-from-path@1.0.3: {} + moment-timezone@0.5.45: dependencies: moment: 2.30.1 @@ -6080,6 +6695,15 @@ snapshots: opener@1.5.2: {} + opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + transitivePeerDependencies: + - supports-color + optional: true + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -6149,6 +6773,18 @@ snapshots: pg-connection-string@2.6.4: {} + pg-int8@1.0.1: {} + + pg-protocol@1.6.1: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + picocolors@1.0.1: {} picomatch@2.3.1: {} @@ -6188,6 +6824,16 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postgres-array@2.0.0: {} + + postgres-bytea@1.0.0: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + prebuild-install@7.1.2: dependencies: detect-libc: 2.0.3 @@ -6295,6 +6941,14 @@ snapshots: require-directory@2.1.1: {} + require-in-the-middle@7.4.0: + dependencies: + debug: 4.3.5 + module-details-from-path: 1.0.3 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + requires-port@1.0.0: {} resolve-from@4.0.0: {} @@ -6408,6 +7062,8 @@ snapshots: shebang-regex@3.0.0: {} + shimmer@1.2.1: {} + side-channel@1.0.6: dependencies: call-bind: 1.0.7 @@ -6834,6 +7490,8 @@ snapshots: wrappy@1.0.2: {} + xtend@4.0.2: {} + y18n@5.0.8: {} yallist@3.1.1: {} From eb1d3777abccd6066e307af077b7b4c8674e2416 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 18:42:56 -0400 Subject: [PATCH 04/18] feat: add config lib --- libs/config/eslint.config.js | 24 ++++++++++++ libs/config/index.ts | 1 + libs/config/package.json | 42 ++++++++++++++++++++ libs/config/src/config.ts | 4 ++ libs/config/src/index.ts | 1 + libs/config/src/types.ts | 24 ++++++++++++ libs/config/test/config.test.ts | 13 +++++++ libs/config/tsconfig.json | 11 ++++++ libs/config/vitest.config.js | 20 ++++++++++ libs/web/package.json | 1 + libs/web/src/processors/processLogin.ts | 8 ++-- pnpm-lock.yaml | 52 +++++++++++++++++++++++++ tsconfig.json | 3 +- 13 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 libs/config/eslint.config.js create mode 100644 libs/config/index.ts create mode 100644 libs/config/package.json create mode 100644 libs/config/src/config.ts create mode 100644 libs/config/src/index.ts create mode 100644 libs/config/src/types.ts create mode 100644 libs/config/test/config.test.ts create mode 100644 libs/config/tsconfig.json create mode 100644 libs/config/vitest.config.js diff --git a/libs/config/eslint.config.js b/libs/config/eslint.config.js new file mode 100644 index 0000000..ec3eb89 --- /dev/null +++ b/libs/config/eslint.config.js @@ -0,0 +1,24 @@ +// @ts-check + +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + { + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ["eslint.config.js", "vitest.config.js", "**/*.d.ts"], + ...tseslint.configs.disableTypeChecked, + }, + { + ignores: ["**/coverage/*", "**/dist/*"], + } +); diff --git a/libs/config/index.ts b/libs/config/index.ts new file mode 100644 index 0000000..76d3657 --- /dev/null +++ b/libs/config/index.ts @@ -0,0 +1 @@ +export { getServerURL } from "./src/index.js"; \ No newline at end of file diff --git a/libs/config/package.json b/libs/config/package.json new file mode 100644 index 0000000..457a2ca --- /dev/null +++ b/libs/config/package.json @@ -0,0 +1,42 @@ +{ + "name": "@rusty/config", + "version": "1.0.0", + "private": true, + "type": "module", + "imports": { + "#internal": "./dist/src/index.js" + }, + "exports": { + ".": "./dist/index.js" + }, + "types": "./dist/index.d.ts", + "scripts": { + "clean": "rm -rf dist", + "build": "tsc --build --verbose", + "test": "vitest", + "check-types": "tsc --noEmit", + "lint": "eslint src" + }, + "keywords": [], + "author": "", + "license": "GPL-3.0-only", + "devDependencies": { + "@types/node": "20.14.12", + "@types/pino": "7.0.5", + "@vitest/coverage-v8": "^2.0.4", + "@vitest/ui": "^2.0.4", + "eslint": "^8.57.0", + "nx": "19.5.1", + "ts-node": "^10.9.2", + "tslib": "^2.6.3", + "typescript": "^5.5.4", + "vite": "2.0.4", + "vitest": "^2.0.4" + }, + "dependencies": { + "@rusty/util": "workspace:*", + "@sentry/node": "^8.20.0", + "@sentry/profiling-node": "^8.20.0", + "pino": "9.3.1" + } +} diff --git a/libs/config/src/config.ts b/libs/config/src/config.ts new file mode 100644 index 0000000..27e5de8 --- /dev/null +++ b/libs/config/src/config.ts @@ -0,0 +1,4 @@ +export function getServerURL(): string { + const serverURL = process.env.SERVER_URL ?? "https://rusty-motors.com"; + return serverURL; +} diff --git a/libs/config/src/index.ts b/libs/config/src/index.ts new file mode 100644 index 0000000..5ffa937 --- /dev/null +++ b/libs/config/src/index.ts @@ -0,0 +1 @@ +export { getServerURL } from "./config.js"; \ No newline at end of file diff --git a/libs/config/src/types.ts b/libs/config/src/types.ts new file mode 100644 index 0000000..c60f7d1 --- /dev/null +++ b/libs/config/src/types.ts @@ -0,0 +1,24 @@ +export type rawHttpRequestData = { + headers: Record; + remoteAddress: string; + method: string; + url: string; +}; + +export type parsedHttpRequestData = { + headers: Record; + remoteAddress: string; + method: string; + pathname: string; + searchParams: URLSearchParams; +}; +export type RequestResponse = { + statusCode: number; + body: string; + headers: Record; +}; + +export type User = { + username: string; + password: string; +}; diff --git a/libs/config/test/config.test.ts b/libs/config/test/config.test.ts new file mode 100644 index 0000000..cea153f --- /dev/null +++ b/libs/config/test/config.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, it } from "vitest"; +import { getServerURL } from "@rusty/config"; + +describe("config", () => { + it("should have a default serverURL if SERVER_URL environment variable is not set", () => { + expect(getServerURL()).toBe("https://rusty-motors.com"); + }); + + it("should use the SERVER_URL environment variable if set", () => { + process.env.SERVER_URL = "https://example.com"; + expect(getServerURL()).toBe("https://example.com"); + }); +}); diff --git a/libs/config/tsconfig.json b/libs/config/tsconfig.json new file mode 100644 index 0000000..6572291 --- /dev/null +++ b/libs/config/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declarationDir": "./dist", + "composite": true + }, + "include": ["index.ts", "src/**/*.ts", "test/**/*.ts"], + "exclude": ["node_modules", "dist"], + "references": [{ "path": "../util" }] +} diff --git a/libs/config/vitest.config.js b/libs/config/vitest.config.js new file mode 100644 index 0000000..c1aed66 --- /dev/null +++ b/libs/config/vitest.config.js @@ -0,0 +1,20 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + coverage: { + enabled: true, + all: true, + exclude: [ + "dist/**", + "eslint.config.js", + "vitest.config.js" + ], + reporter: ["lcov", "text", "cobertura"], + }, + reporters: ["junit", "default", "hanging-process"], + outputFile: "mcos.junit.xml", + pool: "forks", + watch: false, + }, +}); \ No newline at end of file diff --git a/libs/web/package.json b/libs/web/package.json index 56b8f07..174b5a1 100644 --- a/libs/web/package.json +++ b/libs/web/package.json @@ -37,6 +37,7 @@ "@rusty/util": "workspace:*", "@sentry/node": "^8.20.0", "@sentry/profiling-node": "^8.20.0", + "@rusty/config": "workspace:*", "pino": "9.3.1", "sequelize": "6.37.3", "sqlite3": "5.1.7" diff --git a/libs/web/src/processors/processLogin.ts b/libs/web/src/processors/processLogin.ts index c4e3db3..a29f2f0 100644 --- a/libs/web/src/processors/processLogin.ts +++ b/libs/web/src/processors/processLogin.ts @@ -2,6 +2,7 @@ import { log } from "@rusty/util"; import { extractCredentials, validateCredentials } from "../helpers.js"; import { userLogin } from "../services/AuthLogin.js"; import type { parsedHttpRequestData, RequestResponse } from "../types.js"; +import { getServerURL } from "@rusty/config"; /** * Handles the authentication login process. @@ -19,16 +20,13 @@ export async function authenticateUser( await userLogin(username, password); + // TODO: Implement token generation const token = "abc123"; return constructLoginResponse(`Valid=TRUE\nTicket=${token}`); } catch (error: unknown) { log.error(`Error validating credentials: ${(error as Error).message}`); - return generateLoginError( - "INV-200", - "Unable to login", - "https://rusty-motors.com" - ); + return generateLoginError("INV-200", "Unable to login", getServerURL()); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b59ab5..1866027 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,6 +94,55 @@ importers: specifier: ^2.0.4 version: 2.0.4(@types/node@20.14.12)(@vitest/ui@2.0.4) + libs/config: + dependencies: + '@rusty/util': + specifier: workspace:* + version: link:../util + '@sentry/node': + specifier: ^8.20.0 + version: 8.20.0 + '@sentry/profiling-node': + specifier: ^8.20.0 + version: 8.20.0 + pino: + specifier: 9.3.1 + version: 9.3.1 + devDependencies: + '@types/node': + specifier: 20.14.12 + version: 20.14.12 + '@types/pino': + specifier: 7.0.5 + version: 7.0.5 + '@vitest/coverage-v8': + specifier: ^2.0.4 + version: 2.0.4(vitest@2.0.4(@types/node@20.14.12)(@vitest/ui@2.0.4)) + '@vitest/ui': + specifier: ^2.0.4 + version: 2.0.4(vitest@2.0.4) + eslint: + specifier: ^8.57.0 + version: 8.57.0 + nx: + specifier: 19.5.1 + version: 19.5.1 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.14.12)(typescript@5.5.4) + tslib: + specifier: ^2.6.3 + version: 2.6.3 + typescript: + specifier: ^5.5.4 + version: 5.5.4 + vite: + specifier: 2.0.4 + version: 2.0.4 + vitest: + specifier: ^2.0.4 + version: 2.0.4(@types/node@20.14.12)(@vitest/ui@2.0.4) + libs/util: dependencies: '@sentry/node': @@ -142,6 +191,9 @@ importers: libs/web: dependencies: + '@rusty/config': + specifier: workspace:* + version: link:../config '@rusty/util': specifier: workspace:* version: link:../util diff --git a/tsconfig.json b/tsconfig.json index 05fa097..c934dfb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,7 @@ "references": [ { "path": "./apps/server" }, { "path": "./libs/util" }, - { "path": "./libs/web" } + { "path": "./libs/web" }, + { "path": "./libs/config" } ] } From 8ef3e2e2c253ac26b869057bdac6a975a8dcccb4 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 19:14:16 -0400 Subject: [PATCH 05/18] refactor: remove unneeded complexity --- libs/web/index.ts | 13 ++--- libs/web/src/errors.ts | 52 ------------------- libs/web/src/helpers.ts | 5 +- libs/web/src/index.ts | 44 ++-------------- libs/web/src/processors/processWebRequests.ts | 40 ++++++++++++++ libs/web/src/services/AuthLogin.ts | 51 +----------------- libs/web/test/errors.test.ts | 12 ----- libs/web/test/routes.test.ts | 3 +- 8 files changed, 53 insertions(+), 167 deletions(-) delete mode 100644 libs/web/src/errors.ts create mode 100644 libs/web/src/processors/processWebRequests.ts delete mode 100644 libs/web/test/errors.test.ts diff --git a/libs/web/index.ts b/libs/web/index.ts index 8176b15..2da49c0 100644 --- a/libs/web/index.ts +++ b/libs/web/index.ts @@ -1,9 +1,4 @@ -export { processWebRequests } from "./src/index.js"; -export { RouteHandlers } from "./src/processors/index.js"; -export { - ErrorUserNotFound, - handleCreateUserError, - ErrorMissingCredentials, - ErrorUserExists, -} from "./src/errors.js"; -export { createUser, userLogin, deleteUser } from "./src/services/AuthLogin.js"; +/** + * External exports for the web library + */ +export { processWebRequests } from "./src/processors/processWebRequests.js"; diff --git a/libs/web/src/errors.ts b/libs/web/src/errors.ts deleted file mode 100644 index e984c25..0000000 --- a/libs/web/src/errors.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { log } from "@rusty/util"; - -export class ErrorMissingCredentials extends Error { - name = "ErrorMissingCredentials"; - - constructor(message: string) { - super(message); - this.name = "ErrorMissingCredentials"; - } -} - -export class ErrorUserExists extends Error { - name = "ErrorUserExists"; - - constructor(message: string) { - super(message); - this.name = "ErrorUserExists"; - } -} - -export class ErrorUserNotFound extends Error { - name = "ErrorUserNotFound"; - - constructor(message: string) { - super(message); - this.name = "ErrorUserNotFound"; - } -} -/** - * Handles the error that occurs when creating a user. - * If the error message indicates that the username must be unique, - * it throws an ErrorUserExists with the original error as the cause. - * Otherwise, it throws a generic Error with the original error as the cause. - * - * @param error - The error that occurred during user creation. - * @throws {ErrorUserExists} - If the error message indicates that the username must be unique. - * @throws {Error} - If the error message does not indicate that the username must be unique. - * @returns {never} - This function never returns a value. - */ -export function handleCreateUserError(error: unknown): never { - if ((error as Error).message.includes("username must be unique")) { - const err = new Error("Error creating user"); - err.name = "ErrorUserExists"; - err.cause = error; - log.error(`Error creating user: ${(error as Error).message}`); - throw err; - } - const err = new ErrorUserExists("Error creating user"); - err.cause = error; - log.error(`Error creating user: ${(error as Error).message}`); - throw err; -} diff --git a/libs/web/src/helpers.ts b/libs/web/src/helpers.ts index 1513559..d393195 100644 --- a/libs/web/src/helpers.ts +++ b/libs/web/src/helpers.ts @@ -1,4 +1,3 @@ -import { ErrorMissingCredentials } from "./errors.js"; import type { parsedHttpRequestData } from "./types.js"; /** @@ -23,10 +22,10 @@ export function extractCredentials(info: parsedHttpRequestData): { */ export function validateCredentials(username: string, password: string): void { if (username === "") { - throw new ErrorMissingCredentials("Username is required"); + throw new Error("Username is required"); } if (password === "") { - throw new ErrorMissingCredentials("Password is required"); + throw new Error("Password is required"); } } \ No newline at end of file diff --git a/libs/web/src/index.ts b/libs/web/src/index.ts index 2fdec69..4a21cf4 100644 --- a/libs/web/src/index.ts +++ b/libs/web/src/index.ts @@ -1,42 +1,6 @@ -import { log } from "@rusty/util"; -import type { rawHttpRequestData, RequestResponse } from "./types.js"; -import { RouteHandlers } from "./processors/index.js"; +/** + * Internal exports for the web library. + */ export { User } from "./models/User.js"; export type { UserAttributes } from "./models/User.js"; - -export async function processWebRequests({ - headers, - remoteAddress, - method, - url, -}: rawHttpRequestData): Promise { - log.debug("web.handleWebRequests"); - - const { pathname, searchParams } = new URL(url, "http://localhost"); - - log.debug( - `method: ${method} pathname: ${pathname} searchParams: ${searchParams.toString()}` - ); - - const handler = RouteHandlers[pathname]; - - if (handler) { - log.debug(`handler: ${handler.name}`); - return await handler({ - headers, - remoteAddress, - method, - pathname, - searchParams, - }); - } - - log.debug("handler not found"); - return new Promise((resolve) => { - resolve({ - statusCode: 404, - body: "Not Found\n", - headers: { "Content-Type": "text/plain" }, - }); - }); -} +export { RouteHandlers } from "./processors/index.js"; diff --git a/libs/web/src/processors/processWebRequests.ts b/libs/web/src/processors/processWebRequests.ts new file mode 100644 index 0000000..3d1e69d --- /dev/null +++ b/libs/web/src/processors/processWebRequests.ts @@ -0,0 +1,40 @@ +import { log } from "@rusty/util"; +import { RouteHandlers } from "./index.js"; +import type { rawHttpRequestData, RequestResponse } from "../types.js"; + +export async function processWebRequests({ + headers, + remoteAddress, + method, + url, +}: rawHttpRequestData): Promise { + log.debug("web.handleWebRequests"); + + const { pathname, searchParams } = new URL(url, "http://localhost"); + + log.debug( + `method: ${method} pathname: ${pathname} searchParams: ${searchParams.toString()}` + ); + + const handler = RouteHandlers[pathname]; + + if (handler) { + log.debug(`handler: ${handler.name}`); + return await handler({ + headers, + remoteAddress, + method, + pathname, + searchParams, + }); + } + + log.debug("handler not found"); + return new Promise((resolve) => { + resolve({ + statusCode: 404, + body: "Not Found\n", + headers: { "Content-Type": "text/plain" }, + }); + }); +} diff --git a/libs/web/src/services/AuthLogin.ts b/libs/web/src/services/AuthLogin.ts index 4283362..13a9082 100644 --- a/libs/web/src/services/AuthLogin.ts +++ b/libs/web/src/services/AuthLogin.ts @@ -1,38 +1,12 @@ import { User, type UserAttributes } from "../models/User.js"; import { log } from "@rusty/util"; -import { - ErrorUserNotFound, - handleCreateUserError, -} from "../errors.js"; -import { validateCredentials } from "../helpers.js"; - - - -export async function createUser( - username: string, - password: string, - customerId: number -): Promise> { - log.debug("AuthLogin.create"); - - validateCredentials(username, password); - - log.debug(`Creating user: ${username}`); - try { - const user = await User.create({ username, password, customerId }); - log.debug(`User created: ${user ? user.username : "null"}`); - return { username: user.username, password: user.password, customerId: user.customerId }; - } catch (error: unknown) { - handleCreateUserError(error); - } -} /** * Authenticates a user by their username and password. * @param {string} username - The username of the user. * @param {string} password - The password of the user. * @returns {Promise>} - A promise that resolves to the user attributes (excluding the "id" field). - * @throws {ErrorUserNotFound} - If the user is not found. + * @throws {Error} - If the user is not found. */ export async function userLogin( username: string, @@ -48,7 +22,7 @@ export async function userLogin( }); if (!user) { - throw new ErrorUserNotFound("User not found"); + throw new Error("User not found"); } log.debug("User found"); @@ -58,24 +32,3 @@ export async function userLogin( customerId: user.customerId, }; } - - - - - - -/** - * Deletes a user from the database. - * - * @param username - The username of the user to delete. - * @returns A Promise that resolves when the user is successfully deleted. - * @throws If there is an error deleting the user. - */ -export async function deleteUser(username: string): Promise { - log.debug("Deleting user"); - return await User.destroy({ - where: { - username, - }, - }); -} diff --git a/libs/web/test/errors.test.ts b/libs/web/test/errors.test.ts deleted file mode 100644 index 5270fa8..0000000 --- a/libs/web/test/errors.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { ErrorMissingCredentials, ErrorUserExists } from "@rusty/web"; - -describe("Errors", () => { - it("should have an ErrorMissingCredentials class", () => { - expect(ErrorMissingCredentials).toBeDefined(); - }); - - it("should have an ErrorUserExists class", () => { - expect(ErrorUserExists).toBeDefined(); - }); -}); diff --git a/libs/web/test/routes.test.ts b/libs/web/test/routes.test.ts index 553dea3..345cbb3 100644 --- a/libs/web/test/routes.test.ts +++ b/libs/web/test/routes.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it, test } from "vitest"; -import { RouteHandlers } from "@rusty/web"; -import { User } from "#internal"; +import { RouteHandlers, User } from "#internal"; describe("RouteHandlers", () => { it("should have a handler for /AuthLogin", () => { From 73809f8af031871fd0efc3849c302fff9aea4818 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 19:36:38 -0400 Subject: [PATCH 06/18] refactor: Remove unused script for injecting token The `inject-token.sh` script was removed as it was no longer needed. This script was previously used to inject a token into the `nx.json` file, but that functionality has been deprecated. The removal of this script simplifies the codebase and removes unnecessary complexity. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- Makefile | 1 - nx.json | 2 +- scripts/inject-token.sh | 12 ------------ 3 files changed, 1 insertion(+), 14 deletions(-) delete mode 100755 scripts/inject-token.sh diff --git a/Makefile b/Makefile index 30e6c75..bca92a5 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ test: - ./scripts/inject-token.sh pnpm exec nx affected -t lint build test @PHONY: test \ No newline at end of file diff --git a/nx.json b/nx.json index 3fb391a..4742a8d 100644 --- a/nx.json +++ b/nx.json @@ -22,5 +22,5 @@ } }, "defaultBase": "main", - "nxCloudAccessToken": "MmE0NmEwNmEtMzYyNy00Yzk2LTkxYzYtYWY1ZTYwMzZlYmMzfHJlYWQ=" + "nxCloudAccessToken": "NOT_A_REAL_TOKEN-USES_NX_CLOUD_ACCESS_TOKEN_SECRET_ENV" } diff --git a/scripts/inject-token.sh b/scripts/inject-token.sh deleted file mode 100755 index 702c1b4..0000000 --- a/scripts/inject-token.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# Ensure the environment variable is set -if [ -z "$NX_CLOUD_AUTH_TOKEN" ]; then - echo "Error: NX_CLOUD_AUTH_TOKEN is not set." - exit 1 -fi - -# Replace the placeholder in nx.json with the environment variable value -sed -i 's/__NX_CLOUD_AUTH_TOKEN__/'"$NX_CLOUD_AUTH_TOKEN"'/g' nx.json - -echo "Token has been securely injected into nx.json." \ No newline at end of file From 2b5d438bc3a9c11e2f4815167c5561b27e2445e2 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 19:52:27 -0400 Subject: [PATCH 07/18] test: add comment explaining why 200 is the expected response code for errors --- libs/web/test/routes.test.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libs/web/test/routes.test.ts b/libs/web/test/routes.test.ts index 345cbb3..e2077f5 100644 --- a/libs/web/test/routes.test.ts +++ b/libs/web/test/routes.test.ts @@ -1,11 +1,7 @@ -import { describe, expect, it, test } from "vitest"; +import { describe, expect, test } from "vitest"; import { RouteHandlers, User } from "#internal"; describe("RouteHandlers", () => { - it("should have a handler for /AuthLogin", () => { - expect(RouteHandlers["/AuthLogin"]).toBeDefined(); - }); - test("when /AuthLogin is called with a missing username, expect a generic error", async () => { // Arrange const searchParams = new URLSearchParams(); @@ -22,7 +18,7 @@ describe("RouteHandlers", () => { const response = await RouteHandlers["/AuthLogin"](info); // Assert - expect(response.statusCode).toBe(200); + expect(response.statusCode).toBe(200); // The legacy client expects a 200 status code and a specific response body expect(response.body).toBe( "reasoncode=INV-200\nreasontext=Unable to login\nreasonurl=https://rusty-motors.com" ); @@ -45,7 +41,7 @@ describe("RouteHandlers", () => { const response = await RouteHandlers["/AuthLogin"](info); // Assert - expect(response.statusCode).toBe(200); + expect(response.statusCode).toBe(200); // The legacy client expects a 200 status code and a specific response body expect(response.body).toBe( "reasoncode=INV-200\nreasontext=Unable to login\nreasonurl=https://rusty-motors.com" ); From fcbc2c2a9fd6412e004e1d08561427dd047d0961 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 19:53:19 -0400 Subject: [PATCH 08/18] refactor: Remove nx.json from .gitignore The nx.json file was removed from the .gitignore file. This change was made to ensure that the nx.json file is tracked by Git and included in the repository. --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index b41c735..5111138 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,4 @@ node_modules coverage *.xml -# tell Git to ignore/stop ignoring this file -#$ git update-index --assume-unchanged -#$ git update-index --no-assume-unchanged -# Credit: https://web.archive.org/web/20240726013834/https://practicalgit.com/blog/make-git-ignore-local-changes-to-tracked-files.html -nx.json .env From 135028b4fe6a7106ba15513b82315157cc51d606 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 20:02:02 -0400 Subject: [PATCH 09/18] refactor: Add unit tests for extractCredentials and validateCredentials functions --- libs/web/test/helpers.test.ts | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 libs/web/test/helpers.test.ts diff --git a/libs/web/test/helpers.test.ts b/libs/web/test/helpers.test.ts new file mode 100644 index 0000000..9c62086 --- /dev/null +++ b/libs/web/test/helpers.test.ts @@ -0,0 +1,73 @@ +import { describe, expect, test } from "vitest"; +import { extractCredentials, validateCredentials } from "../src/helpers.js"; + +describe("extractCredentials", () => { + test("should extract username and password from parsed HTTP request data", () => { + // Arrange + const info = { + headers: {}, + remoteAddress: "", + method: "", + pathname: "", + searchParams: new URLSearchParams( + "username=testuser&password=testpassword" + ), + }; + + // Act + const result = extractCredentials(info); + + // Assert + expect(result).toEqual({ username: "testuser", password: "testpassword" }); + }); + + test("should return empty strings if username and password are missing", () => { + // Arrange + const info = { + headers: {}, + remoteAddress: "", + method: "", + pathname: "", + searchParams: new URLSearchParams(), + }; + + // Act + const result = extractCredentials(info); + + // Assert + expect(result).toEqual({ username: "", password: "" }); + }); +}); + +describe("validateCredentials", () => { + test("should throw an error if username is empty", () => { + // Arrange + const username = ""; + const password = "testpassword"; + + // Act & Assert + expect(() => validateCredentials(username, password)).toThrow( + "Username is required" + ); + }); + + test("should throw an error if password is empty", () => { + // Arrange + const username = "testuser"; + const password = ""; + + // Act & Assert + expect(() => validateCredentials(username, password)).toThrow( + "Password is required" + ); + }); + + test("should not throw an error if both username and password are provided", () => { + // Arrange + const username = "testuser"; + const password = "testpassword"; + + // Act & Assert + expect(() => validateCredentials(username, password)).not.toThrow(); + }); +}); From 015bf0f753b2ec786698df34d2caae1f9ba70606 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 20:26:05 -0400 Subject: [PATCH 10/18] refactor: Add .github/codecov.yml for component management The `.github/codecov.yml` file was added to the repository. This file defines the component management rules for code coverage. It specifies the default rules for project and patch statuses, as well as individual rules for different components such as server, config, web, utils, and all_tests. These rules determine the code coverage targets for each component. This addition helps in managing code coverage and ensuring that the desired coverage levels are met for different parts of the codebase. --- .github/codecov.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/codecov.yml diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 0000000..e20b9a0 --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,29 @@ +component_management: + default_rules: + statuses: + - type: project + target: auto + - type: patch + target: auto + individual_components: + - component_id: server + name: Server + paths: + - "apps/server/src/**" + - component_id: config + name: Config + paths: + - libs/config/src/** + - component_id: web + name: Web + paths: + - libs/web/src/** + - component_id: utils + name: Utils + paths: + - libs/util/src/** + - component_id: all_tests + name: All Tests + paths: + - "apps/*/test/**" + - "libs/*/test/**" From 2967c1790ed875b8ef80d8e806223ff259368b19 Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 21:00:21 -0400 Subject: [PATCH 11/18] refactor: Update CI workflow to distribute test runs and upload coverage reports --- .github/workflows/ci.yml | 12 +++++++----- Makefile | 14 ++++++++++++-- nx.json | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9975b6f..4e57204 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,6 @@ permissions: actions: read contents: read - jobs: main: runs-on: ubuntu-22.04 @@ -25,8 +24,9 @@ jobs: # Run this command as early as possible, before dependencies are installed # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun # Connect your workspace by running "nx connect" and uncomment this - # - run: pnpm dlx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build" - + - run: pnpm dlx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build" + env: + NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} # Cache node_modules - uses: actions/setup-node@v3 with: @@ -35,19 +35,21 @@ jobs: - run: pnpm install --frozen-lockfile - uses: nrwl/nx-set-shas@v4 - + env: + NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} # Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud # - run: pnpm exec nx-cloud record -- echo Hello World # Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected - run: make test env: NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} + - name: Upload test results to Codecov if: ${{ !cancelled() }} uses: codecov/test-results-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4.0.1 + run: make codecov with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/Makefile b/Makefile index bca92a5..2cf64ed 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,14 @@ test: - pnpm exec nx affected -t lint build test + pnpm exec nx affected -t lint test build -@PHONY: test \ No newline at end of file +codecov_install: + pip install codecov-cli + +codecov: codecov_install + codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Server -F Server -f apps/server/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Config -F Config -f libs/config/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Utils -F Utils -f libs/utils/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Web -F Web -f libs/web/coverage/lcov.info + + +@PHONY: test codecov_install codecov \ No newline at end of file diff --git a/nx.json b/nx.json index 4742a8d..b0ca9a5 100644 --- a/nx.json +++ b/nx.json @@ -2,25 +2,46 @@ "$schema": "./node_modules/nx/schemas/nx-schema.json", "targetDefaults": { "clean": { - "dependsOn": ["^clean"] + "dependsOn": [ + "^clean" + ] }, "build": { - "dependsOn": ["^build"], - "outputs": ["{projectRoot}/dist"], + "dependsOn": [ + "^build" + ], + "outputs": [ + "{projectRoot}/dist" + ], "cache": true }, "check-types": { - "dependsOn": ["check-types", "^check-types"], + "dependsOn": [ + "check-types", + "^check-types" + ], "cache": true }, "test": { - "dependsOn": ["build"], - "outputs": ["{projectRoot}/coverage"] + "dependsOn": [ + "build" + ], + "outputs": [ + "{projectRoot}/coverage" + ] }, "lint": { - "dependsOn": ["build", "^lint"] + "dependsOn": [ + "build", + "^lint" + ] } }, "defaultBase": "main", - "nxCloudAccessToken": "NOT_A_REAL_TOKEN-USES_NX_CLOUD_ACCESS_TOKEN_SECRET_ENV" + "nxCloudAccessToken": "NOT_A_REAL_TOKEN-USES_NX_CLOUD_ACCESS_TOKEN_SECRET_ENV", + "namedInputs": { + "sharedGlobals": [ + "{workspaceRoot}/.github/workflows/ci.yml" + ] + } } From 289d76d943e4790f0bd2d65d51398b20958df77d Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 21:09:19 -0400 Subject: [PATCH 12/18] refactor: Update .github/codecov.yml for comment layout and visibility --- .github/codecov.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/codecov.yml b/.github/codecov.yml index e20b9a0..9bb4a7e 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -27,3 +27,8 @@ component_management: paths: - "apps/*/test/**" - "libs/*/test/**" + +comment: + layout: diff, flags, components, files, header, footer + hide_project_details: false + show_carryforward_flags: true \ No newline at end of file From 0c6443e173caddb23e23964a338e1df110e9870a Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Fri, 26 Jul 2024 21:19:00 -0400 Subject: [PATCH 13/18] refactor: Update .github/codecov.yml to show project coverage --- .github/codecov.yml | 2 +- Makefile | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index 9bb4a7e..0382f14 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -30,5 +30,5 @@ component_management: comment: layout: diff, flags, components, files, header, footer - hide_project_details: false + hide_project_coverage: false show_carryforward_flags: true \ No newline at end of file diff --git a/Makefile b/Makefile index 2cf64ed..235511b 100644 --- a/Makefile +++ b/Makefile @@ -10,5 +10,11 @@ codecov: codecov_install codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Utils -F Utils -f libs/utils/coverage/lcov.info codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Web -F Web -f libs/web/coverage/lcov.info +codecov_local: + codecovcli --verbose upload-process --fail-on-error -n Server -F Server -f apps/server/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -n Config -F Config -f libs/config/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -n Utils -F Utils -f libs/utils/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -n Web -F Web -f libs/web/coverage/lcov.info + @PHONY: test codecov_install codecov \ No newline at end of file From 7c5b372d3a506bde5f888ed25a5905093a916e6b Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Sat, 27 Jul 2024 08:42:52 -0400 Subject: [PATCH 14/18] refactor: Update log.debug statement in processWebRequests --- libs/web/src/processors/processWebRequests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/web/src/processors/processWebRequests.ts b/libs/web/src/processors/processWebRequests.ts index 3d1e69d..bac1e19 100644 --- a/libs/web/src/processors/processWebRequests.ts +++ b/libs/web/src/processors/processWebRequests.ts @@ -8,7 +8,7 @@ export async function processWebRequests({ method, url, }: rawHttpRequestData): Promise { - log.debug("web.handleWebRequests"); + log.debug("processWebRequests"); const { pathname, searchParams } = new URL(url, "http://localhost"); From 7622867284e9d609746e4a4883b15d7825c1954b Mon Sep 17 00:00:00 2001 From: Molly Draven Date: Sat, 27 Jul 2024 08:46:21 -0400 Subject: [PATCH 15/18] refactor: Simplify processWebRequests error handling The `processWebRequests` function has been refactored to simplify the error handling logic. Instead of returning a Promise that resolves to a 404 response, the function now directly returns the 404 response object. This change improves the readability and maintainability of the code. --- libs/web/src/processors/processWebRequests.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libs/web/src/processors/processWebRequests.ts b/libs/web/src/processors/processWebRequests.ts index bac1e19..a5638bb 100644 --- a/libs/web/src/processors/processWebRequests.ts +++ b/libs/web/src/processors/processWebRequests.ts @@ -30,11 +30,9 @@ export async function processWebRequests({ } log.debug("handler not found"); - return new Promise((resolve) => { - resolve({ - statusCode: 404, - body: "Not Found\n", - headers: { "Content-Type": "text/plain" }, - }); - }); + return { + statusCode: 404, + body: "Not Found\n", + headers: { "Content-Type": "text/plain" }, + }; } From 3a5f22921409dcce55075aa57b1e6b6fe0fd8f14 Mon Sep 17 00:00:00 2001 From: Molly Crendraven Date: Wed, 31 Jul 2024 07:16:51 -0400 Subject: [PATCH 16/18] fix(ci): change with to env --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e57204..b6241c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,5 +51,5 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Upload coverage reports to Codecov run: make codecov - with: - token: ${{ secrets.CODECOV_TOKEN }} + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From f7acc943dff17039e50b545c65341720dc15814e Mon Sep 17 00:00:00 2001 From: Molly Crendraven Date: Thu, 1 Aug 2024 08:21:05 -0400 Subject: [PATCH 17/18] Drazisil/issue39 (#40) * feat: Add Session model and exports in web library The Session model and its associated attributes and types have been added to the web library. This allows for the creation and management of sessions in the application. The necessary exports have also been added to the index file for easy access. This change enhances the functionality of the web library by introducing session management capabilities. --- .github/codecov.yml | 5 +++- .github/workflows/ci.yml | 6 ++-- Makefile | 8 ++--- libs/web/index.ts | 5 ++++ libs/web/src/models/Session.ts | 54 ++++++++++++++++++++++++++++++++++ libs/web/test/Session.test.ts | 26 ++++++++++++++++ 6 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 libs/web/src/models/Session.ts create mode 100644 libs/web/test/Session.test.ts diff --git a/.github/codecov.yml b/.github/codecov.yml index 0382f14..19aae8c 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -31,4 +31,7 @@ component_management: comment: layout: diff, flags, components, files, header, footer hide_project_coverage: false - show_carryforward_flags: true \ No newline at end of file + show_carryforward_flags: true + +test_analytics: + flake_detection: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6241c2..b4b3a45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,9 +24,9 @@ jobs: # Run this command as early as possible, before dependencies are installed # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun # Connect your workspace by running "nx connect" and uncomment this - - run: pnpm dlx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build" - env: - NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} + # - run: pnpm dlx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build" + # env: + # NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} # Cache node_modules - uses: actions/setup-node@v3 with: diff --git a/Makefile b/Makefile index 235511b..8a039e0 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,10 @@ codecov_install: pip install codecov-cli codecov: codecov_install - codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Server -F Server -f apps/server/coverage/lcov.info - codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Config -F Config -f libs/config/coverage/lcov.info - codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Utils -F Utils -f libs/utils/coverage/lcov.info - codecovcli --verbose upload-process --fail-on-error -t ${{ secrets.CODECOV_TOKEN }} -n Web -F Web -f libs/web/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -n Server -F Server -f apps/server/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -n Config -F Config -f libs/config/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -n Utils -F Utils -f libs/utils/coverage/lcov.info + codecovcli --verbose upload-process --fail-on-error -n Web -F Web -f libs/web/coverage/lcov.info codecov_local: codecovcli --verbose upload-process --fail-on-error -n Server -F Server -f apps/server/coverage/lcov.info diff --git a/libs/web/index.ts b/libs/web/index.ts index 2da49c0..e755951 100644 --- a/libs/web/index.ts +++ b/libs/web/index.ts @@ -2,3 +2,8 @@ * External exports for the web library */ export { processWebRequests } from "./src/processors/processWebRequests.js"; +export { Session } from "./src/models/Session.js"; +export type { + SessionCreationAttributes, + SessionAttributes, +} from "./src/models/Session.js"; diff --git a/libs/web/src/models/Session.ts b/libs/web/src/models/Session.ts new file mode 100644 index 0000000..30612df --- /dev/null +++ b/libs/web/src/models/Session.ts @@ -0,0 +1,54 @@ +import { + Model, + type Optional, + DataTypes, + type CreationOptional, +} from "sequelize"; +import { db } from "../db.js"; + +export interface SessionAttributes { + id: CreationOptional; + customerId: number; + createdAt: CreationOptional; + sessionToken: string; +} + +export interface SessionCreationAttributes + extends Optional {} + +export class Session + extends Model + implements SessionAttributes +{ + declare id: CreationOptional; + declare customerId: number; + declare createdAt: CreationOptional; + declare sessionToken: string; +} + +Session.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + customerId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + sessionToken: { + type: DataTypes.STRING, + allowNull: false, + }, + }, + { + sequelize: db, + modelName: "Session", + } +); diff --git a/libs/web/test/Session.test.ts b/libs/web/test/Session.test.ts new file mode 100644 index 0000000..40ead91 --- /dev/null +++ b/libs/web/test/Session.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, test } from "vitest"; +import { Session, type SessionCreationAttributes } from "@rusty/web"; + +describe("Session model", () => { + test("should create a new session", () => { + const sessionData: SessionCreationAttributes = { + customerId: 1, + sessionToken: "abc123", + createdAt: new Date(), + }; + + const session = new Session(sessionData); + + expect(session.customerId).toEqual(sessionData.customerId); + expect(session.sessionToken).toEqual(sessionData.sessionToken); + expect(session.createdAt).toEqual(sessionData.createdAt); + }); + + test("should have correct default values", () => { + const session = new Session(); + + expect(session.customerId).toBeUndefined(); + expect(session.sessionToken).toBeUndefined(); + expect(session.createdAt).not.toBeUndefined(); + }); +}); From 5117f9bad66c036eceb95f240e023ee78dab7e02 Mon Sep 17 00:00:00 2001 From: Molly Crendraven Date: Thu, 1 Aug 2024 10:28:06 -0400 Subject: [PATCH 18/18] Create pipeline to run full tests on main and development branches (#43) * Create pipeline to run full tests on main and development branches Fixes #42 * chore: Update CI workflow to include affected branches The CI workflow has been updated to include the affected branches for testing. Previously, only the `main` branch was included, but now both `main` and `develop` branches are included in the workflow. This change ensures that the CI pipeline runs tests on all relevant branches, improving the overall testing coverage. --- .github/workflows/ci.yml | 5 +-- .github/workflows/full_tests.yml | 56 ++++++++++++++++++++++++++++++++ Makefile | 3 ++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/full_tests.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4b3a45..e22dcf4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,10 @@ -name: CI +name: Affected CI on: push: branches: - - main + - !main + - !develop pull_request: permissions: diff --git a/.github/workflows/full_tests.yml b/.github/workflows/full_tests.yml new file mode 100644 index 0000000..e0d7bd1 --- /dev/null +++ b/.github/workflows/full_tests.yml @@ -0,0 +1,56 @@ +name: CI + +on: + push: + branches: + - main + - develop + pull_request: + +permissions: + actions: read + contents: read + +jobs: + main: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: pnpm/action-setup@v2 + + # This enables task distribution via Nx Cloud + # Run this command as early as possible, before dependencies are installed + # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun + # Connect your workspace by running "nx connect" and uncomment this + # - run: pnpm dlx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build" + # env: + # NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} + # Cache node_modules + - uses: actions/setup-node@v3 + with: + node-version: 20 + cache: "pnpm" + + - run: pnpm install --frozen-lockfile + - uses: nrwl/nx-set-shas@v4 + env: + NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} + # Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud + # - run: pnpm exec nx-cloud record -- echo Hello World + # Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected + - run: make test_all + env: + NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} + + - name: Upload test results to Codecov + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload coverage reports to Codecov + run: make codecov + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/Makefile b/Makefile index 8a039e0..4b6957d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ test: pnpm exec nx affected -t lint test build +test_all: + pnpm exec nx run-many --target=test --all + codecov_install: pip install codecov-cli