From 532ba6fb23dab72418632dcb1ee1ed85f662f0a1 Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Sun, 13 Dec 2020 16:01:40 +0000 Subject: [PATCH 01/10] refactor: Restructures deleteActivityProfile --- package.json | 1 + src/apps/AppConfig.ts | 6 ++ src/apps/activities/AppConfig.ts | 6 ++ .../deleteActivityProfile.ts | 55 +++++++++++ .../deleteActivityProfileContent.ts | 28 ++++++ .../deleteActivityProfileContentFromAzure.ts | 34 +++++++ .../deleteActivityProfileContentFromFS.ts | 16 +++ .../deleteActivityProfileContentFromGoogle.ts | 21 ++++ .../deleteActivityProfileContentFromS3.ts | 27 ++++++ .../deleteActivityProfileFromMongo.ts | 76 +++++++++++++++ .../deleteActivityProfileViaHttp.ts | 38 ++++++++ .../utils/HttpInterfaces.ts | 22 +++++ .../deleteActivityProfile/utils/JsonValue.ts | 4 + .../utils/createExpressHandler.ts | 63 ++++++++++++ .../utils/getClient/getClient.ts | 29 ++++++ .../utils/getClient/getClientFromMongo.ts | 97 +++++++++++++++++++ .../utils/getClient/trackClientUsage.ts | 19 ++++ .../getClient/trackClientUsageInNewRelic.ts | 8 ++ .../getFileStorageConfig/FileStorageConfig.ts | 40 ++++++++ .../getFileStorageConfig.ts | 33 +++++++ .../RecordStorageConfig.ts | 9 ++ .../getRecordStorageConfig.ts | 12 +++ .../utils/getTrackingConfig/TrackingConfig.ts | 14 +++ .../getTrackingConfig/getTrackingConfig.ts | 13 +++ .../MongoActivityProfileDoc.ts | 18 ++++ .../mongoDocInterfaces/MongoClientDoc.ts | 17 ++++ .../utils/mongoDocInterfaces/MongoLrsDoc.ts | 5 + .../mongoDocInterfaces/MongoOAuthTokenDoc.ts | 7 ++ .../utils/mongoDocInterfaces/MongoOrgDoc.ts | 6 ++ src/apps/activities/app.ts | 3 + .../activities/expressPresenter/Config.ts | 6 ++ src/apps/activities/expressPresenter/index.ts | 6 +- src/apps/app.ts | 3 + src/server.ts | 6 ++ tslint.json | 3 +- yarn.lock | 5 + 36 files changed, 752 insertions(+), 4 deletions(-) create mode 100644 src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfile.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContent.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromAzure.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromFS.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromGoogle.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromS3.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/HttpInterfaces.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/JsonValue.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsage.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsageInNewRelic.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoActivityProfileDoc.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoClientDoc.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoLrsDoc.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOAuthTokenDoc.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOrgDoc.ts diff --git a/package.json b/package.json index 4495ec225..e20736e92 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "@types/mime-types": "2.1.0", "@types/mocha": "5.2.7", "@types/mongodb": "3.5.32", + "@types/newrelic": "6.13.0", "@types/node": "9.6.61", "@types/node-fetch": "2.5.7", "@types/object-hash": "1.3.4", diff --git a/src/apps/AppConfig.ts b/src/apps/AppConfig.ts index d97c6283c..6ab2f2a71 100644 --- a/src/apps/AppConfig.ts +++ b/src/apps/AppConfig.ts @@ -3,6 +3,9 @@ import { Redis } from 'ioredis'; import Tracker from 'jscommons/dist/tracker/Tracker'; import { Db } from 'mongodb'; import { LoggerInstance } from 'winston'; +import { FileStorageConfig } from './activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig'; +import { MongoRecordStorageConfig } from './activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig'; +import { TrackingConfig } from './activities/_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; export default interface AppConfig { readonly repo: { @@ -77,4 +80,7 @@ export default interface AppConfig { }; readonly tracker: Promise; readonly logger: LoggerInstance; + readonly fileStorageConfig: FileStorageConfig; + readonly recordStorageConfig: MongoRecordStorageConfig; + readonly trackingConfig: TrackingConfig; } diff --git a/src/apps/activities/AppConfig.ts b/src/apps/activities/AppConfig.ts index d5d56783d..5ed6e1111 100644 --- a/src/apps/activities/AppConfig.ts +++ b/src/apps/activities/AppConfig.ts @@ -2,6 +2,9 @@ import S3 from 'aws-sdk/clients/s3'; import Tracker from 'jscommons/dist/tracker/Tracker'; import { Db } from 'mongodb'; import { LoggerInstance } from 'winston'; +import { FileStorageConfig } from './_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig'; +import { TrackingConfig } from './_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; +import { MongoRecordStorageConfig } from './_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig'; export default interface AppConfig { readonly logger: LoggerInstance; @@ -40,4 +43,7 @@ export default interface AppConfig { readonly db: () => Promise; }; }; + readonly fileStorageConfig: FileStorageConfig; + readonly recordStorageConfig: MongoRecordStorageConfig; + readonly trackingConfig: TrackingConfig; } diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfile.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfile.ts new file mode 100644 index 000000000..0e3dc56b9 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfile.ts @@ -0,0 +1,55 @@ +import NoModel from 'jscommons/dist/errors/NoModel'; +import ClientModel from '../../../statements/models/ClientModel'; +import checkProfileWriteScopes from '../../service/utils/checkProfileWriteScopes'; +import validateActivityId from '../../service/utils/validateActivityId'; +import { jsonContentType } from '../../utils/constants'; +import { deleteActivityProfileContent } from './deleteActivityProfileContent'; +import { deleteActivityProfileFromMongo } from './deleteActivityProfileFromMongo'; +import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; +import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; + +interface DeleteActivityProfileConfig { + readonly recordStorageConfig: MongoRecordStorageConfig; + readonly fileStorageConfig: FileStorageConfig; +} + +interface DeleteActivityProfileOptions { + readonly activityId: string; + readonly client: ClientModel; + readonly profileId: string; + readonly ifMatch?: string; +} + +export async function deleteActivityProfile( + config: DeleteActivityProfileConfig, + opts: DeleteActivityProfileOptions, +): Promise { + const client = opts.client; + checkProfileWriteScopes(client.scopes); + validateActivityId(opts.activityId); + + try { + const deleteResult = await deleteActivityProfileFromMongo(config.recordStorageConfig, { + activityId: opts.activityId, + lrs_id: opts.client.lrs_id, + organisation: opts.client.organisation, + ifMatch: opts.ifMatch, + profileId: opts.profileId, + }); + + if (deleteResult.contentType === jsonContentType) { + return; + } + + await deleteActivityProfileContent(config.fileStorageConfig, { + key: `${deleteResult.id}.${deleteResult.extension}`, + lrs_id: client.lrs_id, + }); + } catch (err) { + // If no profile was found, we will handle this as a 204 instead + if (err instanceof NoModel) { + return; + } + throw err; + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContent.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContent.ts new file mode 100644 index 000000000..41a520498 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContent.ts @@ -0,0 +1,28 @@ +import { deleteActivityProfileContentFromAzure } from './deleteActivityProfileContentFromAzure'; +import { deleteActivityProfileContentFromFS } from './deleteActivityProfileContentFromFS'; +import { deleteActivityProfileContentFromGoogle } from './deleteActivityProfileContentFromGoogle'; +import { deleteActivityProfileContentFromS3 } from './deleteActivityProfileContentFromS3'; +import { FileStorageConfig, FileStorageProvider } from './utils/getFileStorageConfig/FileStorageConfig'; + +export interface DeleteActivityProfileContentOptions { + readonly key: string; + readonly lrs_id: string; +} + +export async function deleteActivityProfileContent( + config: FileStorageConfig, + opts: DeleteActivityProfileContentOptions, +): Promise { + switch (config.fileStorageProvider) { + case FileStorageProvider.S3: + return deleteActivityProfileContentFromS3(config, opts); + case FileStorageProvider.Google: + return deleteActivityProfileContentFromGoogle(config, opts); + case FileStorageProvider.Azure: + return deleteActivityProfileContentFromAzure(config, opts); + default: + case FileStorageProvider.Local: { + return deleteActivityProfileContentFromFS(config, opts); + } + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromAzure.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromAzure.ts new file mode 100644 index 000000000..e55443ad8 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromAzure.ts @@ -0,0 +1,34 @@ +import { + Aborter, + BlobURL, + ContainerURL, + ServiceURL, + SharedKeyCredential, + StorageURL, +} from '@azure/storage-blob'; +import getStorageDir from '../../utils/getStorageDir'; +import { DeleteActivityProfileContentOptions } from './deleteActivityProfileContent'; +import { AzureFileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; + +export async function deleteActivityProfileContentFromAzure( + config: AzureFileStorageConfig, + opts: DeleteActivityProfileContentOptions, +): Promise { + const credential = new SharedKeyCredential( + config.azureAccount, + config.azureAccountKey, + ); + const pipeline = StorageURL.newPipeline(credential); + const serviceURL = new ServiceURL( + `https://${config.azureAccount}.blob.core.windows.net`, + pipeline, + ); + const containerUrl = ContainerURL.fromServiceURL(serviceURL, config.azureContainerName); + const profileDir = getStorageDir({ + subfolder: config.azureSubFolder, + lrs_id: opts.lrs_id, + }); + const filePath = `${profileDir}/${opts.key}`; + const blobUrl = BlobURL.fromContainerURL(containerUrl, filePath); + await blobUrl.delete(Aborter.none); +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromFS.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromFS.ts new file mode 100644 index 000000000..f03a905ee --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromFS.ts @@ -0,0 +1,16 @@ +import { unlink } from 'fs-extra'; +import getStorageDir from '../../utils/getStorageDir'; +import { DeleteActivityProfileContentOptions } from './deleteActivityProfileContent'; +import { LocalFileStorageConfig } from "./utils/getFileStorageConfig/FileStorageConfig"; + +export async function deleteActivityProfileContentFromFS( + config: LocalFileStorageConfig, + opts: DeleteActivityProfileContentOptions, +): Promise { + const profileDir = getStorageDir({ + subfolder: config.localStorageDir, + lrs_id: opts.lrs_id, + }); + const filePath = `${profileDir}/${opts.key}`; + await unlink(filePath); +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromGoogle.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromGoogle.ts new file mode 100644 index 000000000..2c2c92bd4 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromGoogle.ts @@ -0,0 +1,21 @@ +import Storage from '@google-cloud/storage'; +import getStorageDir from '../../utils/getStorageDir'; +import { DeleteActivityProfileContentOptions } from './deleteActivityProfileContent'; +import { GoogleFileStorageConfig } from "./utils/getFileStorageConfig/FileStorageConfig"; + +export async function deleteActivityProfileContentFromGoogle( + config: GoogleFileStorageConfig, + opts: DeleteActivityProfileContentOptions, +): Promise { + const storage = Storage({ + keyFilename: config.googleKeyFileName, + projectId: config.googleProjectId, + }); + const profileDir = getStorageDir({ + subfolder: config.googleSubFolder, + lrs_id: opts.lrs_id, + }); + const filePath = `${profileDir}/${opts.key}`; + const file = storage.bucket(config.googleBucketName).file(filePath); + await file.delete(); +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromS3.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromS3.ts new file mode 100644 index 000000000..c03083988 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromS3.ts @@ -0,0 +1,27 @@ +import { S3 } from 'aws-sdk'; +import getStorageDir from '../../utils/getStorageDir'; +import { DeleteActivityProfileContentOptions } from './deleteActivityProfileContent'; +import { S3FileStorageConfig } from "./utils/getFileStorageConfig/FileStorageConfig"; + +export async function deleteActivityProfileContentFromS3( + config: S3FileStorageConfig, + opts: DeleteActivityProfileContentOptions, +): Promise { + const client = new S3({ + accessKeyId: config.awsAccessKeyId, + secretAccessKey: config.awsSecretAccessKey, + region: config.awsRegion, + apiVersion: '2006-03-01', + signatureVersion: 'v4', + sslEnabled: true, + }); + const profileDir = getStorageDir({ + subfolder: config.s3SubFolder, + lrs_id: opts.lrs_id, + }); + const filePath = `${profileDir}/${opts.key}`; + await client.deleteObject({ + Bucket: config.s3BucketName, + Key: filePath, + }).promise(); +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts new file mode 100644 index 000000000..b16fcec7d --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts @@ -0,0 +1,76 @@ +import NoModel from 'jscommons/dist/errors/NoModel'; +import { MongoClient, ObjectID } from 'mongodb'; +import IfMatch from '../../errors/IfMatch'; +import { + MongoActivityProfileDoc, + mongoActivityProfilesCollectionName, +} from './utils/mongoDocInterfaces/MongoActivityProfileDoc'; +import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; + +interface DeleteActivityProfileRecordOptions { + readonly lrs_id: string; + readonly organisation: string; + readonly ifMatch?: string; + readonly activityId: string; + readonly profileId: string; +} + +interface DeleteActivityProfileRecordResult { + readonly id: string; + readonly contentType: string; + readonly extension: string; +} + +export async function deleteActivityProfileFromMongo( + config: MongoRecordStorageConfig, + opts: DeleteActivityProfileRecordOptions, +): Promise { + const client = new MongoClient(config.mongoUri, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); + await client.connect(); + const db = client.db(config.mongoDbName); + const collection = db.collection(mongoActivityProfilesCollectionName); + + // Docs: https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#concurrency + const etagFilter = ( + opts.ifMatch !== undefined + ? { etag: opts.ifMatch } + : {} + ); + + const profileFilter = { + activityId: opts.activityId, + lrs: new ObjectID(opts.lrs_id), + organisation: new ObjectID(opts.organisation), + profileId: opts.profileId, + }; + + // Deletes the document if it matches the profile and etag filters. + const opResult = await collection.findOneAndDelete({ + ...profileFilter, + ...etagFilter, + }); + + // Returns the result of the deletion if the document was deleted. + if (opResult.value !== undefined) { + const deletedDoc = opResult.value; + await client.close(); + return { + contentType: deletedDoc.contentType, + extension: deletedDoc.extension, + id: deletedDoc._id.toString(), + }; + } + + // Attempts to find document without the ETag filter to determine if there was an ETag error. + const foundDoc = await collection.findOne(profileFilter); + await client.close(); + if (foundDoc !== null) { + throw new IfMatch(); + } + + /* istanbul ignore next */ + throw new NoModel('Activity Profile'); +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts new file mode 100644 index 000000000..d54d1afdc --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts @@ -0,0 +1,38 @@ +import { NO_CONTENT } from 'http-status-codes'; +import getActivityId from '../../expressPresenter/utils/getActivityId'; +import getEtag from '../../expressPresenter/utils/getEtag'; +import getProfileId from '../../expressPresenter/utils/getProfileId'; +import validateVersionHeader from '../../expressPresenter/utils/validateVersionHeader'; +import { xapiHeaderVersion } from '../../utils/constants'; +import { deleteActivityProfile } from './deleteActivityProfile'; +import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; +import { getClient } from './utils/getClient/getClient'; +import { TrackingConfig } from './utils/getTrackingConfig/TrackingConfig'; +import { HttpRequest, HttpResponse } from './utils/HttpInterfaces'; +import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; + +export interface DeleteActivityProfileViaHttpConfig { + readonly recordStorageConfig: MongoRecordStorageConfig; + readonly fileStorageConfig: FileStorageConfig; + readonly trackingConfig: TrackingConfig; +} + +export async function deleteActivityProfileViaHttp( + config: DeleteActivityProfileViaHttpConfig, + req: HttpRequest, +): Promise { + const client = await getClient(config, req.headers.authorization); + validateVersionHeader(req.headers['x-experience-api-version']); + + const ifMatch = getEtag(req.headers['if-match']); + const activityId = getActivityId(req.query.activityId); + const profileId = getProfileId(req.query.profileId); + + await deleteActivityProfile(config, { activityId, client, profileId, ifMatch }); + return { + statusCode: NO_CONTENT, + headers: { + 'X-Experience-API-Version': xapiHeaderVersion, + }, + }; +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/HttpInterfaces.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/HttpInterfaces.ts new file mode 100644 index 000000000..2ad93fae3 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/HttpInterfaces.ts @@ -0,0 +1,22 @@ +export interface HttpHeaders { + readonly [headerName: string]: string; +} + +export interface HttpQueryParams { + readonly [queryParam: string]: string; +} + +export interface HttpRequest { + readonly requestId?: string; + readonly query: HttpQueryParams; + readonly body: NodeJS.ReadableStream; + readonly headers: HttpHeaders; +} + +export interface HttpResponse { + readonly statusCode: number; + readonly headers: HttpHeaders; + readonly body?: NodeJS.ReadableStream; +} + +export type HttpHandler = (config: Config, req: HttpRequest) => Promise; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/JsonValue.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/JsonValue.ts new file mode 100644 index 000000000..e67267df8 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/JsonValue.ts @@ -0,0 +1,4 @@ +interface JsonObject { + readonly [key: string]: JsonValue; +} +export type JsonValue = number | string | boolean | null | JsonValue[] | JsonObject; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts new file mode 100644 index 000000000..dcf0ec8ae --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts @@ -0,0 +1,63 @@ +import express from 'express'; +import { INTERNAL_SERVER_ERROR } from 'http-status-codes'; +import { v4 as createV4UUID } from 'uuid'; +import { HttpHandler, HttpHeaders, HttpQueryParams } from './HttpInterfaces'; + +function getExpressRequestHeaders(req: express.Request) { + return Object.keys(req.headers).reduce((result, header) => { + const headerValue = req.headers[header]; + if (typeof headerValue === 'string') { + return { ...result, [header]: headerValue }; + } else if (Array.isArray(headerValue)) { + return { ...result, [header]: headerValue[headerValue.length - 1] }; + } + return result; + }, {}); +} + +function getExpressRequestQuery(req: express.Request) { + return Object.keys(req.query).reduce((result, param) => { + const paramValue = req.query[param]; + if (typeof paramValue === 'string') { + return { ...result, [param]: paramValue }; + } else if (Array.isArray(paramValue)) { + const firstParamValue = paramValue[paramValue.length - 1]; + if (typeof firstParamValue === 'string') { + return { ...result, [param]: firstParamValue }; + } + return { ...result, [param]: JSON.stringify(firstParamValue) }; + } else if (typeof paramValue === 'object') { + return { ...result, [param]: JSON.stringify(paramValue) }; + } + return result; + }, {}); +} + +export function createExpressHandler(handler: HttpHandler) { + return (config: Config) => { + return async (req: express.Request, res: express.Response) => { + const requestId = createV4UUID(); + const headers = getExpressRequestHeaders(req); + const query = getExpressRequestQuery(req); + try { + const body = req; + const response = await handler(config, { requestId, query, body, headers }); + res.writeHead(response.statusCode, response.headers); + if (response.body !== undefined) { + response.body.pipe(res); + } + res.end(); + } catch (err) { + // tslint:disable-next-line: no-console + console.error({ + requestId, + errorMessage: err?.message, + requestHeaders: headers, + errorStack: err?.stack, + }); + res.status(INTERNAL_SERVER_ERROR); + res.send(`Internal Server Error ${requestId}`); + } + }; + }; +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts new file mode 100644 index 000000000..82f6cb266 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts @@ -0,0 +1,29 @@ +import NoModel from 'jscommons/dist/errors/NoModel'; +import Unauthorised from 'jscommons/dist/errors/Unauthorised'; +import { MongoRecordStorageConfig } from '../getRecordStorageConfig/RecordStorageConfig'; +import { getClientFromMongo } from './getClientFromMongo'; +import { track } from './trackClientUsage'; +import { TrackingConfig } from '../getTrackingConfig/TrackingConfig'; + +interface GetClientConfig { + readonly recordStorageConfig: MongoRecordStorageConfig; + readonly trackingConfig: TrackingConfig; +} + +export async function getClient(config: GetClientConfig, authToken = '') { + try { + const client = await getClientFromMongo(config.recordStorageConfig, authToken); + await track(config.trackingConfig, { + organisationId: client.organisation, + lrsId: client.lrs_id, + clientId: client._id, + }); + return client; + } catch (err) { + if (err instanceof NoModel) { + throw new Unauthorised(); + } + /* istanbul ignore next */ + throw err; + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts new file mode 100644 index 000000000..cb61d1ef8 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts @@ -0,0 +1,97 @@ +import atob from 'atob'; +import NoModel from 'jscommons/dist/errors/NoModel'; +import { Db, MongoClient } from 'mongodb'; +import ExpiredClientError from '../../../../errors/ExpiredClientError'; +import UntrustedClientError from '../../../../errors/UntrustedClientError'; +import { MongoRecordStorageConfig } from '../getRecordStorageConfig/RecordStorageConfig'; +import { mongoClientCollectionName, MongoClientDoc } from '../mongoDocInterfaces/MongoClientDoc'; +import { mongoLrsCollectionName, MongoLrsDoc } from '../mongoDocInterfaces/MongoLrsDoc'; +import { mongoOAuthTokenCollectionName, MongoOAuthTokenDoc } from '../mongoDocInterfaces/MongoOAuthTokenDoc'; +import { mongoOrgCollectionName, MongoOrgDoc } from '../mongoDocInterfaces/MongoOrgDoc'; + +async function findClientByAccessToken(db: Db, accessToken: string) { + const accessTokenCollection = db.collection(mongoOAuthTokenCollectionName); + const accessTokenDoc = await accessTokenCollection.findOne({ + accessToken, + }); + if (accessTokenDoc === null) { + return null; + } + const clientCollection = db.collection(mongoClientCollectionName); + const clientDoc = await clientCollection.findOne({ + _id: accessTokenDoc.clientId, + }); + return clientDoc; +} + +async function findClientByBasicAuth(db: Db, encodedBasicAuthToken: string) { + const decodedAuthToken = atob(encodedBasicAuthToken); + const splitAuthToken = decodedAuthToken.split(':'); + const [key, secret] = splitAuthToken; + const clientCollection = db.collection(mongoClientCollectionName); + const clientDoc = await clientCollection.findOne({ + 'api.basic_key': key, + 'api.basic_secret': secret, + }); + return clientDoc; +} + +async function findClientWithAuth(db: Db, authToken: string) { + const [authType, authValue] = authToken.split(' '); + switch (authType) { + case 'Basic': + return findClientByBasicAuth(db, authValue); + case 'Bearer': + return findClientByAccessToken(db, authValue); + default: + throw new NoModel('Client'); + } +} + +export async function getClientFromMongo(config: MongoRecordStorageConfig, authToken: string) { + const mongoClient = new MongoClient(config.mongoUri, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); + try { + const db = mongoClient.db(config.mongoDbName); + const clientDoc = await findClientWithAuth(db, authToken); + + if (clientDoc === null) { + throw new NoModel('Client'); + } + + if (!clientDoc.isTrusted) { + throw new UntrustedClientError(); + } + + const orgCollection = db.collection(mongoOrgCollectionName); + const lrsCollection = db.collection(mongoLrsCollectionName); + const orgDoc = await orgCollection.findOne({ _id: clientDoc.organisation }); + const lrsDoc = await lrsCollection.findOne({ _id: clientDoc.lrs_id }); + + if (orgDoc === null || lrsDoc === null) { + throw new NoModel('Client'); + } + + if (orgDoc.expiration !== null && orgDoc.expiration < new Date()) { + throw new ExpiredClientError(); + } + + const client = { + _id: clientDoc._id.toString(), + isTrusted: clientDoc.isTrusted, + lrs_id: clientDoc.lrs_id.toString(), + organisation: clientDoc.organisation.toString(), + scopes: clientDoc.scopes, + title: clientDoc.title, + authority: clientDoc.authority, + }; + + await mongoClient.close(); + return client; + } catch (err) { + await mongoClient.close(); + throw err; + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsage.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsage.ts new file mode 100644 index 000000000..752af7b21 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsage.ts @@ -0,0 +1,19 @@ +import { trackInNewRelic } from './trackClientUsageInNewRelic'; +import { TrackingConfig, TrackingProvider } from '../getTrackingConfig/TrackingConfig'; + +export interface TrackingOptions { + readonly organisationId: string; + readonly lrsId: string; + readonly clientId: string; +} + +export async function track(config: TrackingConfig, opts: TrackingOptions): Promise { + switch (config.trackingProvider) { + case TrackingProvider.NewRelic: + return trackInNewRelic(opts); + default: + case TrackingProvider.Fake: { + return; + } + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsageInNewRelic.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsageInNewRelic.ts new file mode 100644 index 000000000..a8e5bf2f3 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsageInNewRelic.ts @@ -0,0 +1,8 @@ +import { TrackingOptions } from './trackClientUsage'; + +export async function trackInNewRelic(opts: TrackingOptions) { + const newrelic = await import('newrelic'); + newrelic.addCustomAttribute('org_id', opts.organisationId); + newrelic.addCustomAttribute('lrs_id', opts.lrsId); + newrelic.addCustomAttribute('client_id', opts.clientId); +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig.ts new file mode 100644 index 000000000..b4dc68654 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig.ts @@ -0,0 +1,40 @@ +export type FileStorageConfig = ( + S3FileStorageConfig | GoogleFileStorageConfig | AzureFileStorageConfig | LocalFileStorageConfig +); + +export enum FileStorageProvider { + Azure = 'azure', + Google = 'google', + Local = 'local', + S3 = 's3', +} + +export interface AzureFileStorageConfig { + readonly fileStorageProvider: FileStorageProvider.Azure; + readonly azureAccount: string; + readonly azureAccountKey: string; + readonly azureContainerName: string; + readonly azureSubFolder: string; +} + +export interface GoogleFileStorageConfig { + readonly fileStorageProvider: FileStorageProvider.Google; + readonly googleBucketName: string; + readonly googleKeyFileName: string; + readonly googleProjectId: string; + readonly googleSubFolder: string; +} + +export interface LocalFileStorageConfig { + readonly fileStorageProvider: FileStorageProvider.Local; + readonly localStorageDir: string; +} + +export interface S3FileStorageConfig { + readonly fileStorageProvider: FileStorageProvider.S3; + readonly s3BucketName: string; + readonly s3SubFolder: string; + readonly awsAccessKeyId: string; + readonly awsSecretAccessKey: string; + readonly awsRegion: string; +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts new file mode 100644 index 000000000..1e3fec039 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts @@ -0,0 +1,33 @@ +import config from '../../../../../../config'; +import { FileStorageConfig, FileStorageProvider } from './FileStorageConfig'; + +export function getFileStorageConfig(): FileStorageConfig { + switch (config.repoFactory.storageRepoName) { + case FileStorageProvider.Azure: return { + fileStorageProvider: FileStorageProvider.Azure, + azureAccount: config.azureStorageRepo.account, + azureAccountKey: config.azureStorageRepo.accountKey, + azureContainerName: config.azureStorageRepo.containerName, + azureSubFolder: config.storageSubFolders.activities, + }; + case FileStorageProvider.Google: return { + fileStorageProvider: FileStorageProvider.Google, + googleBucketName: config.googleStorageRepo.bucketName, + googleKeyFileName: config.googleStorageRepo.keyFileName, + googleProjectId: config.googleStorageRepo.projectId, + googleSubFolder: config.storageSubFolders.activities, + }; + case FileStorageProvider.S3: return { + fileStorageProvider: FileStorageProvider.S3, + s3BucketName: config.s3StorageRepo.bucketName, + s3SubFolder: config.storageSubFolders.activities, + awsAccessKeyId: config.s3StorageRepo.awsConfig.accessKeyId as string, + awsSecretAccessKey: config.s3StorageRepo.awsConfig.secretAccessKey as string, + awsRegion: config.s3StorageRepo.awsConfig.region as string, + }; + default: case FileStorageProvider.Local: return { + fileStorageProvider: FileStorageProvider.Local, + localStorageDir: `${config.localStorageRepo.storageDir}/${config.storageSubFolders.activities}`, + }; + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig.ts new file mode 100644 index 000000000..0fe8a9a33 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig.ts @@ -0,0 +1,9 @@ +export interface MongoRecordStorageConfig { + readonly recordStorageProvider: RecordStorageProvider.Mongo; + readonly mongoUri: string; + readonly mongoDbName: string; +} + +export enum RecordStorageProvider { + Mongo = 'mongo', +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig.ts new file mode 100644 index 000000000..c0899fd55 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig.ts @@ -0,0 +1,12 @@ +import config from '../../../../../../config'; +import { MongoRecordStorageConfig, RecordStorageProvider } from './RecordStorageConfig'; + +export function getRecordStorageConfig(): MongoRecordStorageConfig { + switch (config.repoFactory.modelsRepoName) { + default: case RecordStorageProvider.Mongo: return { + recordStorageProvider: RecordStorageProvider.Mongo, + mongoDbName: config.mongoModelsRepo.dbName, + mongoUri: config.mongoModelsRepo.url, + }; + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig.ts new file mode 100644 index 000000000..59695c903 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig.ts @@ -0,0 +1,14 @@ +export enum TrackingProvider { + NewRelic = 'newrelic', + Fake = 'fake', +} + +export interface NewRelicTrackingConfig { + readonly trackingProvider: TrackingProvider.NewRelic; +} + +export interface FakeTrackingConfig { + readonly trackingProvider: TrackingProvider.Fake; +} + +export type TrackingConfig = (NewRelicTrackingConfig | FakeTrackingConfig); diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig.ts new file mode 100644 index 000000000..c093f06fd --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig.ts @@ -0,0 +1,13 @@ +import config from '../../../../../../config'; +import { TrackingConfig, TrackingProvider } from './TrackingConfig'; + +export function getTrackingConfig(): TrackingConfig { + switch (config.repoFactory.storageRepoName) { + case TrackingProvider.NewRelic: return { + trackingProvider: TrackingProvider.NewRelic, + }; + default: case TrackingProvider.Fake: return { + trackingProvider: TrackingProvider.Fake, + }; + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoActivityProfileDoc.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoActivityProfileDoc.ts new file mode 100644 index 000000000..6cd392a92 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoActivityProfileDoc.ts @@ -0,0 +1,18 @@ +import { ObjectID } from 'mongodb'; +import { JsonValue } from '../JsonValue'; + +export interface MongoActivityProfileDoc { + readonly _id: ObjectID; + readonly etag: string; + readonly id: string; + readonly organisation: ObjectID; + readonly activityId: string; + readonly profileId: string; + readonly content?: JsonValue; + readonly contentType: string; + readonly extension: string; + readonly lrs: ObjectID; + readonly updatedAt: Date; +} + +export const mongoActivityProfilesCollectionName = 'activityProfiles'; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoClientDoc.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoClientDoc.ts new file mode 100644 index 000000000..fffb6f4a4 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoClientDoc.ts @@ -0,0 +1,17 @@ +import { ObjectID } from 'mongodb'; +import Actor from 'src/apps/statements/models/Actor'; + +export interface MongoClientDoc { + readonly _id: ObjectID; + readonly api: { + readonly basic_key: string; + readonly basic_secret: string; + }; + readonly isTrusted: boolean; + readonly lrs_id: ObjectID; + readonly organisation: ObjectID; + readonly scopes: string[]; + readonly title: string; + readonly authority: Actor; +} +export const mongoClientCollectionName = 'client'; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoLrsDoc.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoLrsDoc.ts new file mode 100644 index 000000000..01b11e180 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoLrsDoc.ts @@ -0,0 +1,5 @@ +import { ObjectID } from 'mongodb'; +export interface MongoLrsDoc { + readonly _id: ObjectID; +} +export const mongoLrsCollectionName = 'lrs'; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOAuthTokenDoc.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOAuthTokenDoc.ts new file mode 100644 index 000000000..5e5effa5d --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOAuthTokenDoc.ts @@ -0,0 +1,7 @@ +import { ObjectID } from 'mongodb'; +export interface MongoOAuthTokenDoc { + readonly _id: ObjectID; + readonly clientId: ObjectID; + readonly accessToken: string; +} +export const mongoOAuthTokenCollectionName = 'oAuthTokens'; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOrgDoc.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOrgDoc.ts new file mode 100644 index 000000000..328f72803 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOrgDoc.ts @@ -0,0 +1,6 @@ +import { ObjectID } from 'mongodb'; +export interface MongoOrgDoc { + readonly _id: ObjectID; + readonly expiration: Date | null; +} +export const mongoOrgCollectionName = 'organisation'; diff --git a/src/apps/activities/app.ts b/src/apps/activities/app.ts index 33980f1dd..0d96a56e9 100644 --- a/src/apps/activities/app.ts +++ b/src/apps/activities/app.ts @@ -56,6 +56,9 @@ export default (appConfig: AppConfig): Router => { service, tracker: appConfig.tracker, translator, + fileStorageConfig: appConfig.fileStorageConfig, + recordStorageConfig: appConfig.recordStorageConfig, + trackingConfig: appConfig.trackingConfig, }); return presenter; }; diff --git a/src/apps/activities/expressPresenter/Config.ts b/src/apps/activities/expressPresenter/Config.ts index 648b6072e..aef36b162 100644 --- a/src/apps/activities/expressPresenter/Config.ts +++ b/src/apps/activities/expressPresenter/Config.ts @@ -1,5 +1,8 @@ import CommonExpressConfig from 'jscommons/dist/expressPresenter/Config'; import Tracker from 'jscommons/dist/tracker/Tracker'; +import { FileStorageConfig } from '../_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig'; +import { TrackingConfig } from '../_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; +import { MongoRecordStorageConfig } from '../_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig'; import Service from '../serviceFactory/Service'; import Translator from '../translatorFactory/Translator'; @@ -7,6 +10,9 @@ interface Config extends CommonExpressConfig { readonly service: Service; readonly translator: Translator; readonly tracker: Promise; + readonly fileStorageConfig: FileStorageConfig; + readonly recordStorageConfig: MongoRecordStorageConfig; + readonly trackingConfig: TrackingConfig; } export default Config; diff --git a/src/apps/activities/expressPresenter/index.ts b/src/apps/activities/expressPresenter/index.ts index 9ef35d28e..489448137 100644 --- a/src/apps/activities/expressPresenter/index.ts +++ b/src/apps/activities/expressPresenter/index.ts @@ -1,8 +1,9 @@ import { Router } from 'express'; import mixinCors from 'jscommons/dist/expressPresenter/mixins/cors'; import mixinHelmet from 'jscommons/dist/expressPresenter/mixins/helmet'; +import { deleteActivityProfileViaHttp } from '../_functions/deleteActivityProfile/deleteActivityProfileViaHttp'; +import { createExpressHandler } from '../_functions/deleteActivityProfile/utils/createExpressHandler'; import Config from './Config'; -import deleteProfile from './deleteProfile'; import getProfiles from './getProfiles'; import postProfile from './postProfile'; import putProfile from './putProfile'; @@ -12,8 +13,7 @@ export default (config: Config): Router => { router.use(mixinCors()); router.use(mixinHelmet()); - - router.delete('', deleteProfile(config)); + router.delete('', createExpressHandler(deleteActivityProfileViaHttp)(config)); router.get('', getProfiles(config)); router.put('', putProfile(config)); router.post('', postProfile(config)); diff --git a/src/apps/app.ts b/src/apps/app.ts index addd35d54..6230f0976 100644 --- a/src/apps/app.ts +++ b/src/apps/app.ts @@ -25,6 +25,9 @@ export default (appConfig: AppConfig): Router => { storageSubFolder: appConfig.repo.storageSubFolders.activities, }, tracker: appConfig.tracker, + fileStorageConfig: appConfig.fileStorageConfig, + recordStorageConfig: appConfig.recordStorageConfig, + trackingConfig: appConfig.trackingConfig, }); const agentsRouter = agentsApp({ logger: appConfig.logger, diff --git a/src/server.ts b/src/server.ts index 8cb6b1bff..962ebd9f7 100644 --- a/src/server.ts +++ b/src/server.ts @@ -4,6 +4,9 @@ sourceMapSupport.install(); import tracker from './tracker'; import express from 'express'; // tslint:disable-line:ordered-imports import handleListen from 'jscommons/dist/expressPresenter/utils/handleListen'; +import { getFileStorageConfig } from './apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig'; +import { getRecordStorageConfig } from './apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig'; +import { getTrackingConfig } from './apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig'; import app from './apps/app'; import config from './config'; import logger from './logger'; @@ -42,6 +45,9 @@ expressApp.use(app({ statements: config.statementsService, }, tracker, + fileStorageConfig: getFileStorageConfig(), + recordStorageConfig: getRecordStorageConfig(), + trackingConfig: getTrackingConfig(), })); expressApp.listen(config.express.port, () => { diff --git a/tslint.json b/tslint.json index fdb98f14c..0bb0b1e00 100644 --- a/tslint.json +++ b/tslint.json @@ -7,6 +7,7 @@ false, "LF" ], - "import-spacing": false + "import-spacing": false, + "only-arrow-functions": false } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d81755fab..855a9f41c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -649,6 +649,11 @@ "@types/bson" "*" "@types/node" "*" +"@types/newrelic@6.13.0": + version "6.13.0" + resolved "https://registry.yarnpkg.com/@types/newrelic/-/newrelic-6.13.0.tgz#6964b69b87ce83c545ddb364a61af62d0fbd6f0b" + integrity sha512-C2sf+FVOpN+RQVizKDd78KA7383DtAH8jOqdqI+9FP3dozyH8V8ZLH1nLMtRx/y/80hSbVfg1P0AM20jOaioOw== + "@types/node-fetch@2.5.7", "@types/node-fetch@^2.3.7": version "2.5.7" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" From fdc03b45434c937c4988fc5f2c4532cb45910894 Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Sun, 13 Dec 2020 16:11:24 +0000 Subject: [PATCH 02/10] ci(tslint): Fixes linting errors --- src/apps/activities/AppConfig.ts | 2 +- .../deleteActivityProfileContentFromFS.ts | 2 +- .../deleteActivityProfileContentFromGoogle.ts | 2 +- .../deleteActivityProfileContentFromS3.ts | 2 +- .../deleteActivityProfile/deleteActivityProfileFromMongo.ts | 2 +- .../deleteActivityProfile/deleteActivityProfileViaHttp.ts | 4 ++-- .../deleteActivityProfile/utils/getClient/getClient.ts | 2 +- .../deleteActivityProfile/utils/getClient/trackClientUsage.ts | 2 +- .../utils/getFileStorageConfig/getFileStorageConfig.ts | 2 ++ src/apps/activities/expressPresenter/Config.ts | 2 +- 10 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/apps/activities/AppConfig.ts b/src/apps/activities/AppConfig.ts index 5ed6e1111..c7b20be64 100644 --- a/src/apps/activities/AppConfig.ts +++ b/src/apps/activities/AppConfig.ts @@ -3,8 +3,8 @@ import Tracker from 'jscommons/dist/tracker/Tracker'; import { Db } from 'mongodb'; import { LoggerInstance } from 'winston'; import { FileStorageConfig } from './_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig'; -import { TrackingConfig } from './_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; import { MongoRecordStorageConfig } from './_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig'; +import { TrackingConfig } from './_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; export default interface AppConfig { readonly logger: LoggerInstance; diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromFS.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromFS.ts index f03a905ee..bedb73dee 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromFS.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromFS.ts @@ -1,7 +1,7 @@ import { unlink } from 'fs-extra'; import getStorageDir from '../../utils/getStorageDir'; import { DeleteActivityProfileContentOptions } from './deleteActivityProfileContent'; -import { LocalFileStorageConfig } from "./utils/getFileStorageConfig/FileStorageConfig"; +import { LocalFileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; export async function deleteActivityProfileContentFromFS( config: LocalFileStorageConfig, diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromGoogle.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromGoogle.ts index 2c2c92bd4..159d6e877 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromGoogle.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromGoogle.ts @@ -1,7 +1,7 @@ import Storage from '@google-cloud/storage'; import getStorageDir from '../../utils/getStorageDir'; import { DeleteActivityProfileContentOptions } from './deleteActivityProfileContent'; -import { GoogleFileStorageConfig } from "./utils/getFileStorageConfig/FileStorageConfig"; +import { GoogleFileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; export async function deleteActivityProfileContentFromGoogle( config: GoogleFileStorageConfig, diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromS3.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromS3.ts index c03083988..c79b62838 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromS3.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileContentFromS3.ts @@ -1,7 +1,7 @@ import { S3 } from 'aws-sdk'; import getStorageDir from '../../utils/getStorageDir'; import { DeleteActivityProfileContentOptions } from './deleteActivityProfileContent'; -import { S3FileStorageConfig } from "./utils/getFileStorageConfig/FileStorageConfig"; +import { S3FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; export async function deleteActivityProfileContentFromS3( config: S3FileStorageConfig, diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts index b16fcec7d..1da44f780 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts @@ -1,11 +1,11 @@ import NoModel from 'jscommons/dist/errors/NoModel'; import { MongoClient, ObjectID } from 'mongodb'; import IfMatch from '../../errors/IfMatch'; +import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; import { MongoActivityProfileDoc, mongoActivityProfilesCollectionName, } from './utils/mongoDocInterfaces/MongoActivityProfileDoc'; -import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; interface DeleteActivityProfileRecordOptions { readonly lrs_id: string; diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts index d54d1afdc..bab700a5b 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts @@ -5,11 +5,11 @@ import getProfileId from '../../expressPresenter/utils/getProfileId'; import validateVersionHeader from '../../expressPresenter/utils/validateVersionHeader'; import { xapiHeaderVersion } from '../../utils/constants'; import { deleteActivityProfile } from './deleteActivityProfile'; -import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; import { getClient } from './utils/getClient/getClient'; +import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; +import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; import { TrackingConfig } from './utils/getTrackingConfig/TrackingConfig'; import { HttpRequest, HttpResponse } from './utils/HttpInterfaces'; -import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; export interface DeleteActivityProfileViaHttpConfig { readonly recordStorageConfig: MongoRecordStorageConfig; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts index 82f6cb266..15137b7ef 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts @@ -1,9 +1,9 @@ import NoModel from 'jscommons/dist/errors/NoModel'; import Unauthorised from 'jscommons/dist/errors/Unauthorised'; import { MongoRecordStorageConfig } from '../getRecordStorageConfig/RecordStorageConfig'; +import { TrackingConfig } from '../getTrackingConfig/TrackingConfig'; import { getClientFromMongo } from './getClientFromMongo'; import { track } from './trackClientUsage'; -import { TrackingConfig } from '../getTrackingConfig/TrackingConfig'; interface GetClientConfig { readonly recordStorageConfig: MongoRecordStorageConfig; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsage.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsage.ts index 752af7b21..3fd905a6c 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsage.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsage.ts @@ -1,5 +1,5 @@ -import { trackInNewRelic } from './trackClientUsageInNewRelic'; import { TrackingConfig, TrackingProvider } from '../getTrackingConfig/TrackingConfig'; +import { trackInNewRelic } from './trackClientUsageInNewRelic'; export interface TrackingOptions { readonly organisationId: string; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts index 1e3fec039..aa8ad67cb 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts @@ -21,7 +21,9 @@ export function getFileStorageConfig(): FileStorageConfig { fileStorageProvider: FileStorageProvider.S3, s3BucketName: config.s3StorageRepo.bucketName, s3SubFolder: config.storageSubFolders.activities, + // tslint:disable-next-line: deprecation awsAccessKeyId: config.s3StorageRepo.awsConfig.accessKeyId as string, + // tslint:disable-next-line: deprecation awsSecretAccessKey: config.s3StorageRepo.awsConfig.secretAccessKey as string, awsRegion: config.s3StorageRepo.awsConfig.region as string, }; diff --git a/src/apps/activities/expressPresenter/Config.ts b/src/apps/activities/expressPresenter/Config.ts index aef36b162..2fec8db7b 100644 --- a/src/apps/activities/expressPresenter/Config.ts +++ b/src/apps/activities/expressPresenter/Config.ts @@ -1,8 +1,8 @@ import CommonExpressConfig from 'jscommons/dist/expressPresenter/Config'; import Tracker from 'jscommons/dist/tracker/Tracker'; import { FileStorageConfig } from '../_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig'; -import { TrackingConfig } from '../_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; import { MongoRecordStorageConfig } from '../_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig'; +import { TrackingConfig } from '../_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; import Service from '../serviceFactory/Service'; import Translator from '../translatorFactory/Translator'; From 5f154a9b0f2a08d815b5bbbbcaf0ca8799d2b590 Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Sun, 13 Dec 2020 16:26:19 +0000 Subject: [PATCH 03/10] refactor(deleteActivityProfile): Moves tests --- .../deleteActivityProfile/tests}/alternateRequest.test.ts | 2 +- .../tests}/deleteExistingProfile.test.ts | 2 +- .../tests}/deleteNonExistingProfile.test.ts | 2 +- .../deleteActivityProfile/tests}/deleteOutsideOrg.test.ts | 2 +- .../deleteActivityProfile/tests}/deleteOutsideStore.test.ts | 2 +- .../deleteActivityProfile/tests}/deleteWithEtags.test.ts | 2 +- .../deleteActivityProfile/tests}/deleteWithScopes.test.ts | 2 +- .../tests}/utils/assertOutsideClient.ts | 0 .../deleteActivityProfile/tests}/utils/deleteProfile.ts | 2 +- .../activities/expressPresenter/tests/utils/supertest.ts | 6 ++++++ 10 files changed, 14 insertions(+), 8 deletions(-) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/alternateRequest.test.ts (93%) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/deleteExistingProfile.test.ts (91%) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/deleteNonExistingProfile.test.ts (92%) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/deleteOutsideOrg.test.ts (91%) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/deleteOutsideStore.test.ts (91%) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/deleteWithEtags.test.ts (93%) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/deleteWithScopes.test.ts (93%) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/utils/assertOutsideClient.ts (100%) rename src/apps/activities/{expressPresenter/tests/deleteProfile => _functions/deleteActivityProfile/tests}/utils/deleteProfile.ts (85%) diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/alternateRequest.test.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/alternateRequest.test.ts similarity index 93% rename from src/apps/activities/expressPresenter/tests/deleteProfile/alternateRequest.test.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/alternateRequest.test.ts index 2cc4c8fd8..e50e79745 100644 --- a/src/apps/activities/expressPresenter/tests/deleteProfile/alternateRequest.test.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/tests/alternateRequest.test.ts @@ -1,4 +1,5 @@ import { NO_CONTENT } from 'http-status-codes'; +import setup from '../../../expressPresenter/tests/utils/setup'; import assertDeleted from '../../../utils/assertDeleted'; import { route, xapiHeaderVersion } from '../../../utils/constants'; import createTextProfile from '../../../utils/createTextProfile'; @@ -7,7 +8,6 @@ import { TEST_ACTIVITY_ID, TEST_PROFILE_ID, } from '../../../utils/testValues'; -import setup from '../utils/setup'; describe('expressPresenter.deleteProfile using the alternate request syntax', () => { const { supertest } = setup(); diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteExistingProfile.test.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteExistingProfile.test.ts similarity index 91% rename from src/apps/activities/expressPresenter/tests/deleteProfile/deleteExistingProfile.test.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/deleteExistingProfile.test.ts index d29ded7d4..083d17d21 100644 --- a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteExistingProfile.test.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteExistingProfile.test.ts @@ -1,8 +1,8 @@ import { NO_CONTENT } from 'http-status-codes'; +import setup from '../../../expressPresenter/tests/utils/setup'; import assertDeleted from '../../../utils/assertDeleted'; import createJsonProfile from '../../../utils/createJsonProfile'; import createTextProfile from '../../../utils/createTextProfile'; -import setup from '../utils/setup'; import deleteProfile from './utils/deleteProfile'; describe('expressPresenter.deleteProfile with existing profile', () => { diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteNonExistingProfile.test.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteNonExistingProfile.test.ts similarity index 92% rename from src/apps/activities/expressPresenter/tests/deleteProfile/deleteNonExistingProfile.test.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/deleteNonExistingProfile.test.ts index 3f8b34afe..ca124bb64 100644 --- a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteNonExistingProfile.test.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteNonExistingProfile.test.ts @@ -1,6 +1,6 @@ import { BAD_REQUEST, NO_CONTENT } from 'http-status-codes'; +import setup from '../../../expressPresenter/tests/utils/setup'; import { TEST_INVALID_ACTIVITY_ID } from '../../../utils/testValues'; -import setup from '../utils/setup'; import deleteProfile from './utils/deleteProfile'; describe('expressPresenter.deleteProfile with non-existing state', () => { diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteOutsideOrg.test.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteOutsideOrg.test.ts similarity index 91% rename from src/apps/activities/expressPresenter/tests/deleteProfile/deleteOutsideOrg.test.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/deleteOutsideOrg.test.ts index 048d130de..86ce07a3a 100644 --- a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteOutsideOrg.test.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteOutsideOrg.test.ts @@ -1,7 +1,7 @@ +import setup from '../../../expressPresenter/tests/utils/setup'; import overwriteProfileOutsideClient from '../../../utils/overwriteProfileOutsideClient'; import patchProfileOutsideClient from '../../../utils/patchProfileOutsideClient'; import { TEST_CLIENT_OUTSIDE_ORG } from '../../../utils/testValues'; -import setup from '../utils/setup'; import assertOutsideClient from './utils/assertOutsideClient'; describe('expressPresenter.deleteProfile outside the organisation', () => { diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteOutsideStore.test.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteOutsideStore.test.ts similarity index 91% rename from src/apps/activities/expressPresenter/tests/deleteProfile/deleteOutsideStore.test.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/deleteOutsideStore.test.ts index 5347caaf9..f4d748483 100644 --- a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteOutsideStore.test.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteOutsideStore.test.ts @@ -1,7 +1,7 @@ +import setup from '../../../expressPresenter/tests/utils/setup'; import overwriteProfileOutsideClient from '../../../utils/overwriteProfileOutsideClient'; import patchProfileOutsideClient from '../../../utils/patchProfileOutsideClient'; import { TEST_CLIENT_OUTSIDE_STORE } from '../../../utils/testValues'; -import setup from '../utils/setup'; import assertOutsideClient from './utils/assertOutsideClient'; describe('expressPresenter.deleteProfile outside the store', () => { diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteWithEtags.test.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteWithEtags.test.ts similarity index 93% rename from src/apps/activities/expressPresenter/tests/deleteProfile/deleteWithEtags.test.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/deleteWithEtags.test.ts index 42350bed8..2f0c84f48 100644 --- a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteWithEtags.test.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteWithEtags.test.ts @@ -1,7 +1,7 @@ import { NO_CONTENT, PRECONDITION_FAILED } from 'http-status-codes'; +import setup from '../../../expressPresenter/tests/utils/setup'; import createTextProfile from '../../../utils/createTextProfile'; import getTestProfile from '../../../utils/getTestProfile'; -import setup from '../utils/setup'; import deleteProfile from './utils/deleteProfile'; describe('expressPresenter.deleteProfile with etags', () => { diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteWithScopes.test.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteWithScopes.test.ts similarity index 93% rename from src/apps/activities/expressPresenter/tests/deleteProfile/deleteWithScopes.test.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/deleteWithScopes.test.ts index c34db93e4..d9e78d99b 100644 --- a/src/apps/activities/expressPresenter/tests/deleteProfile/deleteWithScopes.test.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/tests/deleteWithScopes.test.ts @@ -1,6 +1,6 @@ import { FORBIDDEN, NO_CONTENT } from 'http-status-codes'; +import setup from '../../../expressPresenter/tests/utils/setup'; import { TEST_EXPIRED_ORG_TOKEN, TEST_UNTRUSTED_TOKEN } from '../../../utils/testValues'; -import setup from '../utils/setup'; import deleteProfile from './utils/deleteProfile'; describe('expressPresenter.deleteProfile with scopes', () => { diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/utils/assertOutsideClient.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/utils/assertOutsideClient.ts similarity index 100% rename from src/apps/activities/expressPresenter/tests/deleteProfile/utils/assertOutsideClient.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/utils/assertOutsideClient.ts diff --git a/src/apps/activities/expressPresenter/tests/deleteProfile/utils/deleteProfile.ts b/src/apps/activities/_functions/deleteActivityProfile/tests/utils/deleteProfile.ts similarity index 85% rename from src/apps/activities/expressPresenter/tests/deleteProfile/utils/deleteProfile.ts rename to src/apps/activities/_functions/deleteActivityProfile/tests/utils/deleteProfile.ts index 1f2ffbb85..6e71e1be1 100644 --- a/src/apps/activities/expressPresenter/tests/deleteProfile/utils/deleteProfile.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/tests/utils/deleteProfile.ts @@ -1,7 +1,7 @@ import { Test } from 'supertest'; +import supertest from '../../../../expressPresenter/tests/utils/supertest'; import { route, xapiHeaderVersion } from '../../../../utils/constants'; import { TEST_ACTIVITY_ID, TEST_PROFILE_ID } from '../../../../utils/testValues'; -import supertest from '../../utils/supertest'; export default (optsOverrides: object = {}): Test => { return supertest diff --git a/src/apps/activities/expressPresenter/tests/utils/supertest.ts b/src/apps/activities/expressPresenter/tests/utils/supertest.ts index 212d8b6ac..631d9f11f 100644 --- a/src/apps/activities/expressPresenter/tests/utils/supertest.ts +++ b/src/apps/activities/expressPresenter/tests/utils/supertest.ts @@ -1,4 +1,7 @@ import express from 'express'; +import { getFileStorageConfig } from 'src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig'; +import { getRecordStorageConfig } from 'src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig'; +import { getTrackingConfig } from 'src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig'; import supertest from 'supertest'; import config from '../../../../../config'; import logger from '../../../../../logger'; @@ -19,6 +22,9 @@ const presenter = presenterFacade({ service, tracker, translator, + fileStorageConfig: getFileStorageConfig(), + recordStorageConfig: getRecordStorageConfig(), + trackingConfig: getTrackingConfig(), }); app.use(route, presenter); From fd8e6324e959abd6e922851855fba5e64185ea94 Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Sun, 13 Dec 2020 16:30:31 +0000 Subject: [PATCH 04/10] ci: Fixes tests --- .../activities/expressPresenter/tests/utils/supertest.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/apps/activities/expressPresenter/tests/utils/supertest.ts b/src/apps/activities/expressPresenter/tests/utils/supertest.ts index 631d9f11f..5c14af5c7 100644 --- a/src/apps/activities/expressPresenter/tests/utils/supertest.ts +++ b/src/apps/activities/expressPresenter/tests/utils/supertest.ts @@ -1,11 +1,11 @@ import express from 'express'; -import { getFileStorageConfig } from 'src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig'; -import { getRecordStorageConfig } from 'src/apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig'; -import { getTrackingConfig } from 'src/apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig'; import supertest from 'supertest'; import config from '../../../../../config'; import logger from '../../../../../logger'; import tracker from '../../../../../tracker'; +import { getFileStorageConfig } from '../../../_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig'; +import { getRecordStorageConfig } from '../../../_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig'; +import { getTrackingConfig } from '../../../_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig'; import translatorFactory from '../../../translatorFactory'; import { route } from '../../../utils/constants'; import service from '../../../utils/testService'; From 9a09ab1d094b97552f63c973e808c52fca4f2366 Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Sun, 13 Dec 2020 16:36:02 +0000 Subject: [PATCH 05/10] ci: Fixes tests --- .../deleteActivityProfile/utils/getClient/getClientFromMongo.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts index cb61d1ef8..ee0b6f716 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts @@ -54,6 +54,7 @@ export async function getClientFromMongo(config: MongoRecordStorageConfig, authT useUnifiedTopology: true, }); try { + await mongoClient.connect(); const db = mongoClient.db(config.mongoDbName); const clientDoc = await findClientWithAuth(db, authToken); From 05a821ac0b2b894036d9ef0ffc9e49a8d66c40fb Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Sun, 13 Dec 2020 17:18:31 +0000 Subject: [PATCH 06/10] fix: Corrects auth for testing --- src/apps/AppConfig.ts | 2 + src/apps/activities/AppConfig.ts | 2 + .../deleteActivityProfile.ts | 6 +-- ...> deleteActivityProfileRecordFromMongo.ts} | 2 +- .../deleteActivityProfileViaHttp.ts | 2 + .../utils/getAuthConfig/AuthConfig.ts | 18 +++++++++ .../utils/getAuthConfig/getAuthConfig.ts | 15 +++++++ .../utils/getClient/getClient.ts | 8 ++-- .../utils/getClient/getClientRecord.ts | 15 +++++++ ...omMongo.ts => getClientRecordFromMongo.ts} | 9 ++++- .../getClient/getClientRecordFromTesters.ts | 39 +++++++++++++++++++ .../mongoDocInterfaces/MongoClientDoc.ts | 2 +- src/apps/activities/app.ts | 1 + .../activities/expressPresenter/Config.ts | 2 + .../expressPresenter/tests/utils/supertest.ts | 2 + src/apps/activities/models/ClientModel.ts | 4 +- src/apps/app.ts | 1 + .../getSequencingMetadata.test.ts | 2 +- src/server.ts | 4 +- 19 files changed, 121 insertions(+), 15 deletions(-) rename src/apps/activities/_functions/deleteActivityProfile/{deleteActivityProfileFromMongo.ts => deleteActivityProfileRecordFromMongo.ts} (97%) create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/AuthConfig.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/getAuthConfig.ts create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecord.ts rename src/apps/activities/_functions/deleteActivityProfile/utils/getClient/{getClientFromMongo.ts => getClientRecordFromMongo.ts} (91%) create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecordFromTesters.ts diff --git a/src/apps/AppConfig.ts b/src/apps/AppConfig.ts index 6ab2f2a71..cb5885d39 100644 --- a/src/apps/AppConfig.ts +++ b/src/apps/AppConfig.ts @@ -3,6 +3,7 @@ import { Redis } from 'ioredis'; import Tracker from 'jscommons/dist/tracker/Tracker'; import { Db } from 'mongodb'; import { LoggerInstance } from 'winston'; +import { AuthConfig } from './activities/_functions/deleteActivityProfile/utils/getAuthConfig/AuthConfig'; import { FileStorageConfig } from './activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig'; import { MongoRecordStorageConfig } from './activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig'; import { TrackingConfig } from './activities/_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; @@ -80,6 +81,7 @@ export default interface AppConfig { }; readonly tracker: Promise; readonly logger: LoggerInstance; + readonly authConfig: AuthConfig; readonly fileStorageConfig: FileStorageConfig; readonly recordStorageConfig: MongoRecordStorageConfig; readonly trackingConfig: TrackingConfig; diff --git a/src/apps/activities/AppConfig.ts b/src/apps/activities/AppConfig.ts index c7b20be64..41c0b72a2 100644 --- a/src/apps/activities/AppConfig.ts +++ b/src/apps/activities/AppConfig.ts @@ -2,6 +2,7 @@ import S3 from 'aws-sdk/clients/s3'; import Tracker from 'jscommons/dist/tracker/Tracker'; import { Db } from 'mongodb'; import { LoggerInstance } from 'winston'; +import { AuthConfig } from './_functions/deleteActivityProfile/utils/getAuthConfig/AuthConfig'; import { FileStorageConfig } from './_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig'; import { MongoRecordStorageConfig } from './_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig'; import { TrackingConfig } from './_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; @@ -43,6 +44,7 @@ export default interface AppConfig { readonly db: () => Promise; }; }; + readonly authConfig: AuthConfig; readonly fileStorageConfig: FileStorageConfig; readonly recordStorageConfig: MongoRecordStorageConfig; readonly trackingConfig: TrackingConfig; diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfile.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfile.ts index 0e3dc56b9..68e6d0b6e 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfile.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfile.ts @@ -1,10 +1,10 @@ import NoModel from 'jscommons/dist/errors/NoModel'; -import ClientModel from '../../../statements/models/ClientModel'; +import ClientModel from '../../models/ClientModel'; import checkProfileWriteScopes from '../../service/utils/checkProfileWriteScopes'; import validateActivityId from '../../service/utils/validateActivityId'; import { jsonContentType } from '../../utils/constants'; import { deleteActivityProfileContent } from './deleteActivityProfileContent'; -import { deleteActivityProfileFromMongo } from './deleteActivityProfileFromMongo'; +import { deleteActivityProfileRecordFromMongo } from './deleteActivityProfileRecordFromMongo'; import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; @@ -29,7 +29,7 @@ export async function deleteActivityProfile( validateActivityId(opts.activityId); try { - const deleteResult = await deleteActivityProfileFromMongo(config.recordStorageConfig, { + const deleteResult = await deleteActivityProfileRecordFromMongo(config.recordStorageConfig, { activityId: opts.activityId, lrs_id: opts.client.lrs_id, organisation: opts.client.organisation, diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileRecordFromMongo.ts similarity index 97% rename from src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts rename to src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileRecordFromMongo.ts index 1da44f780..f0bf6c40e 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileFromMongo.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileRecordFromMongo.ts @@ -21,7 +21,7 @@ interface DeleteActivityProfileRecordResult { readonly extension: string; } -export async function deleteActivityProfileFromMongo( +export async function deleteActivityProfileRecordFromMongo( config: MongoRecordStorageConfig, opts: DeleteActivityProfileRecordOptions, ): Promise { diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts index bab700a5b..b9d267c9a 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts @@ -5,6 +5,7 @@ import getProfileId from '../../expressPresenter/utils/getProfileId'; import validateVersionHeader from '../../expressPresenter/utils/validateVersionHeader'; import { xapiHeaderVersion } from '../../utils/constants'; import { deleteActivityProfile } from './deleteActivityProfile'; +import { AuthConfig } from './utils/getAuthConfig/AuthConfig'; import { getClient } from './utils/getClient/getClient'; import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig'; import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; @@ -12,6 +13,7 @@ import { TrackingConfig } from './utils/getTrackingConfig/TrackingConfig'; import { HttpRequest, HttpResponse } from './utils/HttpInterfaces'; export interface DeleteActivityProfileViaHttpConfig { + readonly authConfig: AuthConfig; readonly recordStorageConfig: MongoRecordStorageConfig; readonly fileStorageConfig: FileStorageConfig; readonly trackingConfig: TrackingConfig; diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/AuthConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/AuthConfig.ts new file mode 100644 index 000000000..3ac46ea67 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/AuthConfig.ts @@ -0,0 +1,18 @@ +export type AuthConfig = ( + MongoAuthConfig | TestAuthConfig +); + +export enum AuthProvider { + Test = 'test', + Mongo = 'mongo', +} + +export interface MongoAuthConfig { + readonly authProvider: AuthProvider.Mongo; + readonly mongoUri: string; + readonly mongoDbName: string; +} + +export interface TestAuthConfig { + readonly authProvider: AuthProvider.Test; +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/getAuthConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/getAuthConfig.ts new file mode 100644 index 000000000..425380c7e --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/getAuthConfig.ts @@ -0,0 +1,15 @@ +import config from '../../../../../../config'; +import { AuthConfig, AuthProvider } from './AuthConfig'; + +export function getAuthConfig(): AuthConfig { + switch (config.repoFactory.authRepoName) { + case AuthProvider.Test: return { + authProvider: AuthProvider.Test, + }; + default: case AuthProvider.Mongo: return { + authProvider: AuthProvider.Mongo, + mongoDbName: config.mongoModelsRepo.dbName, + mongoUri: config.mongoModelsRepo.url, + }; + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts index 15137b7ef..698682b60 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClient.ts @@ -1,18 +1,18 @@ import NoModel from 'jscommons/dist/errors/NoModel'; import Unauthorised from 'jscommons/dist/errors/Unauthorised'; -import { MongoRecordStorageConfig } from '../getRecordStorageConfig/RecordStorageConfig'; +import { AuthConfig } from '../getAuthConfig/AuthConfig'; import { TrackingConfig } from '../getTrackingConfig/TrackingConfig'; -import { getClientFromMongo } from './getClientFromMongo'; +import { getClientRecord } from './getClientRecord'; import { track } from './trackClientUsage'; interface GetClientConfig { - readonly recordStorageConfig: MongoRecordStorageConfig; + readonly authConfig: AuthConfig; readonly trackingConfig: TrackingConfig; } export async function getClient(config: GetClientConfig, authToken = '') { try { - const client = await getClientFromMongo(config.recordStorageConfig, authToken); + const client = await getClientRecord(config.authConfig, authToken); await track(config.trackingConfig, { organisationId: client.organisation, lrsId: client.lrs_id, diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecord.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecord.ts new file mode 100644 index 000000000..c047dd4c1 --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecord.ts @@ -0,0 +1,15 @@ +import ClientModel from '../../../../models/ClientModel'; +import { AuthConfig, AuthProvider } from '../getAuthConfig/AuthConfig'; +import { getClientRecordFromMongo } from './getClientRecordFromMongo'; +import { getClientRecordFromTesters } from './getClientRecordFromTesters'; + +export async function getClientRecord(config: AuthConfig, authToken: string): Promise { + switch (config.authProvider) { + case AuthProvider.Test: + return getClientRecordFromTesters(authToken); + default: + case AuthProvider.Mongo: { + return getClientRecordFromMongo(config, authToken); + } + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecordFromMongo.ts similarity index 91% rename from src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts rename to src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecordFromMongo.ts index ee0b6f716..c7f9981ca 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientFromMongo.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecordFromMongo.ts @@ -3,7 +3,8 @@ import NoModel from 'jscommons/dist/errors/NoModel'; import { Db, MongoClient } from 'mongodb'; import ExpiredClientError from '../../../../errors/ExpiredClientError'; import UntrustedClientError from '../../../../errors/UntrustedClientError'; -import { MongoRecordStorageConfig } from '../getRecordStorageConfig/RecordStorageConfig'; +import ClientModel from '../../../../models/ClientModel'; +import { MongoAuthConfig } from '../getAuthConfig/AuthConfig'; import { mongoClientCollectionName, MongoClientDoc } from '../mongoDocInterfaces/MongoClientDoc'; import { mongoLrsCollectionName, MongoLrsDoc } from '../mongoDocInterfaces/MongoLrsDoc'; import { mongoOAuthTokenCollectionName, MongoOAuthTokenDoc } from '../mongoDocInterfaces/MongoOAuthTokenDoc'; @@ -48,7 +49,10 @@ async function findClientWithAuth(db: Db, authToken: string) { } } -export async function getClientFromMongo(config: MongoRecordStorageConfig, authToken: string) { +export async function getClientRecordFromMongo( + config: MongoAuthConfig, + authToken: string, +): Promise { const mongoClient = new MongoClient(config.mongoUri, { useNewUrlParser: true, useUnifiedTopology: true, @@ -95,4 +99,5 @@ export async function getClientFromMongo(config: MongoRecordStorageConfig, authT await mongoClient.close(); throw err; } + // tslint:disable-next-line: max-file-line-count } diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecordFromTesters.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecordFromTesters.ts new file mode 100644 index 000000000..f0221c42e --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/getClientRecordFromTesters.ts @@ -0,0 +1,39 @@ +import NoModel from 'jscommons/dist/errors/NoModel'; +import ExpiredClientError from '../../../../errors/ExpiredClientError'; +import UntrustedClientError from '../../../../errors/UntrustedClientError'; +import ClientModel from '../../../../models/ClientModel'; +import { + TEST_CLIENT, + TEST_CLIENT_OUTSIDE_ORG, + TEST_CLIENT_OUTSIDE_STORE, + TEST_EXPIRED_ORG_TOKEN, + TEST_INVALID_SCOPE_CLIENT, + TEST_INVALID_SCOPE_TOKEN, + TEST_MISSING_TOKEN, + TEST_OUTSIDE_ORG_TOKEN, + TEST_OUTSIDE_STORE_TOKEN, + TEST_UNTRUSTED_TOKEN, + TEST_VALID_SCOPE_CLIENT, + TEST_VALID_SCOPE_TOKEN, +} from '../../../../utils/testValues'; + +export async function getClientRecordFromTesters(authToken: string): Promise { + switch (authToken) { + case TEST_INVALID_SCOPE_TOKEN: + return TEST_INVALID_SCOPE_CLIENT; + case TEST_VALID_SCOPE_TOKEN: + return TEST_VALID_SCOPE_CLIENT; + case TEST_OUTSIDE_ORG_TOKEN: + return TEST_CLIENT_OUTSIDE_ORG; + case TEST_OUTSIDE_STORE_TOKEN: + return TEST_CLIENT_OUTSIDE_STORE; + case TEST_UNTRUSTED_TOKEN: + throw new UntrustedClientError(); + case TEST_EXPIRED_ORG_TOKEN: + throw new ExpiredClientError(); + case TEST_MISSING_TOKEN: + throw new NoModel('Client'); + default: + return TEST_CLIENT; + } +} diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoClientDoc.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoClientDoc.ts index fffb6f4a4..aca8a0709 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoClientDoc.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoClientDoc.ts @@ -1,5 +1,5 @@ import { ObjectID } from 'mongodb'; -import Actor from 'src/apps/statements/models/Actor'; +import Actor from '../../../../../statements/models/Actor'; export interface MongoClientDoc { readonly _id: ObjectID; diff --git a/src/apps/activities/app.ts b/src/apps/activities/app.ts index 0d96a56e9..d07ebf3ed 100644 --- a/src/apps/activities/app.ts +++ b/src/apps/activities/app.ts @@ -56,6 +56,7 @@ export default (appConfig: AppConfig): Router => { service, tracker: appConfig.tracker, translator, + authConfig: appConfig.authConfig, fileStorageConfig: appConfig.fileStorageConfig, recordStorageConfig: appConfig.recordStorageConfig, trackingConfig: appConfig.trackingConfig, diff --git a/src/apps/activities/expressPresenter/Config.ts b/src/apps/activities/expressPresenter/Config.ts index 2fec8db7b..cb325de25 100644 --- a/src/apps/activities/expressPresenter/Config.ts +++ b/src/apps/activities/expressPresenter/Config.ts @@ -1,5 +1,6 @@ import CommonExpressConfig from 'jscommons/dist/expressPresenter/Config'; import Tracker from 'jscommons/dist/tracker/Tracker'; +import { AuthConfig } from '../_functions/deleteActivityProfile/utils/getAuthConfig/AuthConfig'; import { FileStorageConfig } from '../_functions/deleteActivityProfile/utils/getFileStorageConfig/FileStorageConfig'; import { MongoRecordStorageConfig } from '../_functions/deleteActivityProfile/utils/getRecordStorageConfig/RecordStorageConfig'; import { TrackingConfig } from '../_functions/deleteActivityProfile/utils/getTrackingConfig/TrackingConfig'; @@ -10,6 +11,7 @@ interface Config extends CommonExpressConfig { readonly service: Service; readonly translator: Translator; readonly tracker: Promise; + readonly authConfig: AuthConfig; readonly fileStorageConfig: FileStorageConfig; readonly recordStorageConfig: MongoRecordStorageConfig; readonly trackingConfig: TrackingConfig; diff --git a/src/apps/activities/expressPresenter/tests/utils/supertest.ts b/src/apps/activities/expressPresenter/tests/utils/supertest.ts index 5c14af5c7..7c292d6b5 100644 --- a/src/apps/activities/expressPresenter/tests/utils/supertest.ts +++ b/src/apps/activities/expressPresenter/tests/utils/supertest.ts @@ -3,6 +3,7 @@ import supertest from 'supertest'; import config from '../../../../../config'; import logger from '../../../../../logger'; import tracker from '../../../../../tracker'; +import { getAuthConfig } from '../../../_functions/deleteActivityProfile/utils/getAuthConfig/getAuthConfig'; import { getFileStorageConfig } from '../../../_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig'; import { getRecordStorageConfig } from '../../../_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig'; import { getTrackingConfig } from '../../../_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig'; @@ -22,6 +23,7 @@ const presenter = presenterFacade({ service, tracker, translator, + authConfig: getAuthConfig(), fileStorageConfig: getFileStorageConfig(), recordStorageConfig: getRecordStorageConfig(), trackingConfig: getTrackingConfig(), diff --git a/src/apps/activities/models/ClientModel.ts b/src/apps/activities/models/ClientModel.ts index e2c2d906f..4a8969641 100644 --- a/src/apps/activities/models/ClientModel.ts +++ b/src/apps/activities/models/ClientModel.ts @@ -1,4 +1,4 @@ -interface Model { +interface ClientModel { readonly _id: string; readonly organisation: string; readonly lrs_id: string; @@ -6,4 +6,4 @@ interface Model { readonly scopes: string[]; } -export default Model; +export default ClientModel; diff --git a/src/apps/app.ts b/src/apps/app.ts index 6230f0976..b218366bc 100644 --- a/src/apps/app.ts +++ b/src/apps/app.ts @@ -25,6 +25,7 @@ export default (appConfig: AppConfig): Router => { storageSubFolder: appConfig.repo.storageSubFolders.activities, }, tracker: appConfig.tracker, + authConfig: appConfig.authConfig, fileStorageConfig: appConfig.fileStorageConfig, recordStorageConfig: appConfig.recordStorageConfig, trackingConfig: appConfig.trackingConfig, diff --git a/src/apps/statements/tests/storingStatements/queriables/getMetadataFromStatement/getSequencingMetadata.test.ts b/src/apps/statements/tests/storingStatements/queriables/getMetadataFromStatement/getSequencingMetadata.test.ts index 8d23b209a..6453a6284 100644 --- a/src/apps/statements/tests/storingStatements/queriables/getMetadataFromStatement/getSequencingMetadata.test.ts +++ b/src/apps/statements/tests/storingStatements/queriables/getMetadataFromStatement/getSequencingMetadata.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert'; -import Statement from 'src/apps/statements/models/Statement'; +import Statement from '../../../../models/Statement'; import { getSequencingMetadata } from '../../../../service/storeStatements/queriables/getMetadataFromStatement/getSequencingMetadata'; import { sequencingInteractionActivityStatement, statementDefaults } from './fixtures/statements.fixture'; diff --git a/src/server.ts b/src/server.ts index 962ebd9f7..2cd81b8df 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,15 +1,16 @@ import * as sourceMapSupport from 'source-map-support'; // tslint:disable-line:ordered-imports sourceMapSupport.install(); -import tracker from './tracker'; import express from 'express'; // tslint:disable-line:ordered-imports import handleListen from 'jscommons/dist/expressPresenter/utils/handleListen'; +import { getAuthConfig } from './apps/activities/_functions/deleteActivityProfile/utils/getAuthConfig/getAuthConfig'; import { getFileStorageConfig } from './apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig'; import { getRecordStorageConfig } from './apps/activities/_functions/deleteActivityProfile/utils/getRecordStorageConfig/getRecordStorageConfig'; import { getTrackingConfig } from './apps/activities/_functions/deleteActivityProfile/utils/getTrackingConfig/getTrackingConfig'; import app from './apps/app'; import config from './config'; import logger from './logger'; +import tracker from './tracker'; import connectToMongoDb from './utils/connectToMongoDb'; import connectToRedis from './utils/connectToRedis'; import connectToSentinel from './utils/connectToSentinel'; @@ -45,6 +46,7 @@ expressApp.use(app({ statements: config.statementsService, }, tracker, + authConfig: getAuthConfig(), fileStorageConfig: getFileStorageConfig(), recordStorageConfig: getRecordStorageConfig(), trackingConfig: getTrackingConfig(), From dd572082b245cc3fbfce18d407396bb0c6fb0039 Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Mon, 14 Dec 2020 07:23:19 +0000 Subject: [PATCH 07/10] fix: Handles errors correctly --- .../deleteActivityProfileRecordFromMongo.ts | 4 +- .../deleteActivityProfileViaHttp.ts | 76 +++++++++++++++---- .../utils/createExpressHandler.ts | 11 +-- .../utils/translateWarning.ts | 31 ++++++++ 4 files changed, 98 insertions(+), 24 deletions(-) create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/translateWarning.ts diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileRecordFromMongo.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileRecordFromMongo.ts index f0bf6c40e..3d395dc04 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileRecordFromMongo.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileRecordFromMongo.ts @@ -54,8 +54,8 @@ export async function deleteActivityProfileRecordFromMongo( }); // Returns the result of the deletion if the document was deleted. - if (opResult.value !== undefined) { - const deletedDoc = opResult.value; + const deletedDoc = opResult.value as MongoActivityProfileDoc | null; + if (deletedDoc !== null) { await client.close(); return { contentType: deletedDoc.contentType, diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts index b9d267c9a..a4e4e9a85 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts @@ -1,9 +1,13 @@ -import { NO_CONTENT } from 'http-status-codes'; +import { BAD_REQUEST, NO_CONTENT, UNAUTHORIZED } from 'http-status-codes'; +import NoModel from 'jscommons/dist/errors/NoModel'; +import Unauthorised from 'jscommons/dist/errors/Unauthorised'; +import { Warnings } from 'rulr'; +import stringToStream from 'string-to-stream'; import getActivityId from '../../expressPresenter/utils/getActivityId'; import getEtag from '../../expressPresenter/utils/getEtag'; import getProfileId from '../../expressPresenter/utils/getProfileId'; import validateVersionHeader from '../../expressPresenter/utils/validateVersionHeader'; -import { xapiHeaderVersion } from '../../utils/constants'; +import { jsonContentType, xapiHeaderVersion } from '../../utils/constants'; import { deleteActivityProfile } from './deleteActivityProfile'; import { AuthConfig } from './utils/getAuthConfig/AuthConfig'; import { getClient } from './utils/getClient/getClient'; @@ -11,6 +15,7 @@ import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfi import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; import { TrackingConfig } from './utils/getTrackingConfig/TrackingConfig'; import { HttpRequest, HttpResponse } from './utils/HttpInterfaces'; +import { translateWarning } from './utils/translateWarning'; export interface DeleteActivityProfileViaHttpConfig { readonly authConfig: AuthConfig; @@ -23,18 +28,61 @@ export async function deleteActivityProfileViaHttp( config: DeleteActivityProfileViaHttpConfig, req: HttpRequest, ): Promise { - const client = await getClient(config, req.headers.authorization); - validateVersionHeader(req.headers['x-experience-api-version']); + try { + const client = await getClient(config, req.headers.authorization); + validateVersionHeader(req.headers['x-experience-api-version']); - const ifMatch = getEtag(req.headers['if-match']); - const activityId = getActivityId(req.query.activityId); - const profileId = getProfileId(req.query.profileId); + const ifMatch = getEtag(req.headers['if-match']); + const activityId = getActivityId(req.query.activityId); + const profileId = getProfileId(req.query.profileId); - await deleteActivityProfile(config, { activityId, client, profileId, ifMatch }); - return { - statusCode: NO_CONTENT, - headers: { - 'X-Experience-API-Version': xapiHeaderVersion, - }, - }; + await deleteActivityProfile(config, { activityId, client, profileId, ifMatch }); + return { + statusCode: NO_CONTENT, + headers: { + 'X-Experience-API-Version': xapiHeaderVersion, + }, + }; + } catch (err) { + if (err instanceof Unauthorised) { + return { + statusCode: UNAUTHORIZED, + headers: { + 'X-Experience-API-Version': xapiHeaderVersion, + 'Content-Type': jsonContentType, + }, + body: stringToStream(JSON.stringify({ + message: 'Unauthorized', + requestId: req.requestId, + })), + }; + } + if (err instanceof NoModel) { + return { + statusCode: UNAUTHORIZED, + headers: { + 'X-Experience-API-Version': xapiHeaderVersion, + 'Content-Type': jsonContentType, + }, + body: stringToStream(JSON.stringify({ + message: `No ${err.modelName} found`, + requestId: req.requestId, + })), + }; + } + if (err instanceof Warnings) { + return { + statusCode: BAD_REQUEST, + headers: { + 'X-Experience-API-Version': xapiHeaderVersion, + 'Content-Type': jsonContentType, + }, + body: stringToStream(JSON.stringify({ + warnings: err.warnings.map(translateWarning), + requestId: req.requestId, + })), + }; + } + throw err; + } } diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts index dcf0ec8ae..e5bf68937 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts @@ -37,10 +37,10 @@ export function createExpressHandler(handler: HttpHandler) { return (config: Config) => { return async (req: express.Request, res: express.Response) => { const requestId = createV4UUID(); - const headers = getExpressRequestHeaders(req); - const query = getExpressRequestQuery(req); try { const body = req; + const headers = getExpressRequestHeaders(req); + const query = getExpressRequestQuery(req); const response = await handler(config, { requestId, query, body, headers }); res.writeHead(response.statusCode, response.headers); if (response.body !== undefined) { @@ -49,12 +49,7 @@ export function createExpressHandler(handler: HttpHandler) { res.end(); } catch (err) { // tslint:disable-next-line: no-console - console.error({ - requestId, - errorMessage: err?.message, - requestHeaders: headers, - errorStack: err?.stack, - }); + console.error(requestId, err); res.status(INTERNAL_SERVER_ERROR); res.send(`Internal Server Error ${requestId}`); } diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/translateWarning.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/translateWarning.ts new file mode 100644 index 000000000..5549e4c4a --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/translateWarning.ts @@ -0,0 +1,31 @@ +import XapiTypeWarning from '@learninglocker/xapi-validation/dist/warnings/TypeWarning'; +import stringPath from 'jscommons/dist/translatorFactory/utils/stringPath'; +import { RequiredWarning, RestrictedKeysWarning, TypeWarning, Warning } from 'rulr'; + +export function translateWarning(warning: Warning) { + if (warning instanceof XapiTypeWarning) { + const path = stringPath(warning.path); + const dataString = JSON.stringify(warning.data); + return `Expected ${warning.typeName} in ${path}. Received '${dataString}'`; + } + if (warning instanceof TypeWarning) { + const path = stringPath(warning.path); + const typeName = warning.type.name; + const dataString = JSON.stringify(warning.data); + return `Expected '${path}' to be '${typeName}'. Received '${dataString}'`; + } + if (warning instanceof RequiredWarning) { + const path = stringPath(warning.path); + return `Missing required value in '${path}'`; + } + if (warning instanceof RestrictedKeysWarning) { + const path = stringPath(warning.path); + const keys = warning.keys.join(', '); + return `Unknown keys (${keys}) set in '${path}'`; + } + { + const path = stringPath(warning.path); + const dataString = JSON.stringify(warning.data); + return `Problem in '${path}'. Received '${dataString}'`; + } +} From 8208d717fd4afc6cfc394aa02547115fc710f908 Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Mon, 14 Dec 2020 09:01:58 +0000 Subject: [PATCH 08/10] fix: Fixes tests --- .../deleteActivityProfileViaHttp.ts | 76 +++------------- .../utils/createExpressHandler.ts | 8 +- .../getClient/trackClientUsageInNewRelic.ts | 1 + .../getFileStorageConfig.ts | 6 +- .../utils/handleErrorViaHttp.ts | 89 +++++++++++++++++++ 5 files changed, 113 insertions(+), 67 deletions(-) create mode 100644 src/apps/activities/_functions/deleteActivityProfile/utils/handleErrorViaHttp.ts diff --git a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts index a4e4e9a85..b9d267c9a 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileViaHttp.ts @@ -1,13 +1,9 @@ -import { BAD_REQUEST, NO_CONTENT, UNAUTHORIZED } from 'http-status-codes'; -import NoModel from 'jscommons/dist/errors/NoModel'; -import Unauthorised from 'jscommons/dist/errors/Unauthorised'; -import { Warnings } from 'rulr'; -import stringToStream from 'string-to-stream'; +import { NO_CONTENT } from 'http-status-codes'; import getActivityId from '../../expressPresenter/utils/getActivityId'; import getEtag from '../../expressPresenter/utils/getEtag'; import getProfileId from '../../expressPresenter/utils/getProfileId'; import validateVersionHeader from '../../expressPresenter/utils/validateVersionHeader'; -import { jsonContentType, xapiHeaderVersion } from '../../utils/constants'; +import { xapiHeaderVersion } from '../../utils/constants'; import { deleteActivityProfile } from './deleteActivityProfile'; import { AuthConfig } from './utils/getAuthConfig/AuthConfig'; import { getClient } from './utils/getClient/getClient'; @@ -15,7 +11,6 @@ import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfi import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig'; import { TrackingConfig } from './utils/getTrackingConfig/TrackingConfig'; import { HttpRequest, HttpResponse } from './utils/HttpInterfaces'; -import { translateWarning } from './utils/translateWarning'; export interface DeleteActivityProfileViaHttpConfig { readonly authConfig: AuthConfig; @@ -28,61 +23,18 @@ export async function deleteActivityProfileViaHttp( config: DeleteActivityProfileViaHttpConfig, req: HttpRequest, ): Promise { - try { - const client = await getClient(config, req.headers.authorization); - validateVersionHeader(req.headers['x-experience-api-version']); + const client = await getClient(config, req.headers.authorization); + validateVersionHeader(req.headers['x-experience-api-version']); - const ifMatch = getEtag(req.headers['if-match']); - const activityId = getActivityId(req.query.activityId); - const profileId = getProfileId(req.query.profileId); + const ifMatch = getEtag(req.headers['if-match']); + const activityId = getActivityId(req.query.activityId); + const profileId = getProfileId(req.query.profileId); - await deleteActivityProfile(config, { activityId, client, profileId, ifMatch }); - return { - statusCode: NO_CONTENT, - headers: { - 'X-Experience-API-Version': xapiHeaderVersion, - }, - }; - } catch (err) { - if (err instanceof Unauthorised) { - return { - statusCode: UNAUTHORIZED, - headers: { - 'X-Experience-API-Version': xapiHeaderVersion, - 'Content-Type': jsonContentType, - }, - body: stringToStream(JSON.stringify({ - message: 'Unauthorized', - requestId: req.requestId, - })), - }; - } - if (err instanceof NoModel) { - return { - statusCode: UNAUTHORIZED, - headers: { - 'X-Experience-API-Version': xapiHeaderVersion, - 'Content-Type': jsonContentType, - }, - body: stringToStream(JSON.stringify({ - message: `No ${err.modelName} found`, - requestId: req.requestId, - })), - }; - } - if (err instanceof Warnings) { - return { - statusCode: BAD_REQUEST, - headers: { - 'X-Experience-API-Version': xapiHeaderVersion, - 'Content-Type': jsonContentType, - }, - body: stringToStream(JSON.stringify({ - warnings: err.warnings.map(translateWarning), - requestId: req.requestId, - })), - }; - } - throw err; - } + await deleteActivityProfile(config, { activityId, client, profileId, ifMatch }); + return { + statusCode: NO_CONTENT, + headers: { + 'X-Experience-API-Version': xapiHeaderVersion, + }, + }; } diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts index e5bf68937..6659ed230 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts @@ -1,7 +1,8 @@ import express from 'express'; import { INTERNAL_SERVER_ERROR } from 'http-status-codes'; import { v4 as createV4UUID } from 'uuid'; -import { HttpHandler, HttpHeaders, HttpQueryParams } from './HttpInterfaces'; +import { handleErrorViaHttp } from './handleErrorViaHttp'; +import { HttpHandler, HttpHeaders, HttpQueryParams, HttpRequest } from './HttpInterfaces'; function getExpressRequestHeaders(req: express.Request) { return Object.keys(req.headers).reduce((result, header) => { @@ -41,7 +42,10 @@ export function createExpressHandler(handler: HttpHandler) { const body = req; const headers = getExpressRequestHeaders(req); const query = getExpressRequestQuery(req); - const response = await handler(config, { requestId, query, body, headers }); + const request: HttpRequest = { requestId, query, body, headers }; + const response = await handler(config, request).catch((err) => { + return handleErrorViaHttp(request, err); + }); res.writeHead(response.statusCode, response.headers); if (response.body !== undefined) { response.body.pipe(res); diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsageInNewRelic.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsageInNewRelic.ts index a8e5bf2f3..aa6a37567 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsageInNewRelic.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getClient/trackClientUsageInNewRelic.ts @@ -1,5 +1,6 @@ import { TrackingOptions } from './trackClientUsage'; +/* istanbul ignore next */ export async function trackInNewRelic(opts: TrackingOptions) { const newrelic = await import('newrelic'); newrelic.addCustomAttribute('org_id', opts.organisationId); diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts index aa8ad67cb..cd983d321 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/getFileStorageConfig/getFileStorageConfig.ts @@ -8,14 +8,14 @@ export function getFileStorageConfig(): FileStorageConfig { azureAccount: config.azureStorageRepo.account, azureAccountKey: config.azureStorageRepo.accountKey, azureContainerName: config.azureStorageRepo.containerName, - azureSubFolder: config.storageSubFolders.activities, + azureSubFolder: config.azureStorageRepo.subFolder.replace(/^\//, ''), }; case FileStorageProvider.Google: return { fileStorageProvider: FileStorageProvider.Google, googleBucketName: config.googleStorageRepo.bucketName, googleKeyFileName: config.googleStorageRepo.keyFileName, googleProjectId: config.googleStorageRepo.projectId, - googleSubFolder: config.storageSubFolders.activities, + googleSubFolder: config.googleStorageRepo.subFolder.replace(/^\//, ''), }; case FileStorageProvider.S3: return { fileStorageProvider: FileStorageProvider.S3, @@ -29,7 +29,7 @@ export function getFileStorageConfig(): FileStorageConfig { }; default: case FileStorageProvider.Local: return { fileStorageProvider: FileStorageProvider.Local, - localStorageDir: `${config.localStorageRepo.storageDir}/${config.storageSubFolders.activities}`, + localStorageDir: config.localStorageRepo.storageDir, }; } } diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/handleErrorViaHttp.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/handleErrorViaHttp.ts new file mode 100644 index 000000000..2285033ca --- /dev/null +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/handleErrorViaHttp.ts @@ -0,0 +1,89 @@ +import { + BAD_REQUEST, + FORBIDDEN, + INTERNAL_SERVER_ERROR, + NOT_FOUND, + PRECONDITION_FAILED, + UNAUTHORIZED, +} from 'http-status-codes'; +import Forbidden from 'jscommons/dist/errors/Forbidden'; +import NoModel from 'jscommons/dist/errors/NoModel'; +import Unauthorised from 'jscommons/dist/errors/Unauthorised'; +import { Warnings } from 'rulr'; +import stringToStream from 'string-to-stream'; +import ExpiredClientError from '../../../errors/ExpiredClientError'; +import IfMatch from '../../../errors/IfMatch'; +import IfNoneMatch from '../../../errors/IfNoneMatch'; +import UntrustedClientError from '../../../errors/UntrustedClientError'; +import { jsonContentType, xapiHeaderVersion } from '../../../utils/constants'; +import { HttpRequest, HttpResponse } from './HttpInterfaces'; +import { translateWarning } from './translateWarning'; + +function createErrorResponse(statusCode: number, jsonBody: object): HttpResponse { + return { + statusCode, + headers: { + 'X-Experience-API-Version': xapiHeaderVersion, + 'Content-Type': jsonContentType, + }, + body: stringToStream(JSON.stringify(jsonBody)), + }; +} + +// tslint:disable-next-line: no-any +export function handleErrorViaHttp(req: HttpRequest, err?: any) { + if (err instanceof Unauthorised) { + return createErrorResponse(UNAUTHORIZED, { + message: 'Unauthorized', + requestId: req.requestId, + }); + } + if (err instanceof NoModel) { + return createErrorResponse(NOT_FOUND, { + message: `No ${err.modelName} found`, + requestId: req.requestId, + }); + } + if (err instanceof Warnings) { + return createErrorResponse(BAD_REQUEST, { + warnings: err.warnings.map(translateWarning), + requestId: req.requestId, + }); + } + if (err instanceof IfMatch) { + return createErrorResponse(PRECONDITION_FAILED, { + message: 'IfMatch does not match Etag because a modification has been made since it was retrieved', + requestId: req.requestId, + }); + } + if (err instanceof IfNoneMatch) { + return createErrorResponse(PRECONDITION_FAILED, { + message: 'IfNoneMatch was used to detect that the resource was already present', + requestId: req.requestId, + }); + } + if (err instanceof Forbidden) { + return createErrorResponse(FORBIDDEN, { + message: 'Forbidden', + requestId: req.requestId, + }); + } + if (err instanceof ExpiredClientError) { + return createErrorResponse(FORBIDDEN, { + message: 'Your organisation has expired', + requestId: req.requestId, + }); + } + if (err instanceof UntrustedClientError) { + return createErrorResponse(FORBIDDEN, { + message: 'Your client has been disabled', + requestId: req.requestId, + }); + } + // tslint:disable-next-line: no-console + console.error(req.requestId, err); + return createErrorResponse(INTERNAL_SERVER_ERROR, { + message: 'A server error occurred', + requestId: req.requestId, + }); +} From 789722954a51457ad85db568b7389de0262218ff Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Mon, 14 Dec 2020 15:54:46 +0000 Subject: [PATCH 09/10] chore: Removes unused folder --- scripts/updateDeps.js | 58 ------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 scripts/updateDeps.js diff --git a/scripts/updateDeps.js b/scripts/updateDeps.js deleted file mode 100644 index e388e127c..000000000 --- a/scripts/updateDeps.js +++ /dev/null @@ -1,58 +0,0 @@ -const git = require('simple-git/promise')(); -const rimraf = require('rimraf'); -const { promisify } = require('util'); -const shelljs = require('shelljs'); -const colors = require('colors'); -const { join } = require('path'); - -const getCommitMessage = () => { - const package = require(join(process.cwd(), 'package.json')); - const getDepMessage = (dep) => { - const versionRange = package.dependencies[`@learninglocker/xapi-${dep}`]; - const version = versionRange.replace('^', 'v'); - const releaseLink = `https://github.com/LearningLocker/xapi-${dep}/releases/tag/${version}`; - return `[${dep} ${version}](${releaseLink})`; - }; - const semverMessage = process.argv[2]; - const deps = ['activities', 'agents', 'state', 'statements']; - const depsMessage = deps.map(getDepMessage).join(', '); - return `${semverMessage} Includes ${depsMessage}.`; -}; - -const exec = (command) => { - return new Promise((resolve, reject) => { - console.log(colors.cyan(`Starting: ${command}`)); - const child = shelljs.exec(command, { async: true }, (code) => { - if (code !== 0) { - console.log(colors.cyan(`Completed unsuccessfully: ${command}`)); - reject(new Error()); - return; - } - console.log(colors.cyan(`Completed successfully: ${command}`)); - resolve(); - }); - }); -}; - -const main = async () => { - await git.fetch(); - await git.checkout('master'); - await git.pull(); - await exec('git branch -D xapi-deps'); - await git.checkoutLocalBranch('xapi-deps'); - await promisify(rimraf)(join(process.cwd(), 'node_modules')); - await exec('yarn install --ignore-engines'); - await exec('npm i @learninglocker/xapi-activities@latest @learninglocker/xapi-agents@latest @learninglocker/xapi-state@latest @learninglocker/xapi-statements@latest'); - await exec('yarn add --ignore-engines @learninglocker/xapi-activities@latest @learninglocker/xapi-agents@latest @learninglocker/xapi-state@latest @learninglocker/xapi-statements@latest'); - await git.add('./*'); - await git.commit(getCommitMessage()); - await git.push('origin', 'xapi-deps'); -}; - -main().then(() => { - console.log(colors.green('Completed successfully')); -}).catch((err) => { - console.error(colors.red(err)); - console.log(colors.red('Completed unsuccessfully')); -}); -// node scripts/updateDeps.js "fix: message" From a4b021604851309a0737df3a0e7666ecd7614d16 Mon Sep 17 00:00:00 2001 From: Ryan Smith <0ryansmith1994@gmail.com> Date: Wed, 10 Mar 2021 14:32:24 +0000 Subject: [PATCH 10/10] fix: Corrects org collection name --- .../utils/mongoDocInterfaces/MongoOrgDoc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOrgDoc.ts b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOrgDoc.ts index 328f72803..4f1e6da2f 100644 --- a/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOrgDoc.ts +++ b/src/apps/activities/_functions/deleteActivityProfile/utils/mongoDocInterfaces/MongoOrgDoc.ts @@ -3,4 +3,4 @@ export interface MongoOrgDoc { readonly _id: ObjectID; readonly expiration: Date | null; } -export const mongoOrgCollectionName = 'organisation'; +export const mongoOrgCollectionName = 'organisations';