diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c40405f3e423aa84402c07145aee5800dc9b0c8d..a21703b44ed2bc77d650b22b7abf319dc1dbeb7d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -10,6 +10,7 @@ import { MatInputModule } from '@angular/material/input'; import { MatDialogModule } from '@angular/material/dialog'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; +import { MatSidenavModule } from '@angular/material/sidenav'; import { RepositoryComponent } from './repository/repository.component'; import { AccessapiComponent } from './accessapi/accessapi.component'; @@ -75,6 +76,7 @@ import { DatasetsDialogComponent } from './datasets/datasets-dialog/datasets-dia MatDialogModule, MatProgressSpinnerModule, MatSelectModule, + MatSidenavModule, FormsModule, ReactiveFormsModule, HttpClientModule, @@ -94,7 +96,7 @@ import { DatasetsDialogComponent } from './datasets/datasets-dialog/datasets-dia NbListModule, NbAccordionModule, NbToastrModule.forRoot(), - NbDialogModule.forRoot(), + NbDialogModule.forRoot() ], entryComponents: [DatasetsDialogComponent, FeedbackDialogComponent], providers: [ diff --git a/src/app/datasets/datasets-dialog/datasets-dialog.component.html b/src/app/datasets/datasets-dialog/datasets-dialog.component.html index 3adf474ed50b184e6402a4b006c38ec5c976101a..be5eb9cb98bbf4aa3f80846c88d7110beed1af45 100644 --- a/src/app/datasets/datasets-dialog/datasets-dialog.component.html +++ b/src/app/datasets/datasets-dialog/datasets-dialog.component.html @@ -2,7 +2,7 @@ <div mat-dialog-content> <mat-form-field appearance="fill"> <mat-select multiple [(ngModel)]="datasetIdSelected"> - <mat-option *ngFor="let id of data.datasetId" [value]="id">{{id}}</mat-option> + <mat-option *ngFor="let id of data.datasetId" [value]="id" [title]="id">{{id}}</mat-option> </mat-select> </mat-form-field> diff --git a/src/app/datasets/datasets.component.html b/src/app/datasets/datasets.component.html index 6a790148a033cb2feced2cdde114fe7e4f14537b..f8eb7e72cf7d5f0f398217a1ce7bed5c0ecee460 100644 --- a/src/app/datasets/datasets.component.html +++ b/src/app/datasets/datasets.component.html @@ -1,41 +1,64 @@ -<h6><strong>Repository : </strong>{{urlRepo}} -</h6> +<mat-sidenav-container> + + + <mat-sidenav #sidenav mode="side" position="end" opened id="request-historical"> + <h6 id="history-title">Request history</h6> + <nb-list id="history-list" *ngIf="requestHistorical != null && requestHistorical.length > 0; else noHistory"> + <nb-list-item *ngFor="let request of requestHistorical"><a [href]="request" target="_blank" + rel="noreferrer noopener">{{request}}</a></nb-list-item> + </nb-list> + <ng-template #noHistory> + <p id="no-history-found-message">No history found...</p> + </ng-template> + </mat-sidenav> + + + <mat-sidenav-content class="sidenav-content"> + <div class="grid-container"> + <h6><strong>Repository : </strong>{{urlRepo}}</h6> + <button nbButton (click)="sidenav.toggle()" id="toggle-history">{{toggleHistoryText(sidenav.opened)}}</button> + </div> + + <nb-card *ngFor="let path of openApi.paths"> + <nb-card-header>{{path.pathName}}</nb-card-header> + <nb-accordion> + <nb-accordion-item *ngFor="let request of path.requests"> + <nb-accordion-item-header>{{request.summary}}</nb-accordion-item-header> + <nb-accordion-item-body> + <strong>Parameters</strong> + <nb-list> + <nb-list-item *ngFor="let parameter of request.parameters"> + <label>{{parameter.name}}</label><input nbInput + *ngIf="values.has(path.pathName) && values.get(path.pathName).has(request.httpmethod) && values.get(path.pathName).get(request.httpmethod).has(parameter.name)" + [ngModel]="values.get(path.pathName).get(request.httpmethod).get(parameter.name).value" + (ngModelChange)="values.get(path.pathName).get(request.httpmethod).get(parameter.name).value = $event" /> + </nb-list-item> + </nb-list> + <button class="launch-button" nbButton + (click)="launchRequest(path.pathName, request.httpmethod, true)">Launch</button> + <div class="preview" [nbSpinner]="spinners.get(path.pathName)"> + <ng-container *ngIf="spinners.get(path.pathName); else preview"></ng-container> + <ng-template #preview> + <ng-container *ngIf="request.tags != null && request.tags.length > 0"> + <pre *ngIf="request.tags[0] === tagEnum.dataset">{{ previews.get(path.pathName)|json}}</pre> + <ul *ngIf="request.tags[0] === tagEnum.search"> + <li *ngFor="let datasetId of previews.get(path.pathName)">{{datasetId}}</li> + </ul> + </ng-container> + </ng-template> + </div> + </nb-accordion-item-body> + </nb-accordion-item> + </nb-accordion> + </nb-card> + <div *ngIf="loading" class="overlay"> + <mat-spinner class="spinner"> + </mat-spinner> + </div> + </mat-sidenav-content> +</mat-sidenav-container> + -<nb-card *ngFor="let path of openApi.paths"> - <nb-card-header>{{path.pathName}}</nb-card-header> - <nb-accordion> - <nb-accordion-item *ngFor="let request of path.requests"> - <nb-accordion-item-header>{{request.summary}}</nb-accordion-item-header> - <nb-accordion-item-body> - <strong>Parameters</strong> - <nb-list> - <nb-list-item *ngFor="let parameter of request.parameters"> - <label>{{parameter.name}}</label><input nbInput - *ngIf="values.has(path.pathName) && values.get(path.pathName).has(request.httpmethod) && values.get(path.pathName).get(request.httpmethod).has(parameter.name)" - [ngModel]="values.get(path.pathName).get(request.httpmethod).get(parameter.name).value" - (ngModelChange)="values.get(path.pathName).get(request.httpmethod).get(parameter.name).value = $event" /> - </nb-list-item> - </nb-list> - <button class="launch-button" (click)="launchRequest(path.pathName, request.httpmethod, true)">Launch</button> - <div class="preview" [nbSpinner]="spinners.get(path.pathName)"> - <ng-container *ngIf="spinners.get(path.pathName); else preview"></ng-container> - <ng-template #preview> - <ng-container *ngIf="request.tags != null && request.tags.length > 0"> - <pre *ngIf="request.tags[0] === tagEnum.dataset">{{ previews.get(path.pathName)|json}}</pre> - <ul *ngIf="request.tags[0] === tagEnum.search"> - <li *ngFor="let datasetId of previews.get(path.pathName)">{{datasetId}}</li> - </ul> - </ng-container> - </ng-template> - </div> - </nb-accordion-item-body> - </nb-accordion-item> - </nb-accordion> -</nb-card> -<div *ngIf="loading" class="overlay"> - <mat-spinner class="spinner"> - </mat-spinner> -</div> <!-- <form> diff --git a/src/app/datasets/datasets.component.scss b/src/app/datasets/datasets.component.scss index 37bfe02d69da11212264d1be6e3950a9f0b65d29..8773f3723c538f84207329e219354372ce31a0ed 100644 --- a/src/app/datasets/datasets.component.scss +++ b/src/app/datasets/datasets.component.scss @@ -31,3 +31,48 @@ label { left: 50%; transform: translate(-50%, -50%); } + +.sidenav-content { + background-color: white; + padding-right: 5px; +} + +#request-historical { + width: 20%; + margin-left: 5px; +} + +#history-title { + position: absolute; + background-color: white; + width: 100%; + margin-top: 0px; + padding-bottom: 10px; + margin-left: 0px; + padding-top: 15px; + padding-left: 5px; + border-bottom: 1px #e0e0e0 solid; + border-top: 1px #e0e0e0 solid; + border-right: 1px #e0e0e0 solid; + text-align: center; +} + +#history-list { + padding-top: 48px; +} + +#toggle-history { + height: 40px; +} + +.grid-container { + display: grid; + grid-template-columns: auto 110px; +} + +#no-history-found-message { + color: grey; + font-style: italic; + padding-top: 48px; + margin-left: 5px; +} diff --git a/src/app/datasets/datasets.component.ts b/src/app/datasets/datasets.component.ts index a3990d466ffe18016f8deab25a6d77b004d6b5e8..41aed36729f0e00ae2fa528def95be49e73ddf7e 100644 --- a/src/app/datasets/datasets.component.ts +++ b/src/app/datasets/datasets.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnInit } from '@angular/core'; +import { AfterContentChecked, AfterViewChecked, Component, Input, OnInit } from '@angular/core'; import { DatasetCrudService } from './services/dataset-crud.service'; import { OpenApi } from '../publishapi/class/openapi'; import { ParameterType } from '../publishapi/class/openapi-enum'; @@ -7,6 +7,9 @@ import { environment } from 'src/environments/environment.prod'; import { NbStepperComponent } from '@nebular/theme'; import { DatasetsDialogComponent } from './datasets-dialog/datasets-dialog.component'; import { MatDialog } from '@angular/material/dialog'; +import { HttpClient } from '@angular/common/http'; +import { HttpMethod } from '../publishapi/class/http-enum'; +import { Observable } from 'rxjs'; interface RequestInfo { value?: string; @@ -23,10 +26,12 @@ interface RequestInfo { }) -export class DatasetsComponent implements OnInit, OnChanges { +export class DatasetsComponent implements OnInit, AfterViewChecked, AfterContentChecked { @Input() openApi: OpenApi; + oldOpenApiStringified: string; + loading = false; ready = false; @@ -34,12 +39,12 @@ export class DatasetsComponent implements OnInit, OnChanges { tagEnum = OpenApiTag; - values = new Map<string, Map<string, Map<string, RequestInfo>>>(); - previews = new Map<string, any>(); - spinners = new Map<string, boolean>(); + values: Map<string, Map<string, Map<string, RequestInfo>>>; + previews: Map<string, any>; + spinners: Map<string, boolean>; FDP_URL = environment.fdpUrl; - + requestHistorical: string[] = []; Object = Object; @@ -48,20 +53,59 @@ export class DatasetsComponent implements OnInit, OnChanges { constructor( private dataSetService: DatasetCrudService, - public dialog: MatDialog + public dialog: MatDialog, + private httpClient: HttpClient ) { } - get urlRepo() { + get urlRepo(): string { return this.openApi.servers.length > 0 ? this.openApi.servers[0].url : ''; } + get openApiHasChanged(): boolean { + const openApiStringified: string = JSON.stringify(this.openApi); + if (openApiStringified === this.oldOpenApiStringified) { + return false; + } else { + this.oldOpenApiStringified = openApiStringified; + return true; + } + } + + toggleHistoryText(isOpen: boolean): string { + if (isOpen) { + return 'Hide history'; + } else { + return 'Show history'; + } + } + ngOnInit() { - this.dataSetService.resetDataset() + this.initComponent(); + } + + ngAfterContentChecked() { + this.initComponent(); + } + ngAfterViewChecked() { + window.dispatchEvent(new Event('resize')); } - ngOnChanges() { + initComponent() { + if (!this.openApiHasChanged) { + return; + } + + this.loading = false; + this.ready = false; + + this.values = new Map<string, Map<string, Map<string, RequestInfo>>>(); + this.previews = new Map<string, any>(); + this.spinners = new Map<string, boolean>(); + this.initValueMap(); + this.updateRequestHistorical(); + this.dataSetService.resetDataset(); } initValueMap() { @@ -110,8 +154,7 @@ export class DatasetsComponent implements OnInit, OnChanges { } } - const myHeaders = new Headers(); - myHeaders.append("Content-Type", "Application/json"); + const myHeaders: any = {}; const url = new URL(this.urlRepo + path); @@ -126,32 +169,30 @@ export class DatasetsComponent implements OnInit, OnChanges { url.searchParams.set(parameterName, value); break; case ParameterType.header: - myHeaders.append(parameterName, value); + myHeaders[parameterName] = value; break; case ParameterType.cookie: - if (myHeaders.has('Cookie')) { - myHeaders.set('Cookie', myHeaders.get('Cookie') + `; ${parameterName}=${value}`); + if (myHeaders.Cookie != null) { + myHeaders.Cookie = myHeaders.Cookie + `; ${parameterName}=${value}`; } else { - myHeaders.set('Cookie', `${parameterName}=${value}`); + myHeaders.Cookie = `${parameterName}=${value}`; } } } - const myInit = { method: httpMethod.toUpperCase(), headers: myHeaders }; - const myRequest = new Request(url.href, myInit); - + const requestHref = url.href; let result: any = null; try { - const response = await fetch(myRequest, myInit); - const jsonResponse = await response.json(); + const response = await this.getRequestPromiseByHttpMethod(httpMethod as HttpMethod, requestHref, myHeaders); switch (this.getTagByPathName(pathName, httpMethod)) { case OpenApiTag.search: - result = this.findListId(jsonResponse, pathName, preview); + result = this.findListId(response, pathName, preview); + this.addRequestInHistorical(requestHref); break; case OpenApiTag.dataset: - result = this.processDataSet(jsonResponse, pathName, preview); + result = this.processDataSet(response, pathName, preview); break; } } finally { @@ -161,6 +202,17 @@ export class DatasetsComponent implements OnInit, OnChanges { return result; } + getRequestPromiseByHttpMethod(httpMethod: HttpMethod, requestUrl: string, headers: any): Promise<any> { + let requestObs: Observable<any>; + + switch (httpMethod.toLowerCase()) { + case HttpMethod.GET: + requestObs = this.httpClient.get(requestUrl, { headers, withCredentials: true }); + } + + return requestObs.toPromise(); + } + processDataSet(dataSet: any, pathName: string, preview: boolean) { if (preview) { this.previews.set(pathName, dataSet); @@ -293,6 +345,7 @@ export class DatasetsComponent implements OnInit, OnChanges { datasetRequestPathName: string, datasetRequestHttpMethod: string) { this.launchRequest(searchRequestPathName, searchRequestHttpMethod, false).then((response) => { this.dialog.open(DatasetsDialogComponent, { + width: '100vw', data: { datasetId: response }, @@ -326,6 +379,34 @@ export class DatasetsComponent implements OnInit, OnChanges { }).finally(() => this.loading = false); } + private updateRequestHistorical() { + this.requestHistorical = []; + const catId = this.openApi.info['x-catalog-id']; + + if (catId == null) { + return; + } + + this.dataSetService.getRequestHistorical(catId).subscribe({ + next: (response) => { + this.requestHistorical = []; + response.map((el) => { this.requestHistorical.push(el.request); }); + } + }); + } + + private addRequestInHistorical(request: string) { + const catId = this.openApi.info['x-catalog-id']; + + if (catId == null) { + return; + } + + this.dataSetService.createRequestHistorical(catId, request).subscribe({ + next: () => this.updateRequestHistorical() + }); + } + /*listdatasets() { var myHeaders = new Headers(); myHeaders.append("Content-Type", "Application/json"); diff --git a/src/app/datasets/services/dataset-crud.service.ts b/src/app/datasets/services/dataset-crud.service.ts index 73ba27ffa86b80dace78a65732911ec4e2bacce9..1a56903cdc73cbb2d5d37ba620dbd898f986e020 100644 --- a/src/app/datasets/services/dataset-crud.service.ts +++ b/src/app/datasets/services/dataset-crud.service.ts @@ -1,7 +1,6 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { AppConfiguration } from 'src/app/AppConfiguration'; +import { Observable, of } from 'rxjs'; import { TokenStorageService } from 'src/app/authentication/services/token-storage.service'; import { environment } from 'src/environments/environment.prod'; import { ParseXmlService } from '../../services/parse-xml.service'; @@ -16,21 +15,37 @@ export class DatasetCrudService { public results: string[] = []; itemsDataset: Object[] = []; ids: number[] = []; - constructor(private http: HttpClient, private appConfig: AppConfiguration, private parserService: ParseXmlService, private sessionStorage: TokenStorageService) { } - + constructor( + private http: HttpClient, + private parserService: ParseXmlService, + private sessionStorage: TokenStorageService + ) { } + + get requestHistoricalBaseUrl(): string { + return environment.smartharvesterUrl + '/harvester/api/request-historical'; + } + + get tokenHarvesterHeader() { + return { + headers: new HttpHeaders({ + Authorization: 'Bearer ' + this.sessionStorage.getUser().token + }) + }; + } + async createDataset(data: string): Promise<any> { if (this.fds2Token) { const httpOptions = new Headers(); - httpOptions.append( 'Accept', 'text/turtle'); - httpOptions.append('Content-Type', 'text/turtle'); - httpOptions.append('Authorization', 'Bearer '+ this.fds2Token ); + httpOptions.append('Accept', 'text/turtle'); + httpOptions.append('Content-Type', 'text/turtle'); + httpOptions.append('Authorization', 'Bearer ' + this.fds2Token); - const myInit = { method: 'POST', body: data , headers: httpOptions }; - const myRequest = new Request(`${FDP_URL}/dataset`, myInit); + const myInit = { method: 'POST', body: data, headers: httpOptions }; + const myRequest = new Request(`${FDP_URL}/dataset`, myInit); - return fetch(myRequest, myInit) + return fetch(myRequest, myInit); } } @@ -39,20 +54,20 @@ export class DatasetCrudService { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'text/turtle', - 'Authorization': 'Bearer ' + this.fds2Token + Authorization: 'Bearer ' + this.fds2Token }) }; - return this.http.put(`${url}/meta/state`, { "current": "PUBLISHED" }, httpOptions); + return this.http.put(`${url}/meta/state`, { current: 'PUBLISHED' }, httpOptions); } } - updateDataset(data: string, url: string): Observable<Object> { + updateDataset(data: string, url: string): Observable<object> { if (this.fds2Token) { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'text/turtle', - 'Authorization': 'Bearer ' + this.fds2Token + Authorization: 'Bearer ' + this.fds2Token }) }; @@ -65,13 +80,13 @@ export class DatasetCrudService { if (this.fds2Token) { const httpOptions = { headers: new HttpHeaders({ - 'Accept': 'text/turtle', + Accept: 'text/turtle', 'Content-Type': 'text/turtle', - 'Authorization': 'Bearer ' + this.fds2Token + Authorization: 'Bearer ' + this.fds2Token }) }; - let resultat = this.http.post(FDP_URL + "/distribution", data, httpOptions).subscribe((r) => { + const resultat = this.http.post(FDP_URL + '/distribution', data, httpOptions).subscribe((r) => { console.log('reponse: ', r); return JSON.stringify(resultat); }); @@ -88,7 +103,7 @@ export class DatasetCrudService { findDataSetByTitle(title: string) { let exist = false; - let query1 = 'query=PREFIX dcterms: <http://purl.org/dc/terms/> SELECT * WHERE { ?uri dcterms:title "' + escape(title) + '". }' + const query1 = 'query=PREFIX dcterms: <http://purl.org/dc/terms/> SELECT * WHERE { ?uri dcterms:title "' + escape(title) + '". }'; this.parserService.getXmlResult(query1).subscribe( data => { if (data) { @@ -96,11 +111,11 @@ export class DatasetCrudService { data.results.bindings.forEach(element => { this.results.push(element); }); if (this.results[0]) { - let exist = true; + exist = true; } } }); - return exist + return exist; } getLocally<T = any>(path: string): Observable<T> { @@ -115,7 +130,27 @@ export class DatasetCrudService { this.ids = []; } - + getRequestHistorical(catId: string) { + if (this.sessionStorage.getUser().token == null) { + return of([]); + } + + return this.http.get<{ catId: string, request: string }[]>(this.requestHistoricalBaseUrl + '/getall/' + catId, + this.tokenHarvesterHeader); + } + + createRequestHistorical(catId: string, request: string) { + if (this.sessionStorage.getUser().token == null) { + return of(); + } + + const body = { + catId, + request + }; + + return this.http.post(this.requestHistoricalBaseUrl + '/create', body, this.tokenHarvesterHeader); + } } diff --git a/src/app/publishapi/services/openapi-service.ts b/src/app/publishapi/services/openapi-service.ts index 491b8399dccc0b924f13ccffb20cfec578e82e1c..c111bd0b924f033a86325639a9919121a4dd1bd3 100644 --- a/src/app/publishapi/services/openapi-service.ts +++ b/src/app/publishapi/services/openapi-service.ts @@ -1,10 +1,11 @@ -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { OpenApi } from '../class/openapi'; import { OpenApiDTO } from '../class/openapi-dto'; import { OpenApiDTOMappingService } from './openapi-dto-mapping-service'; import { environment } from 'src/environments/environment'; +import { TokenStorageService } from 'src/app/authentication/services/token-storage.service'; @Injectable({ providedIn: 'root' @@ -14,10 +15,19 @@ export class OpenApiService { constructor( private http: HttpClient, private openApiDTOMappingService: OpenApiDTOMappingService, + private sessionStorage: TokenStorageService ) { } get smartApiUrl(): string { - return environment.smartharvesterUrl + '/harvester/publication' + return environment.smartharvesterUrl + '/harvester/api/publication'; + } + + get tokenHarvesterHeader() { + return { + headers: new HttpHeaders({ + Authorization: 'Bearer ' + this.sessionStorage.getUser().token + }) + }; } getFromRepository(repositoryId: string): Observable<OpenApiDTO> { @@ -56,12 +66,20 @@ export class OpenApiService { } getByCatalogId(catId: string): Observable<OpenApiDTO> { - return this.http.get<OpenApiDTO>(this.smartApiUrl + '/catalogs/' + catId); + if (this.sessionStorage.getUser().token == null) { + return of(); + } + + return this.http.get<OpenApiDTO>(this.smartApiUrl + '/catalogs/' + catId, this.tokenHarvesterHeader); } publishOpenApi(openApi: OpenApi) { + if (this.sessionStorage.getUser().token == null) { + return of(); + } + const openApiDTO = this.openApiDTOMappingService.mapToDto(openApi); - return this.http.post(this.smartApiUrl + '/publish', openApiDTO); + return this.http.post(this.smartApiUrl + '/publish', openApiDTO, this.tokenHarvesterHeader); } private jsonFull = diff --git a/src/app/repository/repository.component.ts b/src/app/repository/repository.component.ts index 14f3ab3372da69ea90d37be718b06fbff0b8c153..8c56129da2c9756073eb60312d7e53933e37374d 100644 --- a/src/app/repository/repository.component.ts +++ b/src/app/repository/repository.component.ts @@ -53,35 +53,52 @@ export class RepositoryComponent implements OnInit { const files = (event.target as HTMLInputElement).files; this.importFile = files[0]; - let text = ""; + let text = ''; let lines = []; - let line = ""; - let map = new Map(); - let fileReader = new FileReader(); + const map = new Map<string, string>(); + const fileReader = new FileReader(); - fileReader.onload = (event) => { + fileReader.onload = () => { text = fileReader.result as string; - lines = text.split("\n"); - for (let i = 0; i < lines.length; i++) { - line = lines[i].split(": "); - map.set(line[0].trim(), line[1]); + lines = text.split('\n'); + for (const line of lines) { + const parameter: string[] = line.split(': '); + + if (parameter == null || parameter.length !== 2) { + continue; + } + + map.set(parameter[0].trim(), parameter[1]); } this.Form.setValue({ - repotype: map.get('type'), - reponame: map.get('title'), - repodescription: map.get('description'), - repourl: map.get('url'), - repoversion: map.get('version'), - repolicence: map.get('licence'), - repolanguage: map.get('language'), + repotype: this.formatValue(map.get('type')), + reponame: this.formatValue(map.get('title')), + repodescription: this.formatValue(map.get('description')), + repourl: this.formatValue(map.get('url')), + repoversion: this.formatValue(map.get('version')), + repolicence: this.formatValue(map.get('licence')), + repolanguage: this.formatValue(map.get('language')), filetofill: '' }); + }; + fileReader.readAsText(this.importFile); + } + private formatValue(value: string): string { + if (value == null) { + return ''; } - fileReader.readAsText(this.importFile); + + value = value.trim(); + + if (value === 'null') { + return ''; + } + + return value; } repositorytoyaml() {