diff --git a/src/app/features/upload/upload.component.html b/src/app/features/upload/upload.component.html index bb37870a..8d3d951a 100644 --- a/src/app/features/upload/upload.component.html +++ b/src/app/features/upload/upload.component.html @@ -26,11 +26,11 @@

Format {{ sortingOptions['FORMAT'] ? '▴' : [style]="collapsed ? 'display: none' : ''" class="files-container">
-
+
-
- - +
+ +
{{ GetSBOMInfo(item).format }} diff --git a/src/app/features/upload/upload.component.ts b/src/app/features/upload/upload.component.ts index c4779e39..5bbe4a80 100644 --- a/src/app/features/upload/upload.component.ts +++ b/src/app/features/upload/upload.component.ts @@ -306,7 +306,7 @@ export class UploadComponent implements OnInit { const files = event.dataTransfer?.files; if (files && files.length > 0) { - const filePaths = Array.from(files).map((file) => (file as any).path); + const filePaths = Array.from(files).map((file: any) => (file.path as string)); this.sbomService.AddFiles(filePaths); } } @@ -324,6 +324,9 @@ export class UploadComponent implements OnInit { if (searchInput) { searchInput.value = ''; } + // Re-enable all formats and schemas so nothing stays hidden + Object.keys(this.GetSBOMFormat()).forEach((key: string) => this.sbomService.SetSBOMFormat(key, true)); + Object.keys(this.GetSBOMSchemas()).forEach((key: string) => this.sbomService.SetSBOMSchema(key, true)); } UpdateSearch(event: any) { diff --git a/src/app/shared/components/toast/toast.component.html b/src/app/shared/components/toast/toast.component.html index a8f65d96..d8e1e0a7 100644 --- a/src/app/shared/components/toast/toast.component.html +++ b/src/app/shared/components/toast/toast.component.html @@ -1,14 +1,14 @@ \ No newline at end of file diff --git a/src/app/shared/components/toast/toast.component.scss b/src/app/shared/components/toast/toast.component.scss index ee2122cb..06d4e6d7 100644 --- a/src/app/shared/components/toast/toast.component.scss +++ b/src/app/shared/components/toast/toast.component.scss @@ -1,15 +1,39 @@ -.warning { - background-color: #F44336; -} + + .warning { + background-color: #FFC107; + } + + .info { + background-color: #4CAF50; + } + .error { background-color: #F44336; } -.toast-header { + + .toast-header.info { + background-color: #4CAF50; + } + + .toast-header.warning { + background-color: #FFC107; + } + + .toast-header.error { background-color: #F44336; } -.toast-body { + + .toast-body.info { + background-color: #E8F5E9; + } + + .toast-body.warning { + background-color: #FFF8E1; + } + + .toast-body.error { background-color: #FDE0DE; } diff --git a/src/app/shared/components/toolbar/modals/generate-modal/generate-modal.component.html b/src/app/shared/components/toolbar/modals/generate-modal/generate-modal.component.html index db7b5bf7..628f46b4 100644 --- a/src/app/shared/components/toolbar/modals/generate-modal/generate-modal.component.html +++ b/src/app/shared/components/toolbar/modals/generate-modal/generate-modal.component.html @@ -1,58 +1,72 @@ - -
- Generate SBOM -
-
- -
Waiting for Project Selection...
-
-
- -
Zipping Project Contents...
-
-
- -
Generating SBOM...
-
-
-
-
Name:
- + +
+ Generate SBOM
-
-
Schema:
- +
+ +
Waiting for Project Selection...
-
-
Format:
- + +
+ +
Zipping Project Contents...
-
-
OSI Tools:
- - +
+ +
Generating SBOM...
-
-
-
- Generate +
+
+
Name:
+ +
+
+
Schema:
+ +
+
+
Format:
+ +
+
+
Type:
+ +
+
+
OSI Tools:
+ + +
-
- Cancel +
+
+ Generate +
+
+ Cancel +
-
- + \ No newline at end of file diff --git a/src/app/shared/components/toolbar/modals/generate-modal/generate-modal.component.ts b/src/app/shared/components/toolbar/modals/generate-modal/generate-modal.component.ts index 1386b3ab..74d9b847 100644 --- a/src/app/shared/components/toolbar/modals/generate-modal/generate-modal.component.ts +++ b/src/app/shared/components/toolbar/modals/generate-modal/generate-modal.component.ts @@ -1,14 +1,13 @@ -import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; -import {SVIPService} from 'src/app/shared/services/SVIP.service'; -import {Subject} from 'rxjs'; -import {SbomService} from 'src/app/shared/services/sbom.service'; -import {ToastService} from 'src/app/shared/services/toast.service'; +import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; +import { SVIPService } from 'src/app/shared/services/SVIP.service'; +import { Subject } from 'rxjs'; +import { SbomService } from 'src/app/shared/services/sbom.service'; +import { ToastService } from 'src/app/shared/services/toast.service'; @Component({ selector: 'app-generate-modal', templateUrl: './generate-modal.component.html', - styleUrls: ['./generate-modal.component.css'], - standalone: false + styleUrls: ['./generate-modal.component.css'] }) export class GenerateModalComponent implements OnInit { public options: { @@ -23,56 +22,59 @@ export class GenerateModalComponent implements OnInit { type: '', }; - public choices: { [key: string]: string[] } = { + public choices: {[key: string]: string[]} = { 'CDX14': ['JSON', 'XML'], 'SPDX23': ['TAGVALUE', 'JSON'], } public types: string[] = ['OSI', 'PARSERS']; - public osiTools: { [name: string]: boolean } = {}; + public osiTools: {[name: string]: boolean} = {}; @Input() opened: boolean = false; @Output() close = new EventEmitter(); + private openedSubject = new Subject(); + public status: GenerationStatus = GenerationStatus.NULL; public zippedFileData: any; - private openedSubject = new Subject(); + // Pre-zipped upload temporarily disabled; keep flag for future use + // public useExistingZip: boolean = false; + // public selectingSource: boolean = true; // when re-enabled, use to gate source step - constructor(private service: SVIPService, private sbomService: SbomService, private toast: ToastService) { - } + constructor(private service: SVIPService, private sbomService: SbomService, private toast: ToastService) {} ngOnInit(): void { this.openedSubject.subscribe((value) => { - if (!value) + if(!value) return; - this.zippedFileData = undefined; - this.status = GenerationStatus.GENERATING; + this.zippedFileData = undefined; + // Reverted to original flow; previous pre-zip source selection kept below as comments + this.status = GenerationStatus.GENERATING; - this.service.getProjectDirectory().then((result) => { - this.status = GenerationStatus.ZIPPING; + this.service.getProjectDirectory().then((result) => { + this.status = GenerationStatus.ZIPPING; - this.service.zipFileDirectory(result).then((data) => { + this.service.zipFileDirectory(result).then((data) => { - //TODO: Prompt user beforehand on OSI or Parsers so don't need to upload project if don't have to OR the backend should be reworked for parsers - this.service.uploadProject(data, 'osi').then((tools: any) => { + this.service.uploadProject(data, 'osi').then((tools: any) => { - tools.forEach((tool: any) => { - this.osiTools[tool] = true; - }) + tools.forEach((tool: any) => { + this.osiTools[tool] = true; + }) - this.zippedFileData = data; - this.status = GenerationStatus.PROJECT_INFO; + this.zippedFileData = data; + this.status = GenerationStatus.PROJECT_INFO; + }).catch((error) => { + this.Close(); + }) }).catch((error) => { this.Close(); }) }).catch((error) => { this.Close(); }) - }).catch((error) => { - this.Close(); - }) }); } @@ -99,12 +101,12 @@ export class GenerateModalComponent implements OnInit { this.options.format, this.options.type, tools).then((data: any) => { - this.sbomService.addSBOMbyID(data); - this.Close(); - }).catch(() => { - this.toast.showErrorToast("SBOM Generation", "Failed"); - this.Close(); - }) + this.sbomService.addSBOMbyID(data); + this.Close(); + }).catch(() => { + this.toast.showErrorToast("SBOM Generation", "Failed"); + this.Close(); + }) } @@ -113,6 +115,38 @@ export class GenerateModalComponent implements OnInit { this.osiTools[event.name] = event.value; } + // --- Begin: Pre-zipped flow (commented out) --- + // SelectFolderAndZip() { + // this.status = GenerationStatus.ZIPPING; + // this.service.getProjectDirectory().then((result) => { + // this.service.zipFileDirectory(result).then((data) => { + // this.service.uploadProject(data, 'osi').then((tools: any) => { + // tools.forEach((tool: any) => { + // this.osiTools[tool] = true; + // }) + // this.zippedFileData = data; + // this.status = GenerationStatus.PROJECT_INFO; + // }).catch(() => { this.Close(); }) + // }).catch(() => { this.Close(); }) + // }).catch(() => { this.Close(); }) + // } + // + // OnZipFileSelected(event: any) { + // const files: FileList = event.target.files; + // if (!files || files.length === 0) + // return; + // const zip = files[0]; + // this.status = GenerationStatus.GENERATING; + // this.service.uploadProject(zip, 'osi').then((tools: any) => { + // tools.forEach((tool: any) => { + // this.osiTools[tool] = true; + // }) + // this.zippedFileData = zip; + // this.status = GenerationStatus.PROJECT_INFO; + // }).catch(() => { this.Close(); }) + // } + // --- End: Pre-zipped flow (commented out) --- + Close() { this.status = GenerationStatus.NULL; @@ -127,4 +161,5 @@ enum GenerationStatus { ZIPPING, PROJECT_INFO, GENERATING, + // SELECTING_SOURCE, // disabled } diff --git a/src/app/shared/services/SVIP.service.ts b/src/app/shared/services/SVIP.service.ts index e4b92e97..f2c1f2ec 100644 --- a/src/app/shared/services/SVIP.service.ts +++ b/src/app/shared/services/SVIP.service.ts @@ -222,6 +222,10 @@ export class SVIPService { async uploadProject(file: any, type: string) { return new Promise(async (resolve, reject) => { let formData = new FormData(); + // Disabled pre-zipped passthrough (kept for future use): + // const fileToSend = (file instanceof File) ? file : new File([file], 'temp.zip'); + // formData.append('project', fileToSend); + // Reverted to original behavior: always wrap provided bytes into a new File formData.append('project', new File([file], 'temp.zip')); let params = new HttpParams(); diff --git a/src/app/shared/services/sbom.service.ts b/src/app/shared/services/sbom.service.ts index 77c4d781..27a6a841 100644 --- a/src/app/shared/services/sbom.service.ts +++ b/src/app/shared/services/sbom.service.ts @@ -1,7 +1,9 @@ -import {Injectable} from '@angular/core'; -import {SVIPService} from './SVIP.service'; -import {PAGES, RoutingService} from './routing.service'; -import File, {FileStatus} from '../models/file'; +import { Injectable } from '@angular/core'; +import { SVIPService } from './SVIP.service'; +import { PAGES, RoutingService } from './routing.service'; +import File, { FileStatus } from '../models/file'; +import { SBOM } from '../models/sbom'; + @Injectable({ providedIn: 'root', @@ -25,14 +27,17 @@ export class SbomService { * @param getSBOM by ID */ addSBOMbyID(id: number) { - this.SVIPService.getSBOM(id as number).subscribe((sbom) => { + this.SVIPService.getSBOM(id as number).subscribe((sbom: SBOM) => { this.SVIPService.getSBOMContents(id as number).subscribe((data: any) => { - let path = data.fileName; - let contents = data.contents; + let path = (data as any).fileName; + let contents = (data as any).contents as string; const file = new File(path).setValid(id, contents, sbom); this.files[id] = file; this.SetSBOMFormat(sbom.format, true); + if ((sbom as any)['schema']) { + this.SetSBOMSchema((sbom as any)['schema'], true); + } }); }); } @@ -41,9 +46,9 @@ export class SbomService { * Gets all SBOMS in database and sets up SBOM service */ getAllSBOMs() { - this.SVIPService.getSBOMS().subscribe((ids) => { + this.SVIPService.getSBOMS().subscribe((ids: number[]) => { if (ids) { - ids.forEach((id) => this.addSBOMbyID(id)); + ids.forEach((id: number) => this.addSBOMbyID(id)); } }); } @@ -57,13 +62,13 @@ export class SbomService { let randomID = -Math.random().toString() + "-loading"; // File is loading this.files[randomID] = new File(path); - this.SVIPService.getFileData(path).then((contents) => { + this.SVIPService.getFileData(path).then((contents: string) => { if (contents) { this.SVIPService.uploadSBOM(path, contents).subscribe( - (id) => { + (id: number) => { if (id) { // Successful upload - this.SVIPService.getSBOM(id).subscribe((sbom) => { + this.SVIPService.getSBOM(id).subscribe((sbom: SBOM) => { delete this.files[randomID]; let file = new File(path).setValid(id, contents, sbom); this.files[id] = file; @@ -88,7 +93,7 @@ export class SbomService { downloadSBOM(id: string): Blob { const file = this.files[id]?.contents; if (file !== null) { - return new Blob([file]); + return new Blob([file as any]); } throw new Error('File does not exist!'); } @@ -108,7 +113,7 @@ export class SbomService { idList.unshift(Number(targetID)); - this.SVIPService.compareSBOMs(idList).subscribe((result) => { + this.SVIPService.compareSBOMs(idList).subscribe((result: any) => { this.comparison = result; }); } @@ -120,7 +125,7 @@ export class SbomService { deleteFile(id: string) { if (id && !isNaN(Number(id))) { // TODO: Add error handling for when file cannot be deleted - this.SVIPService.deleteSBOM(Number(id)).subscribe((deleted) => { + this.SVIPService.deleteSBOM(Number(id)).subscribe((deleted: any) => { if (deleted) { const data = this.routingService.data; if (data === id) { @@ -149,7 +154,7 @@ export class SbomService { overwrite: boolean ) { this.SVIPService.convertSBOM(Number(id), schema, format, overwrite).subscribe( - (result) => { + (result: string) => { if (result) { this.addSBOMbyID(Number(result)); @@ -184,7 +189,7 @@ export class SbomService { * @param id sbom to check for */ GetSBOMSchema(id: string) { - return this.files[id].schema; + return (this.files[id] as any).schema; } //#region SBOM format @@ -209,7 +214,7 @@ export class SbomService { * @param id sbom to check for */ GetSBOMFormat(id: string) { - return this.files[id].format; + return (this.files[id] as any).format; } //#endregion diff --git a/src/app/shared/services/toast.service.ts b/src/app/shared/services/toast.service.ts index 23867bfa..3663fd4f 100644 --- a/src/app/shared/services/toast.service.ts +++ b/src/app/shared/services/toast.service.ts @@ -13,7 +13,15 @@ export class ToastService { constructor() { this.toastEvents = this._toastEvents.asObservable(); } - + + showInfoToast(title: string, message: string) { + this._toastEvents.next({ + message, + title, + type: EventTypes.Info, + }); + } + showWarningToast(title: string, message: string) { this._toastEvents.next({ message,