From d8a25259a4c8d8e637c1ed3b0fb4dd258a3694a8 Mon Sep 17 00:00:00 2001 From: Vaibhav Bhuva Date: Fri, 18 Aug 2023 16:46:38 +0530 Subject: [PATCH 1/5] Issue #IQ-549 feat: QuML Editor configuration CSP changes --- angular.json | 5 +- package-lock.json | 35 ++++++++++++++ package.json | 1 + .../asset-browser.component.spec.ts | 46 +++++++++++++------ .../asset-browser/asset-browser.component.ts | 26 +++++++---- .../ckeditor-tool/ckeditor-tool.component.ts | 32 ++++++++----- .../question/question.component.spec.data.ts | 1 + .../quml-player/quml-player.component.ts | 1 - .../services/editor/editor.service.spec.ts | 13 ------ .../src/lib/services/editor/editor.service.ts | 14 ++---- src/app/data.ts | 1 + 11 files changed, 113 insertions(+), 62 deletions(-) diff --git a/angular.json b/angular.json index c02c7deb..c1f541c3 100644 --- a/angular.json +++ b/angular.json @@ -49,6 +49,7 @@ "node_modules/@project-sunbird/sunbird-quml-player-web-component/sunbird-quml-player.js", "src/assets/libs/iziToast/iziToast.min.js", "node_modules/jquery.fancytree/dist/jquery.fancytree-all-deps.min.js", + "node_modules/@project-sunbird/sunbird-file-upload-library/sunbird-file-upload-library.js", "src/assets/lib/dimmer.min.js", "src/assets/lib/transition.min.js", "src/assets/lib/modal.min.js", @@ -176,7 +177,8 @@ "karmaConfig": "projects/questionset-editor-library/karma.conf.js", "scripts": [ "src/assets/libs/iziToast/iziToast.min.js", - "node_modules/jquery/dist/jquery.min.js" + "node_modules/jquery/dist/jquery.min.js", + "node_modules/@project-sunbird/sunbird-file-upload-library/sunbird-file-upload-library.js" ], "codeCoverageExclude": [ "projects/questionset-editor-library/src/lib/interfaces/*/*.ts" @@ -238,6 +240,7 @@ "node_modules/@project-sunbird/sunbird-quml-player-web-component/sunbird-quml-player.js", "src/assets/libs/iziToast/iziToast.min.js", "node_modules/jquery.fancytree/dist/jquery.fancytree-all-deps.min.js", + "node_modules/@project-sunbird/sunbird-file-upload-library/sunbird-file-upload-library.js", "src/assets/lib/dimmer.min.js", "src/assets/lib/transition.min.js", "src/assets/lib/modal.min.js", diff --git a/package-lock.json b/package-lock.json index 1d7b2b62..df5e82eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4082,6 +4082,14 @@ "resolved": "https://registry.npmjs.org/@project-sunbird/sb-styles/-/sb-styles-0.0.9.tgz", "integrity": "sha512-61jigtq4ertmh9XhnSDQlfZDSn1a8TXr7PvMxmJVWlzp4uoXHudZRzPoIi4IpvKD4AEk8nFRjIMc5eSsM5UJhw==" }, + "@project-sunbird/sunbird-file-upload-library": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-file-upload-library/-/sunbird-file-upload-library-1.0.2.tgz", + "integrity": "sha512-oqbyAECe9ejyGac4vQK9lkkXNOdP3fnK+QXMeoZOZn5rAnStcWgAxyK+tdgoC8FDBFC5JOyWGjM9UfntUMsRaA==", + "requires": { + "axios": "^1.4.0" + } + }, "@project-sunbird/sunbird-quml-player-web-component": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-quml-player-web-component/-/sunbird-quml-player-web-component-2.1.0.tgz", @@ -5245,6 +5253,28 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, + "axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "axobject-query": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", @@ -13588,6 +13618,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", diff --git a/package.json b/package.json index d2427568..45ac7d98 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@project-sunbird/common-form-elements-full": "^6.0.3", "@project-sunbird/sb-styles": "0.0.9", "@project-sunbird/sunbird-quml-player-web-component": "2.1.0", + "@project-sunbird/sunbird-file-upload-library": "1.0.2", "@project-sunbird/sunbird-resource-library": "6.1.0", "@project-sunbird/telemetry-sdk": "0.0.29", "@types/jquery": "^3.5.5", diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts index de4e2f83..a917410a 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts @@ -10,6 +10,7 @@ import { EditorService } from '../../services/editor/editor.service'; import { of, throwError } from 'rxjs'; import * as _ from 'lodash-es'; + const mockEditorService = { editorConfig: { config: { @@ -28,9 +29,6 @@ const mockEditorService = { }, channel: 'sunbird' } - }, - appendCloudStorageHeaders: (config) => { - return {...config, headers: { 'x-ms-blob-type': 'BlockBlob' }}; } }; describe('AssetBrowserComponent', () => { @@ -121,48 +119,50 @@ describe('AssetBrowserComponent', () => { spyOn(component.allImages, 'push'); expect(component.assetsCount).toEqual(1); }); + it('#resetFormData() should reset the form data', () => { component.resetFormData(); expect(component.imageUploadLoader).toEqual(false); expect(component.imageFormValid).toEqual(false); expect(component.formConfig).toBeTruthy(); }) + it('#uploadAndUseImage should upload image on API success', async () => { - const createMediaAssetResponse = mockData.serverResponse; + const createMediaAssetResponse = {...mockData.serverResponse}; createMediaAssetResponse.result = { node_id: 'do_123' } - const preSignedResponse = mockData.serverResponse; + const preSignedResponse = {...mockData.serverResponse}; preSignedResponse.result = { node_id: 'do_234', - pre_signed_url: '/test' + pre_signed_url: 'https://test.blob.core.windows.net/sunbird-content-test/content/assets/do_2138637821867458561248/ggd.png?sv=2017-04-17&se=2023-08-19T01%3A57%3A04Z&sr=b&sp=w&sig=c1jWuT%2BbM0Ex%2BMAs2xWr%2Bw0l7z3TQWqOF/ouMvWMnqo%3D' } let questionService: QuestionService = TestBed.inject(QuestionService); let modal = true; spyOn(questionService, 'createMediaAsset').and.returnValue(of(createMediaAssetResponse)); spyOn(questionService, 'generatePreSignedUrl').and.returnValue(of(preSignedResponse)); + component.imageFile = new File(['mock content'], 'test.png', { type: 'image/png' }); const editorService = TestBed.inject(EditorService); - spyOn(editorService, 'appendCloudStorageHeaders').and.callThrough(); spyOn(component, 'addImageInEditor').and.callThrough(); spyOn(component, 'dismissPops').and.callThrough(); component.uploadAndUseImage(modal); expect(questionService.createMediaAsset).toHaveBeenCalled(); - expect(editorService.appendCloudStorageHeaders).toHaveBeenCalled(); expect(component.loading).toEqual(true); expect(component.isClosable).toEqual(false); expect(component.imageFormValid).toEqual(false); }); - xit('#uploadAndUseImage should upload image and call upload to blob', async () => { - const createMediaAssetResponse = mockData.serverResponse; + + it('#uploadAndUseImage should upload image and call upload to blob', async () => { + const createMediaAssetResponse = {...mockData.serverResponse}; createMediaAssetResponse.result = { node_id: 'do_123' } - const preSignedResponse = mockData.serverResponse; + const preSignedResponse = {...mockData.serverResponse}; preSignedResponse.result = { node_id: 'do_234', - pre_signed_url: '/test?' + pre_signed_url: 'https://test.blob.core.windows.net/sunbird-content-test/content/assets/do_2138637821867458561248/ggd.png?sv=2017-04-17&se=2023-08-19T01%3A57%3A04Z&sr=b&sp=w&sig=c1jWuT%2BbM0Ex%2BMAs2xWr%2Bw0l7z3TQWqOF/ouMvWMnqo%3D' } - const uploadMediaResponse = mockData.serverResponse; + const uploadMediaResponse = {...mockData.serverResponse}; uploadMediaResponse.result = { node_id: 'do_234', content_url: '/test' @@ -170,6 +170,7 @@ describe('AssetBrowserComponent', () => { component.showImageUploadModal = false; let questionService: QuestionService = TestBed.inject(QuestionService); let modal = true; + component.imageFile = new File(['mock content'], 'test.png', { type: 'image/png' }); spyOn(questionService, 'createMediaAsset').and.returnValue(of(createMediaAssetResponse)); spyOn(questionService, 'generatePreSignedUrl').and.returnValue(of(preSignedResponse)); spyOn(component, 'uploadToBlob').and.returnValue(of(true)); @@ -182,6 +183,7 @@ describe('AssetBrowserComponent', () => { expect(questionService.generatePreSignedUrl).toHaveBeenCalled(); expect(component.uploadToBlob).toHaveBeenCalled(); }); + it('#generateAssetCreateRequest() should return asset create request', () => { let fileName = 'test'; let fileType = 'image/png'; @@ -200,10 +202,9 @@ describe('AssetBrowserComponent', () => { it('#uploadToBlob() should upload blob on API success', () => { let signedURL = '/test'; let file = new File([], 'filename'); - let config = {}; let questionService: QuestionService= TestBed.inject(QuestionService); spyOn(questionService.http, 'put').and.returnValue(of({"responseCode": "OK"})); - component.uploadToBlob(signedURL, file, config).subscribe(data => { + component.uploadToBlob(signedURL, file).subscribe(data => { expect(data.responseCode).toEqual('OK'); }) }) @@ -219,16 +220,19 @@ describe('AssetBrowserComponent', () => { component.dismissImageUploadModal(); expect(component.showImageUploadModal).toBeFalsy(); }); + it('#lazyloadMyImages() should get my images ', () => { spyOn(component, 'getMyImages'); component.lazyloadMyImages(); expect(component.getMyImages).toHaveBeenCalledWith(0, undefined, true); }); + it('#lazyloadMyImages() should get all images', () => { spyOn(component, 'getAllImages'); component.lazyloadAllImages(); expect(component.getAllImages).toHaveBeenCalledWith(0, undefined, true); }); + it('#uploadImage() should create asset on API success', () => { const file = new File([''], 'filename', { type: 'image' }); const event = { @@ -258,6 +262,7 @@ describe('AssetBrowserComponent', () => { expect(component.generateAssetCreateRequest).toHaveBeenCalled(); expect(component.populateFormData).toHaveBeenCalled(); }) + it('#dismissImagePicker() should emit modalDismissEmitter ', () => { component.showImagePicker = true; spyOn(component, 'getMyImages'); @@ -266,6 +271,7 @@ describe('AssetBrowserComponent', () => { expect(component.showImagePicker).toBeFalsy(); expect(component.modalDismissEmitter.emit).toHaveBeenCalledWith({}); }); + it('#ngOnDestroy() should call modal deny ', () => { component['modal'] = { deny: jasmine.createSpy('deny') @@ -273,6 +279,7 @@ describe('AssetBrowserComponent', () => { component.ngOnDestroy(); expect(component['modal'].deny).toHaveBeenCalled(); }); + it('#searchImages() should call getMyImages for my images', () => { spyOn(component, 'getMyImages'); component.searchImages('clearInput', 'myImages'); @@ -280,6 +287,7 @@ describe('AssetBrowserComponent', () => { expect(component.searchMyInput).toEqual(''); expect(component.getMyImages).toHaveBeenCalledWith(0, '', true); }); + it('#searchImages() should call allImages for all images ', () => { spyOn(component, 'getAllImages'); component.searchImages('clearInput', 'allImages'); @@ -287,10 +295,12 @@ describe('AssetBrowserComponent', () => { expect(component.searchAllInput).toEqual(''); expect(component.getAllImages).toHaveBeenCalledWith(0, '', true); }); + it('#ngOnInit() should call ngOnInit and define formConfig', () => { component.ngOnInit(); expect(component.formConfig).toBeDefined(); }); + it('#onStatusChanges() should call onStatusChanges and imageUploadLoader is false', () => { component.imageUploadLoader = false; const data = { @@ -303,6 +313,7 @@ describe('AssetBrowserComponent', () => { component.onStatusChanges(data); expect(component.imageFormValid).toBeFalsy(); }); + it('#onStatusChanges() should call onStatusChanges and imageUploadLoader is true and is form valid false', () => { component.imageUploadLoader = true; const data = { @@ -315,6 +326,7 @@ describe('AssetBrowserComponent', () => { component.onStatusChanges(data); expect(component.imageFormValid).toBeFalsy(); }); + it('#onStatusChanges() should call onStatusChanges and imageUploadLoader is true and is form valid true', () => { component.imageUploadLoader = true; const data = { @@ -327,6 +339,7 @@ describe('AssetBrowserComponent', () => { component.onStatusChanges(data); expect(component.imageFormValid).toBeTruthy(); }); + it('#valueChanges() should define assestRequestBody ', () => { component.imageUploadLoader = true; component.assestData = mockData.formData; @@ -338,6 +351,7 @@ describe('AssetBrowserComponent', () => { component.valueChanges(data); expect(component.assestData).toBeDefined(); }); + it('#openImageUploadModal() should reset upload image form ', () => { component.openImageUploadModal(); expect(component.imageUploadLoader).toBeFalsy(); @@ -345,6 +359,7 @@ describe('AssetBrowserComponent', () => { expect(component.showImageUploadModal).toBeTruthy(); expect(component.formData).toBeNull(); }); + it('#dismissPops() should close both pops ', () => { spyOn(component, 'dismissImagePicker'); const modal = { @@ -354,6 +369,7 @@ describe('AssetBrowserComponent', () => { expect(component.dismissImagePicker).toHaveBeenCalled(); expect(modal.deny).toHaveBeenCalled(); }); + it('#dismissImagePicker() should emit modalDismissEmitter event ', () => { spyOn(component, 'dismissImagePicker'); component.dismissImagePicker(); diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts index af5c6af9..9c4c02ef 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts @@ -6,6 +6,7 @@ import { EditorService } from '../../services/editor/editor.service'; import { QuestionService } from '../../services/question/question.service'; import {config} from './asset-browser.data'; import { ConfigService } from '../../services/config/config.service'; +declare const SunbirdFileUploadLib: any; @Component({ selector: 'lib-asset-browser', templateUrl: './asset-browser.component.html', @@ -217,8 +218,7 @@ export class AssetBrowserComponent implements OnInit, OnDestroy { processData: false, contentType: 'Asset' }; - blobConfig = this.editorService.appendCloudStorageHeaders(blobConfig); - this.uploadToBlob(signedURL, this.imageFile, blobConfig).subscribe(() => { + this.uploadToBlob(signedURL, this.imageFile).subscribe(() => { const fileURL = signedURL.split('?')[0]; const data = new FormData(); data.append('fileUrl', fileURL); @@ -260,13 +260,21 @@ export class AssetBrowserComponent implements OnInit, OnDestroy { }; } - uploadToBlob(signedURL, file, config): Observable { - return this.questionService.http.put(signedURL, file, config).pipe(catchError(err => { - const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; - this.isClosable = true; - this.loading = false; - return throwError(this.editorService.apiErrorHandling(err, errInfo)); - }), map(data => data)); + uploadToBlob(signedURL, file): Observable { + const csp = _.get(this.editorService.editorConfig, 'context.cloudStorage.provider', 'azure'); + return new Observable((observer) => { + const uploaderLib = new SunbirdFileUploadLib.FileUploader(); + uploaderLib.upload({ url: signedURL, file, csp }) + .on("error", (error) => { + const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; + this.isClosable = true; + this.loading = false; + observer.error(this.editorService.apiErrorHandling(error, errInfo)); + }).on("completed", (completed) => { + observer.next(completed); + observer.complete(); + }) + }); } dismissImageUploadModal() { diff --git a/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts b/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts index b3b1a4ac..85d77ab3 100644 --- a/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts +++ b/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts @@ -8,6 +8,7 @@ import { EditorService } from '../../services/editor/editor.service'; import { ToasterService } from '../../services/toaster/toaster.service'; import { ConfigService } from '../../services/config/config.service'; import { config } from '../asset-browser/asset-browser.data'; +declare const SunbirdFileUploadLib: any; @Component({ selector: 'lib-ckeditor-tool', templateUrl: './ckeditor-tool.component.html', @@ -619,8 +620,7 @@ export class CkeditorToolComponent implements OnInit, AfterViewInit, OnChanges { processData: false, contentType: 'Asset' }; - blobConfig = this.editorService.appendCloudStorageHeaders(blobConfig); - this.uploadToBlob(signedURL, this.imageFile, blobConfig).subscribe(() => { + this.uploadToBlob(signedURL, this.imageFile).subscribe(() => { const fileURL = signedURL.split('?')[0]; const data = new FormData(); data.append('fileUrl', fileURL); @@ -769,8 +769,7 @@ export class CkeditorToolComponent implements OnInit, AfterViewInit, OnChanges { processData: false, contentType: 'Asset' }; - blobConfig = this.editorService.appendCloudStorageHeaders(blobConfig); - this.uploadToBlob(signedURL, this.videoFile, blobConfig).subscribe(() => { + this.uploadToBlob(signedURL, this.videoFile).subscribe(() => { const fileURL = signedURL.split('?')[0]; this.updateContentWithURL(fileURL, this.videoFile.type, contentId, videoModal); }); @@ -790,14 +789,23 @@ export class CkeditorToolComponent implements OnInit, AfterViewInit, OnChanges { }; } - uploadToBlob(signedURL, file, config): Observable { - return this.questionService.http.put(signedURL, file, config).pipe(catchError(err => { - const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; - this.isClosable = true; - this.loading = false; - this.imageFormValid = true; - return throwError(this.editorService.apiErrorHandling(err, errInfo)); - }), map(data => data)); + uploadToBlob(signedURL, file): Observable { + const csp = _.get(this.editorService.editorConfig, 'context.cloudStorage.provider', 'azure'); + return new Observable((observer) => { + const uploaderLib = new SunbirdFileUploadLib.FileUploader(); + uploaderLib.upload({ url: signedURL, file, csp }) + .on("error", (error) => { + const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; + this.isClosable = true; + this.loading = false; + this.imageFormValid = true; + observer.error(this.editorService.apiErrorHandling(error, errInfo)); + }).on("completed", (completed) => { + observer.next(completed); + observer.complete(); + }) + }); + } updateContentWithURL(fileURL, mimeType, contentId, videoModal?) { diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts index cb904dbc..db768e5a 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts @@ -3016,6 +3016,7 @@ export const mockTreeService = { export const mockEditorCursor = { setQuestionMap: () => {}, clearQuestionMap: () => {}, + removeQuestionMap: () => {} }; export const childMetaData = { diff --git a/projects/questionset-editor-library/src/lib/components/quml-player/quml-player.component.ts b/projects/questionset-editor-library/src/lib/components/quml-player/quml-player.component.ts index 6329a4df..e038a1be 100644 --- a/projects/questionset-editor-library/src/lib/components/quml-player/quml-player.component.ts +++ b/projects/questionset-editor-library/src/lib/components/quml-player/quml-player.component.ts @@ -52,7 +52,6 @@ export class QumlPlayerComponent implements OnInit, AfterViewInit { this.qumlPlayerConfig.config.showLegend = false; } } - console.log('qumlPlayerConfig:: ', this.qumlPlayerConfig); } ngAfterViewInit() { diff --git a/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.ts b/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.ts index 1d59775c..a89c83e9 100755 --- a/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.ts +++ b/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.ts @@ -519,17 +519,4 @@ describe('EditorService', () => { expect(data.responseCode).toEqual('OK'); }); }); - - it('#appendCloudStorageHeaders should set cloud storage headers if exist', () => { - const config = editorService.appendCloudStorageHeaders({}); - expect(config).toEqual({headers: { 'x-ms-blob-type': 'BlockBlob' }}); - }); - - it('#appendCloudStorageHeaders should not set cloud storage headers if not exist', () => { - const editorConfigMock: any = {config: editorConfig.config, context: _.omit(editorConfig.context, 'cloudStorage') }; - editorService.initialize(editorConfigMock); - const config = editorService.appendCloudStorageHeaders({}); - expect(config).toEqual({}); - }); - }); diff --git a/projects/questionset-editor-library/src/lib/services/editor/editor.service.ts b/projects/questionset-editor-library/src/lib/services/editor/editor.service.ts index a1b67694..dc716553 100755 --- a/projects/questionset-editor-library/src/lib/services/editor/editor.service.ts +++ b/projects/questionset-editor-library/src/lib/services/editor/editor.service.ts @@ -715,17 +715,9 @@ getDependentNodes(identifier) { get isReviewerQualityCheckEnabled(){ return this._isReviewerQualityCheckEnabled; - } + } - private setIsReviewerQualityCheckEnabled(value: boolean){ - return this._isReviewerQualityCheckEnabled = value; - } - - appendCloudStorageHeaders(config) { - const headers = _.get(this.editorConfig, 'context.cloudStorage.presigned_headers', {}); - if (!_.isEmpty(headers)) { - config.headers = {...config.headers, ...headers}; - } - return config; + private setIsReviewerQualityCheckEnabled(value: boolean){ + return this._isReviewerQualityCheckEnabled = value; } } diff --git a/src/app/data.ts b/src/app/data.ts index 91ef869d..a6dbfed6 100644 --- a/src/app/data.ts +++ b/src/app/data.ts @@ -114,6 +114,7 @@ export const questionSetEditorConfig = { correctionComments: false, sourcingResourceStatus: true, cloudStorage: { + provider: 'azure', presigned_headers: { 'x-ms-blob-type': 'BlockBlob' } From 13cd6322e2f59614844f69224da6b65e9115b438 Mon Sep 17 00:00:00 2001 From: Vaibhav Bhuva Date: Fri, 18 Aug 2023 16:53:33 +0530 Subject: [PATCH 2/5] Issue #IQ-549 fix: unit test case issue fixes --- .../components/asset-browser/asset-browser.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts index a917410a..330f99bd 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts @@ -199,7 +199,7 @@ describe('AssetBrowserComponent', () => { }) }) - it('#uploadToBlob() should upload blob on API success', () => { + xit('#uploadToBlob() should upload blob on API success', () => { let signedURL = '/test'; let file = new File([], 'filename'); let questionService: QuestionService= TestBed.inject(QuestionService); From 98e9ffb0bbc14356c46aebe24bc07ea722e24519 Mon Sep 17 00:00:00 2001 From: Vaibhav Bhuva Date: Fri, 18 Aug 2023 17:11:41 +0530 Subject: [PATCH 3/5] Issue #IQ-549 fix: sonar code smell issue fixes --- .../components/asset-browser/asset-browser.component.ts | 6 +----- .../components/ckeditor-tool/ckeditor-tool.component.ts | 8 -------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts index 9c4c02ef..28991bc2 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; import * as _ from 'lodash-es'; -import { catchError, map } from 'rxjs/operators'; +import { catchError } from 'rxjs/operators'; import { throwError, Observable } from 'rxjs'; import { EditorService } from '../../services/editor/editor.service'; import { QuestionService } from '../../services/question/question.service'; @@ -214,10 +214,6 @@ export class AssetBrowserComponent implements OnInit, OnDestroy { return throwError(this.editorService.apiErrorHandling(err, errInfo)); })).subscribe((response) => { const signedURL = response.result.pre_signed_url; - let blobConfig = { - processData: false, - contentType: 'Asset' - }; this.uploadToBlob(signedURL, this.imageFile).subscribe(() => { const fileURL = signedURL.split('?')[0]; const data = new FormData(); diff --git a/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts b/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts index 85d77ab3..ecdf0233 100644 --- a/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts +++ b/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts @@ -616,10 +616,6 @@ export class CkeditorToolComponent implements OnInit, AfterViewInit, OnChanges { return throwError(this.editorService.apiErrorHandling(err, errInfo)); })).subscribe((response) => { const signedURL = response.result.pre_signed_url; - let blobConfig = { - processData: false, - contentType: 'Asset' - }; this.uploadToBlob(signedURL, this.imageFile).subscribe(() => { const fileURL = signedURL.split('?')[0]; const data = new FormData(); @@ -765,10 +761,6 @@ export class CkeditorToolComponent implements OnInit, AfterViewInit, OnChanges { return throwError(this.editorService.apiErrorHandling(err, errInfo)); })).subscribe((response) => { const signedURL = response.result.pre_signed_url; - let blobConfig = { - processData: false, - contentType: 'Asset' - }; this.uploadToBlob(signedURL, this.videoFile).subscribe(() => { const fileURL = signedURL.split('?')[0]; this.updateContentWithURL(fileURL, this.videoFile.type, contentId, videoModal); From dc4ec8c650372ab31824f64876ef9223e6a83cbe Mon Sep 17 00:00:00 2001 From: Vaibhav Bhuva Date: Mon, 21 Aug 2023 16:39:13 +0530 Subject: [PATCH 4/5] Issue #IQ-549 fix: sonar code smell issue fixes --- .../asset-browser.component.spec.ts | 4 ++-- .../asset-browser/asset-browser.component.ts | 22 ++++++------------ .../ckeditor-tool/ckeditor-tool.component.ts | 23 ++++++------------- .../lib/services/question/question.service.ts | 14 +++++++++++ 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts index 330f99bd..94aa6969 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts @@ -199,11 +199,11 @@ describe('AssetBrowserComponent', () => { }) }) - xit('#uploadToBlob() should upload blob on API success', () => { + it('#uploadToBlob() should upload blob on API success', () => { let signedURL = '/test'; let file = new File([], 'filename'); let questionService: QuestionService= TestBed.inject(QuestionService); - spyOn(questionService.http, 'put').and.returnValue(of({"responseCode": "OK"})); + spyOn(questionService, 'uploadtoBlob').and.returnValue(of({"responseCode": "OK"})); component.uploadToBlob(signedURL, file).subscribe(data => { expect(data.responseCode).toEqual('OK'); }) diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts index 28991bc2..6cdf65c9 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts @@ -1,12 +1,11 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; import * as _ from 'lodash-es'; -import { catchError } from 'rxjs/operators'; +import { catchError, map } from 'rxjs/operators'; import { throwError, Observable } from 'rxjs'; import { EditorService } from '../../services/editor/editor.service'; import { QuestionService } from '../../services/question/question.service'; import {config} from './asset-browser.data'; import { ConfigService } from '../../services/config/config.service'; -declare const SunbirdFileUploadLib: any; @Component({ selector: 'lib-asset-browser', templateUrl: './asset-browser.component.html', @@ -258,19 +257,12 @@ export class AssetBrowserComponent implements OnInit, OnDestroy { uploadToBlob(signedURL, file): Observable { const csp = _.get(this.editorService.editorConfig, 'context.cloudStorage.provider', 'azure'); - return new Observable((observer) => { - const uploaderLib = new SunbirdFileUploadLib.FileUploader(); - uploaderLib.upload({ url: signedURL, file, csp }) - .on("error", (error) => { - const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; - this.isClosable = true; - this.loading = false; - observer.error(this.editorService.apiErrorHandling(error, errInfo)); - }).on("completed", (completed) => { - observer.next(completed); - observer.complete(); - }) - }); + return this.questionService.uploadtoBlob(signedURL, file, csp).pipe(catchError(err => { + const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; + this.isClosable = true; + this.loading = false; + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + }), map(data => data)); } dismissImageUploadModal() { diff --git a/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts b/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts index ecdf0233..a864350a 100644 --- a/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts +++ b/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts @@ -8,7 +8,6 @@ import { EditorService } from '../../services/editor/editor.service'; import { ToasterService } from '../../services/toaster/toaster.service'; import { ConfigService } from '../../services/config/config.service'; import { config } from '../asset-browser/asset-browser.data'; -declare const SunbirdFileUploadLib: any; @Component({ selector: 'lib-ckeditor-tool', templateUrl: './ckeditor-tool.component.html', @@ -783,21 +782,13 @@ export class CkeditorToolComponent implements OnInit, AfterViewInit, OnChanges { uploadToBlob(signedURL, file): Observable { const csp = _.get(this.editorService.editorConfig, 'context.cloudStorage.provider', 'azure'); - return new Observable((observer) => { - const uploaderLib = new SunbirdFileUploadLib.FileUploader(); - uploaderLib.upload({ url: signedURL, file, csp }) - .on("error", (error) => { - const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; - this.isClosable = true; - this.loading = false; - this.imageFormValid = true; - observer.error(this.editorService.apiErrorHandling(error, errInfo)); - }).on("completed", (completed) => { - observer.next(completed); - observer.complete(); - }) - }); - + return this.questionService.uploadtoBlob(signedURL, file, csp).pipe(catchError(err => { + const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; + this.isClosable = true; + this.loading = false; + this.imageFormValid = true; + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + }), map(data => data)); } updateContentWithURL(fileURL, mimeType, contentId, videoModal?) { diff --git a/projects/questionset-editor-library/src/lib/services/question/question.service.ts b/projects/questionset-editor-library/src/lib/services/question/question.service.ts index 96afce79..99838e6c 100644 --- a/projects/questionset-editor-library/src/lib/services/question/question.service.ts +++ b/projects/questionset-editor-library/src/lib/services/question/question.service.ts @@ -8,6 +8,7 @@ import { v4 as uuidv4 } from 'uuid'; import { HttpClient } from '@angular/common/http'; import { ConfigService } from '../config/config.service'; import { EditorService } from '../editor/editor.service'; +declare const SunbirdFileUploadLib: any; @Injectable({ providedIn: 'root' }) @@ -137,6 +138,19 @@ export class QuestionService { return this.publicDataService.post(reqParam); } + uploadtoBlob(signedURL, file, csp) { + return new Observable((observer) => { + const uploaderLib = new SunbirdFileUploadLib.FileUploader(); + uploaderLib.upload({ url: signedURL, file, csp }) + .on("error", (error) => { + observer.error(error); + }).on("completed", (completed) => { + observer.next(completed); + observer.complete(); + }) + }); + } + getVideo(videoId) { const reqParam = { url: `${this.configService.urlConFig.URLS.ASSET.READ}${videoId}` From d17db93b024f775fede948d73b65a47e79e39999 Mon Sep 17 00:00:00 2001 From: Vaibhav Bhuva Date: Mon, 21 Aug 2023 16:44:09 +0530 Subject: [PATCH 5/5] Issue #IQ-549 fix: test cases issue fixes --- .../components/asset-browser/asset-browser.component.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts index 94aa6969..d3c99d6e 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts @@ -29,7 +29,8 @@ const mockEditorService = { }, channel: 'sunbird' } - } + }, + apiErrorHandling: () => {}, }; describe('AssetBrowserComponent', () => { let component: AssetBrowserComponent;