Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/api/docs/src/paths/archive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ archives-id-folders-shared:
required: true
schema:
type: string
- name: cursor
description: |
The id of the folder right before the first folder you want returned.
In most cases, this will be that of the last folder on the previous page.
in: query
required: false
schema:
type: string
- name: pageSize
in: query
required: true
schema:
type: integer
get:
summary: Get all the top-level shared folders in an archive
security:
Expand All @@ -132,6 +145,8 @@ archives-id-folders-shared:
type: array
items:
$ref: "../models/folder.yaml#/folder"
pagination:
$ref: "../models/pagination_metadata.yaml#/pagination"
"400":
$ref: "../errors.yaml#/400"
"401":
Expand Down
15 changes: 15 additions & 0 deletions packages/api/docs/src/paths/folder.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ folders:
type: array
items:
type: string
- name: cursor
description: |
The id of the folder right before the first folder you want returned.
In most cases, this will be that of the last folder on the previous page.
in: query
required: false
schema:
type: string
- name: pageSize
in: query
required: true
schema:
type: integer
get:
summary: Get folders by their IDs
security:
Expand All @@ -28,6 +41,8 @@ folders:
type: array
items:
$ref: "../models/folder.yaml#/folder"
pagination:
$ref: "../models/pagination_metadata.yaml#/pagination"
"400":
$ref: "../errors.yaml#/400"
"401":
Expand Down
15 changes: 15 additions & 0 deletions packages/api/docs/src/paths/record.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ records:
At least one of recordIds or archiveId must be provided.
schema:
type: string
- name: cursor
description: |
The id of the record right before the first record you want returned.
In most cases, this will be that of the last record on the previous page.
in: query
required: false
schema:
type: string
- name: pageSize
in: query
required: true
schema:
type: integer
security:
- {}
- bearerHttpAuthentication: []
Expand All @@ -39,6 +52,8 @@ records:
type: array
items:
$ref: "../models/record.yaml#/record"
pagination:
$ref: "../models/pagination_metadata.yaml#/pagination"
"400":
$ref: "../errors.yaml#/400"
"401":
Expand Down
16 changes: 16 additions & 0 deletions packages/api/docs/src/paths/share_link.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,20 @@ share-link:
type: array
items:
type: string
- name: cursor
description: |
The id of the share link right before the first share link you want returned.
In most cases, this will be that of the last share link on the previous page.
in: query
required: false
schema:
type: string
- name: pageSize
description: Defaults to 10 if not provided.
in: query
required: false
schema:
type: integer
summary: Retrieve a share link by ID or token
security:
- {}
Expand All @@ -107,6 +121,8 @@ share-link:
type: array
items:
$ref: "../models/share_link.yaml#/shareLink"
pagination:
$ref: "../models/pagination_metadata.yaml#/pagination"
"400":
$ref: "../errors.yaml#/400"
"401":
Expand Down
10 changes: 8 additions & 2 deletions packages/api/src/archive/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
validateBodyFromAuthentication,
validateSearchQuery,
validatePatchArchiveBody,
validateGetSharedFoldersQuery,
} from "../validators";
import { archiveService } from "../service";
import { HTTP_STATUS } from "@pdc/http-status-codes";
Expand Down Expand Up @@ -171,11 +172,16 @@ archiveController.get(
try {
validateArchiveIdFromParams(req.params);
validateBodyFromAuthentication(req.body);
const folders = await archiveService.getSharedFolders(
validateGetSharedFoldersQuery(req.query);
const response = await archiveService.getSharedFolders(
req.params.archiveId,
req.body.emailFromAuthToken,
{
pageSize: req.query.pageSize,
cursor: req.query.cursor,
},
);
res.json({ items: folders });
res.json(response);
} catch (err) {
next(err);
}
Expand Down
82 changes: 69 additions & 13 deletions packages/api/src/archive/controller/get_shared_folders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { logger } from "@stela/logger";
import { app } from "../../app";
import { verifyUserAuthentication } from "../../middleware";
import { db } from "../../database";
import type { Folder } from "../../folder/models";
import type { GetSharedFoldersResponse } from "../models";
import { mockVerifyUserAuthentication } from "../../../test/middleware_mocks";

vi.mock("../../database");
Expand Down Expand Up @@ -46,14 +46,13 @@ describe("getSharedFolders", () => {

test("should return shared folders for an archive", async () => {
const response = await agent
.get(`/api/v2/archive/2/folders/shared`)
.get(`/api/v2/archive/2/folders/shared?pageSize=100`)
.expect(200);

const {
body: { items: folders },
} = response as { body: { items: Folder[] } };
expect(folders.length).toBe(1);
expect(folders[0]?.folderId).toBe("3");
} = response as { body: GetSharedFoldersResponse };
expect(folders.map((folder) => folder.folderId)).toEqual(["3", "200"]);
});

test("should return 401 when not authenticated", async () => {
Expand All @@ -63,18 +62,28 @@ describe("getSharedFolders", () => {
},
);

await agent.get(`/api/v2/archive/2/folders/shared`).expect(401);
await agent
.get(`/api/v2/archive/2/folders/shared?pageSize=100`)
.expect(401);
});

test("should return 400 if the header data is missing", async () => {
mockVerifyUserAuthentication();
await agent
.get(`/api/v2/archive/2/folders/shared?pageSize=100`)
.expect(400);
});

test("should return 400 if pageSize is missing", async () => {
await agent.get(`/api/v2/archive/2/folders/shared`).expect(400);
});

test("should return 500 if database query fails", async () => {
const testError = new Error("error: database connection lost");
vi.spyOn(db, "sql").mockRejectedValueOnce(testError);
await agent.get(`/api/v2/archive/2/folders/shared`).expect(500);
await agent
.get(`/api/v2/archive/2/folders/shared?pageSize=100`)
.expect(500);
expect(logger.error).toHaveBeenCalledWith(testError);
});

Expand All @@ -84,34 +93,81 @@ describe("getSharedFolders", () => {
"553f3cb8-b753-43ce-83af-4443a404741b",
);
const response = await agent
.get(`/api/v2/archive/2/folders/shared`)
.get(`/api/v2/archive/2/folders/shared?pageSize=100`)
.expect(200);

const {
body: { items: folders },
} = response as { body: { items: Folder[] } };
} = response as { body: GetSharedFoldersResponse };
expect(folders.length).toBe(0);
});

test("should not return folders where the share has been deleted", async () => {
const response = await agent
.get(`/api/v2/archive/1/folders/shared`)
.get(`/api/v2/archive/1/folders/shared?pageSize=100`)
.expect(200);

const {
body: { items: folders },
} = response as { body: { items: Folder[] } };
} = response as { body: GetSharedFoldersResponse };
expect(folders.length).toBe(0);
});

test("should not return folders where archive membership has been deleted", async () => {
const response = await agent
.get(`/api/v2/archive/3/folders/shared`)
.get(`/api/v2/archive/3/folders/shared?pageSize=100`)
.expect(200);

const {
body: { items: folders },
} = response as { body: { items: Folder[] } };
} = response as { body: GetSharedFoldersResponse };
expect(folders.length).toBe(0);
});

test("expect no more than pageSize items to be returned", async () => {
const response = await agent
.get(`/api/v2/archive/2/folders/shared?pageSize=1`)
.expect(200);

const { body } = response as { body: GetSharedFoldersResponse };
expect(body.items).toHaveLength(1);
expect(body.items[0]?.folderId).toEqual("3");
expect(body.pagination.totalPages).toEqual(2);
expect(body.pagination.nextCursor).toEqual("3");
});

test("expect to page through all shared folders via cursor, in ascending folderId order", async () => {
const firstResponse = await agent
.get(`/api/v2/archive/2/folders/shared?pageSize=1`)
.expect(200);
const { body: firstPage } = firstResponse as {
body: GetSharedFoldersResponse;
};
expect(firstPage.items.map((folder) => folder.folderId)).toEqual(["3"]);
expect(firstPage.pagination.totalPages).toEqual(2);
expect(firstPage.pagination.nextCursor).toBeDefined();

const secondResponse = await agent
.get(
`/api/v2/archive/2/folders/shared?pageSize=1&cursor=${firstPage.pagination.nextCursor}`,
)
.expect(200);
const { body: secondPage } = secondResponse as {
body: GetSharedFoldersResponse;
};
expect(secondPage.items.map((folder) => folder.folderId)).toEqual(["200"]);
expect(secondPage.pagination.totalPages).toEqual(2);
});

test("expect pagination.nextPage to link to the next page with the same filters", async () => {
const response = await agent
.get(`/api/v2/archive/2/folders/shared?pageSize=1`)
.expect(200);
const { body } = response as { body: GetSharedFoldersResponse };
expect(body.pagination.nextPage).toEqual(
`https://${process.env["SITE_URL"] ?? ""}/api/v2/archives/2/folders/shared?pageSize=1&cursor=${
body.pagination.nextCursor ?? ""
}`,
);
});
});
12 changes: 12 additions & 0 deletions packages/api/src/archive/fixtures/create_test_folder_links.sql
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,16 @@ VALUES
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
'access.role.owner'
),
(
10,
200,
2,
11,
0,
'status.generic.ok',
'type.folder_link.folder',
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
'access.role.owner'
);
12 changes: 12 additions & 0 deletions packages/api/src/archive/fixtures/create_test_folders.sql
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,16 @@ VALUES
CURRENT_TIMESTAMP,
'type.folder.root.root',
NULL
),
(
200,
2,
NULL,
'Second Shared Folder',
'Second Shared Folder',
NULL,
'status.generic.ok',
CURRENT_TIMESTAMP,
'type.folder.private',
NULL
);
12 changes: 12 additions & 0 deletions packages/api/src/archive/fixtures/create_test_shares.sql
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,16 @@ VALUES
0,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
10,
10,
2,
'access.role.viewer',
'status.generic.ok',
'type.share.folder',
NULL,
0,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
10 changes: 10 additions & 0 deletions packages/api/src/archive/models.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ArchiveMembershipRole } from "../access/models";
import type { Folder } from "../folder/models";

export { ArchiveMembershipRole };

Expand Down Expand Up @@ -79,3 +80,12 @@ export interface GetArchivesResponse {
totalPages: number;
};
}

export interface GetSharedFoldersResponse {
items: Folder[];
pagination: {
nextCursor: string | undefined;
nextPage: string | undefined;
totalPages: number;
};
}
Loading