diff --git a/src/app/pages/seller-offerings/offerings/seller-product-spec/update-product-spec/update-product-spec.component.spec.ts b/src/app/pages/seller-offerings/offerings/seller-product-spec/update-product-spec/update-product-spec.component.spec.ts
index c593ad65..67aa8a0e 100644
--- a/src/app/pages/seller-offerings/offerings/seller-product-spec/update-product-spec/update-product-spec.component.spec.ts
+++ b/src/app/pages/seller-offerings/offerings/seller-product-spec/update-product-spec/update-product-spec.component.spec.ts
@@ -13,7 +13,6 @@ import { AttachmentServiceService } from 'src/app/services/attachment-service.se
import { ServiceSpecServiceService } from 'src/app/services/service-spec-service.service';
import { ResourceSpecServiceService } from 'src/app/services/resource-spec-service.service';
import { PaginationService } from 'src/app/services/pagination.service';
-import { QrVerifierService } from 'src/app/services/qr-verifier.service';
class SyncFileReaderMock {
onload: ((event: any) => void) | null = null;
@@ -25,6 +24,13 @@ class SyncFileReaderMock {
}
}
+const asJwt = (payload: any): string => {
+ const encode = (value: any) =>
+ btoa(JSON.stringify(value)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
+
+ return `${encode({ alg: 'none', typ: 'JWT' })}.${encode(payload)}.`;
+};
+
describe('UpdateProductSpecComponent', () => {
let component: UpdateProductSpecComponent;
let fixture: ComponentFixture
;
@@ -37,7 +43,6 @@ describe('UpdateProductSpecComponent', () => {
let attachmentServiceSpy: jasmine.SpyObj;
let servSpecServiceSpy: jasmine.SpyObj;
let resSpecServiceSpy: jasmine.SpyObj;
- let qrVerifierSpy: jasmine.SpyObj;
let paginationServiceSpy: jasmine.SpyObj;
let originalFileReader: any;
@@ -67,15 +72,12 @@ describe('UpdateProductSpecComponent', () => {
attachmentServiceSpy = jasmine.createSpyObj('AttachmentServiceService', ['uploadFile']);
servSpecServiceSpy = jasmine.createSpyObj('ServiceSpecServiceService', ['getServiceSpecByUser']);
resSpecServiceSpy = jasmine.createSpyObj('ResourceSpecServiceService', ['getResourceSpecByUser']);
- qrVerifierSpy = jasmine.createSpyObj('QrVerifierService', ['launchPopup', 'pollCertCredential']);
paginationServiceSpy = jasmine.createSpyObj('PaginationService', ['getItemsPaginated']);
localStorageSpy.getObject.and.returnValue({});
attachmentServiceSpy.uploadFile.and.returnValue(of({ content: 'https://uploaded.file' }));
prodSpecServiceSpy.getResSpecById.and.resolveTo({ id: 'rel-prod', name: 'Rel Prod' } as any);
prodSpecServiceSpy.updateProdSpec.and.returnValue(of({ id: 'created' }));
- qrVerifierSpy.launchPopup.and.returnValue({} as Window);
- qrVerifierSpy.pollCertCredential.and.resolveTo({ subject: { compliance: [] }, vc: 'vc-token' });
paginationServiceSpy.getItemsPaginated.and.resolveTo(defaultPaginationData);
await TestBed.configureTestingModule({
@@ -90,7 +92,6 @@ describe('UpdateProductSpecComponent', () => {
{ provide: AttachmentServiceService, useValue: attachmentServiceSpy },
{ provide: ServiceSpecServiceService, useValue: servSpecServiceSpy },
{ provide: ResourceSpecServiceService, useValue: resSpecServiceSpy },
- { provide: QrVerifierService, useValue: qrVerifierSpy },
{ provide: PaginationService, useValue: paginationServiceSpy }
]
}).compileComponents();
@@ -508,6 +509,33 @@ describe('UpdateProductSpecComponent', () => {
expect(component.prodChars[0].id).toBe('urn:ngsi-ld:characteristic:platinum-id');
});
+ it('populateProductInfo should decode Compliance:VC and expose compliance badge level', () => {
+ const vcToken = asJwt({
+ vc: {
+ credentialSubject: {
+ 'gx:labelLevel': 'P'
+ }
+ }
+ });
+
+ component.prod = {
+ ...component.prod,
+ productSpecCharacteristic: [
+ {
+ id: 'urn:ngsi-ld:characteristic:vc-id',
+ name: 'Compliance:VC',
+ productSpecCharacteristicValue: [{ isDefault: true, value: vcToken }]
+ }
+ ]
+ } as any;
+
+ component.populateProductInfo();
+
+ expect(component.complianceVCId).toBe('urn:ngsi-ld:characteristic:vc-id');
+ expect(component.complianceVC).toBe(vcToken);
+ expect(component.complianceLevel).toBe('P');
+ });
+
it('hasUnsavedComplianceProfileChanges should return false when compliance profile matches persisted data', () => {
component.prod = {
...component.prod,
diff --git a/src/app/pages/seller-offerings/offerings/seller-product-spec/update-product-spec/update-product-spec.component.ts b/src/app/pages/seller-offerings/offerings/seller-product-spec/update-product-spec/update-product-spec.component.ts
index 5eb1875e..87e7ecdc 100644
--- a/src/app/pages/seller-offerings/offerings/seller-product-spec/update-product-spec/update-product-spec.component.ts
+++ b/src/app/pages/seller-offerings/offerings/seller-product-spec/update-product-spec/update-product-spec.component.ts
@@ -17,7 +17,6 @@ import { NgxFileDropEntry, FileSystemFileEntry, FileSystemDirectoryEntry } from
import { certifications } from 'src/app/models/certification-standards.const'
import * as moment from 'moment';
import { v4 as uuidv4 } from 'uuid';
-import { QrVerifierService } from 'src/app/services/qr-verifier.service';
import { jwtDecode } from "jwt-decode";
import { noWhitespaceValidator } from 'src/app/validators/validators';
import { Subject } from 'rxjs';
@@ -114,6 +113,7 @@ export class UpdateProductSpecComponent implements OnInit, OnDestroy {
selectedISOS:any[]=[];
additionalISOS:any[]=[];
verifiedISO:string[] = [];
+ complianceLevel:string='NL';
selectedISO:any;
complianceVC:any = null;
complianceVCId:string = '';
@@ -213,7 +213,6 @@ export class UpdateProductSpecComponent implements OnInit, OnDestroy {
private attachmentService: AttachmentServiceService,
private servSpecService: ServiceSpecServiceService,
private resSpecService: ResourceSpecServiceService,
- private qrVerifier: QrVerifierService,
private paginationService: PaginationService
) {
for(let i=0; i {
- return comp.standard
- })
- }
- }
+ this.applyComplianceDataFromVcToken(this.complianceVC);
} catch (e) {
console.log(e)
}
@@ -638,27 +621,36 @@ export class UpdateProductSpecComponent implements OnInit, OnDestroy {
console.log(this.selectedISOS)
}
- verifyCredential() {
- console.log('verifing credential')
- const state = `cert:${uuidv4()}`
+ private applyComplianceDataFromVcToken(vcToken: any) {
+ if (!vcToken || typeof vcToken !== 'string') {
+ this.complianceLevel = 'NL';
+ return;
+ }
+
+ const allowedLevels = ['NL', 'BL', 'P', 'PP'];
- const qrWin = this.qrVerifier.launchPopup(`${environment.SIOP_INFO.verifierHost}${environment.SIOP_INFO.verifierQRCodePath}?state=${state}&client_callback=${environment.SIOP_INFO.callbackURL}&client_id=${environment.SIOP_INFO.clientID}`, 'Scan QR code', 500, 500)
- this.qrVerifier.pollCertCredential(qrWin, state).then((data) => {
- // Process the VC to verify the cerficates
- // Validate the product ID and company
- const subject = data.subject
+ try {
+ const decoded: any = jwtDecode(vcToken);
+ let credential: any = null;
- if (subject.compliance) {
- subject.compliance.forEach((comp: any) => {
- this.verifiedISO.push(comp.standard)
- })
+ if ('verifiableCredential' in decoded) {
+ credential = decoded.verifiableCredential;
+ } else if ('vc' in decoded) {
+ credential = decoded.vc;
+ }
- this.complianceVC = data.vc;
+ const subject = credential?.credentialSubject;
+ if (!subject) {
+ this.complianceLevel = 'NL';
+ return;
}
- //this.verifiedISO[sel.name] = data.vc
- console.log(`We got the vc: ${data['vc']}`)
- })
+ const level = subject['gx:labelLevel'];
+ this.complianceLevel = (typeof level === 'string' && allowedLevels.includes(level)) ? level : 'NL';
+ } catch (error) {
+ this.complianceLevel = 'NL';
+ console.log(error);
+ }
}
openRequestValidationModal() {