Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(h5p-mongos3): library storage includes library.json in exports #1575

Merged
merged 2 commits into from
Jul 10, 2021
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
71 changes: 68 additions & 3 deletions packages/h5p-mongos3/src/MongoLibraryStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,23 @@ export default class MongoLibraryStorage implements ILibraryStorage {
}

if (!fileData) {
// The library.json file is in the MongoDB document as binary JSON,
// so we get it from there
if (file === 'library.json') {
const metadata = JSON.stringify(
await this.getMetadata(library)
);
const readable = new ReadableStreamBuffer();
readable.put(metadata, 'utf-8');
readable.stop();
return readable;
}
throw new H5pError(
'library-file-missing',
{ ubername: LibraryName.toUberName(library), filename: file },
{
ubername: LibraryName.toUberName(library),
filename: file
},
404
);
} else {
Expand Down Expand Up @@ -613,7 +627,12 @@ export default class MongoLibraryStorage implements ILibraryStorage {
try {
result = await this.mongodb.findOne(
{ _id: LibraryName.toUberName(library) },
{ projection: { metadata: 1, additionalMetadata: 1 } }
{
projection: {
metadata: 1,
additionalMetadata: 1
}
}
);
} catch (error) {
throw new H5pError(
Expand Down Expand Up @@ -725,7 +744,7 @@ export default class MongoLibraryStorage implements ILibraryStorage {
);
}

files = result.files.map((f) => f.filename);
files = result.files.map((f) => f.filename).concat(['library.json']);
log.debug(`Found ${files.length} file(s) in MongoDB.`);
return files;
}
Expand Down Expand Up @@ -831,6 +850,52 @@ export default class MongoLibraryStorage implements ILibraryStorage {
});
}

/**
* Gets the the metadata of a library. In contrast to getLibrary this is
* only the metadata.
* @param library the library
* @returns the metadata about the locally installed library
*/
private async getMetadata(
library: ILibraryName
): Promise<ILibraryMetadata> {
if (!library) {
throw new Error('You must pass in a library name to getLibrary.');
}
let result;

try {
result = await this.mongodb.findOne(
{ _id: LibraryName.toUberName(library) },
{
projection: {
metadata: 1
}
}
);
} catch (error) {
console.log(error);
throw new H5pError(
'mongo-library-storage:error-getting-library-metadata',
{ ubername: LibraryName.toUberName(library) }
);
}
if (!result) {
throw new H5pError(
'mongo-library-storage:library-not-found',
{ ubername: LibraryName.toUberName(library) },
404
);
}
if (!result.metadata) {
throw new H5pError(
'mongo-library-storage:error-getting-library-metadata',
{ ubername: LibraryName.toUberName(library) }
);
}
return result.metadata;
}

private async readableToBuffer(readable: Readable): Promise<Buffer> {
return new Promise((resolve) => {
const chunks = [];
Expand Down
72 changes: 67 additions & 5 deletions packages/h5p-mongos3/src/MongoS3LibraryStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import AWS from 'aws-sdk';
import { Readable } from 'stream';
import * as path from 'path';
import { PromiseResult } from 'aws-sdk/lib/request';

import { ReadableStreamBuffer } from 'stream-buffers';
import {
IAdditionalLibraryMetadata,
IFileStats,
Expand Down Expand Up @@ -159,7 +159,9 @@ export default class MongoS3LibraryStorage implements ILibraryStorage {
}
);
}
const filesToDelete = await this.listFiles(library);
const filesToDelete = await this.listFiles(library, {
withMetadata: false
});
// S3 batch deletes only work with 1000 files at a time, so we
// might have to do this in several requests.
try {
Expand Down Expand Up @@ -459,6 +461,15 @@ export default class MongoS3LibraryStorage implements ILibraryStorage {
): Promise<Readable> {
validateFilename(file, this.options?.invalidCharactersRegexp);

// As the metadata is not S3, we need to get it from MongoDB.
if (file === 'library.json') {
const metadata = JSON.stringify(await this.getMetadata(library));
const readable = new ReadableStreamBuffer();
readable.put(metadata, 'utf-8');
readable.stop();
return readable;
}

return this.s3
.getObject({
Bucket: this.options.s3Bucket,
Expand Down Expand Up @@ -626,10 +637,15 @@ export default class MongoS3LibraryStorage implements ILibraryStorage {

/**
* Gets a list of all library files that exist for this library.
* @param library
* @param library the library name
* @param withMetadata true if the 'library.json' file should be included in
* the list
* @returns all files that exist for the library
*/
public async listFiles(library: ILibraryName): Promise<string[]> {
public async listFiles(
library: ILibraryName,
options: { withMetadata?: boolean } = { withMetadata: true }
): Promise<string[]> {
const prefix = this.getS3Key(library, '');
let files: string[] = [];
try {
Expand All @@ -655,7 +671,7 @@ export default class MongoS3LibraryStorage implements ILibraryStorage {
return [];
}
log.debug(`Found ${files.length} file(s) in S3.`);
return files;
return options?.withMetadata ? files.concat('library.json') : files;
}

/**
Expand Down Expand Up @@ -758,6 +774,52 @@ export default class MongoS3LibraryStorage implements ILibraryStorage {
});
}

/**
* Gets the the metadata of a library. In contrast to getLibrary this is
* only the metadata.
* @param library the library
* @returns the metadata about the locally installed library
*/
private async getMetadata(
library: ILibraryName
): Promise<ILibraryMetadata> {
if (!library) {
throw new Error('You must pass in a library name to getLibrary.');
}
let result;

try {
result = await this.mongodb.findOne(
{ _id: LibraryName.toUberName(library) },
{
projection: {
metadata: 1
}
}
);
} catch (error) {
console.log(error);
throw new H5pError(
'mongo-library-storage:error-getting-library-metadata',
{ ubername: LibraryName.toUberName(library) }
);
}
if (!result) {
throw new H5pError(
'mongo-library-storage:library-not-found',
{ ubername: LibraryName.toUberName(library) },
404
);
}
if (!result.metadata) {
throw new H5pError(
'mongo-library-storage:error-getting-library-metadata',
{ ubername: LibraryName.toUberName(library) }
);
}
return result.metadata;
}

private getS3Key(library: ILibraryName, filename: string): string {
return `${LibraryName.toUberName(library)}/${filename}`;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/h5p-mongos3/test/MongoLibraryStorage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,8 @@ describe('MongoS3LibraryStorage', () => {
).resolves.toMatchObject({ size: realStats.size });
await expect(storage.listFiles(example1Name)).resolves.toMatchObject([
'semantics.json',
'language/.en.json'
'language/.en.json',
'library.json'
]);
});

Expand Down
3 changes: 2 additions & 1 deletion packages/h5p-mongos3/test/MongoS3LibraryStorage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,8 @@ describe('MongoS3LibraryStorage', () => {
).resolves.toMatchObject({ size: realStats.size });
await expect(storage.listFiles(example1Name)).resolves.toMatchObject([
'language/.en.json',
'semantics.json'
'semantics.json',
'library.json'
]);
});

Expand Down
9 changes: 5 additions & 4 deletions scripts/mongo-s3-docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ services:
volumes:
- minio_data:/data
ports:
- '9000:9000'
command: ['server', '/data']
- 9000:9000
- 9001:9001
command: ['server', '--console-address', ':9001', '/data']
environment:
MINIO_ACCESS_KEY: minioaccesskey
MINIO_SECRET_KEY: miniosecret
MINIO_ROOT_USER: minioaccesskey
MINIO_ROOT_PASSWORD: miniosecret

volumes:
mongodb_data:
Expand Down