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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 186 additions & 3 deletions src/controllers/report-builder/folder.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
} from '@loopback/rest';
import _ from 'lodash';
import {FolderModel} from 'rb-core-middleware/dist/models';
import {FolderService} from 'rb-core-middleware/dist/services';
import {FolderService, ReportService} from 'rb-core-middleware/dist/services';
import {Logger} from 'winston';

export class FolderController {
constructor(
@inject(RestBindings.Http.REQUEST) private req: Request,
@inject('services.logger') private logger: Logger,
@inject('services.FolderService') private folderService: FolderService,
@inject('services.ReportService') private reportService: ReportService,
) {}

@post('/folder')
Expand Down Expand Up @@ -67,12 +68,69 @@
// @authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})
async find(
@param.filter(FolderModel) filter?: Filter<FolderModel>,
): Promise<FolderModel[]> {
@param.query.string('includeSubFolders') includeSubFolders?: string,
): Promise<any[]> {
const userId = _.get(this.req, 'user.sub', 'anonymous');
this.logger.info(
`FolderController - find - Fetching folders for user ${userId}`,
);
return this.folderService.find(userId, filter);
if (Boolean(includeSubFolders)) {
return this.folderService.getAllFoldersWithSubfolders(userId, filter);
} else {
const folders = await this.folderService.find(userId, filter);
const folderById = new Map<string, FolderModel>(
folders.map(f => [f.id, f]),
);
const pathCache = new Map<string, string>();
const buildFolderPath = (folderId: string | undefined): string => {
if (!folderId) return 'My Workspace';
const cached = pathCache.get(folderId);
if (cached !== undefined) return cached;
const folder = folderById.get(folderId);
if (!folder) return 'My Workspace';
const parentPath = buildFolderPath(folder.parentId);
const path =
parentPath === 'My Workspace'
? `My Workspace > ${folder.name}`
: `${parentPath} > ${folder.name}`;
pathCache.set(folderId, path);
return path;
};

const foldersWithPath = _.filter(folders, folder => !folder.parentId).map(
folder => {
const locationPath = buildFolderPath(folder.parentId);
return {
...folder,
locationPath,
};
},
);

Check warning on line 108 in src/controllers/report-builder/folder.controller.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Immediately return this expression instead of assigning it to the temporary variable "foldersWithPath".

See more on https://sonarcloud.io/project/issues?id=globalfund_data-explorer-server&issues=AZ5JkmpO0B7yMr1W3GFR&open=AZ5JkmpO0B7yMr1W3GFR&pullRequest=60
return foldersWithPath;
}
}

@get('/folders-structure')
@response(200, {
description: 'Array of FolderModel instances',
content: {
'application/json': {
schema: {
type: 'array',
items: getModelSchemaRef(FolderModel, {includeRelations: true}),
},
},
},
})
// @authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})
async getFoldersStructure(
@param.filter(FolderModel) filter?: Filter<FolderModel>,

Check warning on line 127 in src/controllers/report-builder/folder.controller.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "filter" or rename it to "_filter" to make intention explicit.

See more on https://sonarcloud.io/project/issues?id=globalfund_data-explorer-server&issues=AZ5JkmpO0B7yMr1W3GFS&open=AZ5JkmpO0B7yMr1W3GFS&pullRequest=60
): Promise<FolderModel[]> {
const userId = _.get(this.req, 'user.sub', 'anonymous');
this.logger.info(
`FolderController - getFoldersStructure - Fetching folder structure for user ${userId}`,
);
return this.folderService.getFolderTree(userId);
}

@get('/folder/{id}')
Expand Down Expand Up @@ -173,4 +231,129 @@
);
return this.folderService.duplicate(userId, id);
}

@get('/folder/add-asset/{id}')
@response(200, {
description: 'FolderModel instance',
content: {
'application/json': {
schema: getModelSchemaRef(FolderModel, {includeRelations: true}),
},
},
})
// @authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})
async addAssetToFolder(
@param.path.string('id') id: string,
@param.query.string('assetId') assetId: string,
): Promise<FolderModel | {error: string; errorType: string}> {
const userId = _.get(this.req, 'user.sub', 'anonymous');
this.logger.info(
`FolderController - addAssetToFolder - Adding asset ${assetId} to folder ${id} for user ${userId}`,
);
return this.folderService.addAsset(userId, id, assetId);
}

@get('/folder/remove-asset/{id}')
@response(200, {
description: 'FolderModel instance',
content: {
'application/json': {
schema: getModelSchemaRef(FolderModel, {includeRelations: true}),
},
},
})
// @authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})
async removeAssetFromFolder(
@param.path.string('id') id: string,
@param.query.string('assetId') assetId: string,
): Promise<FolderModel | {error: string; errorType: string}> {
const userId = _.get(this.req, 'user.sub', 'anonymous');
this.logger.info(
`FolderController - removeAssetFromFolder - Removing asset ${assetId} from folder ${id} for user ${userId}`,
);
return this.folderService.removeAsset(userId, id, assetId);
}

@get('/folder/add-report/{id}')
@response(200, {
description: 'FolderModel instance',
content: {
'application/json': {
schema: getModelSchemaRef(FolderModel, {includeRelations: true}),
},
},
})
// @authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})
async addReportToFolder(
@param.path.string('id') id: string,
@param.query.string('reportId') reportId: string,
): Promise<FolderModel | {error: string; errorType: string}> {
const userId = _.get(this.req, 'user.sub', 'anonymous');
this.logger.info(
`FolderController - addReportToFolder - Adding report ${reportId} to folder ${id} for user ${userId}`,
);
const reportPrevFolder = await this.reportService.findById(
[userId],
reportId,
);
return this.folderService.addReport(
userId,
id,
reportId,
// @ts-ignore
reportPrevFolder?.folderId ?? undefined,
);
}

@get('/folder/remove-report/{id}')
@response(200, {
description: 'FolderModel instance',
content: {
'application/json': {
schema: getModelSchemaRef(FolderModel, {includeRelations: true}),
},
},
})
// @authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})
async removeReportFromFolder(
@param.path.string('id') id: string,
@param.query.string('reportId') reportId: string,
): Promise<FolderModel | {error: string; errorType: string}> {
const userId = _.get(this.req, 'user.sub', 'anonymous');
this.logger.info(
`FolderController - removeReportFromFolder - Removing report ${reportId} from folder ${id} for user ${userId}`,
);
return this.folderService.removeReport(userId, id, reportId);
}

@get('/folder/add-folder/{id}')
@response(200, {
description: 'FolderModel instance',
content: {
'application/json': {
schema: getModelSchemaRef(FolderModel, {includeRelations: true}),
},
},
})
// @authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})
async addFolderToFolder(
@param.path.string('id') id: string,
@param.query.string('folderId') folderId: string,
): Promise<FolderModel | {error: string; errorType: string}> {
const userId = _.get(this.req, 'user.sub', 'anonymous');
this.logger.info(
`FolderController - addFolderToFolder - Adding folder ${folderId} to folder ${id} for user ${userId}`,
);
const folderPrevFolder = await this.folderService.findById(
[userId],
folderId,
);
return this.folderService.addFolder(
userId,
id,
folderId,
// @ts-ignore
folderPrevFolder?.folderId ?? undefined,
);
}
}
136 changes: 91 additions & 45 deletions src/controllers/report-builder/report.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import {
import axios, {AxiosResponse} from 'axios';
import fs from 'fs/promises';
import _ from 'lodash';
import {ReportModel} from 'rb-core-middleware/dist/models';
import {ReportService} from 'rb-core-middleware/dist/services';
import {FolderModel, ReportModel} from 'rb-core-middleware/dist/models';
import {FolderService, ReportService} from 'rb-core-middleware/dist/services';
import {Logger} from 'winston';
import {queueReportThumbnailGeneration} from '../../queues/report.queue';
import {handleDataApiError} from '../../utils/dataApiError';
Expand All @@ -32,6 +32,7 @@ export class ReportController {
@inject(RestBindings.Http.RESPONSE) private response: Response,
@inject('services.logger') private logger: Logger,
@inject('services.ReportService') private reportService: ReportService,
@inject('services.FolderService') private folderService: FolderService,
) {}

@post('/report/render-chart-data')
Expand All @@ -44,47 +45,6 @@ export class ReportController {
}
}

@get('/report/dummy')
@response(200)
async dummy() {
this.logger.info('ReportController - dummy - Dummy endpoint called');
return this.reportService.create('dummy-user', {
name: 'Dummy Report',
nameLower: 'dummy report',
description: 'This is a dummy report',
items: [],
public: false,
baseline: false,
owner: 'dummy-user',
updatedDate: new Date().toISOString(),
createdDate: new Date().toISOString(),
settings: {
width: 800,
height: 600,
paddingLeft: 10,
paddingTop: 10,
paddingRight: 10,
paddingBottom: 10,
stroke: 0,
strokeColor: '#000000',
backgroundColor: '#FFFFFF',
borderRadius: 0,
},
getId: function () {
return '';
},
getIdObject: function () {
return {};
},
toJSON: function () {
return {};
},
toObject: function () {
return {};
},
});
}

@post('/report')
@response(200, {
description: 'ReportModel instance',
Expand Down Expand Up @@ -140,12 +100,98 @@ export class ReportController {
// @authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})
async find(
@param.filter(ReportModel) filter?: Filter<ReportModel>,
): Promise<ReportModel[]> {
@param.query.string('onlyRootLevel') onlyRootLevel?: boolean,
@param.filter(FolderModel) folderFilter?: Filter<FolderModel>,
@param.query.string('includeFolders') includeFolders?: boolean,
): Promise<
{
id: string;
name: string;
owner: string;
public: boolean;
description: string;
createdDate: string;
updatedDate: string;
isFolder?: boolean;
assetCount?: number;
reportCount?: number;
locationPath: string;
}[]
> {
const userId = _.get(this.req, 'user.sub', 'anonymous');
this.logger.info(
`ReportController - find - Fetching reports for user ${userId}`,
);
return this.reportService.find(userId, filter);
const reports = await this.reportService.find(userId, filter);
const allFolders = await this.folderService.find(userId, folderFilter);

const folderById = new Map<string, FolderModel>(
allFolders.map(f => [f.id, f]),
);
const pathCache = new Map<string, string>();
const buildFolderPath = (folderId: string | undefined): string => {
if (!folderId) return 'My Workspace';
const cached = pathCache.get(folderId);
if (cached !== undefined) return cached;
const folder = folderById.get(folderId);
if (!folder) return 'My Workspace';
const parentPath = buildFolderPath(folder.parentId);
const path =
parentPath === 'My Workspace'
? `My Workspace > ${folder.name}`
: `${parentPath} > ${folder.name}`;
pathCache.set(folderId, path);
return path;
};

const reportsWithPath = _.filter(
reports,
report => !onlyRootLevel || !report.folderId,
).map(report => {
const locationPath = buildFolderPath(report.folderId);
return {
..._.omit(report, ['folderId']),
locationPath,
};
});

const foldersWithPath = _.filter(
allFolders,
folder => !onlyRootLevel || !folder.parentId,
).map(folder => {
const locationPath = buildFolderPath(folder.parentId);
return {
..._.omit(folder, ['parentId']),
locationPath,
};
});

if (includeFolders) {
const orderFilter = _.get(filter, 'order[0]', 'createdDate DESC');
const [orderByField, orderByDirection] = orderFilter.split(' ');
return _.orderBy(
[
...reportsWithPath,
...foldersWithPath.map(folder => ({
id: folder.id,
name: folder.name,
public: false,
owner: folder.owner,
createdDate: folder.createdDate,
updatedDate: folder.updatedDate,
description: '',
isFolder: true,
assetCount: folder.assets ? folder.assets.length : 0,
reportCount: folder.reports ? folder.reports.length : 0,
folderCount: folder.children ? folder.children.length : 0,
locationPath: folder.locationPath,
})),
],
[orderByField],
[orderByDirection.toLowerCase() as 'asc' | 'desc'],
);
}
return reportsWithPath;
}

@get('/report/{id}')
Expand Down