diff --git a/package-lock.json b/package-lock.json index 239cbbc2db7119dd57a1941057f846471f810e38..69adfc8fc5b44b3a9cd4af7575aaa85734d967e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3157,6 +3157,11 @@ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, + "@types/lodash": { + "version": "4.14.165", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz", + "integrity": "sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg==" + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", diff --git a/package.json b/package.json index 9c7fbc243b8f6a8ac10c29fb563aa5da0868d834..af389bb3d398c371d5b0363d1eb3d16c70e16ff7 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@angular/compiler": "~9.1.11", "@angular/core": "~9.1.11", "@angular/forms": "~9.1.11", + "@types/lodash": "^4.14.165", "@angular/material": "^9.2.4", "@angular/platform-browser": "~9.1.11", "@angular/platform-browser-dynamic": "~9.1.11", diff --git a/src/app/accessapi/accessapi.component.html b/src/app/accessapi/accessapi.component.html index 6da8eb4e648e3008cc18e67f634ffc811074cc9e..9ce0466581179c98ef80a929f64b8dafe641795a 100644 --- a/src/app/accessapi/accessapi.component.html +++ b/src/app/accessapi/accessapi.component.html @@ -6,14 +6,12 @@ <label>Or describe your repository and save a new file for publishing in the FDP database:</label><br> <div class="form-group"> - <label>title - <input type="text" class="form-control" formControlName="title" required> - </label> + <label>title</label> + <input type="text" class="form-control" formControlName="title" required> </div> <div class="form-group"> - <label>Description - <textarea class="form-control" formControlName="description"></textarea> - </label> + <label>Description</label> + <textarea class="form-control" formControlName="description"></textarea> </div> <div class="form-group"> <label>version</label> @@ -24,9 +22,8 @@ <input type="text" class="form-control" formControlName="url"> </div> <div class="form-group"> - <label>Description - <textarea class="form-control" formControlName="urldescription"></textarea> - </label> + <label>Description</label> + <textarea class="form-control" formControlName="urldescription"></textarea> </div> <div class="form-group"> <label>paths</label> diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index ee5d8da9982c73e968276728222627adde1c2d96..8ffa3622296cc788340a013035d88209308fac67 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -4,13 +4,9 @@ import { UserComponent } from './user/user.component'; import { RepositoryComponent } from './repository/repository.component'; import { AccessapiComponent } from './accessapi/accessapi.component'; import { DatasetsComponent } from './datasets/datasets.component'; -import { OntologieComponent } from './ontologie/ontologie.component'; -import { PublishComponent } from './publish/publish.component'; import { SettingfdpComponent } from './settingfdp/settingfdp.component'; import { SettingsmartapiComponent } from './settingsmartapi/settingsmartapi.component'; import { RepositoryinfoComponent } from './repositoryinfo/repositoryinfo.component'; -import { SwaggereditorComponent } from './swaggereditor/swaggereditor.component'; -import { ElasticsearchComponent } from './elasticsearch/elasticsearch.component' import { PublishApiComponent } from './publishapi/publishapi.component' import { SearchComponent } from './search/search.component'; import { AuthenticationComponent } from './authentication/authentication.component'; @@ -25,6 +21,7 @@ export interface ICustomRoute extends Route { name?: string; } + const routes: ICustomRoute[] = [ { path: '', redirectTo: '/auth/signin', pathMatch: 'full' }, { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuardService], @@ -35,13 +32,9 @@ const routes: ICustomRoute[] = [ { path: 'repositoryinfo', component: RepositoryinfoComponent }, { path: 'accessapi', component: AccessapiComponent }, { path: 'datasets', component: DatasetsComponent }, - { path: 'ontology', component: OntologieComponent }, - { path: 'publish', component: PublishComponent }, { path: 'stats', component: StatsComponent }, { path: 'settingfdp', component: SettingfdpComponent }, { path: 'settingsmartharvester', component: SettingsmartapiComponent }, - { path: 'swaggerapi', component: SwaggereditorComponent }, - { path: 'elasticsearch', component: ElasticsearchComponent }, { path: 'publishapi', component: PublishApiComponent }, { path: 'advancedsearch', component: SearchComponent }, ] diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a88ee54d82151d2e88e56eda7613c8cd1f555727..55e84899a5e393fe0775a18b0fb8e6df68c64ef3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -13,8 +13,6 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { RepositoryComponent } from './repository/repository.component'; import { AccessapiComponent } from './accessapi/accessapi.component'; import { DatasetsComponent } from './datasets/datasets.component'; -import { OntologieComponent } from './ontologie/ontologie.component'; -import { PublishComponent } from './publish/publish.component'; import { SettingfdpComponent } from './settingfdp/settingfdp.component'; import { SettingsmartapiComponent } from './settingsmartapi/settingsmartapi.component'; import { UserComponent } from './user/user.component'; @@ -26,9 +24,7 @@ import { RepositoryinfoComponent } from './repositoryinfo/repositoryinfo.compone import { HttpClientModule, HttpClient } from '@angular/common/http'; import { FileSaverModule } from 'ngx-filesaver'; -import { SwaggereditorComponent } from './swaggereditor/swaggereditor.component'; import { AppConfiguration } from './AppConfiguration'; -import { ElasticsearchComponent } from './elasticsearch/elasticsearch.component'; import { PublishApiComponent } from './publishapi/publishapi.component'; import { SearchComponent } from './search/search.component' import { ParseXmlService } from './services/parse-xml.service'; @@ -40,7 +36,7 @@ import { StatsComponent } from './stats/stats.component'; import { NebularModule } from './nebular.module'; import { AppRoutingModule } from './app-routing.module'; -import { NbLayoutModule, NbThemeModule } from '@nebular/theme'; +import { NbLayoutModule, NbThemeModule, NbTooltipModule, NbSpinnerModule, NbSelectModule, NbTabsetModule } from '@nebular/theme'; import { NbAuthModule } from '@nebular/auth/auth.module'; import { NbDummyAuthStrategy } from '@nebular/auth/strategies/dummy/dummy-strategy'; import { DashboardComponent } from './dashboard/dashboard.component'; @@ -52,14 +48,10 @@ import { DashboardComponent } from './dashboard/dashboard.component'; RepositoryComponent, AccessapiComponent, DatasetsComponent, - OntologieComponent, - PublishComponent, SettingfdpComponent, SettingsmartapiComponent, UserComponent, RepositoryinfoComponent, - SwaggereditorComponent, - ElasticsearchComponent, PublishApiComponent, SearchComponent, StatsComponent, @@ -84,15 +76,19 @@ import { DashboardComponent } from './dashboard/dashboard.component'; NebularModule, BrowserAnimationsModule, NbThemeModule.forRoot({ name: 'default' }), - NbLayoutModule + NbLayoutModule, + NbSpinnerModule, + NbSelectModule, + NbTabsetModule, + NbTooltipModule ], providers: [ AppConfiguration, ParseXmlService, - { - provide: APP_INITIALIZER, - useFactory: AppConfigurationFactory, + { + provide: APP_INITIALIZER, + useFactory: AppConfigurationFactory, deps: [AppConfiguration, HttpClient], multi: true }, ], bootstrap: [AppComponent] @@ -101,4 +97,4 @@ export class AppModule { } export function AppConfigurationFactory(appConfig: AppConfiguration) { return () => appConfig.ensureInit(); - } \ No newline at end of file + } diff --git a/src/app/authentication/services/auth.service.ts b/src/app/authentication/services/auth.service.ts index 9156988958747372c7099084e1ef288b505edc2a..3537a9a64a3d6d702c7f28684bf7d0f3849d5361 100644 --- a/src/app/authentication/services/auth.service.ts +++ b/src/app/authentication/services/auth.service.ts @@ -4,9 +4,11 @@ import { Observable } from 'rxjs'; import { TokenStorageService } from './token-storage.service'; import { map } from "rxjs/operators"; import { AppConfiguration } from 'src/app/AppConfiguration'; +import { env } from 'process'; +import { environment } from 'src/environments/environment'; -const AUTH_API = "https://f2ds.eosc-pillar.eu/harvester/auth"; +const AUTH_API = environment.smartharvesterUrl; const F2DSAPI = "https://f2ds.eosc-pillar.eu" const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) @@ -37,6 +39,14 @@ export class AuthService { }, httpOptions); } + + update(user): Observable<any> { + return this.http.post(AUTH_API + '/user', { + catalog_id: user.cat_id + }, httpOptions); + } + + getF2DSAuthToken():Observable<any> { return this.http.post(this.appConfig.fdpurl+'/tokens', { email: this.appConfig.fdpemail, diff --git a/src/app/datasets/datasets.component.html b/src/app/datasets/datasets.component.html index da68ea9b8d480d8ce602201e0a66dc1fba49004c..b8a2f55ac1ed3222c9a1db1b9d72160f41a03361 100644 --- a/src/app/datasets/datasets.component.html +++ b/src/app/datasets/datasets.component.html @@ -4,15 +4,25 @@ <div> <button type="submit" (click)="listdatasets()" class="btn btn-primary">List datasets</button> </div> - <ul> - <li *ngFor="let item of dataresult"><a href={{item}} target="_blank" >{{item}} </a> </li> - </ul> - -</form> + <div *ngFor="let key of Object.keys(itemsdataset)"> + <ng-container *ngIf=" itemsdataset[key] == '[object Object]' ; then first else second"></ng-container> + <ng-template #second><p>{{key}}</p></ng-template> + <ng-template #first> + <div><p>{{key}}:</p></div> + <div *ngFor="let keyobj of Object.keys(itemsdataset[key])"> + <ng-container *ngIf=" itemsdataset[key][keyobj] == '[object Object]' ; then first2 else second2"></ng-container> + <ng-template #second2><p STYLE="padding:0 0 0 50px;">{{key}} : {{keyobj}}</p></ng-template> + <ng-template #first2> + <div><p STYLE="padding:0 0 0 50px;">{{key}} : {{keyobj}}: </p></div> + <div *ngFor="let keyobj2 of Object.keys(itemsdataset[key][keyobj])"> + <p STYLE="padding:0 0 0 100px;">{{key}} : {{keyobj}} : {{keyobj2}}</p> + </div> + </ng-template> + </div> + </ng-template> + </div> -<br><br> -<br><br> +</form> -<button class="next-step-button" routerLink="/ontologie" routerLinkActive="active" >Next step</button> diff --git a/src/app/datasets/datasets.component.ts b/src/app/datasets/datasets.component.ts index a63f446a7f4797beff2e996533783de692fc1043..f4528aafa273a19af5eedf07e9827f4018914aaf 100644 --- a/src/app/datasets/datasets.component.ts +++ b/src/app/datasets/datasets.component.ts @@ -4,6 +4,7 @@ import { AppConfiguration } from '../AppConfiguration'; import { FileSaverService } from 'ngx-filesaver'; import { environment } from 'src/environments/environment.prod'; import { DatasetCrudService } from './services/dataset-crud.service'; +import { ArrayDataSource } from '@angular/cdk/collections'; @Component({ @@ -14,8 +15,12 @@ import { DatasetCrudService } from './services/dataset-crud.service'; export class DatasetsComponent implements OnInit { - dataresult: any; itemsdatasets: any; + itemsdataset: any; + Object = Object; + + urlrepo = "dataverse.ird.fr"; +// urlrepo = "data.inrae.fr"; constructor( private appConfig: AppConfiguration, @@ -25,6 +30,7 @@ export class DatasetsComponent implements OnInit { ) { } ngOnInit() { + this.itemsdataset = []; } @@ -36,21 +42,21 @@ export class DatasetsComponent implements OnInit { //appeler smart havester pour récuperer l'api à lancer - - // https://data.inrae.fr/api/search?q=*&per_page=10&type=dataset&start=16000&show_entity_ids=true&show_my_data=true - - // var myRequest = new Request(this.appConfig.smartapiurl+'harvest/allurls',myInit); - var myRequest = new Request('https://dataverse.ird.fr/api/search?q=*&per_page=100&show_facets=true&show_my_data=true', myInit); + + var myRequest = new Request('https://'+this.urlrepo+'/api/search?q=*&per_page=5&type=dataset&start=50&show_facets=true&show_my_data=true', myInit); fetch(myRequest, myInit) .then(response => { response.json() .then(data => { - //this.dataresult =data; this.itemsdatasets = data['data']['items']; - console.log(this.itemsdatasets); + // console.log(this.itemsdatasets); for (var i = 0; i < this.itemsdatasets.length; i++) { - this.createdataset(data['data']['items'][i]); + if (!this.dataSetService.findDataSetByTitle(data['data']['items'][i]['name'])) { + if (data['data']['items'][i]['global_id']) { + this.populatecatalogue(data['data']['items'][i]['global_id']); + } + } } }); }); @@ -58,15 +64,53 @@ export class DatasetsComponent implements OnInit { } + populatecatalogue(id: string) { + var myHeaders = new Headers(); + myHeaders.append("Content-Type", "Application/json"); + var myInit = { method: 'GET', headers: myHeaders }; + + //appeler smart havester pour récuperer l'api à lancer + + var myRequest = new Request('https://'+this.urlrepo+'/api/datasets/export?exporter=dataverse_json&persistentId='+id , myInit); + + fetch(myRequest, myInit) + .then(response => { + response.json() + .then(data => { + this.itemsdataset = data; + + console.log( this.itemsdataset); + + this.createdataset(data); + //this.createdistribution(this.itemsdataset); + }); + }); + + + + + } + + + + createdataset(item: any) { - console.log(item); + //console.log(item); let data: string; - let description: string; - description = JSON.stringify(item['description']); - let name: string; - name = JSON.stringify(item['name']); + let description = ""; + let name = ""; let url: string; - url = JSON.stringify(item['url']); + + for (var i = 0; i < item['datasetVersion']['metadataBlocks']['citation']['fields'].length; i++) { + if (item['datasetVersion']['metadataBlocks']['citation']['fields'][i]['typeName'] == "dsDescription") { + description = JSON.stringify(item['datasetVersion']['metadataBlocks']['citation']['fields'][i]['value']); + } + if (item['datasetVersion']['metadataBlocks']['citation']['fields'][i]['typeName'] == "title") { + name = JSON.stringify( item['datasetVersion']['metadataBlocks']['citation']['fields'][i]['value'] ); + } + } + + url = JSON.stringify(item['persistentUrl']); data = '\@prefix dcat: <http://www.w3.org/ns/dcat#>.\n\ @prefix dct: <http://purl.org/dc/terms/>.\n\ @@ -84,19 +128,55 @@ export class DatasetsComponent implements OnInit { dct:title '+ name + ';\n\ dcat:keyword '+ url + '.\n' - /* const httpOptions = { - headers: new HttpHeaders({ - 'Accept': 'text/turtle', - 'Content-Type': 'text/turtle', - 'Authorization': 'Bearer '+ environment.token - }) - }; - - return this.http.post(this.appConfig.fdpurl+"/dataset", data, httpOptions ).subscribe( (r)=>{console.log('got r', r)}) ; */ - return this.dataSetService.createDataSet(data); + console.log(data); + // return this.dataSetService.createDataSet(data); + + } + + + + createdistribution(item: any) { + + // si file alors download url https://dataverse.ird.fr/api/access/datafile/['files']['dataFile'][i]['id']] + + console.log(item); + let data: string; + let description: string; + description = JSON.stringify(item['description']); + let name: string; + name = JSON.stringify(item['name']); + let url: string; + url = JSON.stringify(item['url']); + + data='@prefix dcat: <http://www.w3.org/ns/dcat#>.\n\ + @prefix dct: <http://purl.org/dc/terms/>.\n\ + @prefix language: <http://id.loc.gov/vocabulary/iso639-1/>.\n\ + @prefix ffd: <https://ffds.eosc-pillar.eu/>.\n\ + @prefix d: <https://ffds.eosc-pillar.eu/dataset/>.\n\ + @prefix GVW: <https://dataverse.ird.fr/dataset.xhtml?persistentId=doi:10.23708/GVWCA0#>.\n\ + \n\ + ffd:new\n\ + a dcat:Distribution, dcat:Resource;\n\ + dct:description "Botinelli_VNM_2016.png ";\n\ + dct:hasVersion "v1.0";\n\ + dct:isPartOf d:ca1b25a9-901c-4822-8554-b1bb416ee99d;\n\ + dct:language language:fr;\n\ + dct:license <http://rdflicense.appspot.com/rdflicense/cc-by-nc-nd3.0>;\n\ + dct:title "Botinelli_VNM_2016.png ";\n\ + dcat:accessURL GVW:;\n\ + dcat:downloadURL\n\ + <https://dataverse.ird.fr/api/access/datafile/262?imageThumb=400>;\n\ + dcat:format "png";\n\ + dcat:mediaType "png".' + + + return this.dataSetService.createDistribution(data); } + + + } diff --git a/src/app/datasets/services/dataset-crud.service.ts b/src/app/datasets/services/dataset-crud.service.ts index 1f134dd0b370d40efc8acfbf461be7f26f40f43c..3abb6be9e7efcc4028a6335e9c904c24cb4e5fd4 100644 --- a/src/app/datasets/services/dataset-crud.service.ts +++ b/src/app/datasets/services/dataset-crud.service.ts @@ -2,6 +2,8 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { AppConfiguration } from 'src/app/AppConfiguration'; import { AuthService } from 'src/app/authentication/services/auth.service'; +import { ParseXmlService } from '../../services/parse-xml.service'; + @Injectable({ providedIn: 'root' @@ -9,7 +11,8 @@ import { AuthService } from 'src/app/authentication/services/auth.service'; export class DatasetCrudService { fds2Token: string - constructor(private http: HttpClient,private appConfig: AppConfiguration, private authService: AuthService) { } + public results: string[] = []; + constructor(private http: HttpClient,private appConfig: AppConfiguration, private authService: AuthService, private parserService: ParseXmlService) { } createDataSet(data:string){ @@ -33,4 +36,48 @@ export class DatasetCrudService { return "The repository has not been published" } } + + createDistribution(data:string){ + + this.authService.getF2DSAuthToken().subscribe(data=>{ + this.fds2Token = data.token + }) + if (this.fds2Token) { + const httpOptions = { + headers: new HttpHeaders({ + 'Accept': 'text/turtle', + 'Content-Type': 'text/turtle', + 'Authorization': 'Bearer '+ this.fds2Token + }) + }; + + let resultat = this.http.post(this.appConfig.fdpurl+"/distribution", data, httpOptions ).subscribe( (r)=>{console.log('reponse: ', r)}) ; + if (resultat){ + console.log("resultat: " + JSON.stringify(resultat)); + return JSON.stringify(resultat); + } + } + } + + + + findDataSetByTitle(title:string){ + + let exist = false; + let query1='query=PREFIX dcterms: <http://purl.org/dc/terms/> SELECT * WHERE { ?uri dcterms:title "'+ escape(title) +'". }' + this.parserService.getXmlResult(query1).subscribe( + data=>{ + if (data){ + this.results = []; + data.results.bindings.forEach(element => { this.results.push(element);}); + if (this.results[0]) { + console.log(this.results[0]['uri'].value.substring(36,72)); + let exist = true; + } + }}); + return exist + } + + + } diff --git a/src/app/elasticsearch/elasticsearch.component.html b/src/app/elasticsearch/elasticsearch.component.html deleted file mode 100644 index 3a53fb8d67c12ebd649bffdfcd149d5acd78fa95..0000000000000000000000000000000000000000 --- a/src/app/elasticsearch/elasticsearch.component.html +++ /dev/null @@ -1,15 +0,0 @@ - -<form [formGroup]="Form" (ngSubmit)="SaveElasticsearchSetting()"> - - <div class="form-group"> - <label>Elasticsearch Url - <input matInput placeholder="url http://" type="text" class="form-control" formControlName="elasticurl" required> - </label> - </div> - - <div> - <br> - <button type="submit" class="btn btn-primary">Save elasticsearch setting</button> - </div> - </form> - diff --git a/src/app/elasticsearch/elasticsearch.component.scss b/src/app/elasticsearch/elasticsearch.component.scss deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/elasticsearch/elasticsearch.component.spec.ts b/src/app/elasticsearch/elasticsearch.component.spec.ts deleted file mode 100644 index 96b18a8a1512222f38e391409fb917d05f0c253f..0000000000000000000000000000000000000000 --- a/src/app/elasticsearch/elasticsearch.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ElasticsearchComponent } from './elasticsearch.component'; - -describe('ElasticsearchComponent', () => { - let component: ElasticsearchComponent; - let fixture: ComponentFixture<ElasticsearchComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ ElasticsearchComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ElasticsearchComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/ontologie/ontologie.component.html b/src/app/ontologie/ontologie.component.html deleted file mode 100644 index a553b287d3791311c0cb7198fa5afdc71f376845..0000000000000000000000000000000000000000 --- a/src/app/ontologie/ontologie.component.html +++ /dev/null @@ -1,6 +0,0 @@ -<h2>FAIRify your data: </h2> - -<h3>1 - map your metadata schema with FDP schema</h3> -<h3>2 - add missing informations</h3> -<h3>3 - and align with ontologies</h3> - diff --git a/src/app/ontologie/ontologie.component.scss b/src/app/ontologie/ontologie.component.scss deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/ontologie/ontologie.component.spec.ts b/src/app/ontologie/ontologie.component.spec.ts deleted file mode 100644 index d22b186847922ac13c6b66ae492792ef223918eb..0000000000000000000000000000000000000000 --- a/src/app/ontologie/ontologie.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { OntologieComponent } from './ontologie.component'; - -describe('OntologieComponent', () => { - let component: OntologieComponent; - let fixture: ComponentFixture<OntologieComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ OntologieComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(OntologieComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/ontologie/ontologie.component.ts b/src/app/ontologie/ontologie.component.ts deleted file mode 100644 index bd02d1ca2631654069f028cc6a6c493facaba656..0000000000000000000000000000000000000000 --- a/src/app/ontologie/ontologie.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-ontologie', - templateUrl: './ontologie.component.html', - styleUrls: ['./ontologie.component.scss'] -}) -export class OntologieComponent implements OnInit { - - constructor() { } - - ngOnInit() { - } - -} diff --git a/src/app/publish/publish.component.html b/src/app/publish/publish.component.html deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/publish/publish.component.scss b/src/app/publish/publish.component.scss deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/publish/publish.component.spec.ts b/src/app/publish/publish.component.spec.ts deleted file mode 100644 index 2c37e9751d39cf0b0432c1eabb1d21780927ef41..0000000000000000000000000000000000000000 --- a/src/app/publish/publish.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PublishComponent } from './publish.component'; - -describe('PublishComponent', () => { - let component: PublishComponent; - let fixture: ComponentFixture<PublishComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ PublishComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(PublishComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/publish/publish.component.ts b/src/app/publish/publish.component.ts deleted file mode 100644 index 1e050fac53253010dbf70860727722252d4fb9de..0000000000000000000000000000000000000000 --- a/src/app/publish/publish.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - - -@Component({ - selector: 'app-publish', - templateUrl: './publish.component.html', - styleUrls: ['./publish.component.scss'] -}) -export class PublishComponent implements OnInit { - - - constructor() { } - - ngOnInit() { - } - - -} diff --git a/src/app/publishapi/class/http-enum.ts b/src/app/publishapi/class/http-enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..78893d497a82af229b1c2241e0cef1b1beb87c79 --- /dev/null +++ b/src/app/publishapi/class/http-enum.ts @@ -0,0 +1,394 @@ +"use strict"; + +export enum HttpMethod { + GET = 'get', + POST = 'post', + PUT = 'put', + PATCH = 'patch', + DELETE = 'delete', + HEAD = 'head', + OPTIONS = 'options' +} + + +/** + * Hypertext Transfer Protocol (HTTP) response status codes. + * @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes} + */ +enum HttpStatusCode { + + /** + * The server has received the request headers and the client should proceed to send the request body + * (in the case of a request for which a body needs to be sent; for example, a POST request). + * Sending a large request body to a server after a request has been rejected for inappropriate headers would be inefficient. + * To have a server check the request's headers, a client must send Expect: 100-continue as a header in its initial request + * and receive a 100 Continue status code in response before sending the body. The response 417 Expectation Failed indicates the request should not be continued. + */ + CONTINUE = 100, + + /** + * The requester has asked the server to switch protocols and the server has agreed to do so. + */ + SWITCHING_PROTOCOLS = 101, + + /** + * A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request. + * This code indicates that the server has received and is processing the request, but no response is available yet. + * This prevents the client from timing out and assuming the request was lost. + */ + PROCESSING = 102, + + /** + * Standard response for successful HTTP requests. + * The actual response will depend on the request method used. + * In a GET request, the response will contain an entity corresponding to the requested resource. + * In a POST request, the response will contain an entity describing or containing the result of the action. + */ + OK = 200, + + /** + * The request has been fulfilled, resulting in the creation of a new resource. + */ + CREATED = 201, + + /** + * The request has been accepted for processing, but the processing has not been completed. + * The request might or might not be eventually acted upon, and may be disallowed when processing occurs. + */ + ACCEPTED = 202, + + /** + * SINCE HTTP/1.1 + * The server is a transforming proxy that received a 200 OK from its origin, + * but is returning a modified version of the origin's response. + */ + NON_AUTHORITATIVE_INFORMATION = 203, + + /** + * The server successfully processed the request and is not returning any content. + */ + NO_CONTENT = 204, + + /** + * The server successfully processed the request, but is not returning any content. + * Unlike a 204 response, this response requires that the requester reset the document view. + */ + RESET_CONTENT = 205, + + /** + * The server is delivering only part of the resource (byte serving) due to a range header sent by the client. + * The range header is used by HTTP clients to enable resuming of interrupted downloads, + * or split a download into multiple simultaneous streams. + */ + PARTIAL_CONTENT = 206, + + /** + * The message body that follows is an XML message and can contain a number of separate response codes, + * depending on how many sub-requests were made. + */ + MULTI_STATUS = 207, + + /** + * The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response, + * and are not being included again. + */ + ALREADY_REPORTED = 208, + + /** + * The server has fulfilled a request for the resource, + * and the response is a representation of the result of one or more instance-manipulations applied to the current instance. + */ + IM_USED = 226, + + /** + * Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation). + * For example, this code could be used to present multiple video format options, + * to list files with different filename extensions, or to suggest word-sense disambiguation. + */ + MULTIPLE_CHOICES = 300, + + /** + * This and all future requests should be directed to the given URI. + */ + MOVED_PERMANENTLY = 301, + + /** + * This is an example of industry practice contradicting the standard. + * The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect + * (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 + * with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 + * to distinguish between the two behaviours. However, some Web applications and frameworks + * use the 302 status code as if it were the 303. + */ + FOUND = 302, + + /** + * SINCE HTTP/1.1 + * The response to the request can be found under another URI using a GET method. + * When received in response to a POST (or PUT/DELETE), the client should presume that + * the server has received the data and should issue a redirect with a separate GET message. + */ + SEE_OTHER = 303, + + /** + * Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match. + * In such case, there is no need to retransmit the resource since the client still has a previously-downloaded copy. + */ + NOT_MODIFIED = 304, + + /** + * SINCE HTTP/1.1 + * The requested resource is available only through a proxy, the address for which is provided in the response. + * Many HTTP clients (such as Mozilla and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons. + */ + USE_PROXY = 305, + + /** + * No longer used. Originally meant "Subsequent requests should use the specified proxy." + */ + SWITCH_PROXY = 306, + + /** + * SINCE HTTP/1.1 + * In this case, the request should be repeated with another URI; however, future requests should still use the original URI. + * In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. + * For example, a POST request should be repeated using another POST request. + */ + TEMPORARY_REDIRECT = 307, + + /** + * The request and all future requests should be repeated using another URI. + * 307 and 308 parallel the behaviors of 302 and 301, but do not allow the HTTP method to change. + * So, for example, submitting a form to a permanently redirected resource may continue smoothly. + */ + PERMANENT_REDIRECT = 308, + + /** + * The server cannot or will not process the request due to an apparent client error + * (e.g., malformed request syntax, too large size, invalid request message framing, or deceptive request routing). + */ + BAD_REQUEST = 400, + + /** + * Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet + * been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the + * requested resource. See Basic access authentication and Digest access authentication. 401 semantically means + * "unauthenticated",i.e. the user does not have the necessary credentials. + */ + UNAUTHORIZED = 401, + + /** + * Reserved for future use. The original intention was that this code might be used as part of some form of digital + * cash or micro payment scheme, but that has not happened, and this code is not usually used. + * Google Developers API uses this status if a particular developer has exceeded the daily limit on requests. + */ + PAYMENT_REQUIRED = 402, + + /** + * The request was valid, but the server is refusing action. + * The user might not have the necessary permissions for a resource. + */ + FORBIDDEN = 403, + + /** + * The requested resource could not be found but may be available in the future. + * Subsequent requests by the client are permissible. + */ + NOT_FOUND = 404, + + /** + * A request method is not supported for the requested resource; + * for example, a GET request on a form that requires data to be presented via POST, or a PUT request on a read-only resource. + */ + METHOD_NOT_ALLOWED = 405, + + /** + * The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request. + */ + NOT_ACCEPTABLE = 406, + + /** + * The client must first authenticate itself with the proxy. + */ + PROXY_AUTHENTICATION_REQUIRED = 407, + + /** + * The server timed out waiting for the request. + * According to HTTP specifications: + * "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time." + */ + REQUEST_TIMEOUT = 408, + + /** + * Indicates that the request could not be processed because of conflict in the request, + * such as an edit conflict between multiple simultaneous updates. + */ + CONFLICT = 409, + + /** + * Indicates that the resource requested is no longer available and will not be available again. + * This should be used when a resource has been intentionally removed and the resource should be purged. + * Upon receiving a 410 status code, the client should not request the resource in the future. + * Clients such as search engines should remove the resource from their indices. + * Most use cases do not require clients and search engines to purge the resource, and a "404 Not Found" may be used instead. + */ + GONE = 410, + + /** + * The request did not specify the length of its content, which is required by the requested resource. + */ + LENGTH_REQUIRED = 411, + + /** + * The server does not meet one of the preconditions that the requester put on the request. + */ + PRECONDITION_FAILED = 412, + + /** + * The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large". + */ + PAYLOAD_TOO_LARGE = 413, + + /** + * The URI provided was too long for the server to process. Often the result of too much data being encoded as a query-string of a GET request, + * in which case it should be converted to a POST request. + * Called "Request-URI Too Long" previously. + */ + URI_TOO_LONG = 414, + + /** + * The request entity has a media type which the server or resource does not support. + * For example, the client uploads an image as image/svg+xml, but the server requires that images use a different format. + */ + UNSUPPORTED_MEDIA_TYPE = 415, + + /** + * The client has asked for a portion of the file (byte serving), but the server cannot supply that portion. + * For example, if the client asked for a part of the file that lies beyond the end of the file. + * Called "Requested Range Not Satisfiable" previously. + */ + RANGE_NOT_SATISFIABLE = 416, + + /** + * The server cannot meet the requirements of the Expect request-header field. + */ + EXPECTATION_FAILED = 417, + + /** + * This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, + * and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by + * teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including Google.com. + */ + I_AM_A_TEAPOT = 418, + + /** + * The request was directed at a server that is not able to produce a response (for example because a connection reuse). + */ + MISDIRECTED_REQUEST = 421, + + /** + * The request was well-formed but was unable to be followed due to semantic errors. + */ + UNPROCESSABLE_ENTITY = 422, + + /** + * The resource that is being accessed is locked. + */ + LOCKED = 423, + + /** + * The request failed due to failure of a previous request (e.g., a PROPPATCH). + */ + FAILED_DEPENDENCY = 424, + + /** + * The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field. + */ + UPGRADE_REQUIRED = 426, + + /** + * The origin server requires the request to be conditional. + * Intended to prevent "the 'lost update' problem, where a client + * GETs a resource's state, modifies it, and PUTs it back to the server, + * when meanwhile a third party has modified the state on the server, leading to a conflict." + */ + PRECONDITION_REQUIRED = 428, + + /** + * The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes. + */ + TOO_MANY_REQUESTS = 429, + + /** + * The server is unwilling to process the request because either an individual header field, + * or all the header fields collectively, are too large. + */ + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + + /** + * A server operator has received a legal demand to deny access to a resource or to a set of resources + * that includes the requested resource. The code 451 was chosen as a reference to the novel Fahrenheit 451. + */ + UNAVAILABLE_FOR_LEGAL_REASONS = 451, + + /** + * A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. + */ + INTERNAL_SERVER_ERROR = 500, + + /** + * The server either does not recognize the request method, or it lacks the ability to fulfill the request. + * Usually this implies future availability (e.g., a new feature of a web-service API). + */ + NOT_IMPLEMENTED = 501, + + /** + * The server was acting as a gateway or proxy and received an invalid response from the upstream server. + */ + BAD_GATEWAY = 502, + + /** + * The server is currently unavailable (because it is overloaded or down for maintenance). + * Generally, this is a temporary state. + */ + SERVICE_UNAVAILABLE = 503, + + /** + * The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. + */ + GATEWAY_TIMEOUT = 504, + + /** + * The server does not support the HTTP protocol version used in the request + */ + HTTP_VERSION_NOT_SUPPORTED = 505, + + /** + * Transparent content negotiation for the request results in a circular reference. + */ + VARIANT_ALSO_NEGOTIATES = 506, + + /** + * The server is unable to store the representation needed to complete the request. + */ + INSUFFICIENT_STORAGE = 507, + + /** + * The server detected an infinite loop while processing the request. + */ + LOOP_DETECTED = 508, + + /** + * Further extensions to the request are required for the server to fulfill it. + */ + NOT_EXTENDED = 510, + + /** + * The client needs to authenticate to gain network access. + * Intended for use by intercepting proxies used to control access to the network (e.g., "captive portals" used + * to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot). + */ + NETWORK_AUTHENTICATION_REQUIRED = 511 +} + +export default HttpStatusCode; diff --git a/src/app/publishapi/class/openapi-dto.ts b/src/app/publishapi/class/openapi-dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..69ed5c5e379aa176e12ff88ca57a2d81b4f39bcb --- /dev/null +++ b/src/app/publishapi/class/openapi-dto.ts @@ -0,0 +1,61 @@ +import HttpStatusCode, { HttpMethod } from './http-enum' +import { ParameterType, ShemaType } from './openapi-enum'; + + +export interface ServerDTO { + url:string; +} + +export interface SchemaDTO { + type: ShemaType, + default: string +} + +export interface ParameterDTO { + in: ParameterType, + name: string, + schema: SchemaDTO, + required: boolean +} + +export interface ItemDTO { + type: string +} +export interface ResponseSchemaDTO { + type: string, + items: ItemDTO[] +} + +export interface ContentDTO { + schema: ResponseSchemaDTO +} + +export type MimeTypeToContentMapDTO = Map<string,ContentDTO>; + +export interface ResponseDTO { + description: string, + content: MimeTypeToContentMapDTO +} + +export type HttpCodeToResponseMapDTO = Map<HttpStatusCode,ResponseDTO>; + +export interface RequestDTO { + description: string, + tags: string[] + parameters: ParameterDTO[], + responses : HttpCodeToResponseMapDTO, +} + +export type MethodToRequestMapDTO = Map<HttpMethod,RequestDTO>; + +export type PathMapDTO = Map<string,MethodToRequestMapDTO>; + +export type InfoMapDTO = Map<string,string>; + +export interface OpenApiDTO { + openapi: string; + info: InfoMapDTO; + servers: ServerDTO[]; + paths: PathMapDTO; +} + diff --git a/src/app/publishapi/class/openapi-enum.ts b/src/app/publishapi/class/openapi-enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..54cbf3f144c63e2189e123bbec77d7e3ce0918cd --- /dev/null +++ b/src/app/publishapi/class/openapi-enum.ts @@ -0,0 +1,13 @@ +export enum ShemaType { + string = 'string', + integer = 'integer', + boolean = 'boolean' +} + +export enum ParameterType { + query = 'query', + path = 'path', + header = 'header', + cookie = 'cookie', + body = 'body' +} diff --git a/src/app/publishapi/class/openapi.ts b/src/app/publishapi/class/openapi.ts new file mode 100644 index 0000000000000000000000000000000000000000..2baff963205ea4dd5f42b6a622ff17f78d34f290 --- /dev/null +++ b/src/app/publishapi/class/openapi.ts @@ -0,0 +1,62 @@ +import HttpStatusCode, { HttpMethod } from './http-enum' +import { ParameterType } from './openapi-enum'; + +export interface Server { + url:string; +} + +export interface Schema { + type: string, + default: string +} + +export interface Parameter { + in: ParameterType, + name: string, + schema: Schema, + required: boolean +} + +export interface Item { + type: string +} + +export interface ResponseSchema { + type: string, + items: Item[] +} + +export interface Content { + contentType: string, + schema: ResponseSchema +} + +export interface Response { + httpStatusCode: HttpStatusCode, + description: string, + contents: Content[] +} + +export interface Request { + description: string, + tags: string[] + parameters: Parameter[], + responses: Response[], + httpmethod: HttpMethod, + toggled: boolean +} + +export interface Path { + pathName: string, + requests: Request[] +} + +export type InfoMap = Map<string,string>; + +export interface OpenApi { + openapi: string; + info: InfoMap; + servers: Server[]; + paths: Path[]; +} + diff --git a/src/app/publishapi/publishapi.component.html b/src/app/publishapi/publishapi.component.html index 1dcac66588012d515f310b067d5df891454602f9..952801a18e58b54cebf9c552dec763adfccce337 100644 --- a/src/app/publishapi/publishapi.component.html +++ b/src/app/publishapi/publishapi.component.html @@ -5,19 +5,234 @@ <nb-card> <nb-card-body> - <nb-stepper orientation="horizontal" disableStepNavigation> + <nb-stepper orientation="horizontal" disableStepNavigation> <nb-step [label]="labelOne"> <ng-template #labelOne>First step</ng-template> - <h4>Describe API </h4> - <p class="lorem"> - Describe your repository api to access datasets - </p> - <label>Choose your Repository - <select class="form-control" formControlName="repotype" required> - <option value="1">Inrae</option>> - <option value="2">Inrae-2</option>> - </select> - </label> + <h4>Describe API</h4> + <nb-card> + <nb-card-body> + <nb-tabset> + <nb-tab class="repository" tabTitle="Repository" tabIcon="hard-drive-outline"> + <label>Choose your Repository</label> + <nb-select class="form-control" required> + <nb-option value="1">Inrae</nb-option> + <nb-option value="2">Inrae-2</nb-option> + </nb-select> + </nb-tab> + <nb-tab class="header" tabTitle="Headers" tabIcon="file-text-outline"> + <div class="field"> + <label class="" for="title">Title</label> + <div> + <input name="title" [(ngModel)]="openApi.info.title" fullWidth nbInput/> + </div> + </div> + <div class="field"> + <label for="description">Description</label> + <div> + <textarea name="description " [(ngModel)]="openApi.info.description" placeholder="Textarea" fullWidth nbInput></textarea> + </div> + </div> + <div class="field"> + <label for="version">version</label> + <div> + <input name="version" [(ngModel)]="openApi.info.version" fullWidth nbInput/> + </div> + </div> + <div class="field"> + <label for="x-format">x-format</label> + <div> + <input name="x-format" [(ngModel)]="openApi.info['x-format']" fullWidth nbInput/> + </div> + </div> + <div class="field"> + <label for="x-result">x-result</label> + <div> + <input name="x-result" [(ngModel)]="openApi.info['x-result']" fullWidth nbInput/> + </div> + </div> + <div class="field"> + <label for="x-start-param">x-start-param</label> + <div> + <input name="x-start-param" [(ngModel)]="openApi.info['x-start-param']" fullWidth nbInput/> + </div> + </div> + <div class="field"> + <label for="x-page-param">x-page-param</label> + <div> + <input name="x-page-param" [(ngModel)]="openApi.info['x-page-param']" fullWidth nbInput/> + </div> + </div> + </nb-tab> + <nb-tab class="main" tabTitle="Main" tabIcon="edit-2-outline"> + <div *ngFor="let path of openApi.paths"> + <nb-card> + <nb-card-body> + <!-- <nb-icon icon="plus-outline" nbTooltip="Add path" (click)="addPath()"></nb-icon> + <nb-icon icon="copy-outline" nbTooltip="Duplicate path"(click)="duplicatePath()"></nb-icon> + <nb-icon icon="edit-2-outline" nbTooltip="Edit path description"(click)="editDescriptionPath()"></nb-icon> + <ng-container *ngIf="openApi.paths.length>1"> + <nb-icon icon="trash-2-outline" nbTooltip="Delete path" (click)="deletePath(path)"></nb-icon> + </ng-container> --> + <h5 *ngIf="path.requests[0].tags[0]=='datasetlist'">Get list of datasets</h5> + <h5 *ngIf="path.requests[0].tags[0]=='dataset'">Get dataset</h5> + <label for="Path">Path</label> + <input name="path" [(ngModel)]="path.pathName" nbInput/> + <div *ngFor="let request of path.requests"> + <nb-card> + <nb-card-body> + <div class="request"> + <label>Request</label><br/> + <!-- <nb-icon icon="menu-outline" nbTooltip="Toggle Request" (click)="togglRequest(request)"></nb-icon> + <nb-icon icon="plus-outline" nbTooltip="Add Request" (click)="addRequest(path)"></nb-icon> + <nb-icon icon="copy-outline" nbTooltip="Duplicate Request" (click)="DuplicateRequest(path)"></nb-icon> + <ng-container *ngIf="path.requests.length>1"> + <nb-icon icon="trash-2-outline" nbTooltip="Delete Request" (click)="deleteRequest(path, request)"></nb-icon> + </ng-container> --> + <span class="title">{{request.httpmethod}}</span> + <span class="title">{{request.description}}</span> + <div class="requestbody"> + <nb-tabset> + <nb-tab tabTitle="Main" tabIcon="settings-2-outline"> + <nb-card> + <nb-card-body> + <table> + <thead> + <tr> + <th>Method</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <nb-select [(ngModel)]="request.httpmethod"> + <nb-option value="get">GET</nb-option> + <nb-option value="post">POST</nb-option> + <nb-option value="put">PUT</nb-option> + <nb-option value="patch">PATCH</nb-option> + <nb-option value="delete">DELETE</nb-option> + <nb-option value="head">HEAD</nb-option> + <nb-option value="options">OPTIONS</nb-option> + </nb-select> + </td> + <td> + <input name="description" [(ngModel)]="request.description" nbInput/> + </td> + </tr> + </tbody> + </table> + </nb-card-body> + </nb-card> + </nb-tab> + <nb-tab tabTitle="Parameters" tabIcon="list-outline"> + <nb-card> + <nb-card-body> + <label>Parameters</label> + <table> + <thead> + <tr> + <th>Name</th> + <th>Location</th> + <th>Type</th> + <th>Default value</th> + <th></th> + </tr> + </thead> + <tbody> + <tr *ngFor="let parameter of request.parameters"> + <td> + <input [(ngModel)]="parameter.name" nbInput/> + </td> + <td> + <nb-select [(ngModel)]="parameter.in"> + <nb-option *ngFor="let type of parametersTypes" value="{{type}}">{{type}}</nb-option> + </nb-select> + </td> + <td> + <nb-select [(ngModel)]="parameter.schema.type"> + <nb-option *ngFor="let type of shemaTypes" value="{{type}}">{{type}}</nb-option> + </nb-select> + </td> + <td> + <input name="default" [(ngModel)]="parameter.schema.default" nbInput/> + </td> + <td> + <nb-icon icon="plus-outline" nbTooltip="Add parameter" (click)="addParameter(request)"></nb-icon> + <nb-icon icon="copy-outline" nbTooltip="Duplicate parameter" (click)="duplicateParameter(request, parameter)"></nb-icon> + <ng-container *ngIf="request.parameters.length>1"> + <nb-icon icon="trash-2-outline" nbTooltip="Delete parameter" (click)="deleteParameter(request, parameter)"></nb-icon> + </ng-container> + </td> + </tr> + </tbody> + </table> + </nb-card-body> + </nb-card> + </nb-tab> + <nb-tab tabTitle="Response" tabIcon="checkmark-circle-2-outline"> + <div *ngFor="let response of request.responses"> + <nb-card> + <nb-card-body> + <label>Reponse</label><br/> + <!-- <nb-icon icon="plus-outline" nbTooltip="Add Response" (click)="addResponse(request)"></nb-icon> + <nb-icon icon="copy-outline" nbTooltip="Duplicate Response" (click)="duplicateResponse(request)"></nb-icon> + <ng-container *ngIf="request.responses.length>1"> + <nb-icon icon="trash-2-outline" nbTooltip="Delete Response" (click)="deleteResponse(request, response)"></nb-icon> + </ng-container> --> + <table> + <thead> + <tr> + <th>Http Status Code</th> + <th>Media-Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <nb-select> + <nb-option *ngFor="let code of httpStatusCodes" value="{{code}}">{{code}}</nb-option> + </nb-select> + </td> + <td> + <div *ngFor="let content of response.contents"> + <input name="default" [(ngModel)]="content.contentType" nbInput/> + </div> + </td> + <td> + <input name="default" [(ngModel)]="response.description" nbInput/> + </td> + </tr> + </tbody> + </table> + </nb-card-body> + </nb-card> + </div> + </nb-tab> + </nb-tabset> + </div> + </div> + </nb-card-body> + </nb-card> + </div> + </nb-card-body> + </nb-card> + </div> + </nb-tab> + <nb-tab class="json" tabTitle="JSON" tabIcon="file-text-outline"> + <nb-card> + <nb-card-body> + <button nbButton type="button" (click)="selectAllJson()"><nb-icon icon="copy-outline"></nb-icon>Select all</button> + <button nbButton type="button" (click)="downloadJson()"><nb-icon icon="download-outline"></nb-icon>Download</button> + <button nbButton type="button" (click)="openFileInputDialog()"><nb-icon icon="upload-outline"></nb-icon>Import</button> + <input id="jsonfileinput" type="file" (change)="importJson($event)" /> + <pre id="json">{{getPrettyJson()}}</pre> + </nb-card-body> + </nb-card> + </nb-tab> + </nb-tabset> + </nb-card-body> + </nb-card> <button class="prev-button" nbButton disabled nbStepperNext>prev</button> <button class="next-button" nbButton nbStepperNext>next</button> </nb-step> @@ -34,7 +249,7 @@ <ng-template #labelThree>Third step</ng-template> <h4>Map DCAT Schema </h4> <p class="lorem"> - Map your metadata schema with DCAT schema + Map your metadata schema with DCAT schema </p> <button class="prev-button" nbButton nbStepperPrevious>prev</button> <button class="next-button" nbButton nbStepperNext>next</button> @@ -50,4 +265,4 @@ </nb-step> </nb-stepper> </nb-card-body> - </nb-card> \ No newline at end of file + </nb-card> diff --git a/src/app/publishapi/publishapi.component.scss b/src/app/publishapi/publishapi.component.scss index b4051cfb62afc7cfb62edb1afaceae2cd9c4698c..c4758be30d2e47fa0406ac67c2a6ac22b2635bf8 100644 --- a/src/app/publishapi/publishapi.component.scss +++ b/src/app/publishapi/publishapi.component.scss @@ -1,4 +1,86 @@ .prev-button { margin-right: 1rem; - } \ No newline at end of file +} + + +/** +HEADER TAB +*/ +.header label { + display: inline-block; + text-align: right; + width: 10rem; + margin-right: 1.5rem; + vertical-align: top; +} + +.header input { + max-width: 100%; + width: 100%; +} + +.header textarea { + max-height: 600px; + min-height: 120px; +} + +.header div.field { + margin-bottom: .75rem; +} + +.header div.field div { + display: inline-block; + position: relative; + width: 100%; + max-width: 40rem; + min-width: 10rem; +} + +/** +MAIN TAB +*/ + +.main label { + display: inline-block; + margin-right: 1.5rem; +} + +.main nb-icon { + margin: 0.32em; + background-color: transparent; +} + +.main nb-icon:hover { + color: #598bff !important; + cursor: pointer; +} + +.main nb-card { + background: rgba(0, 0, 0, 0.030); +} + +.main div.request span.title { + margin-right: 0.32em; +} + +/** +JSON TAB +*/ + +#jsonfileinput { + display: none; +} + +.json button{ + margin: 0.32em; +} + +/** +SWAGGER TAB +*/ + +#swaggereditor{ + height: 600px; + position: relative; +} \ No newline at end of file diff --git a/src/app/publishapi/publishapi.component.ts b/src/app/publishapi/publishapi.component.ts index f3ed412022d7c2f0a6d2da45fe297c661d7aa69f..5520c04bb0386e7a7d13c87fef3cd95fa9e66974 100644 --- a/src/app/publishapi/publishapi.component.ts +++ b/src/app/publishapi/publishapi.component.ts @@ -1,22 +1,139 @@ import { ComponentFactoryResolver, OnDestroy, ViewChild } from '@angular/core'; -import { Component, Input, OnInit, TemplateRef } from '@angular/core'; +import { Component, Input, OnInit, TemplateRef } from '@angular/core'; import { Directive, ViewContainerRef } from '@angular/core'; - - - - +import { FormControl } from '@angular/forms'; +import { cloneDeep } from 'lodash'; +import HttpStatusCode from './class/http-enum'; +import { OpenApi, Parameter, Path, Request, Response } from './class/openapi'; +import { OpenApiDTO } from './class/openapi-dto'; +import { ParameterType, ShemaType } from './class/openapi-enum'; +import { OpenApiService } from './services/openapi-service'; @Component({ selector: 'app-publishapi', templateUrl: './publishapi.component.html', styleUrls: ['./publishapi.component.scss'] }) + export class PublishApiComponent implements OnInit { - constructor() { } + constructor(private openApiService:OpenApiService) {} + + openApi: OpenApi; + parametersTypes: string[]; + shemaTypes: string[]; + httpStatusCodes: string[]; ngOnInit(): void { - + this.openApi = this.openApiService.getFromJson(); + this.parametersTypes = Object.keys(ParameterType); + this.shemaTypes = Object.keys(ShemaType); + const httpStatusCodeskeys = Object.keys(HttpStatusCode).filter(k => typeof HttpStatusCode[k as any] === "number"); + this.httpStatusCodes = httpStatusCodeskeys.map(k => HttpStatusCode[k as any]); + } + + getPrettyJson(): string { + return this.openApiService.getPrettyJson(this.openApi); + } + + selectAllJson(): void { + var range = document.createRange(); + range.selectNodeContents(document.getElementById('json')); + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } + + downloadJson(): void { + const newBlob = new Blob([this.openApiService.getPrettyJson(this.openApi)], { type: 'application/json' }); + if (window.navigator && window.navigator.msSaveOrOpenBlob) { + window.navigator.msSaveOrOpenBlob(newBlob); + return; + } + const data = window.URL.createObjectURL(newBlob); + const link = document.createElement('a'); + link.href = data; + link.download = 'openAPI.json'; + // this is necessary as link.click() does not work on the latest firefox + link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); + setTimeout( () => { + // For Firefox it is necessary to delay revoking the ObjectURL + window.URL.revokeObjectURL(data); + link.remove(); + }, 100); + } + + addPath(): void { + let emptyPath: Path = this.openApiService.getEmptyPath(); + this.openApi.paths.unshift(emptyPath); + } + + deletePath(path: Path): void { + const index: number = this.openApi.paths.indexOf(path); + if (index !== -1) { + this.openApi.paths.splice(index, 1); + } + } + + addRequest(path: Path): void { + let emptyRequest: Request = this.openApiService.getEmptyRequest(); + path.requests.unshift(emptyRequest); + } + + deleteRequest(path: Path, request: Request): void { + const indexRequest: number = path.requests.indexOf(request); + if (indexRequest !== -1) { + path.requests.splice(indexRequest, 1); + } + } + + togglRequest(request: Request): void { + request.toggled = !request.toggled; + } + + addParameter(request: Request): void { + let emptyParameter: Parameter = this.openApiService.getEmptyParameter(); + request.parameters.unshift(emptyParameter); + } + + duplicateParameter(request: Request, parameter: Parameter): void { + request.parameters.unshift(cloneDeep(parameter)); + } + + deleteParameter(request: Request, parameter: Parameter): void { + const indexParameter: number = request.parameters.indexOf(parameter); + if (indexParameter !== -1) { + request.parameters.splice(indexParameter, 1); + } + } + + addResponse(request: Request): void { + let emptyResponse: Response = this.openApiService.getEmptyResponse(); + request.responses.unshift(emptyResponse); + } + + deleteResponse(request: Request, response: Response): void { + const indexResponse: number = request.responses.indexOf(response); + if (indexResponse !== -1) { + request.responses.splice(indexResponse, 1); + } + } + + openFileInputDialog(): void { + document.getElementById('jsonfileinput').click(); + } + + importJson(event:Event): void { + const files: FileList = (event.target as HTMLInputElement).files; + const jsonFile: File = files[0]; + var reader = new FileReader(); + + reader.onload = ()=> { + console.log(reader.result as string); + this.openApi = this.openApiService.getFromString(reader.result as string); + }; + + reader.readAsText(jsonFile); } goToLink() { diff --git a/src/app/publishapi/services/openapi-dto-mapping-service.ts b/src/app/publishapi/services/openapi-dto-mapping-service.ts new file mode 100644 index 0000000000000000000000000000000000000000..35ca0498a6bfe5f7d74296b914419eae6766ae1c --- /dev/null +++ b/src/app/publishapi/services/openapi-dto-mapping-service.ts @@ -0,0 +1,117 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import HttpStatusCode, { HttpMethod } from '../class/http-enum'; +import { InfoMap, OpenApi, Path, Server, Request, Parameter, Response, Content, ResponseSchema } from '../class/openapi'; +import { ContentDTO, HttpCodeToResponseMapDTO, InfoMapDTO, MethodToRequestMapDTO, MimeTypeToContentMapDTO, OpenApiDTO, ParameterDTO, PathMapDTO, RequestDTO, ResponseDTO, ResponseSchemaDTO, ServerDTO } from '../class/openapi-dto'; + +@Injectable({ + providedIn: 'root' + }) + +export class OpenApiDTOMappingService { + constructor() {} + + mapContentFromDTO(mimeTypeToContentMapDTO: MimeTypeToContentMapDTO, contentType: string){ + let contentDTO: ContentDTO = mimeTypeToContentMapDTO[contentType]; + let content: Content = {} as Content; + content.contentType = contentType; + content.schema = contentDTO.schema as ResponseSchema; + return content; + } + + mapResponseFromDTO(httpCodeToResponseMapDTO: HttpCodeToResponseMapDTO, httpStatusCodeToreponseDTOMap: string): Response { + let responseDTO: ResponseDTO = httpCodeToResponseMapDTO[httpStatusCodeToreponseDTOMap]; + let response: Response = {} as Response; + response.description = responseDTO.description; + response.httpStatusCode = httpStatusCodeToreponseDTOMap as unknown as HttpStatusCode; + response.contents = [] as Content[]; + Object.keys(responseDTO.content).forEach((contentType)=>{ + let content: Content = this.mapContentFromDTO(responseDTO.content, contentType); + response.contents.push(content); + }); + return response; + } + + mapParametersFromDTO(parameterDTO: ParameterDTO[]): Parameter[] { + return parameterDTO as Parameter[]; + } + + mapRequestFromDTO(methodToRequestMapDTO: MethodToRequestMapDTO, httpMethod: HttpMethod): Request{ + let requestDTO: RequestDTO = methodToRequestMapDTO[httpMethod]; + let request: Request = {} as Request; + request.httpmethod = httpMethod; + request.description = requestDTO.description; + request.tags = requestDTO.tags as string[]; + request.parameters = this.mapParametersFromDTO(requestDTO.parameters); + request.responses = [] as Response[]; + request.toggled = false; + Object.keys(requestDTO.responses).forEach((HttpStatusCode)=>{ + let response: Response = this.mapResponseFromDTO(requestDTO.responses, HttpStatusCode); + request.responses.push(response); + }); + return request; + } + + mapPathFromDTO(pathMapDTO: PathMapDTO, pathName: string): Path{ + let methodToRequestMapDTO: MethodToRequestMapDTO = pathMapDTO[pathName]; + let path:Path = {} as Path; + path.pathName = pathName.trim(); + path.requests = [] as Request[]; + Object.keys(methodToRequestMapDTO).forEach((httpMethod:HttpMethod)=>{ + let request: Request = this.mapRequestFromDTO(methodToRequestMapDTO, httpMethod); + path.requests.push(request); + }); + return path; + } + + mapOpenApiFromDTO(openApiDTO:OpenApiDTO): OpenApi { + let openApi:OpenApi = {} as OpenApi; + openApi.info = openApiDTO.info as InfoMap; + openApi.openapi = openApiDTO.openapi; + openApi.servers = openApiDTO.servers as Server[]; + openApi.paths = [] as Path[]; + Object.keys(openApiDTO.paths).forEach((pathName)=>{ + let path: Path = this.mapPathFromDTO(openApiDTO.paths, pathName); + openApi.paths.push(path); + }); + return openApi; + } + + /** + * @param openApi + */ + mapToDto(openApi:OpenApi): OpenApiDTO { + let openApiDTO:OpenApiDTO = {} as OpenApiDTO; + openApiDTO.openapi = openApi.openapi; + openApiDTO.info = openApi.info as InfoMapDTO; + openApiDTO.servers = openApi.servers as ServerDTO[]; + openApiDTO.paths = new Map<string,MethodToRequestMapDTO>(); + openApi.paths.forEach(path=>{ + let methodToRequestMapDTO: MethodToRequestMapDTO = new Map<HttpMethod, RequestDTO>(); + path.requests.forEach(request=>{ + let requestDTO: RequestDTO = {} as RequestDTO; + requestDTO.tags = request.tags; + requestDTO.description = request.description; + requestDTO.parameters = request.parameters as ParameterDTO[]; + requestDTO.responses = {} as HttpCodeToResponseMapDTO; + request.responses.forEach(response=>{ + let httpCodeToResponseMapDTO: HttpCodeToResponseMapDTO = new Map<HttpStatusCode, ResponseDTO>(); + let responseDTO: ResponseDTO = {} as ResponseDTO; + responseDTO.content = new Map<string, ContentDTO>(); + responseDTO.description = response.description; + response.contents.forEach(content=>{ + let contentDTO: ContentDTO = {} as ContentDTO; + contentDTO.schema = content.schema as ResponseSchemaDTO; + responseDTO.content.set(content.contentType, contentDTO); + }); + httpCodeToResponseMapDTO.set(response.httpStatusCode,responseDTO); + requestDTO.responses=httpCodeToResponseMapDTO; + }); + methodToRequestMapDTO.set(request.httpmethod, requestDTO); + }); + openApiDTO.paths.set(path.pathName, methodToRequestMapDTO); + }); + return openApiDTO; + } + +} diff --git a/src/app/publishapi/services/openapi-service.ts b/src/app/publishapi/services/openapi-service.ts new file mode 100644 index 0000000000000000000000000000000000000000..140a1bf96ee09d3078f37ec394b2291d68a9c7f3 --- /dev/null +++ b/src/app/publishapi/services/openapi-service.ts @@ -0,0 +1,378 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import HttpStatusCode, { HttpMethod } from '../class/http-enum'; +import { OpenApi, Parameter, Path, Request, Response } from '../class/openapi'; +import { HttpCodeToResponseMapDTO, MethodToRequestMapDTO, OpenApiDTO, ParameterDTO, PathMapDTO } from '../class/openapi-dto'; +import { OpenApiDTOMappingService } from './openapi-dto-mapping-service'; + +@Injectable({ + providedIn: 'root' + }) + +export class OpenApiService { + constructor(private http: HttpClient, private openApiDTOMappingService: OpenApiDTOMappingService) {} + + getFromRepository(repositoryId:string): Observable<OpenApiDTO> { + return this.http.get<OpenApiDTO>('/openapi/' + repositoryId); + } + + /** + * Replacer for JSON.stringify + * + * Allows to serialize Map + * @param key + * @param value + */ + private replacer(key, value) { + const originalObject = this[key]; + if(originalObject instanceof Map) { + return [...originalObject].reduce((o, [key, value]) => (o[key] = value, o), {}); + } else { + return value; + } + } + + getPrettyJson(openApi: OpenApi): string { + let openApiDTO: OpenApiDTO = this.openApiDTOMappingService.mapToDto(openApi); + return JSON.stringify(openApiDTO, this.replacer, 2); + } + + getFromJson(): OpenApi { + let openApiDTO:OpenApiDTO = <OpenApiDTO>JSON.parse(this.jsonFull); + return this.openApiDTOMappingService.mapOpenApiFromDTO(openApiDTO); + } + + getFromString(json: string): OpenApi { + let openApiDTO:OpenApiDTO = <OpenApiDTO>JSON.parse(json); + return this.openApiDTOMappingService.mapOpenApiFromDTO(openApiDTO); + } + + getEmptyPath(): Path { + let pathDTO: PathMapDTO = <PathMapDTO>JSON.parse(this.emptyPathJson); + let paths = [] as Path[]; + Object.keys(pathDTO).forEach((pathName)=>{ + let path: Path = this.openApiDTOMappingService.mapPathFromDTO(pathDTO, pathName); + paths.push(path); + }); + return paths[0]; + } + + getEmptyRequest(): Request { + let methodToRequestMapDTO: MethodToRequestMapDTO = <MethodToRequestMapDTO>JSON.parse('{' + this.emptyMethodJson + '}'); + let requests = [] as Request[]; + Object.keys(methodToRequestMapDTO).forEach((httpMethod:HttpMethod)=>{ + let request: Request = this.openApiDTOMappingService.mapRequestFromDTO(methodToRequestMapDTO, httpMethod); + requests.push(request); + }); + return requests[0]; + } + + getEmptyParameter(): Parameter { + let parameterDTO: ParameterDTO[] = <ParameterDTO[]>JSON.parse(this.emptyParameter); + return this.openApiDTOMappingService.mapParametersFromDTO(parameterDTO)[0]; + } + + getEmptyResponse(): Response { + let httpCodeToResponseMapDTO : HttpCodeToResponseMapDTO = <HttpCodeToResponseMapDTO>JSON.parse('{' + this.emptyResponse + '}'); + let responses = [] as Response[]; + Object.keys(httpCodeToResponseMapDTO).forEach((httpStatusCode:string)=>{ + let response: Response = this.openApiDTOMappingService.mapResponseFromDTO(httpCodeToResponseMapDTO, httpStatusCode); + responses.push(response); + }); + return responses[0]; + } + + private jsonFull = + '{' + + ' "openapi" : "3.0.0",' + + ' "info" : {' + + ' "title" : "inrae list datasets",' + + ' "version" : "0",' + + ' "x-format" : "dataverse",' + + ' "x-catalog-id" : "09de0761-f1b5-4dc1-8c5c-92e5e782cb2c",' + + ' "x-result" : "json",' + + ' "x-start-param" : "start",' + + ' "x-page-param" : "per_page"' + + ' },' + + ' "servers" : [ ' + + ' {' + + ' "url" : "https://data.inrae.fr/api"' + + ' }' + + ' ],' + + ' "paths" : {' + + ' "/search" : {' + + ' "get" : {' + + ' "description" : "Returns a list of datasets",' + + ' "tags" : [ ' + + ' "datasetlist"' + + ' ],' + + ' "parameters" : [ ' + + ' {' + + ' "in" : "query",' + + ' "name" : "q",' + + ' "schema" : {' + + ' "type" : "string",' + + ' "default" : "*"' + + ' },' + + ' "required" : true,' + + ' "description" : "for all star"' + + ' }, ' + + ' {' + + ' "in" : "query",' + + ' "name" : "per_page",' + + ' "schema" : {' + + ' "type" : "integer",' + + ' "default" : 10' + + ' },' + + ' "required" : true,' + + ' "description" : "pages"' + + ' }, ' + + ' {' + + ' "in" : "query",' + + ' "name" : "type",' + + ' "schema" : {' + + ' "type" : "string",' + + ' "default" : "dataset"' + + ' },' + + ' "required" : true,' + + ' "description" : "dataset"' + + ' }, ' + + ' {' + + ' "in" : "query",' + + ' "name" : "start",' + + ' "schema" : {' + + ' "type" : "integer",' + + ' "default" : 0' + + ' },' + + ' "required" : true,' + + ' "description" : "start"' + + ' }, ' + + ' {' + + ' "in" : "query",' + + ' "name" : "show_entity_ids",' + + ' "schema" : {' + + ' "type" : "boolean",' + + ' "default" : true' + + ' },' + + ' "required" : true,' + + ' "description" : "id"' + + ' }, ' + + ' {' + + ' "in" : "query",' + + ' "name" : "show_my_data",' + + ' "schema" : {' + + ' "type" : "boolean",' + + ' "default" : true' + + ' },' + + ' "required" : true,' + + ' "description" : "aaa"' + + ' }' + + ' ],' + + ' "responses" : {' + + ' "200" : {' + + ' "description" : "A JSON array of datasets",' + + ' "content" : {' + + ' "application/json" : {' + + ' "schema" : {' + + ' "type" : "array",' + + ' "items" : {' + + ' "type" : "string"' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' },' + + ' "/datasets/{entity_id}" : {' + + ' "get" : {' + + ' "description" : "Get the details on the dataset that the identifier is in parameter ",' + + ' "tags" : [ ' + + ' "dataset"' + + ' ],' + + ' "parameters" : [ ' + + ' {' + + ' "in" : "path",' + + ' "name" : "entity_id",' + + ' "schema" : {' + + ' "type" : "string",' + + ' "default" : "95707"' + + ' },' + + ' "required" : true,' + + ' "description" : "he Digital Object Identifier (DOI) of the dataset searched."' + + ' }' + + ' ],' + + ' "responses" : {' + + ' "200" : {' + + ' "description" : "A JSON array of one dataset",' + + ' "content" : {' + + ' "application/json" : {' + + ' "schema" : {' + + ' "type" : "array",' + + ' "items" : {' + + ' "type" : "string"' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + '}'; + + private jsonEmpty = + '{' + + ' "openapi" : "3.0.0",' + + ' "info" : {' + + ' "title" : "",' + + ' "version" : "",' + + ' "x-format" : "",' + + ' "x-catalog-id" : "",' + + ' "x-result" : "",' + + ' "x-start-param" : "",' + + ' "x-page-param" : ""' + + ' },' + + ' "servers" : [ ' + + ' {' + + ' "url" : ""' + + ' }' + + ' ],' + + ' "paths" : {' + + ' " " : {' + + ' "get" : {' + + ' "description" : "",' + + ' "tags" : [ ' + + ' "datasetlist"' + + ' ],' + + ' "parameters" : [ ' + + ' {' + + ' "in" : "query",' + + ' "name" : "",' + + ' "schema" : {' + + ' "type" : "",' + + ' "default" : ""' + + ' },' + + ' "description" : ""' + + ' }' + + ' ],' + + ' "responses" : {' + + ' "200" : {' + + ' "description" : "",' + + ' "content" : {' + + ' " " : {' + + ' "schema" : {' + + ' "type" : "array",' + + ' "items" : {' + + ' "type" : "string"' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' },' + + ' " " : {' + + ' "get" : {' + + ' "description" : "",' + + ' "tags" : [ ' + + ' "dataset"' + + ' ],' + + ' "parameters" : [ ' + + ' {' + + ' "in" : "query",' + + ' "name" : "",' + + ' "schema" : {' + + ' "type" : "",' + + ' "default" : ""' + + ' },' + + ' "description" : ""' + + ' }' + + ' ],' + + ' "responses" : {' + + ' "200" : {' + + ' "description" : "",' + + ' "content" : {' + + ' " " : {' + + ' "schema" : {' + + ' "type" : "array",' + + ' "items" : {' + + ' "type" : "string"' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + '}'; + + private emptyResponse = + '"200" : {' + + ' "description" : "",' + + ' "content" : {' + + ' " " : {' + + ' "schema" : {' + + ' "type" : "array",' + + ' "items" : {' + + ' "type" : "string"' + + ' }' + + ' }' + + ' }' + + ' }' + + '}'; + + private emptyParameter = + '[' + + ' {' + + ' "name" : "",' + + ' "in" : "query",' + + ' "schema" : {' + + ' "type" : "string",' + + ' "default" : ""' + + ' }' + + ' "required" : true,' + + ' "description" : ""' + + ' }' + + ']'; + + + private emptyMethodJson = + ' "get" : {' + + ' "description" : "",' + + ' "tags" : [ ' + + ' "datasetlist"' + + ' ],' + + ' "parameters" : ' + + this.emptyParameter + + ' ,' + + ' "responses" : {' + + ' "200" : {' + + ' "description" : "",' + + ' "content" : {' + + ' "" : {' + + ' "schema" : {' + + ' "type" : "array",' + + ' "items" : {' + + ' "type" : "string"' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }' + + ' }'; + + private emptyPathJson = '{' + + ' " " : {' + + this.emptyMethodJson + + ' }' + + '}'; + +} diff --git a/src/app/repository/repository.component.html b/src/app/repository/repository.component.html index 787fd5ea8e52c6100633385912cf0971f5db5a7a..a5a3abd635b580ced05d4fe83dc7ac895b1011ec 100644 --- a/src/app/repository/repository.component.html +++ b/src/app/repository/repository.component.html @@ -7,22 +7,19 @@ <div> - <label>Repository Type - <select class="form-control" formControlName="repotype" required> - <option value="Dataverse">Dataverse</option>> - <option value="Custom">Custom</option>> - </select> - </label> + <label>Repository Type</label> + <select class="form-control" formControlName="repotype" required> + <option value="Dataverse">Dataverse</option>> + <option value="Custom">Custom</option>> + </select> </div> <div class="form-group"> - <label>Repository Name + <label>Repository Name</label> <input type="text" class="form-control" formControlName="reponame" required> - </label> </div> <div class="form-group"> - <label>Description + <label>Description</label> <textarea class="form-control" formControlName="repodescription"></textarea> - </label> </div> <div class="form-group"> <label>url</label> diff --git a/src/app/repository/services/publish-repository.service.ts b/src/app/repository/services/publish-repository.service.ts index 7a592fda1f3381cefd6210b471e89b95affeea90..c97dd28c377170bf8082b676b6ca77aabee5148c 100644 --- a/src/app/repository/services/publish-repository.service.ts +++ b/src/app/repository/services/publish-repository.service.ts @@ -1,4 +1,4 @@ -import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { AppConfiguration } from 'src/app/AppConfiguration'; import { AuthService } from 'src/app/authentication/services/auth.service'; @@ -21,19 +21,54 @@ export class PublishRepositoryService { this.fds2Token = data.token }) if (this.fds2Token) { + + var myHeaders = new Headers(); + myHeaders.append( 'Accept', 'text/turtle'); + myHeaders.append('Content-Type', 'text/turtle'); + myHeaders.append('Authorization', 'Bearer '+ this.fds2Token ); + var myInit = { method: 'POST', body:data, headers: myHeaders }; + + var myRequest = new Request(this.appConfig.fdpurl+"/catalog", myInit); + + fetch(myRequest, myInit) + .then(response => { + response.text() + .then(data => { + console.log('catalog creaetd with fdp / id = ' + data.substring(38,74)); + /* update user (mongo ) with ( this.authService.user('data.substring(38,74)') -> smartharvester api userRpositories create/update) + catalog: { + 'cat_id':'qsdqs-qsd-qs-dqs-d-qsd-q', + 'cat_id': 'data.substring(38,74)' + } */ + + }); + }); +/* const httpOptions = { headers: new HttpHeaders({ 'Accept': 'text/turtle', 'Content-Type': 'text/turtle', - 'Authorization': 'Bearer '+ this.fds2Token + 'Authorization': 'Bearer '+ this.fds2Token, + observe: 'response', + responseType: 'text' }) }; + + let resultat = this.http.post(this.appConfig.fdpurl+"/catalog", data, httpOptions ).subscribe( + (r:HttpResponse<any>)=>{ + console.log(r.headers.get('location')) + }, + (error: any) => { + console.log(error) + }); - let resultat = this.http.post(this.appConfig.fdpurl+"/catalog", data, httpOptions ).subscribe( (r)=>{console.log('reponse: ', r)}) ; if (resultat){ - console.log("resultat: " + JSON.stringify(resultat)); - return JSON.stringify(resultat); + console.log("resultat: " + resultat); + return resultat; } + +*/ + return "The repository has not been published" } } diff --git a/src/app/services/parse-xml.service.ts b/src/app/services/parse-xml.service.ts index 44089781b77774996a00a125f44fc6f9963f1ea8..5cec017911e2893dea7278a125af01ceea0512fd 100644 --- a/src/app/services/parse-xml.service.ts +++ b/src/app/services/parse-xml.service.ts @@ -1,13 +1,14 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { environment } from 'src/environments/environment.prod'; @Injectable({ providedIn: 'root' }) export class ParseXmlService { - blazePath = "https://f2ds.eosc-pillar.eu/blazegraph/sparql" + blazePath = environment.fdpUrl+ "/blazegraph/sparql"; constructor(private http:HttpClient) { } diff --git a/src/app/swaggereditor/swaggereditor.component.html b/src/app/swaggereditor/swaggereditor.component.html deleted file mode 100644 index ddef9c9e0e2a2b3f00df6a7b5bbf76c5dbb6b45b..0000000000000000000000000000000000000000 --- a/src/app/swaggereditor/swaggereditor.component.html +++ /dev/null @@ -1,2 +0,0 @@ -<div id="swagger-editor"></div> - diff --git a/src/app/swaggereditor/swaggereditor.component.scss b/src/app/swaggereditor/swaggereditor.component.scss deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/swaggereditor/swaggereditor.component.spec.ts b/src/app/swaggereditor/swaggereditor.component.spec.ts deleted file mode 100644 index 0c18a9fbe040c37a307c0533abb53161f4e56202..0000000000000000000000000000000000000000 --- a/src/app/swaggereditor/swaggereditor.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SwaggereditorComponent } from './swaggereditor.component'; - -describe('SwaggereditorComponent', () => { - let component: SwaggereditorComponent; - let fixture: ComponentFixture<SwaggereditorComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ SwaggereditorComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SwaggereditorComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/doc/FFDS_process.pdf b/src/doc/FFDS_process.pdf new file mode 100644 index 0000000000000000000000000000000000000000..57ac8a7b247e7c3aa27a1f4da3787995572b7e96 Binary files /dev/null and b/src/doc/FFDS_process.pdf differ diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 3d96a7919a426dd0734f2bcb09949c9d9279b46d..888c5f3919440d3d4e8c8d016e69489ed43a8cc5 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,4 +1,9 @@ export const environment = { production: true, +<<<<<<< HEAD + smartharvesterUrl: 'https://f2ds.eosc-pillar.eu', + fdpUrl: 'https://f2ds.eosc-pillar.eu' +======= apiurl: 'f2ds.eosc-pillar.eu', +>>>>>>> 204b4c8a0b9295ded53eabe75800df493c9b253b }; diff --git a/src/styles.scss b/src/styles.scss index 3c4e4a3546289e5046b589caf285a69ceb86db39..a931d951bfd8e264ed758b75c1731e45013d27b4 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -40,7 +40,6 @@ button:disabled { label { display: block; - width: 40em; margin: .5em 0; color: #607D8B; font-weight: bold; @@ -87,4 +86,4 @@ textarea { // install the framework styles @include nb-install() { @include nb-theme-global(); -}; \ No newline at end of file +}; diff --git a/version b/version index 5edcff03642dbdd807897066639be8d0038fefb1..51066d2ddd04cdf3b65c41ebc7250edeb274bdd1 100644 --- a/version +++ b/version @@ -1 +1 @@ -v16 \ No newline at end of file +v17 \ No newline at end of file