From 79cb3282052731d145d212635f35fddf872ae046 Mon Sep 17 00:00:00 2001 From: dhrn <14145706+dhrn@users.noreply.github.com> Date: Mon, 6 Apr 2020 19:51:54 +0530 Subject: [PATCH] [ADF-1881] Consolidate NodeService and NodesApiService (#5591) * [ADF-1881] Consolidate NodeService and NodesApiService * * docs added --- docs/core/services/node.service.md | 14 +- docs/core/services/nodes-api.service.md | 19 ++ lib/core/form/services/node.service.spec.ts | 2 +- lib/core/form/services/node.service.ts | 65 ++----- .../{form => }/models/node-metadata.model.ts | 0 lib/core/models/public-api.ts | 1 + lib/core/services/nodes-api.service.spec.ts | 164 ++++++++++++++++++ lib/core/services/nodes-api.service.ts | 121 +++++++++---- 8 files changed, 297 insertions(+), 89 deletions(-) rename lib/core/{form => }/models/node-metadata.model.ts (100%) create mode 100644 lib/core/services/nodes-api.service.spec.ts diff --git a/docs/core/services/node.service.md b/docs/core/services/node.service.md index b127152f11..6574c31c87 100644 --- a/docs/core/services/node.service.md +++ b/docs/core/services/node.service.md @@ -5,7 +5,9 @@ Status: Active Last reviewed: 2018-11-20 --- -# [Node Service](../../../lib/core/form/services/node.service.ts "Defined in node.service.ts") +# [Node Service](../../../lib/core/form/services/node.service.ts "Defined in node.service.ts") **Deprecated** + +use [Nodes Api service](./nodes-api.service.md) instead of this. Gets Alfresco Repository node metadata and creates nodes with metadata. @@ -14,24 +16,24 @@ Gets Alfresco Repository node metadata and creates nodes with metadata. ### Methods - **createNode**(name: `string`, nodeType: `string`, properties: `any`, path: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`>`<br/> - Create a new Node from form metadata + (**Deprecated:** in 3.8.0, use `createNodeInsideRoot` method from NodesApiService instead. Create a new Node from form metadata) - _name:_ `string` - [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) name - _nodeType:_ `string` - [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) type - _properties:_ `any` - [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) body properties - _path:_ `string` - Path to the node - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`>` - The created node - **createNodeMetadata**(nodeType: `string`, nameSpace: `any`, data: `any`, path: `string`, name?: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`>`<br/> - Create a new Node from form metadata. + (**Deprecated:** in 3.8.0, use NodesApiService instead. Create a new Node from form metadata.) - _nodeType:_ `string` - [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) type - _nameSpace:_ `any` - Namespace for properties - _data:_ `any` - [Property](../../../lib/content-services/src/lib/content-metadata/interfaces/property.interface.ts) data to store in the node under namespace - _path:_ `string` - Path to the node - _name:_ `string` - (Optional) [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) name - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`>` - The created node -- **getNodeMetadata**(nodeId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeMetadata`](../../../lib/core/form/models/node-metadata.model.ts)`>`<br/> - Get the metadata and the nodeType for a nodeId cleaned by the prefix. +- **getNodeMetadata**(nodeId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeMetadata`](../../../lib/core/models/node-metadata.model.ts)`>`<br/> + (**Deprecated:** in 3.8.0, use NodesApiService instead. Get the metadata and the nodeType for a nodeId cleaned by the prefix.) - _nodeId:_ `string` - ID of the target node - - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeMetadata`](../../../lib/core/form/models/node-metadata.model.ts)`>` - Node metadata + - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeMetadata`](../../../lib/core/models/node-metadata.model.ts)`>` - Node metadata ## Details diff --git a/docs/core/services/nodes-api.service.md b/docs/core/services/nodes-api.service.md index 7715704909..23601c1148 100644 --- a/docs/core/services/nodes-api.service.md +++ b/docs/core/services/nodes-api.service.md @@ -36,6 +36,21 @@ Accesses and manipulates ACS document nodes using their node IDs. - _nodeBody:_ `any` - Data for the new node - _options:_ `any` - Optional parameters supported by JS-API - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`MinimalNode`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeMinimalEntry.md)`>` - Details of the new node +- **createNodeInsideRoot**(name: `string`, nodeType: `string`, properties: `any`, path: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`>`<br/> + Create a new Node inside `-root-` folder + - _name:_ `string` - [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) name + - _nodeType:_ `string` - [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) type + - _properties:_ `any` - [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) body properties + - _path:_ `string` - Path to the node + - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`>` - The created node +- **createNodeMetadata**(nodeType: `string`, nameSpace: `any`, data: `any`, path: `string`, name?: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`>`<br/> + Create a new Node from form metadata. + - _nodeType:_ `string` - [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) type + - _nameSpace:_ `any` - Namespace for properties + - _data:_ `any` - [Property](../../../lib/content-services/src/lib/content-metadata/interfaces/property.interface.ts) data to store in the node under namespace + - _path:_ `string` - Path to the node + - _name:_ `string` - (Optional) [Node](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) name + - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`>` - The created node - **deleteNode**(nodeId: `string`, options: `any` = `{}`): [`Observable`](http://reactivex.io/documentation/observable.html)`<any>`<br/> Moves a node to the trashcan. - _nodeId:_ `string` - ID of the target node @@ -51,6 +66,10 @@ Accesses and manipulates ACS document nodes using their node IDs. - _nodeId:_ `string` - ID of the target node - _options:_ `any` - Optional parameters supported by JS-API - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/NodePaging.md)`>` - List of child items from the folder +- **getNodeMetadata**(nodeId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeMetadata`](../../../lib/core/models/node-metadata.model.ts)`>`<br/> + Get the metadata and the nodeType for a nodeId cleaned by the prefix. + - _nodeId:_ `string` - ID of the target node + - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodeMetadata`](../../../lib/core/models/node-metadata.model.ts)`>` - Node metadata - **restoreNode**(nodeId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`MinimalNode`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeMinimalEntry.md)`>`<br/> Restores a node previously moved to the trashcan. - _nodeId:_ `string` - ID of the node to restore diff --git a/lib/core/form/services/node.service.spec.ts b/lib/core/form/services/node.service.spec.ts index 39d574668c..16ba3871fd 100644 --- a/lib/core/form/services/node.service.spec.ts +++ b/lib/core/form/services/node.service.spec.ts @@ -16,7 +16,7 @@ */ import { TestBed } from '@angular/core/testing'; -import { NodeMetadata } from '../models/node-metadata.model'; +import { NodeMetadata } from '../../models/node-metadata.model'; import { EcmModelService } from './ecm-model.service'; import { NodeService } from './node.service'; import { setupTestBed } from '../../testing/setup-test-bed'; diff --git a/lib/core/form/services/node.service.ts b/lib/core/form/services/node.service.ts index be942956a1..591d12f5d5 100644 --- a/lib/core/form/services/node.service.ts +++ b/lib/core/form/services/node.service.ts @@ -15,32 +15,34 @@ * limitations under the License. */ -import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { Injectable } from '@angular/core'; -import { Observable, from } from 'rxjs'; -import { NodeMetadata } from '../models/node-metadata.model'; -import { map } from 'rxjs/operators'; -import { AlfrescoApiCompatibility, NodeEntry } from '@alfresco/js-api'; +import { Observable } from 'rxjs'; +import { NodeEntry } from '@alfresco/js-api'; +import { NodeMetadata } from '../../models/node-metadata.model'; +import { NodesApiService } from '../../services/nodes-api.service'; @Injectable({ providedIn: 'root' }) +/** + * @deprecated in 3.8.0, use NodesApiService instead. + */ export class NodeService { - constructor(private apiService: AlfrescoApiService) { - } + constructor(private nodesApiService: NodesApiService) {} /** + * @deprecated in 3.8.0, use NodesApiService instead. * Get the metadata and the nodeType for a nodeId cleaned by the prefix. * @param nodeId ID of the target node * @returns Node metadata */ public getNodeMetadata(nodeId: string): Observable<NodeMetadata> { - return from(this.apiService.getInstance().nodes.getNode(nodeId)) - .pipe(map(this.cleanMetadataFromSemicolon)); + return this.nodesApiService.getNodeMetadata(nodeId); } /** + * @deprecated in 3.8.0, use NodesApiService instead. * Create a new Node from form metadata. * @param path Path to the node * @param nodeType Node type @@ -50,17 +52,11 @@ export class NodeService { * @returns The created node */ public createNodeMetadata(nodeType: string, nameSpace: any, data: any, path: string, name?: string): Observable<NodeEntry> { - const properties = {}; - for (const key in data) { - if (data[key]) { - properties[nameSpace + ':' + key] = data[key]; - } - } - - return this.createNode(name || this.generateUuid(), nodeType, properties, path); + return this.nodesApiService.createNodeMetadata(nodeType, nameSpace, data, path, name); } /** + * @deprecated in 3.8.0, use `createNodeInsideRoot` method from NodesApiService instead. * Create a new Node from form metadata * @param name Node name * @param nodeType Node type @@ -69,39 +65,6 @@ export class NodeService { * @returns The created node */ public createNode(name: string, nodeType: string, properties: any, path: string): Observable<NodeEntry> { - const body = { - name: name, - nodeType: nodeType, - properties: properties, - relativePath: path - }; - - const apiService: AlfrescoApiCompatibility = this.apiService.getInstance(); - return from(apiService.nodes.addNode('-root-', body, {})); - } - - private generateUuid() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - } - - private cleanMetadataFromSemicolon(nodeEntry: NodeEntry): NodeMetadata { - const metadata = {}; - - if (nodeEntry && nodeEntry.entry.properties) { - for (const key in nodeEntry.entry.properties) { - if (key) { - if (key.indexOf(':') !== -1) { - metadata [key.split(':')[1]] = nodeEntry.entry.properties[key]; - } else { - metadata [key] = nodeEntry.entry.properties[key]; - } - } - } - } - - return new NodeMetadata(metadata, nodeEntry.entry.nodeType); + return this.nodesApiService.createNodeInsideRoot(name, nodeType, properties, path); } } diff --git a/lib/core/form/models/node-metadata.model.ts b/lib/core/models/node-metadata.model.ts similarity index 100% rename from lib/core/form/models/node-metadata.model.ts rename to lib/core/models/node-metadata.model.ts diff --git a/lib/core/models/public-api.ts b/lib/core/models/public-api.ts index 5f402d7933..0516710427 100644 --- a/lib/core/models/public-api.ts +++ b/lib/core/models/public-api.ts @@ -34,3 +34,4 @@ export * from './identity-user.model'; export * from './identity-role.model'; export * from './identity-group.model'; export * from './search-text-input.model'; +export * from './node-metadata.model'; diff --git a/lib/core/services/nodes-api.service.spec.ts b/lib/core/services/nodes-api.service.spec.ts new file mode 100644 index 0000000000..810fed225d --- /dev/null +++ b/lib/core/services/nodes-api.service.spec.ts @@ -0,0 +1,164 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { NodesApiService } from './nodes-api.service'; +import { setupTestBed } from '../testing/setup-test-bed'; +import { CoreModule } from '../core.module'; +import { AlfrescoApiService } from './alfresco-api.service'; +import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock'; +import { NodeMetadata } from '../models/node-metadata.model'; + +describe('NodesApiService', () => { + let service: NodesApiService; + let apiService: AlfrescoApiServiceMock; + + const MODEL_NAMESPACE = 'activitiForms'; + const responseBody = { + entry: { + id: '111-222-33-44-1123', + nodeType: 'typeTest', + properties: { + test: 'test', + testdata: 'testdata' + } + } + }; + const mockSpy = { + core: { + nodesApi: { + getNode: jasmine.createSpy('getNode'), + getNodeChildren: jasmine.createSpy('getNodeChildren'), + addNode: jasmine.createSpy('addNode') + } + } + }; + + setupTestBed({ + imports: [ + NoopAnimationsModule, + CoreModule.forRoot() + ], + providers: [ + { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock } + ] + }); + + beforeEach(() => { + service = TestBed.get(NodesApiService); + apiService = TestBed.get(AlfrescoApiService); + spyOn(apiService, 'getInstance').and.returnValue(mockSpy); + }); + + afterEach(() => { + mockSpy.core.nodesApi.getNode.calls.reset(); + mockSpy.core.nodesApi.getNodeChildren.calls.reset(); + mockSpy.core.nodesApi.addNode.calls.reset(); + }); + + it('Should return the node information', (done) => { + mockSpy.core.nodesApi.getNode.and.returnValue(Promise.resolve(responseBody)); + + service.getNode('-nodeid-').subscribe((result) => { + const args = [ + '-nodeid-', + { 'include': ['path', 'properties', 'allowableOperations', 'permissions'] } + ]; + expect(mockSpy.core.nodesApi.getNode.calls.mostRecent().args).toEqual(args); + expect(result).toEqual(<any> responseBody.entry); + done(); + }); + }); + + it('Should return the node child information', (done) => { + const fakeNodeList = { + list: { + entries: [ + { entry: { id: 'fake-node-id', name: 'fake-node-name', isFolder: true } }, + { entry: { id: 'fake-file-id', name: 'fake-file-name', isFolder: false } } + ] + } + }; + mockSpy.core.nodesApi.getNodeChildren.and.returnValue(Promise.resolve(fakeNodeList)); + + service.getNodeChildren('-nodeid-', {}).subscribe((result) => { + const args = [ + '-nodeid-', + { + 'include': ['path', 'properties', 'allowableOperations', 'permissions'], + maxItems: 25, + skipCount: 0 + } + ]; + expect(mockSpy.core.nodesApi.getNodeChildren.calls.mostRecent().args).toEqual(args); + expect(result).toBe(<any> fakeNodeList); + done(); + }); + }); + + it('Should fetch and node metadata', (done) => { + mockSpy.core.nodesApi.getNode.and.returnValue(Promise.resolve(responseBody)); + + service.getNodeMetadata('-nodeid-').subscribe((result) => { + const node = new NodeMetadata({ + test: 'test', + testdata: 'testdata' + }, 'typeTest'); + expect(result).toEqual(node); + done(); + }); + }); + + it('Should create a node with metadata', (done) => { + const data = { + test: 'test', + testdata: 'testdata' + }; + mockSpy.core.nodesApi.addNode.and.returnValue(Promise.resolve(responseBody)); + + service.createNodeMetadata('typeTest', MODEL_NAMESPACE, data, '/Sites/swsdp/documentLibrary', 'testNode').subscribe((response) => { + const args = [ + '-root-', + { + 'name': 'testNode', + 'nodeType': 'typeTest', + 'properties': { + 'activitiForms:test': 'test', + 'activitiForms:testdata': 'testdata' + }, + 'relativePath': '/Sites/swsdp/documentLibrary' + }, + {} + ]; + expect(mockSpy.core.nodesApi.addNode.calls.mostRecent().args).toEqual(args); + expect(response).toBe(<any> responseBody); + done(); + }); + }); + + it('Should create a random name node with metadata', (done) => { + const uuidRegex = /[0-9a-z]{8}-[0-9a-z]{4}-4[0-9a-z]{3}-[0-9a-z]{4}-[0-9a-z]{12}/; + mockSpy.core.nodesApi.addNode.and.returnValue(Promise.resolve(responseBody)); + + service.createNodeMetadata('typeTest', MODEL_NAMESPACE, {}, '/Sites/swsdp/documentLibrary').subscribe(() => { + expect(mockSpy.core.nodesApi.addNode.calls.mostRecent().args[0]).toEqual('-root-'); + expect(uuidRegex.test(mockSpy.core.nodesApi.addNode.calls.mostRecent().args[1].name)).toBe(true); + done(); + }); + }); +}); diff --git a/lib/core/services/nodes-api.service.ts b/lib/core/services/nodes-api.service.ts index af9d8778a1..2ce0cb26bf 100644 --- a/lib/core/services/nodes-api.service.ts +++ b/lib/core/services/nodes-api.service.ts @@ -16,20 +16,20 @@ */ import { Injectable } from '@angular/core'; -import { NodeEntry, MinimalNode, NodePaging } from '@alfresco/js-api'; -import { Observable, from, throwError } from 'rxjs'; +import { MinimalNode, NodeEntry, NodePaging } from '@alfresco/js-api'; +import { from, Observable, throwError } from 'rxjs'; import { AlfrescoApiService } from './alfresco-api.service'; import { UserPreferencesService } from './user-preferences.service'; -import { catchError } from 'rxjs/operators'; +import { catchError, map } from 'rxjs/operators'; +import { NodeMetadata } from '../models/node-metadata.model'; @Injectable({ providedIn: 'root' }) export class NodesApiService { - constructor( - private api: AlfrescoApiService, - private preferences: UserPreferencesService) {} + constructor(private api: AlfrescoApiService, + private preferences: UserPreferencesService) {} private get nodesApi() { return this.api.getInstance().core.nodesApi; @@ -50,11 +50,9 @@ export class NodesApiService { include: [ 'path', 'properties', 'allowableOperations', 'permissions' ] }; const queryOptions = Object.assign(defaults, options); - const promise = this.nodesApi - .getNode(nodeId, queryOptions) - .then(this.getEntryFromEntity); - return from(promise).pipe( + return from(this.nodesApi.getNode(nodeId, queryOptions)).pipe( + map(this.getEntryFromEntity), catchError((err) => throwError(err)) ); } @@ -72,10 +70,8 @@ export class NodesApiService { include: [ 'path', 'properties', 'allowableOperations', 'permissions' ] }; const queryOptions = Object.assign(defaults, options); - const promise = this.nodesApi - .getNodeChildren(nodeId, queryOptions); - return from(promise).pipe( + return from(this.nodesApi.getNodeChildren(nodeId, queryOptions)).pipe( catchError((err) => throwError(err)) ); } @@ -88,11 +84,8 @@ export class NodesApiService { * @returns Details of the new node */ createNode(parentNodeId: string, nodeBody: any, options: any = {}): Observable<MinimalNode> { - const promise = this.nodesApi - .addNode(parentNodeId, nodeBody, options) - .then(this.getEntryFromEntity); - - return from(promise).pipe( + return from(this.nodesApi.addNode(parentNodeId, nodeBody, options)).pipe( + map(this.getEntryFromEntity), catchError((err) => throwError(err)) ); } @@ -122,11 +115,8 @@ export class NodesApiService { }; const queryOptions = Object.assign(defaults, options); - const promise = this.nodesApi - .updateNode(nodeId, nodeBody, queryOptions) - .then(this.getEntryFromEntity); - - return from(promise).pipe( + return from(this.nodesApi.updateNode(nodeId, nodeBody, queryOptions)).pipe( + map(this.getEntryFromEntity), catchError((err) => throwError(err)) ); } @@ -138,9 +128,7 @@ export class NodesApiService { * @returns Empty result that notifies when the deletion is complete */ deleteNode(nodeId: string, options: any = {}): Observable<any> { - const promise = this.nodesApi.deleteNode(nodeId, options); - - return from(promise).pipe( + return from(this.nodesApi.deleteNode(nodeId, options)).pipe( catchError((err) => throwError(err)) ); } @@ -151,12 +139,83 @@ export class NodesApiService { * @returns Details of the restored node */ restoreNode(nodeId: string): Observable<MinimalNode> { - const promise = this.nodesApi - .restoreNode(nodeId) - .then(this.getEntryFromEntity); - - return from(promise).pipe( + return from(this.nodesApi.restoreNode(nodeId)).pipe( + map(this.getEntryFromEntity), catchError((err) => throwError(err)) ); } + + /** + * Get the metadata and the nodeType for a nodeId cleaned by the prefix. + * @param nodeId ID of the target node + * @returns Node metadata + */ + public getNodeMetadata(nodeId: string): Observable<NodeMetadata> { + return from(this.nodesApi.getNode(nodeId)) + .pipe(map(this.cleanMetadataFromSemicolon)); + } + + /** + * Create a new Node from form metadata. + * @param path Path to the node + * @param nodeType Node type + * @param name Node name + * @param nameSpace Namespace for properties + * @param data Property data to store in the node under namespace + * @returns The created node + */ + public createNodeMetadata(nodeType: string, nameSpace: any, data: any, path: string, name?: string): Observable<NodeEntry> { + const properties = {}; + for (const key in data) { + if (data[key]) { + properties[nameSpace + ':' + key] = data[key]; + } + } + + return this.createNodeInsideRoot(name || this.generateUuid(), nodeType, properties, path); + } + + /** + * Create a new Node inside `-root-` folder + * @param name Node name + * @param nodeType Node type + * @param properties Node body properties + * @param path Path to the node + * @returns The created node + */ + public createNodeInsideRoot(name: string, nodeType: string, properties: any, path: string): Observable<NodeEntry> { + const body = { + name: name, + nodeType: nodeType, + properties: properties, + relativePath: path + }; + return from(this.nodesApi.addNode('-root-', body, {})); + } + + private generateUuid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + + private cleanMetadataFromSemicolon(nodeEntry: NodeEntry): NodeMetadata { + const metadata = {}; + + if (nodeEntry && nodeEntry.entry.properties) { + for (const key in nodeEntry.entry.properties) { + if (key) { + if (key.indexOf(':') !== -1) { + metadata [key.split(':')[1]] = nodeEntry.entry.properties[key]; + } else { + metadata [key] = nodeEntry.entry.properties[key]; + } + } + } + } + + return new NodeMetadata(metadata, nodeEntry.entry.nodeType); + } + }