Skip to content
Open
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
57 changes: 57 additions & 0 deletions src/services/item/item.controller.read.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,63 @@ describe('Item routes tests', () => {
data.forEach((i) => expectThumbnails(i, MOCK_SIGNED_URL, false));
});

it('Returns file URL only for file children', async () => {
const {
actor,
items: [parentItem],
} = await seedFromJson({
items: [
{
memberships: [{ account: 'actor', permission: 'admin' }],
children: [
{
type: 'file',
extra: {
file: {
name: 'file.pdf',
path: 'files/file.pdf',
mimetype: 'application/pdf',
size: 123,
content: '',
},
},
},
{
type: 'document',
extra: {
document: {
content: 'Some document content',
},
},
},
{},
],
},
],
});
assertIsDefined(actor);
assertIsMemberForTest(actor);
mockAuthenticate(actor);

const response = await app.inject({
method: HttpMethod.Get,
url: `/api/items/${parentItem.id}/children`,
});

const data = response.json<PackedItem[]>();
expect(response.statusCode).toBe(StatusCodes.OK);

const fileChild = data.find(
(item): item is Extract<PackedItem, { type: 'file' }> => item.type === 'file',
);
const documentChild = data.find((item) => item.type === 'document');
const folderChild = data.find((item) => item.type === 'folder');

expect(fileChild?.extra.file.url).toEqual(MOCK_SIGNED_URL);
expect(documentChild?.extra).not.toHaveProperty('file');
expect(folderChild?.extra).not.toHaveProperty('file');
});

it('Returns a child h5p successfully', async () => {
const {
actor,
Expand Down
12 changes: 10 additions & 2 deletions src/services/item/item.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
getParentFromPath,
} from '@graasp/sdk';

import { resolveDependency } from '../../di/utils';
import { type DBConnection } from '../../drizzle/db';
import {
type ItemGeolocationRaw,
Expand Down Expand Up @@ -45,6 +46,7 @@ import {
filterOutPackedItems,
} from '../authorization.utils';
import { AuthorizedItemService } from '../authorizedItem.service';
import FileService from '../file/file.service';
import { ItemMembershipRepository } from '../itemMembership/membership.repository';
import { ThumbnailService } from '../thumbnail/thumbnail.service';
import { DEFAULT_ORDER, IS_COPY_REGEX, MAX_COPY_SUFFIX_LENGTH } from './constants';
Expand Down Expand Up @@ -540,7 +542,7 @@ export class ItemService {
children,
thumbnails,
);
return filteredChildren.map((children) => this.transformItemByType(children));
return Promise.all(filteredChildren.map((child) => this.transformItemByType(child)));
}

async getDescendants(
Expand Down Expand Up @@ -1050,7 +1052,8 @@ export class ItemService {
}
}

private transformItemByType(item: PackedItem) {
// Add response-only data that depends on the item type
private async transformItemByType(item: PackedItem): Promise<PackedItem> {
switch (item.type) {
case 'h5p': {
const { h5p: h5pExtraProperties } = item.extra as H5PItemExtra;
Expand All @@ -1065,6 +1068,11 @@ export class ItemService {
};
return { ...item, extra: newExtra };
}
case 'file': {
const fileService = resolveDependency(FileService);
const url = await fileService.getUrl({ path: item.extra.file.path });
return { ...item, extra: { ...item.extra, file: { ...item.extra.file, url } } };
}
default:
return item;
}
Expand Down
1 change: 1 addition & 0 deletions src/services/item/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type FileItem = Omit<ItemRaw, 'type'> & {
path: string;
mimetype: string;
size: number;
url?: string;
altText?: string;
content?: string;
/** @deprecated */
Expand Down
1 change: 1 addition & 0 deletions src/services/item/plugins/file/itemFile.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const fileItemSchema = Type.Composite(
path: Type.String(),
mimetype: Type.String(),
size: Type.Integer({ minimum: 0 }),
url: Type.Optional(Type.String()),
altText: Type.Optional(
Type.String({
description: 'alternative text of the file if it is an image',
Expand Down
Loading