From 1c7f267c6301bc3c4e01bbcf908d843f2fcdcc50 Mon Sep 17 00:00:00 2001 From: Vito Date: Mon, 21 May 2018 12:14:11 +0100 Subject: [PATCH] [ADF-2726] fixed save content for external repository (#3341) * [ADF-2726] start fixing the show of files loaded from CS * [ADF-2726] start fixing the show of files loaded from CS * [ADF-2726] fixed save content for external repository| * [ADF-2726] fixed save content for external repository| * [ADF-2726] reeanabled and fixed the tests * [ADF-2726] reeanabled and fixed the tests * [ADF-2726] added tests for attach file widget and activiti alfresco service * [ADF-2726] added tests for attach file widget and activiti alfresco service * [ADF-2726] fixed test --- .../process-service.component.html | 1 + .../process-service.component.ts | 6 +- .../widgets/upload/upload.widget.ts | 3 +- .../activiti-alfresco.service.spec.ts | 290 ++++++++++++++++++ .../services/activiti-alfresco.service.ts | 20 +- .../attach-file-widget.component.html | 2 +- .../attach-file-widget.component.ts | 25 +- .../attach-file-widget.components.spec.ts | 24 +- .../components/start-process.component.html | 2 +- 9 files changed, 344 insertions(+), 29 deletions(-) create mode 100644 lib/core/form/services/activiti-alfresco.service.spec.ts diff --git a/demo-shell/src/app/components/process-service/process-service.component.html b/demo-shell/src/app/components/process-service/process-service.component.html index 9d236b9cc9..94e99d7ef6 100644 --- a/demo-shell/src/app/components/process-service/process-service.component.html +++ b/demo-shell/src/app/components/process-service/process-service.component.html @@ -198,6 +198,7 @@ [appId]="appId" [name]="defaultProcessName" [processDefinitionName]="defaultProcessDefinitionName" + (formContentClicked)="onContentClick($event)" (start)="onStartProcessInstance($event)" (cancel)="onCancelProcessInstance()"> diff --git a/demo-shell/src/app/components/process-service/process-service.component.ts b/demo-shell/src/app/components/process-service/process-service.component.ts index 08be8e4f76..946c98d0f0 100644 --- a/demo-shell/src/app/components/process-service/process-service.component.ts +++ b/demo-shell/src/app/components/process-service/process-service.component.ts @@ -389,7 +389,11 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit } onContentClick(content: any): void { - this.preview.showBlob(content.name, content.contentBlob); + if (content.contentBlob) { + this.preview.showBlob(content.name, content.contentBlob); + } else { + this.preview.showResource(content.sourceId.split(';')[0]); + } } onAuditClick(event: any) { diff --git a/lib/core/form/components/widgets/upload/upload.widget.ts b/lib/core/form/components/widgets/upload/upload.widget.ts index 9ddb9e029b..0a861b78e6 100644 --- a/lib/core/form/components/widgets/upload/upload.widget.ts +++ b/lib/core/form/components/widgets/upload/upload.widget.ts @@ -84,9 +84,8 @@ export class UploadWidgetComponent extends WidgetComponent implements OnInit { () => { this.field.value = filesSaved; this.field.json.value = filesSaved; + this.hasFile = true; }); - - this.hasFile = true; } } diff --git a/lib/core/form/services/activiti-alfresco.service.spec.ts b/lib/core/form/services/activiti-alfresco.service.spec.ts new file mode 100644 index 0000000000..3e802bf695 --- /dev/null +++ b/lib/core/form/services/activiti-alfresco.service.spec.ts @@ -0,0 +1,290 @@ +/*! + * @license + * Copyright 2016 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 { ActivitiContentService } from './activiti-alfresco.service'; +import { setupTestBed } from '../../testing/setupTestBed'; +import { CoreModule } from '../../core.module'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { AlfrescoApiService } from '../../services/alfresco-api.service'; +import { AlfrescoApiServiceMock } from '../../mock/alfresco-api.service.mock'; +import { MinimalNodeEntryEntity } from 'alfresco-js-api'; +import { ExternalContent } from '../components/widgets/core/external-content'; + +declare let jasmine: any; + +describe('ActivitiContentService', () => { + + let service: ActivitiContentService; + + setupTestBed({ + imports: [ + NoopAnimationsModule, + CoreModule.forRoot() + ], + providers: [ + { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock } + ] + }); + + beforeEach(() => { + service = TestBed.get(ActivitiContentService); + }); + + beforeEach(() => { + jasmine.Ajax.install(); + }); + + afterEach(() => { + jasmine.Ajax.uninstall(); + }); + + it('Should fetch node from content repository', (done) => { + let responseBody = { + data: [ + { + folder: true, + id: 'fake-folder-id', + simpleType: 'folder', + title: 'fake-folder' + } + ], + size: 1, + start: 0, + total: 1 + }; + + service.getAlfrescoNodes('alfresco-2', 'fake-node-id').subscribe(result => { + expect(jasmine.Ajax.requests.mostRecent().url.endsWith('enterprise/integration/alfresco/2/folders/fake-node-id/content')).toBeTruthy(); + expect(result[0].id).toBe('fake-folder-id'); + expect(result[0].folder).toBeTruthy(); + done(); + }); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(responseBody) + }); + }); + + it('Should fetch the repository list for the selected tenant', (done) => { + const fakeRepositoryListAnswer = { + data: [ + { + 'authorized': true, + 'serviceId': 'alfresco-9999-SHAREME', + 'metaDataAllowed': true, + 'name': 'SHAREME' + }, + { + 'authorized': true, + 'serviceId': 'alfresco-0000-GOKUSHARE', + 'metaDataAllowed': true, + 'name': 'GOKUSHARE' + }], + size: 2, + start: 0, + total: 2 + }; + + service.getAlfrescoRepositories(0, true).subscribe(result => { + expect(jasmine.Ajax.requests.mostRecent().url.endsWith('enterprise/profile/accounts/alfresco?tenantId=0&includeAccounts=true')).toBeTruthy(); + expect(result[0].serviceId).toBe('alfresco-9999-SHAREME'); + expect(result[1].serviceId).toBe('alfresco-0000-GOKUSHARE'); + done(); + }); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + responseText: JSON.stringify(fakeRepositoryListAnswer) + }); + }); + + it('Should be able to link an alfresco content', (done) => { + const fakeContentCreated = { + contentAvailable: true, + created: '2018-05-18T09:01:02.614Z', + createdBy: { + company: 'fake-company', + email: 'fake-email', + externalId: 'fake-external-id', + firstName: 'fake-first-name', + id: 9999999, + lastName: 'fake-last-name', + pictureId: 999999 + }, + id: 999, + link: true, + linkUrl: 'fake-link-url', + mimeType: 'fake-mimeType', + name: 'fake-name', + previewStatus: 'fake-previous-status', + relatedContent: true, + simpleType: 'fake-simple-type', + source: 'fake-source', + sourceId: 'fake-source-id', + thumbnailStatus: 'fake-thumbnail-status' + }; + + const alfRepoAccountId = 'alfresco-2'; + const siteId = 'sample-workspace'; + const externalContentNode: ExternalContent = { + id: 'da196918-1324-4e97-9d26-d28f1837a0b6', + simpleType: 'content', + title: 'simple.txt', + folder: false + }; + + service.linkAlfrescoNode(alfRepoAccountId, externalContentNode, siteId).subscribe(result => { + expect(jasmine.Ajax.requests.mostRecent().url.endsWith('api/enterprise/content')).toBeTruthy(); + expect(jasmine.Ajax.requests.mostRecent().data().name).toBe(externalContentNode.title); + expect(jasmine.Ajax.requests.mostRecent().data().simpleType).toBe(externalContentNode.simpleType); + expect(jasmine.Ajax.requests.mostRecent().data().link).toBeTruthy(); + expect(jasmine.Ajax.requests.mostRecent().data().sourceId).toBe(externalContentNode.id + '@' + siteId); + expect(jasmine.Ajax.requests.mostRecent().data().source).toBe(alfRepoAccountId); + expect(result.id).toBe(999); + done(); + }); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + response: JSON.stringify(fakeContentCreated) + }); + }); + + it('Should be able to upload an alfresco content with site id', (done) => { + const fakeContentCreated = { + contentAvailable: true, + created: '2018-05-18T09:01:02.614Z', + createdBy: { + company: 'fake-company', + email: 'fake-email', + externalId: 'fake-external-id', + firstName: 'fake-first-name', + id: 9999999, + lastName: 'fake-last-name', + pictureId: 999999 + }, + id: 999, + link: true, + linkUrl: 'fake-link-url', + mimeType: 'fake-mimeType', + name: 'fake-name', + previewStatus: 'fake-previous-status', + relatedContent: true, + simpleType: 'fake-simple-type', + source: 'fake-source', + sourceId: 'fake-source-id', + thumbnailStatus: 'fake-thumbnail-status' + }; + + const alfRepoAccountId = 'alfresco-2'; + const siteId = 'sample-workspace'; + const minimalNode: MinimalNodeEntryEntity = { + id: 'da196918-1324-4e97-9d26-d28f1837a0b6', + name: 'fake-node-sample', + isFolder: false, + content: { + mimeType: 'fake-mimeType' + }, + properties: { ['cm:versionLabel']: '1.0' } + }; + + service.applyAlfrescoNode(minimalNode, siteId, alfRepoAccountId).subscribe(result => { + expect(jasmine.Ajax.requests.mostRecent().url.endsWith('api/enterprise/content')).toBeTruthy(); + expect(jasmine.Ajax.requests.mostRecent().data().name).toBe(minimalNode.name); + expect(jasmine.Ajax.requests.mostRecent().data().mimeType).toBe(minimalNode.content.mimeType); + expect(jasmine.Ajax.requests.mostRecent().data().link).toBeFalsy(); + expect(jasmine.Ajax.requests.mostRecent().data().sourceId).toBe(minimalNode.id + ';' + minimalNode.properties['cm:versionLabel'] + '@' + siteId); + expect(jasmine.Ajax.requests.mostRecent().data().source).toBe(alfRepoAccountId); + expect(result.id).toBe(999); + done(); + }); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + response: JSON.stringify(fakeContentCreated) + }); + }); + + it('Should be able to upload an alfresco content retrieving the siteId from the node path', (done) => { + const fakeContentCreated = { + contentAvailable: true, + created: '2018-05-18T09:01:02.614Z', + createdBy: { + company: 'fake-company', + email: 'fake-email', + externalId: 'fake-external-id', + firstName: 'fake-first-name', + id: 9999999, + lastName: 'fake-last-name', + pictureId: 999999 + }, + id: 999, + link: true, + linkUrl: 'fake-link-url', + mimeType: 'fake-mimeType', + name: 'fake-name', + previewStatus: 'fake-previous-status', + relatedContent: true, + simpleType: 'fake-simple-type', + source: 'fake-source', + sourceId: 'fake-source-id', + thumbnailStatus: 'fake-thumbnail-status' + }; + + const alfRepoAccountId = 'alfresco-2'; + const minimalNode: any = { + id: 'da196918-1324-4e97-9d26-d28f1837a0b6', + name: 'fake-node-sample', + isFolder: false, + path: { + elements: [ + { id: 'fake-parent-1', name: 'no-site', nodeType: 'cm:fake' }, + { id: 'fake-parent-2', name: 'Sites', nodeType: 'st:site' }, + { id: 'fake-parent-3', name: 'good-site', nodeType: 'st:site' } + ] + }, + content: { + mimeType: 'fake-mimeType' + }, + properties: { ['cm:versionLabel']: '1.0' } + }; + + service.applyAlfrescoNode(minimalNode, null, alfRepoAccountId).subscribe(result => { + expect(jasmine.Ajax.requests.mostRecent().url.endsWith('api/enterprise/content')).toBeTruthy(); + expect(jasmine.Ajax.requests.mostRecent().data().name).toBe(minimalNode.name); + expect(jasmine.Ajax.requests.mostRecent().data().mimeType).toBe(minimalNode.content.mimeType); + expect(jasmine.Ajax.requests.mostRecent().data().link).toBeFalsy(); + expect(jasmine.Ajax.requests.mostRecent().data().sourceId).toBe(minimalNode.id + ';' + minimalNode.properties['cm:versionLabel'] + '@good-site'); + expect(jasmine.Ajax.requests.mostRecent().data().source).toBe(alfRepoAccountId); + expect(result.id).toBe(999); + done(); + }); + + jasmine.Ajax.requests.mostRecent().respondWith({ + 'status': 200, + contentType: 'application/json', + response: JSON.stringify(fakeContentCreated) + }); + }); + +}); diff --git a/lib/core/form/services/activiti-alfresco.service.ts b/lib/core/form/services/activiti-alfresco.service.ts index 1a34ed1c68..d371dce770 100644 --- a/lib/core/form/services/activiti-alfresco.service.ts +++ b/lib/core/form/services/activiti-alfresco.service.ts @@ -18,7 +18,7 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { LogService } from '../../services/log.service'; import { Injectable } from '@angular/core'; -import { AlfrescoApi, MinimalNodeEntryEntity } from 'alfresco-js-api'; +import { AlfrescoApi, MinimalNodeEntryEntity, RelatedContentRepresentation } from 'alfresco-js-api'; import { Observable } from 'rxjs/Observable'; import { ExternalContent } from '../components/widgets/core/external-content'; import { ExternalContentLink } from '../components/widgets/core/external-content-link'; @@ -85,9 +85,11 @@ export class ActivitiContentService { applyAlfrescoNode(node: MinimalNodeEntryEntity, siteId: string, accountId: string) { let apiService: AlfrescoApi = this.apiService.getInstance(); - let params: any = { + const currentSideId = siteId ? siteId : this.getSiteNameFromNodePath(node); + const params: RelatedContentRepresentation = { source: accountId, - sourceId: node.id + ';1.0@' + siteId, + mimeType: node.content.mimeType, + sourceId: node.id + ';' + node.properties['cm:versionLabel'] + '@' + currentSideId, name: node.name, link: false }; @@ -97,6 +99,18 @@ export class ActivitiContentService { .catch(err => this.handleError(err)); } + private getSiteNameFromNodePath(node: MinimalNodeEntryEntity): string { + let siteName = ''; + if (node.path) { + const foundNode = node.path + .elements.find((pathNode: MinimalNodeEntryEntity) => + pathNode.nodeType === 'st:site' && + pathNode.name !== 'Sites'); + siteName = foundNode ? foundNode.name : ''; + } + return siteName.toLocaleLowerCase(); + } + toJson(res: any) { if (res) { return res || {}; diff --git a/lib/process-services/content-widget/attach-file-widget.component.html b/lib/process-services/content-widget/attach-file-widget.component.html index d7d83ff0d2..4de97ca362 100644 --- a/lib/process-services/content-widget/attach-file-widget.component.html +++ b/lib/process-services/content-widget/attach-file-widget.component.html @@ -63,7 +63,7 @@ elem.name === file.name) >= 0; } openSelectDialogFromFileSource() { @@ -129,6 +131,7 @@ export class AttachFileWidgetComponent extends UploadWidgetComponent implements if (this.isDefinedSourceFolder()) { this.contentDialog.openFileBrowseDialogByFolderId(params.fileSource.selectedFolder.pathId).subscribe( (selections: MinimalNodeEntryEntity[]) => { + this.tempFilesList.push(...selections); this.uploadFileFromCS(selections, this.field.params.fileSource.selectedFolder.accountId, this.field.params.fileSource.selectedFolder.siteId); @@ -142,14 +145,14 @@ export class AttachFileWidgetComponent extends UploadWidgetComponent implements } onRemoveAttachFile(file: any) { - if (this.isTemporaryFile(file.contentBlob)) { + if (this.isTemporaryFile(file)) { this.tempFilesList.splice(this.tempFilesList.indexOf(file.contentBlob), 1); } this.removeFile(file); } onAttachFileClicked(file: any) { - if (this.isTemporaryFile(file.contentBlob)) { + if (this.isTemporaryFile(file)) { this.formService.formContentClicked.next(file); } else { this.fileClicked(file); @@ -157,8 +160,8 @@ export class AttachFileWidgetComponent extends UploadWidgetComponent implements } downloadContent(file: any): void { - if (this.isTemporaryFile(file.contentBlob)) { - this.contentService.downloadBlob(file.contentBlob, file.name); + if (this.isTemporaryFile(file)) { + this.contentService.downloadBlob(file, file.name); } else { this.processContentService.getFileRawContent(file.id).subscribe( (blob: Blob) => { @@ -172,9 +175,10 @@ export class AttachFileWidgetComponent extends UploadWidgetComponent implements } openSelectDialog(repoId: string, repoName: string) { - const accountIdentifier = 'alfresco-' + repoId + repoName; + const accountIdentifier = 'alfresco-' + repoId + '-' + repoName; this.contentDialog.openFileBrowseDialogBySite().subscribe( (selections: MinimalNodeEntryEntity[]) => { + this.tempFilesList.push(...selections); this.uploadFileFromCS(selections, accountIdentifier); }); } @@ -183,10 +187,11 @@ export class AttachFileWidgetComponent extends UploadWidgetComponent implements let filesSaved = []; Observable.from(fileNodeList) .mergeMap(node => - this.activitiContentService.applyAlfrescoNode(node, + zip(of(node.content.mimeType), this.activitiContentService.applyAlfrescoNode(node, siteId, - accountId) - ).subscribe((res) => { + accountId)) + ).subscribe(([mymeType, res]) => { + res.mimeType = mymeType; filesSaved.push(res); }, (error) => { @@ -195,8 +200,8 @@ export class AttachFileWidgetComponent extends UploadWidgetComponent implements () => { this.field.value = filesSaved; this.field.json.value = filesSaved; + this.hasFile = true; }); - this.hasFile = true; } } diff --git a/lib/process-services/content-widget/attach-file-widget.components.spec.ts b/lib/process-services/content-widget/attach-file-widget.components.spec.ts index ce4abebf17..6b596058c2 100644 --- a/lib/process-services/content-widget/attach-file-widget.components.spec.ts +++ b/lib/process-services/content-widget/attach-file-widget.components.spec.ts @@ -29,7 +29,7 @@ import { ContentService, setupTestBed } from '@alfresco/adf-core'; -import { ContentNodeDialogService } from '@alfresco/adf-content-services'; +import { ContentNodeDialogService, ContentNodeSelectorModule } from '@alfresco/adf-content-services'; import { Observable } from 'rxjs/Observable'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; import { ProcessTestingModule } from '../testing/process.testing.module'; @@ -72,7 +72,10 @@ const definedSourceParams = { const fakeMinimalNode: MinimalNodeEntryEntity = { id: 'fake', - name: 'fake-name' + name: 'fake-name', + content: { + mimeType: 'application/pdf' + } }; const fakePngAnswer = { @@ -89,9 +92,7 @@ const fakePngAnswer = { 'thumbnailStatus': 'queued' }; -/* tslint:disable */ -// TODO: crashes because of LogService problem -xdescribe('AttachFileWidgetComponent', () => { +describe('AttachFileWidgetComponent', () => { let widget: AttachFileWidgetComponent; let fixture: ComponentFixture; @@ -103,7 +104,7 @@ xdescribe('AttachFileWidgetComponent', () => { let formService: FormService; setupTestBed({ - imports: [ProcessTestingModule] + imports: [ProcessTestingModule, ContentNodeSelectorModule] }); beforeEach(async(() => { @@ -165,15 +166,15 @@ xdescribe('AttachFileWidgetComponent', () => { })); it('should be able to upload files coming from content node selector', async(() => { + spyOn(activitiContentService, 'getAlfrescoRepositories').and.returnValue(Observable.of(fakeRepositoryListAnswer)); + spyOn(activitiContentService, 'applyAlfrescoNode').and.returnValue(Observable.of(fakePngAnswer)); + spyOn(contentNodeDialogService, 'openFileBrowseDialogBySite').and.returnValue(Observable.of([fakeMinimalNode])); widget.field = new FormFieldModel(new FormModel(), { type: FormFieldTypes.UPLOAD, value: [] }); widget.field.id = 'attach-file-attach'; widget.field.params = allSourceParams; - spyOn(activitiContentService, 'getAlfrescoRepositories').and.returnValue(Observable.of(fakeRepositoryListAnswer)); - spyOn(activitiContentService, 'applyAlfrescoNode').and.returnValue(Observable.of(fakePngAnswer)); - spyOn(contentNodeDialogService, 'openFileBrowseDialogBySite').and.returnValue(Observable.of([fakeMinimalNode])); fixture.detectChanges(); fixture.whenStable().then(() => { let attachButton: HTMLButtonElement = element.querySelector('#attach-file-attach'); @@ -182,8 +183,9 @@ xdescribe('AttachFileWidgetComponent', () => { fixture.detectChanges(); fixture.debugElement.query(By.css('#attach-SHAREME')).nativeElement.click(); fixture.detectChanges(); - - expect(element.querySelector('#file-1155-icon')).not.toBeNull(); + fixture.whenStable().then(() => { + expect(element.querySelector('#file-1155-icon')).not.toBeNull(); + }); }); })); diff --git a/lib/process-services/process-list/components/start-process.component.html b/lib/process-services/process-list/components/start-process.component.html index 4dba152516..151b745ab9 100644 --- a/lib/process-services/process-list/components/start-process.component.html +++ b/lib/process-services/process-list/components/start-process.component.html @@ -25,7 +25,7 @@ *ngIf="hasStartForm()" [data]="values" [disableStartProcessButton]="!hasProcessName()" - [processDefinitionId]="selectedProcessDef.id" + [processDefinitionId]="selectedProcessDef.id" (outcomeClick)="onOutcomeClick($event)" [showRefreshButton]="false">