diff --git a/source/common/changes/@awssolutions/cdf-assetlibrary/main_2023-10-06-06-40.json b/source/common/changes/@awssolutions/cdf-assetlibrary/main_2023-10-06-06-40.json new file mode 100644 index 000000000..d676abe84 --- /dev/null +++ b/source/common/changes/@awssolutions/cdf-assetlibrary/main_2023-10-06-06-40.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@awssolutions/cdf-assetlibrary", + "comment": "Change connection life cycle between Lambda and Neptune", + "type": "none" + } + ], + "packageName": "@awssolutions/cdf-assetlibrary" +} \ No newline at end of file diff --git a/source/packages/services/assetlibrary/src/@types/gremlin/index.d.ts b/source/packages/services/assetlibrary/src/@types/gremlin/index.d.ts index a396f3070..98b6b9cad 100644 --- a/source/packages/services/assetlibrary/src/@types/gremlin/index.d.ts +++ b/source/packages/services/assetlibrary/src/@types/gremlin/index.d.ts @@ -414,7 +414,10 @@ declare module 'gremlin' { export class RemoteStrategy extends process.TraversalStrategy {} export class DriverRemoteConnection extends RemoteConnection { + open(): Promise; close(): Promise; + addListener(event: string | symbol, handler: (...args: any[]) => void): void; + removeListener(event: string | symbol, handler: (...args: any[]) => void): void; } } } diff --git a/source/packages/services/assetlibrary/src/authz/authz.full.dao.ts b/source/packages/services/assetlibrary/src/authz/authz.full.dao.ts index 1df8cdac7..56251a107 100644 --- a/source/packages/services/assetlibrary/src/authz/authz.full.dao.ts +++ b/source/packages/services/assetlibrary/src/authz/authz.full.dao.ts @@ -48,44 +48,39 @@ export class AuthzDaoFull extends BaseDaoFull { const ids: string[] = deviceIds.map((d) => `device___${d}`); ids.push(...groupPaths.map((g) => `group___${g}`)); - let results; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(ids) - .as('entity') - .union( - // return an item if the entity exists - __.project('entity', 'exists') - .by( - __.select('entity').coalesce( - __.values('deviceId'), - __.values('groupPath') - ) + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(ids) + .as('entity') + .union( + // return an item if the entity exists + __.project('entity', 'exists') + .by( + __.select('entity').coalesce( + __.values('deviceId'), + __.values('groupPath') ) - .by(__.constant(true)), - // return an item if the entity is authorized - __.local( - __.until(__.has('groupPath', process.P.within(hierarchies))) - .repeat( - __.outE().has('isAuthCheck', true).otherV().simplePath().dedup() - ) - .as('authorizedPath') ) - .project('entity', 'authorizedPath') - .by( - __.select('entity').coalesce( - __.values('deviceId'), - __.values('groupPath') - ) + .by(__.constant(true)), + // return an item if the entity is authorized + __.local( + __.until(__.has('groupPath', process.P.within(hierarchies))) + .repeat( + __.outE().has('isAuthCheck', true).otherV().simplePath().dedup() ) - .by(__.select('authorizedPath').values('groupPath')) - ); + .as('authorizedPath') + ) + .project('entity', 'authorizedPath') + .by( + __.select('entity').coalesce( + __.values('deviceId'), + __.values('groupPath') + ) + ) + .by(__.select('authorizedPath').values('groupPath')) + ); - results = await traverser.toList(); - } finally { - await conn.close(); - } + const results = await traverser.toList(); logger.debug( `authz.full.dao listAuthorizedHierarchies: results:${JSON.stringify(results)}` diff --git a/source/packages/services/assetlibrary/src/data/base.full.dao.ts b/source/packages/services/assetlibrary/src/data/base.full.dao.ts index e882237dc..a781c62d2 100644 --- a/source/packages/services/assetlibrary/src/data/base.full.dao.ts +++ b/source/packages/services/assetlibrary/src/data/base.full.dao.ts @@ -18,23 +18,40 @@ import { TYPES } from '../di/types'; @injectable() export class BaseDaoFull { private _graph: structure.Graph; + private _conn: driver.DriverRemoteConnection | null; public constructor( @inject('neptuneUrl') private neptuneUrl: string, @inject(TYPES.GraphSourceFactory) graphSourceFactory: () => structure.Graph ) { this._graph = graphSourceFactory(); + this._conn = null; } - protected getConnection(): NeptuneConnection { + protected async getConnection(): Promise { logger.debug(`base.full.dao getConnection: in:`); - const conn = new driver.DriverRemoteConnection(this.neptuneUrl, { - mimeType: 'application/vnd.gremlin-v2.0+json', - pingEnabled: false, - }); + + if (this._conn == null) { + logger.debug(`base.full.dao getConnection: create new connection:`); + this._conn = new driver.DriverRemoteConnection(this.neptuneUrl, { + mimeType: 'application/vnd.gremlin-v2.0+json', + pingEnabled: false, + connectOnStartup: false, + }); + this._conn.addListener('close', (code: number, message: string) => { + logger.info(`base.full.dao connection close: code: ${code}, message: ${message}`); + this._conn = null; + if (code === 1006) { + throw new Error('Connection closed prematurely'); + } + }); + await this._conn.open(); + } logger.debug(`base.full.dao getConnection: withRemote:`); - const res = new NeptuneConnection(this._graph.traversal().withRemote(conn), conn); + const res = new NeptuneConnection( + this._graph.traversal().withRemote(this._conn), + ); logger.debug(`base.full.dao getConnection: exit:`); return res; @@ -44,14 +61,9 @@ export class BaseDaoFull { export class NeptuneConnection { constructor( private _traversal: process.GraphTraversalSource, - private _connection: driver.DriverRemoteConnection ) {} public get traversal(): process.GraphTraversalSource { return this._traversal; } - - public async close(): Promise { - await this._connection.close(); - } } diff --git a/source/packages/services/assetlibrary/src/data/common.full.dao.ts b/source/packages/services/assetlibrary/src/data/common.full.dao.ts index ccb14ed68..c091d4b8f 100644 --- a/source/packages/services/assetlibrary/src/data/common.full.dao.ts +++ b/source/packages/services/assetlibrary/src/data/common.full.dao.ts @@ -146,26 +146,21 @@ export class CommonDaoFull extends BaseDaoFull { relatedUnion.range(offsetAsInt, offsetAsInt + countAsInt); // build the main part of the query, unioning the related traversers with the main entity we want to return - let results; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(entityDbId) - .as('main') - .union( - relatedUnion, - __.select('main').valueMap().with_(process.withOptions.tokens) - ); - - // execute and retrieve the results - logger.debug( - `common.full.dao listRelated: traverser: ${JSON.stringify(traverser.toString())}` + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(entityDbId) + .as('main') + .union( + relatedUnion, + __.select('main').valueMap().with_(process.withOptions.tokens) ); - results = await traverser.toList(); - logger.debug(`common.full.dao listRelated: results: ${JSON.stringify(results)}`); - } finally { - await conn.close(); - } + + // execute and retrieve the results + logger.debug( + `common.full.dao listRelated: traverser: ${JSON.stringify(traverser.toString())}` + ); + const results = await traverser.toList(); + logger.debug(`common.full.dao listRelated: results: ${JSON.stringify(results)}`); if (results === undefined || results.length === 0) { logger.debug(`common.full.dao listRelated: exit: node: undefined`); @@ -193,20 +188,15 @@ export class CommonDaoFull extends BaseDaoFull { return {}; } - let results; - const conn = super.getConnection(); - try { - const query = conn.traversal - .V(entityDbIds) - .project('id', 'labels') - .by(__.coalesce(__.values('deviceId'), __.values('groupPath'))) - .by(__.label().fold()); - - logger.silly(`common.full.dao getLabels: query: ${JSON.stringify(query)}`); - results = await query.toList(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const query = conn.traversal + .V(entityDbIds) + .project('id', 'labels') + .by(__.coalesce(__.values('deviceId'), __.values('groupPath'))) + .by(__.label().fold()); + + logger.silly(`common.full.dao getLabels: query: ${JSON.stringify(query)}`); + const results = await query.toList(); logger.silly(`common.full.dao getLabels: results: ${JSON.stringify(results)}`); if ((results?.length ?? 0) === 0) { diff --git a/source/packages/services/assetlibrary/src/devices/devices.full.dao.ts b/source/packages/services/assetlibrary/src/devices/devices.full.dao.ts index 78292891a..a54158877 100644 --- a/source/packages/services/assetlibrary/src/devices/devices.full.dao.ts +++ b/source/packages/services/assetlibrary/src/devices/devices.full.dao.ts @@ -200,26 +200,21 @@ export class DevicesDaoFull extends BaseDaoFull { .with_(process.withOptions.tokens); // build the main part of the query, unioning the related traversers with the main entity we want to return - let results: process.Traverser[]; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(dbIds) - .as('devices') - .values('deviceId') - .as('entityId') - .select('devices') - .union(relatedIn, relatedOut, deviceProps); - - // execute and retrieve the results - logger.debug( - `common.full.dao listRelated: traverser: ${JSON.stringify(traverser.toString())}` - ); - results = await traverser.toList(); - logger.debug(`common.full.dao listRelated: results: ${JSON.stringify(results)}`); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(dbIds) + .as('devices') + .values('deviceId') + .as('entityId') + .select('devices') + .union(relatedIn, relatedOut, deviceProps); + + // execute and retrieve the results + logger.debug( + `common.full.dao listRelated: traverser: ${JSON.stringify(traverser.toString())}` + ); + const results = await traverser.toList(); + logger.debug(`common.full.dao listRelated: results: ${JSON.stringify(results)}`); if ((results?.length ?? 0) === 0) { logger.debug(`device.full.dao get: exit: node: undefined`); @@ -280,49 +275,45 @@ export class DevicesDaoFull extends BaseDaoFull { const labels = n.types.join('::'); /* create the device */ - const conn = super.getConnection(); - try { - const traversal = conn.traversal.addV(labels).property(process.t.id, id); - - /* set all the device properties */ - for (const key of Object.keys(n.attributes)) { - if (n.attributes[key] !== undefined) { - traversal.property(process.cardinality.single, key, n.attributes[key]); - } - } - traversal.as('device'); - - /* associate device with the related devices and/or groups */ - associateRels(traversal, groups?.in, 'group', 'in'); - associateRels(traversal, groups?.out, 'group', 'out'); - associateRels(traversal, devices?.in, 'device', 'in'); - associateRels(traversal, devices?.out, 'device', 'out'); - - /* create the components */ - if (components) { - components.forEach((c) => { - const componentId = c.attributes['deviceId'] as string; - const componentDbId = `${id}___${componentId}`; - const componentLabels = c.types.join('::'); - - traversal.addV(componentLabels).property(process.t.id, componentDbId); - - for (const key of Object.keys(c.attributes)) { - if (c.attributes[key] !== undefined) { - traversal.property(process.cardinality.single, key, c.attributes[key]); - } - } + const conn = await super.getConnection(); + const traversal = conn.traversal.addV(labels).property(process.t.id, id); - traversal.as(componentId).addE('component_of').from_(componentId).to('device'); - }); + /* set all the device properties */ + for (const key of Object.keys(n.attributes)) { + if (n.attributes[key] !== undefined) { + traversal.property(process.cardinality.single, key, n.attributes[key]); } + } + traversal.as('device'); + + /* associate device with the related devices and/or groups */ + associateRels(traversal, groups?.in, 'group', 'in'); + associateRels(traversal, groups?.out, 'group', 'out'); + associateRels(traversal, devices?.in, 'device', 'in'); + associateRels(traversal, devices?.out, 'device', 'out'); + + /* create the components */ + if (components) { + components.forEach((c) => { + const componentId = c.attributes['deviceId'] as string; + const componentDbId = `${id}___${componentId}`; + const componentLabels = c.types.join('::'); + + traversal.addV(componentLabels).property(process.t.id, componentDbId); + + for (const key of Object.keys(c.attributes)) { + if (c.attributes[key] !== undefined) { + traversal.property(process.cardinality.single, key, c.attributes[key]); + } + } - logger.debug(`devices.full.dao create: traversal:${traversal}`); - await traversal.iterate(); - } finally { - await conn.close(); + traversal.as(componentId).addE('component_of').from_(componentId).to('device'); + }); } + logger.debug(`devices.full.dao create: traversal:${traversal}`); + await traversal.iterate(); + logger.debug(`devices.full.dao create: exit: id:${id}`); return id; } @@ -337,28 +328,24 @@ export class DevicesDaoFull extends BaseDaoFull { const labels = n.types.join('::'); /* create the component */ - const conn = super.getConnection(); - try { - const traversal = conn.traversal.addV(labels).property(process.t.id, componentId); + const conn = await super.getConnection(); + const traversal = conn.traversal.addV(labels).property(process.t.id, componentId); - for (const key of Object.keys(n.attributes)) { - if (n.attributes[key] !== undefined) { - traversal.property(process.cardinality.single, key, n.attributes[key]); - } + for (const key of Object.keys(n.attributes)) { + if (n.attributes[key] !== undefined) { + traversal.property(process.cardinality.single, key, n.attributes[key]); } - traversal.as('component'); + } + traversal.as('component'); - /* add to the parent device */ - traversal.V(id).as('device').addE('component_of').from_('component').to('device'); + /* add to the parent device */ + traversal.V(id).as('device').addE('component_of').from_('component').to('device'); - /* for simplification, always add isAuthCheck from the component to the device, regardless fo whether used or not */ - traversal.property(process.cardinality.single, 'isAuthCheck', true); + /* for simplification, always add isAuthCheck from the component to the device, regardless fo whether used or not */ + traversal.property(process.cardinality.single, 'isAuthCheck', true); - logger.debug(`devices.full.dao createComponent: traversal:${traversal}`); - await traversal.iterate(); - } finally { - await conn.close(); - } + logger.debug(`devices.full.dao createComponent: traversal:${traversal}`); + await traversal.iterate(); logger.debug(`devices.full.dao createComponent: exit: componentId:${componentId}`); return componentId; @@ -373,126 +360,122 @@ export class DevicesDaoFull extends BaseDaoFull { const id = `device___${n.attributes['deviceId']}`; - const conn = super.getConnection(); - try { - const traversal = conn.traversal.V(id).as('device'); - // drop() step terminates a traversal, process all drops as part of a final union step - const dropTraversals: process.GraphTraversal[] = []; - - for (const [key, val] of Object.entries(n.attributes)) { - if (val !== undefined) { - if (val === null) { - dropTraversals.push(__.properties(key)); - } else { - traversal.property(process.cardinality.single, key, val); - } + const conn = await super.getConnection(); + const traversal = conn.traversal.V(id).as('device'); + // drop() step terminates a traversal, process all drops as part of a final union step + const dropTraversals: process.GraphTraversal[] = []; + + for (const [key, val] of Object.entries(n.attributes)) { + if (val !== undefined) { + if (val === null) { + dropTraversals.push(__.properties(key)); + } else { + traversal.property(process.cardinality.single, key, val); } } + } - // Check if related groups or devices part of update request - if (groups !== undefined && (groups.in || groups.out || devices.in || devices.out)) { - // Update request contains relationships to enforce. This requires current - // relationships be dropped where specified and new relations created. - logger.info( - `devices.full.dao update groups/devices relations specified as part of update: ${JSON.stringify( - { groups: groups } - )}/${JSON.stringify({ devices: devices })}` + // Check if related groups or devices part of update request + if (groups !== undefined && (groups.in || groups.out || devices.in || devices.out)) { + // Update request contains relationships to enforce. This requires current + // relationships be dropped where specified and new relations created. + logger.info( + `devices.full.dao update groups/devices relations specified as part of update: ${JSON.stringify( + { groups: groups } + )}/${JSON.stringify({ devices: devices })}` + ); + const result = await this.get([`${n.attributes['deviceId']}`], false, [], false); + let currentDevice: DeviceItem; + if (result !== undefined && result.length > 0) { + currentDevice = this.devicesAssembler.toDeviceItem(result[0]); + } + const existingGroups = currentDevice.groups ? currentDevice.groups : {}; + const existingDevices = currentDevice.devices ? currentDevice.devices : {}; + logger.debug(`Current device defintion: ${JSON.stringify(currentDevice)}`); + // Methodology + // 1. Collect relations to be dropped as independent traversal objects via diassociateRels + // 2. Union and then drop() these with traversal.sideEffect(...) + // -- Use of sideEffect acts on the traversal then passes results to next step. Without this, a drop() will terminate traversal. + // 3. Add specified relations for groups and devices directly to traversal in associateRels + const relationsToDropTraversals: process.GraphTraversal[] = []; + if (groups.in && 'in' in existingGroups) { + logger.debug( + `devices.full.dao update device ${id} dropping existing relations for groups.in: ${JSON.stringify( + existingGroups.in + )}` ); - const result = await this.get([`${n.attributes['deviceId']}`], false, [], false); - let currentDevice: DeviceItem; - if (result !== undefined && result.length > 0) { - currentDevice = this.devicesAssembler.toDeviceItem(result[0]); - } - const existingGroups = currentDevice.groups ? currentDevice.groups : {}; - const existingDevices = currentDevice.devices ? currentDevice.devices : {}; - logger.debug(`Current device defintion: ${JSON.stringify(currentDevice)}`); - // Methodology - // 1. Collect relations to be dropped as independent traversal objects via diassociateRels - // 2. Union and then drop() these with traversal.sideEffect(...) - // -- Use of sideEffect acts on the traversal then passes results to next step. Without this, a drop() will terminate traversal. - // 3. Add specified relations for groups and devices directly to traversal in associateRels - const relationsToDropTraversals: process.GraphTraversal[] = []; - if (groups.in && 'in' in existingGroups) { - logger.debug( - `devices.full.dao update device ${id} dropping existing relations for groups.in: ${JSON.stringify( - existingGroups.in - )}` - ); - diassociateRels(relationsToDropTraversals, existingGroups.in, 'group', 'in'); - } - if (groups.out && 'out' in existingGroups) { - logger.debug( - `devices.full.dao update device ${id} dropping existing relations for groups.out: ${JSON.stringify( - existingGroups.out - )}` - ); - diassociateRels(relationsToDropTraversals, existingGroups.out, 'group', 'out'); - } - if (devices.in && 'in' in existingDevices) { - logger.debug( - `devices.full.dao update device ${id} dropping existing relations for devices.in: ${JSON.stringify( - existingDevices.in - )}` - ); - diassociateRels(relationsToDropTraversals, existingDevices.in, 'device', 'in'); - } - if (devices.out && 'out' in existingDevices) { - logger.debug( - `devices.full.dao update device ${id} dropping existing relations for devices.out:: ${JSON.stringify( - existingDevices.out - )}` - ); - diassociateRels( - relationsToDropTraversals, - existingDevices.out, - 'device', - 'out' - ); - } - traversal.sideEffect(__.union(...relationsToDropTraversals).drop()); - if (groups.in) { - logger.debug( - `devices.full.dao update device ${id} adding relations for groups.in: ${JSON.stringify( - groups.in - )}` - ); - associateRels(traversal, groups.in, 'group', 'in'); - } - if (groups.out) { - logger.debug( - `devices.full.dao update device ${id} adding relations for groups.out: ${JSON.stringify( - groups.out - )}` - ); - associateRels(traversal, groups.out, 'group', 'out'); - } - if (devices.in) { - logger.debug( - `devices.full.dao update device ${id} adding relations for devices.in: ${JSON.stringify( - devices.in - )}` - ); - associateRels(traversal, devices.in, 'device', 'in'); - } - if (devices.out) { - logger.debug( - `devices.full.dao update device ${id} adding relations for devices.out: ${JSON.stringify( - devices.out - )}` - ); - associateRels(traversal, devices.out, 'device', 'out'); - } + diassociateRels(relationsToDropTraversals, existingGroups.in, 'group', 'in'); } - if (dropTraversals.length > 0) { - traversal.local(__.union(...dropTraversals)).drop(); + if (groups.out && 'out' in existingGroups) { + logger.debug( + `devices.full.dao update device ${id} dropping existing relations for groups.out: ${JSON.stringify( + existingGroups.out + )}` + ); + diassociateRels(relationsToDropTraversals, existingGroups.out, 'group', 'out'); } - logger.debug( - `devices.full.dao update traversal before iterate is: ${JSON.stringify(traversal)}` - ); - await traversal.iterate(); - } finally { - await conn.close(); + if (devices.in && 'in' in existingDevices) { + logger.debug( + `devices.full.dao update device ${id} dropping existing relations for devices.in: ${JSON.stringify( + existingDevices.in + )}` + ); + diassociateRels(relationsToDropTraversals, existingDevices.in, 'device', 'in'); + } + if (devices.out && 'out' in existingDevices) { + logger.debug( + `devices.full.dao update device ${id} dropping existing relations for devices.out:: ${JSON.stringify( + existingDevices.out + )}` + ); + diassociateRels( + relationsToDropTraversals, + existingDevices.out, + 'device', + 'out' + ); + } + traversal.sideEffect(__.union(...relationsToDropTraversals).drop()); + if (groups.in) { + logger.debug( + `devices.full.dao update device ${id} adding relations for groups.in: ${JSON.stringify( + groups.in + )}` + ); + associateRels(traversal, groups.in, 'group', 'in'); + } + if (groups.out) { + logger.debug( + `devices.full.dao update device ${id} adding relations for groups.out: ${JSON.stringify( + groups.out + )}` + ); + associateRels(traversal, groups.out, 'group', 'out'); + } + if (devices.in) { + logger.debug( + `devices.full.dao update device ${id} adding relations for devices.in: ${JSON.stringify( + devices.in + )}` + ); + associateRels(traversal, devices.in, 'device', 'in'); + } + if (devices.out) { + logger.debug( + `devices.full.dao update device ${id} adding relations for devices.out: ${JSON.stringify( + devices.out + )}` + ); + associateRels(traversal, devices.out, 'device', 'out'); + } + } + if (dropTraversals.length > 0) { + traversal.local(__.union(...dropTraversals)).drop(); } + logger.debug( + `devices.full.dao update traversal before iterate is: ${JSON.stringify(traversal)}` + ); + await traversal.iterate(); logger.debug(`devices.full.dao update: exit:`); } @@ -501,12 +484,8 @@ export class DevicesDaoFull extends BaseDaoFull { const id = `device___${deviceId}`; - const conn = super.getConnection(); - try { - await conn.traversal.V(id).drop().iterate(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + await conn.traversal.V(id).drop().iterate(); logger.debug(`devices.full.dao delete: exit`); } @@ -533,26 +512,22 @@ export class DevicesDaoFull extends BaseDaoFull { targetId = `device___${deviceId}`; } - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(targetId) - .as('target') - .V(sourceId) - .as('source') - .addE(relationship) - .to('target'); - - if (isAuthCheck) { - traverser.property(process.cardinality.single, 'isAuthCheck', true); - } + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(targetId) + .as('target') + .V(sourceId) + .as('source') + .addE(relationship) + .to('target'); + + if (isAuthCheck) { + traverser.property(process.cardinality.single, 'isAuthCheck', true); + } - const result = await traverser.iterate(); + const result = await traverser.iterate(); - logger.debug(`devices.full.dao attachToGroup: result:${JSON.stringify(result)}`); - } finally { - await conn.close(); - } + logger.debug(`devices.full.dao attachToGroup: result:${JSON.stringify(result)}`); logger.debug(`devices.full.dao attachToGroup: exit:`); } @@ -596,25 +571,21 @@ export class DevicesDaoFull extends BaseDaoFull { const sourceId = `device___${source}`; const targetId = `device___${target}`; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(targetId) - .as('other') - .V(sourceId) - .addE(relationship) - .to('other'); - - if (isAuthCheck) { - traverser.property(process.cardinality.single, 'isAuthCheck', true); - } + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(targetId) + .as('other') + .V(sourceId) + .addE(relationship) + .to('other'); - const result = await traverser.iterate(); - logger.debug(`devices.full.dao attachToDevice: result:${JSON.stringify(result)}`); - } finally { - await conn.close(); + if (isAuthCheck) { + traverser.property(process.cardinality.single, 'isAuthCheck', true); } + const result = await traverser.iterate(); + logger.debug(`devices.full.dao attachToDevice: result:${JSON.stringify(result)}`); + logger.debug(`devices.full.dao attachToDevice: exit:`); } @@ -671,17 +642,13 @@ export class DevicesDaoFull extends BaseDaoFull { edgesToDelete.push(t); } - const conn = super.getConnection(); - try { - await conn.traversal - .V(`device___${deviceId}`) - .as('source') - .union(...edgesToDelete) - .drop() - .iterate(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + await conn.traversal + .V(`device___${deviceId}`) + .as('source') + .union(...edgesToDelete) + .drop() + .iterate(); logger.debug(`devices.full.dao detachFromOthers: exit:`); } diff --git a/source/packages/services/assetlibrary/src/groups/groups.full.dao.ts b/source/packages/services/assetlibrary/src/groups/groups.full.dao.ts index f81757e6a..3c7920b4c 100644 --- a/source/packages/services/assetlibrary/src/groups/groups.full.dao.ts +++ b/source/packages/services/assetlibrary/src/groups/groups.full.dao.ts @@ -134,29 +134,24 @@ export class GroupsDaoFull extends BaseDaoFull { * return the group, but when retrieving linked entities we need to retrieve * all groups excluding linked via 'parent' and ignore linked devices */ - let results: process.Traverser[]; - const conn = super.getConnection(); - try { - const traverser = await conn.traversal - .V(dbIds) - .as('main') - .values('groupPath') - .as('entityId') - .select('main'); - - // TODO: verify and optimize this further - !includeGroups - ? traverser.union(groupProps) - : traverser.union(relatedIn, relatedOut, groupProps); - - logger.debug( - `groups.full.dao get: traverser: ${JSON.stringify(traverser.toString())}` - ); - results = await traverser.toList(); - logger.debug(`groups.full.dao get: result: ${JSON.stringify(results)}`); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const traverser = await conn.traversal + .V(dbIds) + .as('main') + .values('groupPath') + .as('entityId') + .select('main'); + + // TODO: verify and optimize this further + !includeGroups + ? traverser.union(groupProps) + : traverser.union(relatedIn, relatedOut, groupProps); + + logger.debug( + `groups.full.dao get: traverser: ${JSON.stringify(traverser.toString())}` + ); + const results = await traverser.toList(); + logger.debug(`groups.full.dao get: result: ${JSON.stringify(results)}`); if (results === undefined || results.length === 0) { logger.debug(`groups.full.dao get: exit: node: undefined`); @@ -205,29 +200,25 @@ export class GroupsDaoFull extends BaseDaoFull { const labels = n.types.join('::'); const parentId = `group___${n.attributes['parentPath']}`; - const conn = super.getConnection(); - try { - const traversal = conn.traversal - .V(parentId) - .as('parent') - .addV(labels) - .property(process.t.id, id); - - for (const key of Object.keys(n.attributes)) { - if (n.attributes[key] !== undefined) { - traversal.property(process.cardinality.single, key, n.attributes[key]); - } + const conn = await super.getConnection(); + const traversal = conn.traversal + .V(parentId) + .as('parent') + .addV(labels) + .property(process.t.id, id); + + for (const key of Object.keys(n.attributes)) { + if (n.attributes[key] !== undefined) { + traversal.property(process.cardinality.single, key, n.attributes[key]); } + } - traversal.as('group').addE('parent').from_('group').to('parent'); + traversal.as('group').addE('parent').from_('group').to('parent'); - associateRels(traversal, groups?.in, 'in'); - associateRels(traversal, groups?.out, 'out'); + associateRels(traversal, groups?.in, 'in'); + associateRels(traversal, groups?.out, 'out'); - await traversal.next(); - } finally { - await conn.close(); - } + await traversal.next(); logger.debug(`groups.full.dao create: exit: id:${id}`); return id; @@ -238,68 +229,64 @@ export class GroupsDaoFull extends BaseDaoFull { const id = `group___${n.attributes['groupPath'].toString()}`; - const conn = super.getConnection(); - try { - const traversal = conn.traversal.V(id).as('group'); - // drop() step terminates a traversal, process all drops as part of a final union step - const dropTraversals: process.GraphTraversal[] = []; - - for (const [key, val] of Object.entries(n.attributes)) { - if (val !== undefined) { - if (val === null) { - dropTraversals.push(__.properties(key)); - } else { - traversal.property(process.cardinality.single, key, val); - } - } - } + const conn = await super.getConnection(); + const traversal = conn.traversal.V(id).as('group'); + // drop() step terminates a traversal, process all drops as part of a final union step + const dropTraversals: process.GraphTraversal[] = []; - // Check if related groups part of update request - if (groups?.in || groups?.out) { - // Update request contains relationships to enforce. This requires current - // relationships be dropped where specified and new relations created. - logger.info( - `groups.full.dao update groups relations specified as part of update: ${JSON.stringify( - { groups: groups } - )}` - ); - const result = await this.get([`${n.attributes['groupPath']}`], true); - let currentGroup: GroupItem; - if (result !== undefined && result.length > 0) { - currentGroup = this.groupsAssembler.toGroupItem(result[0]); - } - const existingGroups = currentGroup.groups ? currentGroup.groups : {}; - logger.debug(`Current group definition: ${JSON.stringify(currentGroup)}`); - // Methodology - // 1. Collect relations to be dropped as independent traversal objects via disassociateRels - // 2. Union and then drop() these with traversal.sideEffect(...) - // -- Use of sideEffect acts on the traversal then passes results to next step. Without this, a drop() will terminate traversal. - // 3. Add specified relations for groups and devices directly to traversal in associateRels - const relationsToDropTraversals: process.GraphTraversal[] = []; - if (groups.in && 'in' in existingGroups) { - disassociateRels(relationsToDropTraversals, existingGroups.in, 'in'); - } - if (groups.out && 'out' in existingGroups) { - disassociateRels(relationsToDropTraversals, existingGroups.out, 'out'); - } - traversal.sideEffect(__.union(...relationsToDropTraversals).drop()); - if (groups.in) { - associateRels(traversal, groups.in, 'in'); - } - if (groups.out) { - associateRels(traversal, groups.out, 'out'); + for (const [key, val] of Object.entries(n.attributes)) { + if (val !== undefined) { + if (val === null) { + dropTraversals.push(__.properties(key)); + } else { + traversal.property(process.cardinality.single, key, val); } } + } - if (dropTraversals.length > 0) { - traversal.local(__.union(...dropTraversals)).drop(); + // Check if related groups part of update request + if (groups?.in || groups?.out) { + // Update request contains relationships to enforce. This requires current + // relationships be dropped where specified and new relations created. + logger.info( + `groups.full.dao update groups relations specified as part of update: ${JSON.stringify( + { groups: groups } + )}` + ); + const result = await this.get([`${n.attributes['groupPath']}`], true); + let currentGroup: GroupItem; + if (result !== undefined && result.length > 0) { + currentGroup = this.groupsAssembler.toGroupItem(result[0]); + } + const existingGroups = currentGroup.groups ? currentGroup.groups : {}; + logger.debug(`Current group definition: ${JSON.stringify(currentGroup)}`); + // Methodology + // 1. Collect relations to be dropped as independent traversal objects via disassociateRels + // 2. Union and then drop() these with traversal.sideEffect(...) + // -- Use of sideEffect acts on the traversal then passes results to next step. Without this, a drop() will terminate traversal. + // 3. Add specified relations for groups and devices directly to traversal in associateRels + const relationsToDropTraversals: process.GraphTraversal[] = []; + if (groups.in && 'in' in existingGroups) { + disassociateRels(relationsToDropTraversals, existingGroups.in, 'in'); } + if (groups.out && 'out' in existingGroups) { + disassociateRels(relationsToDropTraversals, existingGroups.out, 'out'); + } + traversal.sideEffect(__.union(...relationsToDropTraversals).drop()); + if (groups.in) { + associateRels(traversal, groups.in, 'in'); + } + if (groups.out) { + associateRels(traversal, groups.out, 'out'); + } + } - await traversal.iterate(); - } finally { - await conn.close(); + if (dropTraversals.length > 0) { + traversal.local(__.union(...dropTraversals)).drop(); } + await traversal.iterate(); + logger.debug(`groups.full.dao update: exit: id:${id}`); return id; } @@ -345,24 +332,19 @@ export class GroupsDaoFull extends BaseDaoFull { const id = 'group___' + groupPath; - let results; - const conn = super.getConnection(); - try { - results = await conn.traversal - .V(id) - .local( - __.union( - __.identity().valueMap().with_(process.withOptions.tokens), - __.repeat(__.out('parent').simplePath().dedup()) - .emit() - .valueMap() - .with_(process.withOptions.tokens) - ) + const conn = await super.getConnection(); + const results = await conn.traversal + .V(id) + .local( + __.union( + __.identity().valueMap().with_(process.withOptions.tokens), + __.repeat(__.out('parent').simplePath().dedup()) + .emit() + .valueMap() + .with_(process.withOptions.tokens) ) - .toList(); - } finally { - await conn.close(); - } + ) + .toList(); logger.debug(`groups.full.dao listParentGroups: results: ${JSON.stringify(results)}`); @@ -385,12 +367,8 @@ export class GroupsDaoFull extends BaseDaoFull { const dbId = `group___${groupPath}`; - const conn = super.getConnection(); - try { - await conn.traversal.V(dbId).drop().next(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + await conn.traversal.V(dbId).drop().next(); logger.debug(`groups.full.dao delete: exit`); } @@ -408,26 +386,22 @@ export class GroupsDaoFull extends BaseDaoFull { const sourceId = `group___${sourceGroupPath}`; const targetId = `group___${targetGroupPath}`; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(targetId) - .as('target') - .V(sourceId) - .as('source') - .addE(relationship) - .to('target'); - - if (isAuthCheck) { - traverser.property(process.cardinality.single, 'isAuthCheck', true); - } + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(targetId) + .as('target') + .V(sourceId) + .as('source') + .addE(relationship) + .to('target'); + + if (isAuthCheck) { + traverser.property(process.cardinality.single, 'isAuthCheck', true); + } - const result = await traverser.iterate(); + const result = await traverser.iterate(); - logger.verbose(`groups.full.dao attachToGroup: result:${JSON.stringify(result)}`); - } finally { - await conn.close(); - } + logger.verbose(`groups.full.dao attachToGroup: result:${JSON.stringify(result)}`); logger.debug(`groups.full.dao attachToGroup: exit:`); } @@ -444,25 +418,21 @@ export class GroupsDaoFull extends BaseDaoFull { const sourceId = `group___${sourceGroupPath}`; const targetId = `group___${targetGroupPath}`; - const conn = super.getConnection(); - try { - const result = await conn.traversal - .V(sourceId) - .as('source') - .outE(relationship) - .as('edge') - .inV() - .has(process.t.id, targetId) - .as('target') - .select('edge') - .dedup() - .drop() - .iterate(); - - logger.verbose(`groups.full.dao detachFromGroup: result:${JSON.stringify(result)}`); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const result = await conn.traversal + .V(sourceId) + .as('source') + .outE(relationship) + .as('edge') + .inV() + .has(process.t.id, targetId) + .as('target') + .select('edge') + .dedup() + .drop() + .iterate(); + + logger.verbose(`groups.full.dao detachFromGroup: result:${JSON.stringify(result)}`); logger.debug(`groups.full.dao detachFromGroup: exit:`); } diff --git a/source/packages/services/assetlibrary/src/init/init.full.dao.ts b/source/packages/services/assetlibrary/src/init/init.full.dao.ts index e65d3bb55..218020a76 100644 --- a/source/packages/services/assetlibrary/src/init/init.full.dao.ts +++ b/source/packages/services/assetlibrary/src/init/init.full.dao.ts @@ -28,13 +28,8 @@ export class InitDaoFull extends BaseDaoFull { public async isInitialized(): Promise { logger.debug('init.dao isInitialized: in: '); - let query; - const conn = super.getConnection(); - try { - query = await conn.traversal.V('type___device').next(); - } finally { - conn.close(); - } + const conn = await super.getConnection(); + const query = await conn.traversal.V('type___device').next(); logger.debug(`init.dao isInitialized: query: ${JSON.stringify(query)}`); @@ -51,34 +46,25 @@ export class InitDaoFull extends BaseDaoFull { public async initialize(): Promise { logger.debug('init.dao initialize: in:'); - const conn = super.getConnection(); - try { - await conn.traversal - .addV('type') - .property(process.t.id, 'type___device') - .addV('type') - .property(process.t.id, 'type___group') - .addV('root') - .property(process.t.id, 'group___/') - .property('name', '/') - .property('groupPath', '/') - .iterate(); - } finally { - conn.close(); - } + const conn = await super.getConnection(); + await conn.traversal + .addV('type') + .property(process.t.id, 'type___device') + .addV('type') + .property(process.t.id, 'type___group') + .addV('root') + .property(process.t.id, 'group___/') + .property('name', '/') + .property('groupPath', '/') + .iterate(); } public async getVersion(): Promise { logger.debug('init.dao getVersion: in: '); - let results; const id = 'app_version'; - const conn = super.getConnection(); - try { - results = await conn.traversal.V(id).valueMap('version').next(); - } finally { - conn.close(); - } + const conn = await super.getConnection(); + const results = await conn.traversal.V(id).valueMap('version').next(); logger.debug(`init.dao getVersion: results: ${JSON.stringify(results)}`); @@ -97,7 +83,7 @@ export class InitDaoFull extends BaseDaoFull { const currentVersion = await this.getVersion(); const id = 'app_version'; - const conn = super.getConnection(); + const conn = await super.getConnection(); if (currentVersion === 0) { await conn.traversal .addV(id) @@ -117,113 +103,109 @@ export class InitDaoFull extends BaseDaoFull { public async upgrade_from_0(): Promise { logger.debug(`init.dao upgrade_from_0: in:`); - const conn = super.getConnection(); - try { - // set groupPath of root group '/' - await conn.traversal.V('group___/').property('groupPath', '/').iterate(); - - await conn.traversal - .V('type___device') - .as('type') - // add missing template id - .property(process.cardinality.single, 'templateId', 'device') - // add type definition for the root device template - .addV('typeDefinition') - .property(process.t.id, 'type___device___1') - .property(process.cardinality.single, 'version', 1) - .property( - process.cardinality.single, - 'definition', - JSON.stringify({ - properties: { - deviceId: { - type: 'string', - }, - templateId: { - type: 'string', - }, - category: { - type: ['string', 'null'], - const: 'device', - }, - name: { - type: 'string', - }, - description: { - type: ['string', 'null'], - }, - imageUrl: { - type: ['string', 'null'], - }, - awsIotThingArn: { - type: ['string', 'null'], - }, - connected: { - type: 'boolean', - }, - state: { - enum: ['unprovisioned', 'active', 'decommisioned', 'retired'], - }, + const conn = await super.getConnection(); + // set groupPath of root group '/' + await conn.traversal.V('group___/').property('groupPath', '/').iterate(); + + await conn.traversal + .V('type___device') + .as('type') + // add missing template id + .property(process.cardinality.single, 'templateId', 'device') + // add type definition for the root device template + .addV('typeDefinition') + .property(process.t.id, 'type___device___1') + .property(process.cardinality.single, 'version', 1) + .property( + process.cardinality.single, + 'definition', + JSON.stringify({ + properties: { + deviceId: { + type: 'string', }, - required: ['deviceId', 'templateId'], - }) - ) - .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()) - .as('definition') - .addE('current_definition') - .property('status', 'published') - .from_('type') - .to('definition') - .next(); - + templateId: { + type: 'string', + }, + category: { + type: ['string', 'null'], + const: 'device', + }, + name: { + type: 'string', + }, + description: { + type: ['string', 'null'], + }, + imageUrl: { + type: ['string', 'null'], + }, + awsIotThingArn: { + type: ['string', 'null'], + }, + connected: { + type: 'boolean', + }, + state: { + enum: ['unprovisioned', 'active', 'decommisioned', 'retired'], + }, + }, + required: ['deviceId', 'templateId'], + }) + ) + .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()) + .as('definition') + .addE('current_definition') + .property('status', 'published') + .from_('type') + .to('definition') + .next(); + + // add type definition for the root group template + await conn.traversal + .V('type___group') + .as('type') + // add missing template id + .property(process.cardinality.single, 'templateId', 'group') // add type definition for the root group template - await conn.traversal - .V('type___group') - .as('type') - // add missing template id - .property(process.cardinality.single, 'templateId', 'group') - // add type definition for the root group template - .addV('typeDefinition') - .property(process.t.id, 'type___group___1') - .property(process.cardinality.single, 'version', 1) - .property( - process.cardinality.single, - 'definition', - JSON.stringify({ - properties: { - groupPath: { - type: 'string', - }, - parentPath: { - type: 'string', - }, - templateId: { - type: 'string', - }, - category: { - type: ['string'], - const: 'group', - }, - name: { - type: 'string', - }, - description: { - type: ['string', 'null'], - }, + .addV('typeDefinition') + .property(process.t.id, 'type___group___1') + .property(process.cardinality.single, 'version', 1) + .property( + process.cardinality.single, + 'definition', + JSON.stringify({ + properties: { + groupPath: { + type: 'string', }, - required: ['name', 'parentPath', 'templateId'], - }) - ) - .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()) - .as('definition') - .addE('current_definition') - .property('status', 'published') - .from_('type') - .to('definition') - .next(); - } finally { - conn.close(); - } + parentPath: { + type: 'string', + }, + templateId: { + type: 'string', + }, + category: { + type: ['string'], + const: 'group', + }, + name: { + type: 'string', + }, + description: { + type: ['string', 'null'], + }, + }, + required: ['name', 'parentPath', 'templateId'], + }) + ) + .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()) + .as('definition') + .addE('current_definition') + .property('status', 'published') + .from_('type') + .to('definition') + .next(); logger.debug(`init.dao upgrade_from_0: exit:`); } diff --git a/source/packages/services/assetlibrary/src/policies/policies.full.dao.ts b/source/packages/services/assetlibrary/src/policies/policies.full.dao.ts index fd16cb4e9..83b8c1705 100644 --- a/source/packages/services/assetlibrary/src/policies/policies.full.dao.ts +++ b/source/packages/services/assetlibrary/src/policies/policies.full.dao.ts @@ -33,19 +33,14 @@ export class PoliciesDaoFull extends BaseDaoFull { const id = `policy___${policyId.toLowerCase()}`; - let query; - const conn = super.getConnection(); - try { - query = await conn.traversal - .V(id) - .as('policy') - .project('policy', 'groups') - .by(__.select('policy').valueMap().with_(process.withOptions.tokens)) - .by(__.select('policy').out('appliesTo').hasLabel('group').fold()) - .next(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const query = await conn.traversal + .V(id) + .as('policy') + .project('policy', 'groups') + .by(__.select('policy').valueMap().with_(process.withOptions.tokens)) + .by(__.select('policy').out('appliesTo').hasLabel('group').fold()) + .next(); logger.debug(`policy.full.dao get: query: ${JSON.stringify(query)}`); @@ -64,25 +59,21 @@ export class PoliciesDaoFull extends BaseDaoFull { const id = `policy___${model.policyId.toLowerCase()}`; - const conn = super.getConnection(); - try { - const traversal = conn.traversal - .addV('policy') - .property(process.t.id, id) - .property('policyId', model.policyId.toLowerCase()) - .property('type', model.type) - .property('description', model.description) - .property('document', model.document) - .as('policy'); - - model.appliesTo.forEach((path, index) => { - this.addCreateAppliesToTraversal(path, index, traversal); - }); - - await traversal.iterate(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const traversal = conn.traversal + .addV('policy') + .property(process.t.id, id) + .property('policyId', model.policyId.toLowerCase()) + .property('type', model.type) + .property('description', model.description) + .property('document', model.document) + .as('policy'); + + model.appliesTo.forEach((path, index) => { + this.addCreateAppliesToTraversal(path, index, traversal); + }); + + await traversal.iterate(); logger.debug(`policies.dao create: exit: id: ${id}`); return id; @@ -113,51 +104,47 @@ export class PoliciesDaoFull extends BaseDaoFull { const id = `policy___${existing.policyId}`; /* update the main policy object */ - const conn = super.getConnection(); - try { - const traversal = conn.traversal.V(id); + const conn = await super.getConnection(); + const traversal = conn.traversal.V(id); - if (updated.type) { - traversal.property(process.cardinality.single, 'type', updated.type.toLowerCase()); - } - if (updated.description) { - traversal.property(process.cardinality.single, 'description', updated.description); - } - if (updated.document) { - traversal.property(process.cardinality.single, 'document', updated.document); - } - traversal.as('policy'); - - /* identfy any changes in the appliesTo relationship */ - const changedAppliesTo = this.identifyChangedAppliesTo( - existing.appliesTo, - updated.appliesTo - ); - logger.debug( - `policies.dao update: changedAppliesTo: ${JSON.stringify(changedAppliesTo)}` - ); - - /* any new appliesTo we can simply add the step to the traversal */ - changedAppliesTo.add.forEach((path, index) => { - this.addCreateAppliesToTraversal(path, index, traversal); - }); - - /* as a drop() step terminates a traversal, we need to process all these as part of a single union step as the last step */ - const removedAppliesToSteps: process.GraphTraversal[] = []; - changedAppliesTo.remove.forEach((path) => { - removedAppliesToSteps.push(this.addRemoveAppliesToTraversal(path)); - }); - if (removedAppliesToSteps.length > 0) { - traversal.local(__.union(...removedAppliesToSteps)).drop(); - } + if (updated.type) { + traversal.property(process.cardinality.single, 'type', updated.type.toLowerCase()); + } + if (updated.description) { + traversal.property(process.cardinality.single, 'description', updated.description); + } + if (updated.document) { + traversal.property(process.cardinality.single, 'document', updated.document); + } + traversal.as('policy'); + + /* identfy any changes in the appliesTo relationship */ + const changedAppliesTo = this.identifyChangedAppliesTo( + existing.appliesTo, + updated.appliesTo + ); + logger.debug( + `policies.dao update: changedAppliesTo: ${JSON.stringify(changedAppliesTo)}` + ); - /* lets execute it */ - const query = await traversal.iterate(); - logger.debug(`policies.dao update: query: ${JSON.stringify(query)}`); - } finally { - await conn.close(); + /* any new appliesTo we can simply add the step to the traversal */ + changedAppliesTo.add.forEach((path, index) => { + this.addCreateAppliesToTraversal(path, index, traversal); + }); + + /* as a drop() step terminates a traversal, we need to process all these as part of a single union step as the last step */ + const removedAppliesToSteps: process.GraphTraversal[] = []; + changedAppliesTo.remove.forEach((path) => { + removedAppliesToSteps.push(this.addRemoveAppliesToTraversal(path)); + }); + if (removedAppliesToSteps.length > 0) { + traversal.local(__.union(...removedAppliesToSteps)).drop(); } + /* lets execute it */ + const query = await traversal.iterate(); + logger.debug(`policies.dao update: query: ${JSON.stringify(query)}`); + logger.debug(`policies.dao update: exit: id: ${id}`); return id; } @@ -196,27 +183,22 @@ export class PoliciesDaoFull extends BaseDaoFull { const id = `device___${deviceId.toLowerCase()}`; - let results; - const conn = super.getConnection(); - try { - results = await conn.traversal - .V(id) - .as('device') - .union(__.out(), __.out().repeat(__.out('parent').simplePath().dedup()).emit()) - .as('deviceGroups') - .in_('appliesTo') - .hasLabel('policy') - .has('type', type) - .dedup() - .as('policies') - .project('policy', 'groups', 'policyGroups') - .by(__.identity().valueMap().with_(process.withOptions.tokens)) - .by(__.select('device').out().hasLabel('group').fold()) - .by(__.local(__.out('appliesTo').fold())) - .toList(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const results = await conn.traversal + .V(id) + .as('device') + .union(__.out(), __.out().repeat(__.out('parent').simplePath().dedup()).emit()) + .as('deviceGroups') + .in_('appliesTo') + .hasLabel('policy') + .has('type', type) + .dedup() + .as('policies') + .project('policy', 'groups', 'policyGroups') + .by(__.identity().valueMap().with_(process.withOptions.tokens)) + .by(__.select('device').out().hasLabel('group').fold()) + .by(__.local(__.out('appliesTo').fold())) + .toList(); const policies: AttachedPolicy[] = []; for (const result of results) { @@ -240,34 +222,29 @@ export class PoliciesDaoFull extends BaseDaoFull { const ids: string[] = []; groupPaths.forEach((v) => ids.push(`group___${v.toLowerCase()}`)); - let results; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(ids) - .as('groups') - .union(__.identity(), __.repeat(__.out('parent').simplePath().dedup()).emit()) - .as('parentGroups') - .in_('appliesTo') - .hasLabel('policy'); - - if (type !== undefined) { - traverser.has('type', type); - } - - traverser - .dedup() - .as('policies') - .project('policy', 'groups', 'policyGroups') - .by(__.identity().valueMap().with_(process.withOptions.tokens)) - .by(__.select('groups').fold()) - .by(__.local(__.out('appliesTo').fold())); - - results = await traverser.toList(); - } finally { - await conn.close(); + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(ids) + .as('groups') + .union(__.identity(), __.repeat(__.out('parent').simplePath().dedup()).emit()) + .as('parentGroups') + .in_('appliesTo') + .hasLabel('policy'); + + if (type !== undefined) { + traverser.has('type', type); } + traverser + .dedup() + .as('policies') + .project('policy', 'groups', 'policyGroups') + .by(__.identity().valueMap().with_(process.withOptions.tokens)) + .by(__.select('groups').fold()) + .by(__.local(__.out('appliesTo').fold())); + + const results = await traverser.toList(); + const policies: AttachedPolicy[] = []; for (const result of results) { policies.push(JSON.parse(JSON.stringify(result)) as AttachedPolicy); @@ -282,32 +259,27 @@ export class PoliciesDaoFull extends BaseDaoFull { public async listPolicies(type: string, offset: number, count: number): Promise { logger.debug(`policies.dao listPolicies: type:${type}, offset:${offset}, count:${count}`); - let results; - const conn = super.getConnection(); - try { - const traverser = conn.traversal.V().hasLabel('policy'); - if (type !== undefined) { - traverser.has('type', type.toLowerCase()); - } - traverser - .as('policies') - .project('policy', 'groups') - .by(__.valueMap().with_(process.withOptions.tokens)) - .by(__.out('appliesTo').hasLabel('group').fold()); - - // apply pagination - if (offset !== undefined && count !== undefined) { - // note: workaround for wierd typescript issue. even though offset/count are declared as numbers - // througout, they are being interpreted as strings within gremlin, therefore need to force to int beforehand - const offsetAsInt = parseInt(offset.toString(), 0); - const countAsInt = parseInt(count.toString(), 0); - traverser.range(offsetAsInt, offsetAsInt + countAsInt); - } - - results = await traverser.toList(); - } finally { - await conn.close(); + const conn = await super.getConnection(); + const traverser = conn.traversal.V().hasLabel('policy'); + if (type !== undefined) { + traverser.has('type', type.toLowerCase()); } + traverser + .as('policies') + .project('policy', 'groups') + .by(__.valueMap().with_(process.withOptions.tokens)) + .by(__.out('appliesTo').hasLabel('group').fold()); + + // apply pagination + if (offset !== undefined && count !== undefined) { + // note: workaround for wierd typescript issue. even though offset/count are declared as numbers + // througout, they are being interpreted as strings within gremlin, therefore need to force to int beforehand + const offsetAsInt = parseInt(offset.toString(), 0); + const countAsInt = parseInt(count.toString(), 0); + traverser.range(offsetAsInt, offsetAsInt + countAsInt); + } + + const results = await traverser.toList(); logger.debug(`results: ${JSON.stringify(results)}`); @@ -325,12 +297,8 @@ export class PoliciesDaoFull extends BaseDaoFull { const dbId = `policy___${policyId.toLowerCase()}`; - const conn = super.getConnection(); - try { - await conn.traversal.V(dbId).drop().next(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + await conn.traversal.V(dbId).drop().next(); logger.debug(`policies.dao delete: exit`); } diff --git a/source/packages/services/assetlibrary/src/profiles/profiles.full.dao.ts b/source/packages/services/assetlibrary/src/profiles/profiles.full.dao.ts index 9d438cbf3..905f3578c 100644 --- a/source/packages/services/assetlibrary/src/profiles/profiles.full.dao.ts +++ b/source/packages/services/assetlibrary/src/profiles/profiles.full.dao.ts @@ -39,34 +39,30 @@ export class ProfilesDaoFull extends BaseDaoFull { const labels = n.types.join('::'); /* create the profile */ - const conn = super.getConnection(); - try { - const traversal = conn.traversal - .V(templateId) - .as('type') - .addV(labels) - .property(process.t.id, profileId); - - /* set the profiles attributes */ - for (const key of Object.keys(n.attributes)) { - if (n.attributes[key] !== undefined) { - traversal.property(process.cardinality.single, key, n.attributes[key]); - } + const conn = await super.getConnection(); + const traversal = conn.traversal + .V(templateId) + .as('type') + .addV(labels) + .property(process.t.id, profileId); + + /* set the profiles attributes */ + for (const key of Object.keys(n.attributes)) { + if (n.attributes[key] !== undefined) { + traversal.property(process.cardinality.single, key, n.attributes[key]); } + } - /* save any groups */ - if (n.groups) { - traversal.property(process.cardinality.single, 'groups', JSON.stringify(n.groups)); - } + /* save any groups */ + if (n.groups) { + traversal.property(process.cardinality.single, 'groups', JSON.stringify(n.groups)); + } - /* link the profile to the type */ - traversal.as('profile').addE('profiles').from_('profile').to('type'); + /* link the profile to the type */ + traversal.as('profile').addE('profiles').from_('profile').to('type'); - // logger.debug(`profiles.full.dao create: traversal:${traversal}`); - await traversal.iterate(); - } finally { - await conn.close(); - } + // logger.debug(`profiles.full.dao create: traversal:${traversal}`); + await traversal.iterate(); logger.debug(`profiles.full.dao create: exit: id:${profileId}`); return profileId; @@ -80,26 +76,21 @@ export class ProfilesDaoFull extends BaseDaoFull { const id = `profile___${templateId}___${profileId}`; // assemble the main query - const conn = super.getConnection(); - let result; - try { - const traverser = conn.traversal - .V(id) - .as('profile') - .out('profiles') - .as('template') - .out('super_type') - .as('category') - .project('profile', 'template', 'category') - .by(__.select('profile').valueMap().with_(process.withOptions.tokens)) - .by(__.select('template').valueMap().with_(process.withOptions.tokens)) - .by(__.select('category').valueMap().with_(process.withOptions.tokens)); - - // execute and retrieve the resutls - result = await traverser.next(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(id) + .as('profile') + .out('profiles') + .as('template') + .out('super_type') + .as('category') + .project('profile', 'template', 'category') + .by(__.select('profile').valueMap().with_(process.withOptions.tokens)) + .by(__.select('template').valueMap().with_(process.withOptions.tokens)) + .by(__.select('category').valueMap().with_(process.withOptions.tokens)); + + // execute and retrieve the resutls + const result = await traverser.next(); if (result === undefined || result.value === undefined || result.value === null) { logger.debug(`profiles.full.dao get: exit: node: undefined`); @@ -118,30 +109,26 @@ export class ProfilesDaoFull extends BaseDaoFull { const id = `profile___${n.templateId}___${n.attributes['profileId']}`; - const conn = super.getConnection(); - try { - const traversal = conn.traversal.V(id); - // drop() step terminates a traversal, process all drops as part of a final union step - const dropTraversals: process.GraphTraversal[] = []; - - for (const [key, val] of Object.entries(n.attributes)) { - if (val !== undefined) { - if (val === null) { - dropTraversals.push(__.properties(key)); - } else { - traversal.property(process.cardinality.single, key, val); - } + const conn = await super.getConnection(); + const traversal = conn.traversal.V(id); + // drop() step terminates a traversal, process all drops as part of a final union step + const dropTraversals: process.GraphTraversal[] = []; + + for (const [key, val] of Object.entries(n.attributes)) { + if (val !== undefined) { + if (val === null) { + dropTraversals.push(__.properties(key)); + } else { + traversal.property(process.cardinality.single, key, val); } } - if (dropTraversals.length > 0) { - traversal.local(__.union(...dropTraversals)).drop(); - } - - await traversal.iterate(); - } finally { - await conn.close(); + } + if (dropTraversals.length > 0) { + traversal.local(__.union(...dropTraversals)).drop(); } + await traversal.iterate(); + logger.debug(`profiles.full.dao update: exit: id:${id}`); return id; } @@ -183,12 +170,8 @@ export class ProfilesDaoFull extends BaseDaoFull { const id = `profile___${templateId}___${profileId}`; - const conn = super.getConnection(); - try { - await conn.traversal.V(id).drop().iterate(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + await conn.traversal.V(id).drop().iterate(); logger.debug(`profiles.full.dao delete: exit`); } @@ -198,26 +181,21 @@ export class ProfilesDaoFull extends BaseDaoFull { const id = `type___${templateId}`; - const conn = super.getConnection(); - let result; - try { - const traverser = conn.traversal - .V(id) - .as('template') - .out('super_type') - .as('category') - .select('template') - .in_('profiles') - .as('profiles') - .project('profiles', 'template', 'category') - .by(__.select('profiles').valueMap().with_(process.withOptions.tokens).fold()) - .by(__.select('template').valueMap().with_(process.withOptions.tokens)) - .by(__.select('category').valueMap().with_(process.withOptions.tokens)); - - result = await traverser.next(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(id) + .as('template') + .out('super_type') + .as('category') + .select('template') + .in_('profiles') + .as('profiles') + .project('profiles', 'template', 'category') + .by(__.select('profiles').valueMap().with_(process.withOptions.tokens).fold()) + .by(__.select('template').valueMap().with_(process.withOptions.tokens)) + .by(__.select('category').valueMap().with_(process.withOptions.tokens)); + + const result = await traverser.next(); logger.debug(`profiles.full.dao get: results: ${JSON.stringify(result)}`); diff --git a/source/packages/services/assetlibrary/src/search/search.full.dao.ts b/source/packages/services/assetlibrary/src/search/search.full.dao.ts index 1939266c9..b191f7031 100644 --- a/source/packages/services/assetlibrary/src/search/search.full.dao.ts +++ b/source/packages/services/assetlibrary/src/search/search.full.dao.ts @@ -243,37 +243,32 @@ export class SearchDaoFull extends BaseDaoFull { )}, authorizedPaths:${authorizedPaths}` ); - let results; - const conn = super.getConnection(); - try { - const traverser = this.buildSearchTraverser(conn, request, authorizedPaths); - - if (request.sort?.length > 0) { - traverser.order(); - request.sort.forEach((s) => { - const order = s.direction === 'ASC' ? process.order.asc : process.order.desc; - traverser.by(__.coalesce(__.values(s.field), __.constant('')), order); - }); - } + const conn = await super.getConnection(); + const traverser = this.buildSearchTraverser(conn, request, authorizedPaths); + + if (request.sort?.length > 0) { + traverser.order(); + request.sort.forEach((s) => { + const order = s.direction === 'ASC' ? process.order.asc : process.order.desc; + traverser.by(__.coalesce(__.values(s.field), __.constant('')), order); + }); + } - // TODO: this should be done from any service that calls this, so we should replace this with a simple number/range validation - const { offsetAsInt, countAsInt } = this.typeUtils.parseAndValidateOffsetAndCount( - request.offset, - request.count - ); - traverser - .range(offsetAsInt, offsetAsInt + countAsInt) - .valueMap() - .with_(process.withOptions.tokens); + // TODO: this should be done from any service that calls this, so we should replace this with a simple number/range validation + const { offsetAsInt, countAsInt } = this.typeUtils.parseAndValidateOffsetAndCount( + request.offset, + request.count + ); + traverser + .range(offsetAsInt, offsetAsInt + countAsInt) + .valueMap() + .with_(process.withOptions.tokens); - logger.debug( - `search.full.dao search: traverser:${JSON.stringify(traverser.toString())}` - ); + logger.debug( + `search.full.dao search: traverser:${JSON.stringify(traverser.toString())}` + ); - results = await traverser.toList(); - } finally { - await conn.close(); - } + const results = await traverser.toList(); logger.debug(`search.full.dao search: results:${JSON.stringify(results)}`); @@ -304,17 +299,13 @@ export class SearchDaoFull extends BaseDaoFull { request )}, authorizedPaths:${authorizedPaths}` ); - const conn = super.getConnection(); - try { - const traverser = this.buildSearchTraverser(conn, request, authorizedPaths).union( - __.hasLabel('group'), - __.has('deviceId') - ); - logger.debug(`search.full.dao delete: in: traverser: ${traverser.toString()}`); - await traverser.drop().iterate(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const traverser = this.buildSearchTraverser(conn, request, authorizedPaths).union( + __.hasLabel('group'), + __.has('deviceId') + ); + logger.debug(`search.full.dao delete: in: traverser: ${traverser.toString()}`); + await traverser.drop().iterate(); } public async facet( @@ -327,32 +318,27 @@ export class SearchDaoFull extends BaseDaoFull { )}, authorizedPaths:${authorizedPaths}` ); - let results; - const conn = super.getConnection(); - try { - const traverser = this.buildSearchTraverser(conn, request, authorizedPaths); - - if (request.facetField !== undefined) { - if (request.facetField.traversals !== undefined) { - request.facetField.traversals.forEach((t) => { - if (t.direction === SearchRequestFilterDirection.in) { - traverser.in_(t.relation); - } else { - traverser.out(t.relation); - } - }); - } - traverser.values(request.facetField.field).groupCount(); + const conn = await super.getConnection(); + const traverser = this.buildSearchTraverser(conn, request, authorizedPaths); + + if (request.facetField !== undefined) { + if (request.facetField.traversals !== undefined) { + request.facetField.traversals.forEach((t) => { + if (t.direction === SearchRequestFilterDirection.in) { + traverser.in_(t.relation); + } else { + traverser.out(t.relation); + } + }); } - logger.debug( - `search.full.dao buildSearchTraverser: traverser: ${JSON.stringify( - traverser.toString() - )}` - ); - results = await traverser.next(); - } finally { - await conn.close(); + traverser.values(request.facetField.field).groupCount(); } + logger.debug( + `search.full.dao buildSearchTraverser: traverser: ${JSON.stringify( + traverser.toString() + )}` + ); + const results = await traverser.next(); logger.debug(`search.full.dao facet: results: ${JSON.stringify(results)}`); @@ -374,14 +360,9 @@ export class SearchDaoFull extends BaseDaoFull { )}, authorizedPaths:${authorizedPaths}` ); - const conn = super.getConnection(); - let result; - try { - const traverser = this.buildSearchTraverser(conn, request, authorizedPaths); - result = await traverser.count().next(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const traverser = this.buildSearchTraverser(conn, request, authorizedPaths); + const result = await traverser.count().next(); const total = result.value as number; logger.debug(`search.full.dao summarize: exit: total: ${total}`); diff --git a/source/packages/services/assetlibrary/src/types/types.full.dao.ts b/source/packages/services/assetlibrary/src/types/types.full.dao.ts index 849d6b7d1..96a4f0563 100644 --- a/source/packages/services/assetlibrary/src/types/types.full.dao.ts +++ b/source/packages/services/assetlibrary/src/types/types.full.dao.ts @@ -51,56 +51,51 @@ export class TypesDaoFull extends BaseDaoFull { const dbId = `type___${templateId}`; - let result; - const conn = super.getConnection(); - try { - const traverser = conn.traversal.V(dbId).as('type'); + const conn = await super.getConnection(); + const traverser = conn.traversal.V(dbId).as('type'); - if (category !== undefined) { - const superId = `type___${category}`; - traverser.out('super_type').has(process.t.id, superId); - } + if (category !== undefined) { + const superId = `type___${category}`; + traverser.out('super_type').has(process.t.id, superId); + } - // only return published relations when we're looking at published definitions - let relationsTraversal: process.GraphTraversal; - if (status === TypeDefinitionStatus.draft) { - relationsTraversal = __.bothE('relationship') - .valueMap() - .with_(process.withOptions.tokens) - .fold(); - } else { - relationsTraversal = __.as('definition') - .bothE('relationship') - .match( - __.as('relationship') - .otherV() - .inE('current_definition') - .has('status', TypeDefinitionStatus.published) - .as('other') - ) - .select('relationship') - .valueMap() - .with_(process.withOptions.tokens) - .fold(); - } + // only return published relations when we're looking at published definitions + let relationsTraversal: process.GraphTraversal; + if (status === TypeDefinitionStatus.draft) { + relationsTraversal = __.bothE('relationship') + .valueMap() + .with_(process.withOptions.tokens) + .fold(); + } else { + relationsTraversal = __.as('definition') + .bothE('relationship') + .match( + __.as('relationship') + .otherV() + .inE('current_definition') + .has('status', TypeDefinitionStatus.published) + .as('other') + ) + .select('relationship') + .valueMap() + .with_(process.withOptions.tokens) + .fold(); + } - traverser - .select('type') - .outE('current_definition') - .has('status', status) - .inV() - .as('definition') - .project('type', 'definition', 'relations') - .by(__.select('type').valueMap().with_(process.withOptions.tokens)) - .by(__.valueMap().with_(process.withOptions.tokens).fold()) - .by(relationsTraversal); + traverser + .select('type') + .outE('current_definition') + .has('status', status) + .inV() + .as('definition') + .project('type', 'definition', 'relations') + .by(__.select('type').valueMap().with_(process.withOptions.tokens)) + .by(__.valueMap().with_(process.withOptions.tokens).fold()) + .by(relationsTraversal); - result = await traverser.toList(); + const result = await traverser.toList(); - // logger.debug(`types.full.dao get: traverser: ${traverser.toString()}`); - } finally { - await conn.close(); - } + // logger.debug(`types.full.dao get: traverser: ${traverser.toString()}`); logger.debug(`types.full.dao get: query: ${JSON.stringify(result)}`); @@ -150,45 +145,40 @@ export class TypesDaoFull extends BaseDaoFull { .fold(); } - let results; - const conn = super.getConnection(); - try { - const traverser = conn.traversal.V(superId).inE('super_type').outV().as('a'); - - // apply sorting - if (sort?.length > 0) { - traverser.order(); - sort.forEach((s) => { - const order = s.direction === 'ASC' ? process.order.asc : process.order.desc; - traverser.by(__.coalesce(__.values(s.field), __.constant('')), order); - }); - traverser.as('a'); - } - - traverser - .outE('current_definition') - .has('status', status) - .inV() - .as('def') - .project('type', 'definition', 'relations') - .by(__.select('a').valueMap().with_(process.withOptions.tokens)) - .by(__.select('def').valueMap().with_(process.withOptions.tokens).fold()) - .by(relationsTraversal); - - // apply pagination - if (offset !== undefined && count !== undefined) { - // note: workaround for wierd typescript issue. even though offset/count are declared as numbers - // througout, they are being interpreted as strings within gremlin, therefore need to force to int beforehand - const offsetAsInt = parseInt(offset.toString(), 0); - const countAsInt = parseInt(count.toString(), 0); - traverser.range(offsetAsInt, offsetAsInt + countAsInt); - } + const conn = await super.getConnection(); + const traverser = conn.traversal.V(superId).inE('super_type').outV().as('a'); + + // apply sorting + if (sort?.length > 0) { + traverser.order(); + sort.forEach((s) => { + const order = s.direction === 'ASC' ? process.order.asc : process.order.desc; + traverser.by(__.coalesce(__.values(s.field), __.constant('')), order); + }); + traverser.as('a'); + } - results = await traverser.toList(); - } finally { - await conn.close(); + traverser + .outE('current_definition') + .has('status', status) + .inV() + .as('def') + .project('type', 'definition', 'relations') + .by(__.select('a').valueMap().with_(process.withOptions.tokens)) + .by(__.select('def').valueMap().with_(process.withOptions.tokens).fold()) + .by(relationsTraversal); + + // apply pagination + if (offset !== undefined && count !== undefined) { + // note: workaround for wierd typescript issue. even though offset/count are declared as numbers + // througout, they are being interpreted as strings within gremlin, therefore need to force to int beforehand + const offsetAsInt = parseInt(offset.toString(), 0); + const countAsInt = parseInt(count.toString(), 0); + traverser.range(offsetAsInt, offsetAsInt + countAsInt); } + const results = await traverser.toList(); + logger.debug(`types.full.dao get: results: ${JSON.stringify(results)}`); if (results === undefined || results.length === 0) { @@ -212,48 +202,43 @@ export class TypesDaoFull extends BaseDaoFull { const id = `type___${model.templateId}`; const defId = `${id}___v${model.schema.version}`; - let result; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(superId) - .as('superType') - .addV('type') - .property(process.t.id, id) - .property(process.cardinality.single, 'templateId', model.templateId) - .as('type') - .addV('typeDefinition') - .property(process.t.id, defId) - .property(process.cardinality.single, 'version', model.schema.version) - .property( - process.cardinality.single, - 'definition', - JSON.stringify(model.schema.definition) - ) - .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()) - .as('definition') - .addE('current_definition') - .property('status', 'draft') - .from_('type') - .to('definition') - .addE('super_type') - .from_('type') - .to('superType'); - - this.addCreateRelationStepsToTraversal( - model.schema.relations, - model.templateId, - traverser - ); + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(superId) + .as('superType') + .addV('type') + .property(process.t.id, id) + .property(process.cardinality.single, 'templateId', model.templateId) + .as('type') + .addV('typeDefinition') + .property(process.t.id, defId) + .property(process.cardinality.single, 'version', model.schema.version) + .property( + process.cardinality.single, + 'definition', + JSON.stringify(model.schema.definition) + ) + .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()) + .as('definition') + .addE('current_definition') + .property('status', 'draft') + .from_('type') + .to('definition') + .addE('super_type') + .from_('type') + .to('superType'); + + this.addCreateRelationStepsToTraversal( + model.schema.relations, + model.templateId, + traverser + ); - logger.silly( - `types.full.dao create: traverser: ${JSON.stringify(traverser.toString())}` - ); - result = await traverser.next(); - logger.silly(`types.full.dao create: result: ${JSON.stringify(result)}`); - } finally { - await conn.close(); - } + logger.silly( + `types.full.dao create: traverser: ${JSON.stringify(traverser.toString())}` + ); + const result = await traverser.next(); + logger.silly(`types.full.dao create: result: ${JSON.stringify(result)}`); if (result === undefined) { logger.debug(`types.full.dao create: exit: query: undefined`); @@ -391,104 +376,99 @@ export class TypesDaoFull extends BaseDaoFull { const id = `type___${existing.templateId}`; - let result; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(id) - .outE('current_definition') - .has('status', 'draft') - .inV() - .as('definition') - .property( - process.cardinality.single, - 'definition', - JSON.stringify(updated.schema.definition) - ) - .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()); - - if (updated.schema.relations) { - const changedRelations = this.identifyChangedRelations( - existing.schema.relations, - updated.schema.relations - ); - logger.debug( - `types.full.dao updateDraft: changedRelations: ${JSON.stringify( - changedRelations - )}` - ); + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(id) + .outE('current_definition') + .has('status', 'draft') + .inV() + .as('definition') + .property( + process.cardinality.single, + 'definition', + JSON.stringify(updated.schema.definition) + ) + .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()); + + if (updated.schema.relations) { + const changedRelations = this.identifyChangedRelations( + existing.schema.relations, + updated.schema.relations + ); + logger.debug( + `types.full.dao updateDraft: changedRelations: ${JSON.stringify( + changedRelations + )}` + ); - const removedRelations: process.GraphTraversal[] = []; + const removedRelations: process.GraphTraversal[] = []; - Object.keys(changedRelations.remove.in).forEach((key) => { - changedRelations.remove.in[key].forEach((value) => { - removedRelations.push( - __.select('definition') - .inE('relationship') - .has('name', key) - .has('fromTemplate', value) - ); - }); + Object.keys(changedRelations.remove.in).forEach((key) => { + changedRelations.remove.in[key].forEach((value) => { + removedRelations.push( + __.select('definition') + .inE('relationship') + .has('name', key) + .has('fromTemplate', value) + ); }); - - Object.keys(changedRelations.remove.out).forEach((key) => { - changedRelations.remove.out[key].forEach((value) => { - removedRelations.push( - __.select('definition') - .outE('relationship') - .has('name', key) - .has('toTemplate', value) - ); - }); + }); + + Object.keys(changedRelations.remove.out).forEach((key) => { + changedRelations.remove.out[key].forEach((value) => { + removedRelations.push( + __.select('definition') + .outE('relationship') + .has('name', key) + .has('toTemplate', value) + ); }); + }); - if (removedRelations.length > 0) { - traverser - .select('definition') - .local(__.union(...removedRelations)) - .drop(); - } + if (removedRelations.length > 0) { + traverser + .select('definition') + .local(__.union(...removedRelations)) + .drop(); + } - Object.keys(changedRelations.add.in).forEach((relation) => { - changedRelations.add.in[relation].forEach((t) => { - const templateName = isRelationTargetExpanded(t) ? t.name : t; - const includeInAuth = isRelationTargetExpanded(t) - ? t.includeInAuth - : undefined; - this.addCreateInboundRelationStepToTraversal( - existing.templateId, - templateName, - relation, - includeInAuth, - traverser - ); - }); + Object.keys(changedRelations.add.in).forEach((relation) => { + changedRelations.add.in[relation].forEach((t) => { + const templateName = isRelationTargetExpanded(t) ? t.name : t; + const includeInAuth = isRelationTargetExpanded(t) + ? t.includeInAuth + : undefined; + this.addCreateInboundRelationStepToTraversal( + existing.templateId, + templateName, + relation, + includeInAuth, + traverser + ); }); + }); - Object.keys(changedRelations.add.out).forEach((relation) => { - changedRelations.add.out[relation].forEach((t) => { - const templateName = isRelationTargetExpanded(t) ? t.name : t; - const includeInAuth = isRelationTargetExpanded(t) - ? t.includeInAuth - : undefined; - this.addCreateOutboundRelationStepToTraversal( - existing.templateId, - templateName, - relation, - includeInAuth, - traverser - ); - }); + Object.keys(changedRelations.add.out).forEach((relation) => { + changedRelations.add.out[relation].forEach((t) => { + const templateName = isRelationTargetExpanded(t) ? t.name : t; + const includeInAuth = isRelationTargetExpanded(t) + ? t.includeInAuth + : undefined; + this.addCreateOutboundRelationStepToTraversal( + existing.templateId, + templateName, + relation, + includeInAuth, + traverser + ); }); - } - - logger.silly(`types.full.dao updateDraft: traverser: ${JSON.stringify(traverser)}`); - result = await traverser.next(); - logger.silly(`types.full.dao updateDraft: result: ${JSON.stringify(result)}`); - } finally { - await conn.close(); + }); } + logger.silly(`types.full.dao updateDraft: traverser: ${JSON.stringify(traverser)}`); + const result = await traverser.next(); + logger.silly(`types.full.dao updateDraft: result: ${JSON.stringify(result)}`); + if (result === undefined || result.value === null) { logger.debug(`types.full.dao updateDraft: exit: result: undefined`); return undefined; @@ -700,36 +680,31 @@ export class TypesDaoFull extends BaseDaoFull { const draftVersion = model.schema.version; const defId = `${id}___v${draftVersion}`; - let result; - const conn = super.getConnection(); - try { - const traverser = conn.traversal - .V(id) - .as('type') - .addV('typeDefinition') - .property(process.t.id, defId) - .property(process.cardinality.single, 'version', draftVersion) - .property( - process.cardinality.single, - 'definition', - JSON.stringify(model.schema.definition) - ) - .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()) - .as('definition') - .addE('current_definition') - .property('status', 'draft') - .from_('type'); - - this.addCreateRelationStepsToTraversal( - model.schema.relations, - model.templateId, - traverser - ); + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(id) + .as('type') + .addV('typeDefinition') + .property(process.t.id, defId) + .property(process.cardinality.single, 'version', draftVersion) + .property( + process.cardinality.single, + 'definition', + JSON.stringify(model.schema.definition) + ) + .property(process.cardinality.single, 'lastUpdated', new Date().toISOString()) + .as('definition') + .addE('current_definition') + .property('status', 'draft') + .from_('type'); + + this.addCreateRelationStepsToTraversal( + model.schema.relations, + model.templateId, + traverser + ); - result = await traverser.next(); - } finally { - await conn.close(); - } + const result = await traverser.next(); logger.debug(`types.full.dao createDraft: result: ${JSON.stringify(result)}`); @@ -757,51 +732,47 @@ export class TypesDaoFull extends BaseDaoFull { // if we don't have a published version (new type), we just need to change the current_definition status let query: { value: process.Traverser | process.TraverserValue; done: boolean }; - const conn = super.getConnection(); - try { - if (published === undefined) { - query = await conn.traversal - // 1st get a handle on all the vertices/edges that we need to update - .V(id) - // upgrade the draft edge to published - .outE('current_definition') - .has('status', TypeDefinitionStatus.draft) - .property('status', 'published') - .property('from', now) - .next(); - } else { - query = await conn.traversal - // 1st get a handle on all the vertices/edges that we need to update - .V(id) - .as('type') - .select('type') - .outE('current_definition') - .has('status', TypeDefinitionStatus.published) - .as('published_edge') - .inV() - .as('published') - .select('type') - .outE('current_definition') - .has('status', TypeDefinitionStatus.draft) - .as('draft_edge') - // create a expired_definition edge to identify the previously published definition as expired - .addE('expired_definition') - .property('from', __.select('published_edge').values('from')) - .property('to', now) - .from_('type') - .to('published') - // upgrade the draft edge to published - .select('draft_edge') - .property('status', TypeDefinitionStatus.published) - .property('from', now) - // remove the old published edge - .select('published_edge') - .drop() - .select('type', 'draft_edge') - .next(); - } - } finally { - await conn.close(); + const conn = await super.getConnection(); + if (published === undefined) { + query = await conn.traversal + // 1st get a handle on all the vertices/edges that we need to update + .V(id) + // upgrade the draft edge to published + .outE('current_definition') + .has('status', TypeDefinitionStatus.draft) + .property('status', 'published') + .property('from', now) + .next(); + } else { + query = await conn.traversal + // 1st get a handle on all the vertices/edges that we need to update + .V(id) + .as('type') + .select('type') + .outE('current_definition') + .has('status', TypeDefinitionStatus.published) + .as('published_edge') + .inV() + .as('published') + .select('type') + .outE('current_definition') + .has('status', TypeDefinitionStatus.draft) + .as('draft_edge') + // create a expired_definition edge to identify the previously published definition as expired + .addE('expired_definition') + .property('from', __.select('published_edge').values('from')) + .property('to', now) + .from_('type') + .to('published') + // upgrade the draft edge to published + .select('draft_edge') + .property('status', TypeDefinitionStatus.published) + .property('from', now) + // remove the old published edge + .select('published_edge') + .drop() + .select('type', 'draft_edge') + .next(); } if (query === undefined) { @@ -816,22 +787,18 @@ export class TypesDaoFull extends BaseDaoFull { const dbId = `type___${templateId}`; - const conn = super.getConnection(); - try { - const g = conn.traversal; + const conn = await super.getConnection(); + const g = conn.traversal; - await g - .V(dbId) - .out() - .hasLabel('typeDefinition') - .as('typeDefinitions') - .drop() - .iterate(); + await g + .V(dbId) + .out() + .hasLabel('typeDefinition') + .as('typeDefinitions') + .drop() + .iterate(); - await g.V(dbId).drop().iterate(); - } finally { - await conn.close(); - } + await g.V(dbId).drop().iterate(); logger.debug(`types.full.dao delete: exit`); } @@ -848,44 +815,39 @@ export class TypesDaoFull extends BaseDaoFull { const id = `type___${templateId}`; - const conn = super.getConnection(); - let result; - try { - const traverser = conn.traversal - .V(id) - .outE('current_definition') - .has('status', 'published') - .inV() - .as('def'); + const conn = await super.getConnection(); + const traverser = conn.traversal + .V(id) + .outE('current_definition') + .has('status', 'published') + .inV() + .as('def'); - if (relations?.in) { - Object.entries(relations.in).forEach(([relation, templates]) => { - templates.forEach((template) => { - traverser - .select('def') - .bothE('relationship') - .has('name', relation) - .has('fromTemplate', template); - }); + if (relations?.in) { + Object.entries(relations.in).forEach(([relation, templates]) => { + templates.forEach((template) => { + traverser + .select('def') + .bothE('relationship') + .has('name', relation) + .has('fromTemplate', template); }); - } - if (relations?.out) { - Object.entries(relations.out).forEach(([relation, templates]) => { - templates.forEach((template) => { - traverser - .select('def') - .bothE('relationship') - .has('name', relation) - .has('toTemplate', template); - }); + }); + } + if (relations?.out) { + Object.entries(relations.out).forEach(([relation, templates]) => { + templates.forEach((template) => { + traverser + .select('def') + .bothE('relationship') + .has('name', relation) + .has('toTemplate', template); }); - } - - result = await traverser.next(); - } finally { - await conn.close(); + }); } + const result = await traverser.next(); + const isValid = result?.value !== undefined; logger.debug(`types.full.dao validateRelationshipsByType: exit: ${isValid}`); return isValid; @@ -901,7 +863,7 @@ export class TypesDaoFull extends BaseDaoFull { const typesAsLower = types.map((t) => `type___${t.toLowerCase()}`); - const conn = super.getConnection(); + const conn = await super.getConnection(); try { const count = await conn.traversal .V(...typesAsLower) @@ -913,7 +875,6 @@ export class TypesDaoFull extends BaseDaoFull { return isValid; } catch (err) { logger.error(JSON.stringify(err)); - await conn.close(); } return true; @@ -922,13 +883,8 @@ export class TypesDaoFull extends BaseDaoFull { public async countInUse(templateId: string): Promise { logger.debug(`types.full.dao countInUse: in: templateId:${templateId}`); - let result; - const conn = super.getConnection(); - try { - result = await conn.traversal.V().hasLabel(templateId).count().next(); - } finally { - await conn.close(); - } + const conn = await super.getConnection(); + const result = await conn.traversal.V().hasLabel(templateId).count().next(); logger.debug(`types.full.dao countInUse: exit: ${JSON.stringify(result)}`);