UNPKG

@researchdatabox/sails-hook-redbox-omero

Version:

A Sails hook to add functionality to redbox that provisions OMERO

493 lines (459 loc) 18.6 kB
declare var module; declare var sails, Model; declare var _; import {Observable, of} from 'rxjs'; declare var BrandingService, WorkspaceService, RecordsService, OMEROService; /** * Package that contains all Controllers. */ import { Controllers as controller} from '@researchdatabox/redbox-core-types'; export module Controllers { /** * Omero related features.... * */ export class OMEROController extends controller.Core.Controller { protected _exportedMethods: any = [ 'info', 'login', 'projects', 'create', 'link', 'checkLink', 'images' ]; protected config: any; constructor() { super(); this.config = new Config(); } public info(req, res) { this.config.set(); this.ajaxOk(req, res, null, {host: this.config.host, status: true}); } login(req, res) { this.config.set(); if (!req.isAuthenticated()) { this.ajaxFail(req, res, `User not authenticated`); } else { const user = { username: req.param('username') || '', password: req.param('password') || '' }; let csrf: any = {}; let info = {}; const userId = req.user.id; sails.services.omeroservice.csrf(this.config) .flatMap(response => { csrf = JSON.parse(response); return sails.services.omeroservice.login(this.config, csrf.data, user); }) .flatMap(response => { const cookies = response.headers['set-cookie']; const body = JSON.parse(response.body); const login = body.eventContext; const sessionUuid = login.sessionUuid; const cookieJar = sails.services.omeroservice.getCookies(cookies); info = { csrf: csrf.data, sessionid: sails.services.omeroservice.getCookieValue(cookieJar, 'sessionid'), sessionUuid: sessionUuid, memberOfGroups: login.memberOfGroups, groupId: login.groupId, userId: login.userId }; return WorkspaceService.workspaceAppFromUserId(userId, this.config.appName); }) .flatMap(response => { if (response && response.id) { return WorkspaceService.updateWorkspaceInfo(response.id, info); } else { return WorkspaceService.createWorkspaceInfo(userId, this.config.appName, info); } }) .subscribe(response => { const data = {status: true, login: true}; this.ajaxOk(req, res, null, data); }, error => { const errorMessage = `Failed to login for user ${user.username}`; sails.log.error(errorMessage); sails.log.error(this.config.host + ' => ' + this.config.domain); this.ajaxFail(req, res, errorMessage, {status: false, message: errorMessage}); }); } } projects(req, res) { this.config.set(); if (!req.isAuthenticated()) { this.ajaxFail(req, res, `User not authenticated`); } else { const userId = req.user.id; const limit = req.param('limit') || 10; const offset = req.param('offset') || 0; return WorkspaceService.workspaceAppFromUserId(userId, this.config.appName) .flatMap(response => { sails.log.debug('projects:workspaceAppFromUserId'); if (response.info) { const app = response.info; return sails.services.omeroservice.projects(this.config, app.csrf, app.sessionid, app.sessionUuid, limit, offset, app.userId); } else { throw new Error('Missing application credentials'); } }) .subscribe(response => { sails.log.debug('projects'); const data = {status: true, projects: JSON.parse(response)}; this.ajaxOk(req, res, null, data); }, error => { const errorMessage = `Failed to get projects for user ${req.user.username}`; sails.log.error(errorMessage); this.ajaxFail(req, res, errorMessage, error); }); } } create(req, res) { this.config.set(); if (!req.isAuthenticated()) { this.ajaxFail(req, res, `User not authenticated`); } else { const userId = req.user.id; return WorkspaceService.workspaceAppFromUserId(userId, this.config.appName) .flatMap(response => { sails.log.debug('userInfo'); const appId = this.config.appId; const app = response.info; const project = req.param('creation'); //TODO: add type checking object map here project.type = 'project'; return sails.services.omeroservice.createContainer(this.config, app, project, 'Project', app.groupId || this.config.defaultGroupId); }) .subscribe(response => { sails.log.debug('createProject'); let status = true; let data = {} if (!response.data) { data = {status: false, create: {}}; } else { data = {status: status, create: response.data}; } this.ajaxOk(req, res, null, data); }, error => { const errorMessage = `Failed to create project for user ${req.user.username}`; sails.log.error(errorMessage); this.ajaxFail(req, res, errorMessage, error); }); } } link(req, res) { this.config.set(); if (!req.isAuthenticated()) { this.ajaxFail(req, res, `User not authenticated`); } else { this.config.brandingAndPortalUrl = BrandingService.getFullPath(req); const userId = req.user.id; const username = req.user.username; const project = req.param('project'); const rdmp = req.param('rdmp'); const recordMap = req.param('recordMap'); let annId = null; let mapAnnotation = []; let record = WorkspaceService.mapToRecord(project, recordMap); record = _.merge(record, {type: this.config.recordType}); record.rdmpOid = rdmp; let app = {}; let annotations = []; let rowAnnotation; let idAnnotation; let workspaceId; return WorkspaceService.workspaceAppFromUserId(userId, this.config.appName) .flatMap(response => { sails.log.debug('userInfo'); app = response.info; return sails.services.omeroservice.annotations({config: this.config, app: app, id: record.omeroId}); }).flatMap(response => { sails.log.debug('annotations'); response = (JSON.parse(response)); mapAnnotation = response['annotations']; //Check whether there is a workspace created const ann = this.findAnnotation('stash', mapAnnotation); if (!ann) { rowAnnotation = undefined; idAnnotation = undefined; return this.createAnnotation({ app: app, record: record, rowAnnotation: rowAnnotation, idAnnotation: idAnnotation, annotations: annotations, username: username, rdmp: rdmp, brandingAndPortalUrl: this.config.brandingAndPortalUrl }); } else return Observable.of({body: ann}); }).subscribe(response => { sails.log.debug('linkWorkspace'); const data = {status: true, response}; this.ajaxOk(req, res, null, data); }, error => { const errorMessage = `Failed to link project for user ${req.user.username}`; sails.log.error(errorMessage); this.ajaxFail(req, res, errorMessage, error); }); } } checkLink(req, res) { this.config.set(); if (!req.isAuthenticated()) { this.ajaxFail(req, res, `User not authenticated`); } else { this.config.brandingAndPortalUrl = BrandingService.getFullPath(req); const userId = req.user.id; const rdmpId = req.param('rdmpId'); const omeroId = req.param('omeroId'); let app = {}; let info = {}; let check = { ws: false, omero: false }; let annotations = []; let mapAnnotation = []; return WorkspaceService.workspaceAppFromUserId(userId, this.config.appName) .flatMap(response => { const app = response.info; return sails.services.omeroservice.annotations({ config: this.config, app: app, id: omeroId }) }).subscribe(async (response) => { try { response = JSON.parse(response) || {}; mapAnnotation = response.annotations || null; } catch (e) { mapAnnotation = []; } sails.log.verbose(`OMEROController annotation info: ${JSON.stringify(mapAnnotation)}`) //Check whether there is a workspace created // const ann = _.first(this.findAnnotation('stash', mapAnnotation));// Trying to find an annotation like this: e0582136e22f13059f5d36769282403d.75a2c64ca8c7ad77e9a68adc11e2015b // if (ann) { // info = this.workspaceInfoFromRepo(mapAnnotation[ann.index]['values'][ann.row][1]); // } const annEntry = this.findAnnotation('stash', mapAnnotation);// Trying to find an annotation like this: e0582136e22f13059f5d36769282403d.75a2c64ca8c7ad77e9a68adc11e2015b sails.log.verbose(`OMEROController annotation entry: ${JSON.stringify(annEntry)}`) if (annEntry) { info = this.workspaceInfoFromRepo(annEntry); } const recordInfo = await RecordsService.getMeta(rdmpId); const recordMetadata = recordInfo['metadata']; if (recordMetadata && recordMetadata['workspaces']) { sails.log.verbose(`OMEROController recordMetadata workspaces: ${JSON.stringify(recordMetadata['workspaces'])}`) const wss = recordMetadata.workspaces.find((wrk) => info['workspaceId'] == wrk.id); if (info['rdmpId']) { // the workspace is linked to a record check.ws = true; } if (wss && rdmpId == info['rdmpId']) { // the workspace is linked to this record check.omero = true; } } this.ajaxOk(req, res, null, {status: true, check: check}); }, error => { const errorMessage = `Failed compare link workspace project: ${omeroId}`; //sails.log.error(error); sails.log.error(errorMessage); this.ajaxFail(req, res, errorMessage, error); }); } } createAnnotation({app, record, rowAnnotation, idAnnotation, annotations, username, rdmp, brandingAndPortalUrl}) { sails.log.debug('createWorkspaceRecord'); this.config.set(); let workspaceId = ''; let recordMetadata = null; this.config.brandingAndPortalUrl = brandingAndPortalUrl; return WorkspaceService.getRecordMeta(this.config, rdmp) .flatMap(response => { sails.log.debug('recordMetadata'); recordMetadata = response; record.rdmpTitle = recordMetadata.title; sails.log.debug(record); return WorkspaceService.createWorkspaceRecord(this.config, username, record, this.config.recordType, this.config.workflowStage); }).flatMap(response => { sails.log.verbose(`createAnnotation -> createWorkspaceRecord:`); sails.log.verbose(response); // FIXED: response is now an Axios response, adding '.data' path workspaceId = response.data.workspaceOid; const create = this.mapAnnotation( rowAnnotation, this.getAnnotation(idAnnotation, annotations), 'stash', `${rdmp}.${workspaceId}`, 'stash RDMP', `${brandingAndPortalUrl}/record/view/${rdmp}` ); const annId = idAnnotation || null; const mapAnnotation = create.values; return sails.services.omeroservice.annotateMap({ config: this.config, app: app, id: record.omeroId, annId: annId, mapAnnotation: mapAnnotation }); }) // .flatMap(() => { // if (recordMetadata.workspaces) { // const wss = recordMetadata.workspaces.find(id => workspaceId === id); // if (!wss) { // recordMetadata.workspaces.push({id: workspaceId}); // } // } // return WorkspaceService.updateRecordMeta(this.config, recordMetadata, rdmp); // }); } getAnnotation(id: number, annotations: any) { return annotations.find(an => an.id === id); } mapAnnotation(row: number, annotation: any, key, newValue: string, humanKey: string, humanAnnotation: string) { //OMERO stores annotations as array of arrays. Each element being array[0] property and array[1] value if (annotation) { annotation.values[row.toString()][1] = newValue; return annotation; } else { const annotation = { values: [[key, newValue.toString()], [humanKey, humanAnnotation]] }; return annotation; } } findAnnotation(annotation: string, annotations: any[]) { // 2024-03-26 Refactor, current annotations data structure is array of objects, e.g. : // [ // { // "id": 2854, // "ns": "openmicroscopy.org/omero/client/mapAnnotation", // "description": null, // "owner": { // "id": 552 // }, // "date": "2024-03-26T05:38:49Z", // "permissions": { // "canDelete": true, // "canAnnotate": true, // "canLink": true, // "canEdit": true // }, // "link": { // "id": 603, // "owner": { // "id": 552 // }, // "parent": { // "id": 3153, // "class": "ProjectI", // "name": "test-rb-ws-omero-3" // }, // "date": "2024-03-26T05:38:49Z", // "permissions": { // "canDelete": true, // "canAnnotate": true, // "canLink": true, // "canEdit": true // } // }, // "class": "MapAnnotationI", // "values": [ // [ // "stash", // "0c8c48e0e4c311ee917c75953819bfae.0c8c48e0e4c311ee917c75953819bfae" // ], // [ // "stash RDMP", // "http://localhost:1500/default/rdmp/record/view/0c8c48e0e4c311ee917c75953819bfae" // ] // ] // } // ] // Instead of returning the whole annotation object, will now return the actual 'value' entry let valEntry = undefined; const annVal = _.find(annotations, an => an['values'].find((val) => { if (val[0] == annotation) {valEntry = val[1]} return valEntry != undefined; })); sails.log.verbose(`Found stash annotation link: ${JSON.stringify(annVal)}`); sails.log.verbose(`Found stash annotation link value: ${valEntry}`) return valEntry; // return annotations.map((anns, index) => { // const row = _.findIndex(anns['values'], an => an[0] === annotation); // return {index: index, id: anns['id'], row: row != -1 ? row : null} // }).filter((cur) => { // return cur.row != null; // }); } workspaceInfoFromRepo(workspaceLink) { if (workspaceLink) { const workspaceInfo = workspaceLink.split('.'); return {rdmpId: _.first(workspaceInfo), workspaceId: _.last(workspaceInfo)}; } else { return {rdmpId: null, workspaceId: null}; } } images(req, res) { this.config.set(); if (!req.isAuthenticated()) { this.ajaxFail(req, res, `User not authenticated`); } else { const userId = req.user.id; const offset = 10; const limit = 10; const normalize = 'false'; let app = {}; return WorkspaceService.workspaceAppFromUserId(userId, this.config.appName) .flatMap(response => { app = response.info; if (response.info) { const app = response.info; return sails.services.omeroservice.images({ config: this.config, app: app, offset: offset, limit: limit, owner: app.userId, group: app.ownerId, normalize: normalize }); } else { throw new Error('Missing application credentials'); } }).subscribe(response => { this.ajaxOk(req, res, null, {status: true, response: response}); }, error => { const errorMessage = `Failed to get images of user: ${userId}`; sails.log.error(errorMessage); this.ajaxFail(req, res, errorMessage, error); }); } } } class Config { host: string; recordType: string; formName: string; workflowStage: string; appId: string; serverId: string; appName: string; parentRecord: string; provisionerUser: string; brandingAndPortalUrl: string; redboxHeaders: any; domain: string; defaultGroupId: number; set() { const workspaceConfig = sails.config.workspaces; const OMEROConfig = workspaceConfig.omero; this.host = OMEROConfig.host; this.recordType = OMEROConfig.recordType; this.workflowStage = OMEROConfig.workflowStage; this.formName = OMEROConfig.formName; this.appName = OMEROConfig.appName; this.domain = OMEROConfig.domain; this.parentRecord = workspaceConfig.parentRecord; this.provisionerUser = workspaceConfig.provisionerUser; this.serverId = OMEROConfig.serverId; this.appId = OMEROConfig.appId; this.brandingAndPortalUrl = ''; this.redboxHeaders = { 'Cache-Control': 'no-cache', 'Content-Type': 'application/json', 'Authorization': workspaceConfig.portal.authorization, }; this.defaultGroupId = OMEROConfig.defaultGroupId } } } module.exports = new Controllers.OMEROController().exports();