Skip to content

Commit

Permalink
PER-9255 Clear date on backend
Browse files Browse the repository at this point in the history
  • Loading branch information
iuliandanea committed Nov 28, 2024
1 parent a783d7c commit bf65dab
Show file tree
Hide file tree
Showing 13 changed files with 539 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/api/docs/present/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ paths:
$ref: "./paths/feature_flags.yaml#/feature-flags~1{id}"
/record/{id}:
$ref: "./paths/records.yaml#/record~1{id}"
/folder/{id}:
$ref: "./paths/folders.yaml#/folder~1{id}"
23 changes: 23 additions & 0 deletions packages/api/docs/present/models/folder.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
folder:
description: >
Folder object
type: object
required:
- folderId
- archiveNumber
- archiveId
- displayDate
- displayEndDate
properties:
folderId:
type: string
archiveNumber:
type: string
archiveId:
type: string
displayDate:
type: string
format: date-time
displayEndDate:
type: string
format: date-time
37 changes: 37 additions & 0 deletions packages/api/docs/present/paths/folders.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
folder/{id}:
patch:
parameters:
- name: id
in: path
required: true
schema:
type: integer
summary: Update a folder
security:
- bearerHttpAuthentication: []
operationId: patch-folder
requestBody:
content:
application/json:
schema:
type: object
properties:
archiveNumber:
type: string
displayDate:
type: string
format: date-time
responses:
"200":
description: The updated folder
content:
application/json:
schema:
type: object
properties:
data:
$ref: "../models/folder.yaml#/folder"
"400":
$ref: "../../shared/errors.yaml#/400"
"500":
$ref: "../../shared/errors.yaml#/500"
128 changes: 128 additions & 0 deletions packages/api/src/folder/controller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import type { Request, NextFunction } from "express";
import { logger } from "@stela/logger";
import request from "supertest";
import { app } from "../app";
import { db } from "../database";
import {
extractUserEmailFromAuthToken,
verifyUserAuthentication,
} from "../middleware";

jest.mock("../database");
jest.mock("../middleware");
jest.mock("@stela/logger");

const setupDatabase = async (): Promise<void> => {
await db.sql("fixtures.create_test_folders");
};

const clearDatabase = async (): Promise<void> => {
await db.query(
`TRUNCATE
folder CASCADE`
);
};

describe("patch folder", () => {
const agent = request(app);

beforeEach(async () => {
(verifyUserAuthentication as jest.Mock).mockImplementation(
async (
req: Request<
unknown,
unknown,
{ userSubjectFromAuthToken?: string; emailFromAuthToken?: string }
>,
__,
next: NextFunction
) => {
req.body.emailFromAuthToken = "test+1@permanent.org";
req.body.userSubjectFromAuthToken =
"b5461dc2-1eb0-450e-b710-fef7b2cafe1e";

next();
}
);
await clearDatabase();
await setupDatabase();
});

afterEach(async () => {
await clearDatabase();
jest.restoreAllMocks();
jest.clearAllMocks();
});

test("expect an empty query to cause a 400 error", async () => {
await agent.patch("/api/v2/folder/1").send({}).expect(400);
});

test("expect non existent record to cause a 404 error", async () => {
await agent
.patch("/api/v2/folder/111111111")
.send({ displayDate: "2024-09-26T15:09:52.000Z" })
.expect(404);
});

test("expect request to have an email from auth token if an auth token exists", async () => {
(extractUserEmailFromAuthToken as jest.Mock).mockImplementation(
(req: Request, __, next: NextFunction) => {
(req.body as { emailFromAuthToken: string }).emailFromAuthToken =
"not an email";
next();
}
);

await agent.patch("/api/v2/folder/1").expect(400);
});

test("expect displayDate is updated", async () => {
await agent.patch("/api/v2/folder/1").send({ displayDate: '2024-09-26T15:09:52' }).expect(200);

const result = await db.query(
"SELECT to_char(displaydt, 'YYYY-MM-DD HH24:MI:SS') as displaydt FROM folder WHERE folderid = :folderId",
{
folderId: 1,
}
);

expect(result.rows[0]).toEqual({ displaydt: '2024-09-26 15:09:52' });
});

test("expect displayDate is updated when set to null", async () => {
await agent
.patch("/api/v2/folder/1")
.send({ displayDate: null })
.expect(200);

const result = await db.query(
"SELECT displaydt FROM folder WHERE folderid = :folderId",
{
folderId: 1,
}
);

expect(result.rows[0]).toStrictEqual({ displaydt: null });
});

test("expect 400 error if display date is wrong type", async () => {
await agent
.patch("/api/v2/folder/1")
.send({
displayDate: false,
})
.expect(400);
});

test("expect to log error and return 500 if database update fails", async () => {
const testError = new Error("test error");
jest.spyOn(db, "sql").mockImplementation(async () => {
throw testError;
});

await agent.patch("/api/v2/folder/1").send({ displayDate: "2024-09-26T15:09:52.000Z" }).expect(500);

expect(logger.error).toHaveBeenCalledWith(testError);
});
});
38 changes: 38 additions & 0 deletions packages/api/src/folder/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
Router,
type Request,
type Response,
type NextFunction,
} from "express";
import { verifyUserAuthentication } from "../middleware";
import { getFolderById, patchFolder } from "./service";
import {
validatePatchFolderRequest,
validateFolderRequest,
} from "./validators";
import { isValidationError } from "../validators/validator_util";

export const folderController = Router();

folderController.patch(
"/:folderId",
verifyUserAuthentication,
async (req: Request, res: Response, next: NextFunction) => {
try {
validateFolderRequest(req.params);
validatePatchFolderRequest(req.body);
const folderId = await patchFolder(req.params.folderId, req.body);
const folder = await getFolderById({
folderId,
});

res.status(200).send({ data: folder });
} catch (err) {
if (isValidationError(err)) {
res.status(400).json({ error: err.message });
return;
}
next(err);
}
}
);
14 changes: 14 additions & 0 deletions packages/api/src/folder/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { PatchFolderRequest, FolderColumnsForUpdate } from "./models";

export function requestFieldsToDatabaseFields(
request: PatchFolderRequest,
folderId: string
): FolderColumnsForUpdate {
return {
archivenbr: request.archiveNumber,
archiveid: request.archiveId,
displaydt: request.displayDate,
displayenddt: request.displayEndDate,
folderId,
};
}
1 change: 1 addition & 0 deletions packages/api/src/folder/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { folderController } from "./controller";
107 changes: 107 additions & 0 deletions packages/api/src/folder/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
export interface Folder {
folderId: string;
archiveNumber: string;
archiveId: string;
displayName: string;
downloadName?: string;
downloadNameOk?: boolean;
displayDate?: string;
displayEndDate?: string;
timeZoneId?: bigint;
note?: string;
description?: string;
special?: string;
sort?: string;
locationId?: bigint;
view?: string;
viewProperty?: string;
thumbArchiveNumber?: string;
imageRatio?: number;
thumbStatus?: string;
thumbUrl200?: string;
thumbUrl500?: string;
thumbUrl1000?: string;
thumbUrl2000?: string;
status: FolderStatus;
type: FolderType;
publicAt: string;
createdAt: string;
updatedAt?: string;
thumbnail256?: string;
thumbnail256CloudPath?: string;
}

export interface FolderRow {
folderId: string;
archiveNumber: string;
archiveId: string;
displayName: string;
downloadName?: string;
downloadNameOk?: boolean;
displayDate?: string;
displayEndDate?: string;
timeZoneId?: bigint;
note?: string;
description?: string;
special?: string;
sort?: string;
locationId?: bigint;
view?: string;
viewProperty?: string;
thumbArchiveNumber?: string;
imageRatio?: number;
thumbStatus?: string;
thumbUrl200?: string;
thumbUrl500?: string;
thumbUrl1000?: string;
thumbUrl2000?: string;
status: FolderStatus;
type: FolderType;
publicAt: string;
createdAt: string;
updatedAt?: string;
thumbnail256?: string;
thumbnail256CloudPath?: string;
}

export interface FolderColumnsForUpdate {
folderId: string;
archivenbr: string;
archiveid: bigint;
displaydt: string;
displayenddt: string;
}

export interface PatchFolderRequest {
emailFromAuthToken: string;
archiveNumber: string;
archiveId: bigint;
displayDate: string;
displayEndDate: string;
}

export enum FolderStatus {
Deleted = "status.generic.deleted",
Error = "status.generic.error",
ManualReview = "status.generic.manual_review",
Ok = "status.generic.ok",
Copying = "status.folder.copying",
Moving = "status.folder.moving",
New = "status.folder.new",
ThumbnailGenerating = "status.folder.genthumb",
ThumbnailBroken = "status.folder.broken_thumbnail",
NoThumbnailCandidates = "status.folder.no_thumbnail_candidates",
Nested = "status.folder.nested",
Empty = "status.folder.empty",
}

export enum FolderType {
App = "type.folder.app",
Private = "type.folder.private",
Public = "type.folder.public",
Share = "type.folder.share",
RootApp = "type.folder.root.app",
RootPrivate = "type.folder.root.private",
RootPublic = "type.folder.root.public",
RootRoot = "type.folder.root.root",
}
9 changes: 9 additions & 0 deletions packages/api/src/folder/queries/get_folder_by_id.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SELECT
folder.folderid AS "folderId",
folder.archivenbr AS "archiveNumber",
folder.archiveid AS "archiveId",
folder.displaydt AS "displayDate",
folder.displayenddt AS "displayEndDate"
FROM
folder
WHERE folder.folderid = :folderId
Loading

0 comments on commit bf65dab

Please sign in to comment.