From d272059a7bf2fd2f01fbf9ee82c16cb59468ef63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr>
Date: Tue, 2 Nov 2021 21:11:29 +0100
Subject: [PATCH] Search type SVOM Json Keywords => done

---
 client/src/app/instance/instance.reducer.ts   |  7 +-
 .../criteria-by-family.component.html         |  3 +
 .../criteria-by-family.component.spec.ts      | 14 ++-
 .../criteria/criteria-by-family.component.ts  |  5 +-
 .../criteria/criteria-tabs.component.html     |  3 +
 .../criteria/criteria-tabs.component.spec.ts  |  3 +-
 .../criteria/criteria-tabs.component.ts       |  5 +-
 .../criteria/search-type/json.component.html  | 15 ++-
 .../search-type/json.component.spec.ts        |  5 +-
 .../search-type/svom-json-kw.component.html   | 28 +++---
 .../svom-json-kw.component.spec.ts            | 93 +++++++++++++++++++
 .../search-type/svom-json-kw.component.ts     | 52 ++++-------
 .../search/containers/criteria.component.html |  3 +
 .../containers/criteria.component.spec.ts     |  3 +-
 .../search/containers/criteria.component.ts   | 14 ++-
 .../store/actions/svom-json-kw.actions.ts     | 18 ++++
 .../src/app/instance/store/effects/index.ts   |  4 +-
 .../effects/svom-json-kw.effects.spec.ts      | 44 +++++++++
 .../store/effects/svom-json-kw.effects.ts     | 71 ++++++++++++++
 client/src/app/instance/store/models/index.ts |  1 +
 .../store/models/svom-keyword.model.ts        |  6 ++
 .../reducers/svom-json-kw.reducer.spec.ts     | 82 ++++++++++++++++
 .../store/reducers/svom-json-kw.reducer.ts    | 64 +++++++++++++
 .../selectors/svom-json-kw.selector.spec.ts   | 24 +++++
 .../store/selectors/svom-json-kw.selector.ts  | 38 ++++++++
 .../src/app/instance/store/services/index.ts  |  4 +-
 .../services/svom-json-kw.service.spec.ts     | 48 ++++++++++
 .../store/services/svom-json-kw.service.ts    | 30 ++++++
 client/src/styles.scss                        |  8 ++
 29 files changed, 625 insertions(+), 70 deletions(-)
 create mode 100644 client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.spec.ts
 create mode 100644 client/src/app/instance/store/actions/svom-json-kw.actions.ts
 create mode 100644 client/src/app/instance/store/effects/svom-json-kw.effects.spec.ts
 create mode 100644 client/src/app/instance/store/effects/svom-json-kw.effects.ts
 create mode 100644 client/src/app/instance/store/models/svom-keyword.model.ts
 create mode 100644 client/src/app/instance/store/reducers/svom-json-kw.reducer.spec.ts
 create mode 100644 client/src/app/instance/store/reducers/svom-json-kw.reducer.ts
 create mode 100644 client/src/app/instance/store/selectors/svom-json-kw.selector.spec.ts
 create mode 100644 client/src/app/instance/store/selectors/svom-json-kw.selector.ts
 create mode 100644 client/src/app/instance/store/services/svom-json-kw.service.spec.ts
 create mode 100644 client/src/app/instance/store/services/svom-json-kw.service.ts

diff --git a/client/src/app/instance/instance.reducer.ts b/client/src/app/instance/instance.reducer.ts
index 2c3b3c97..062c160c 100644
--- a/client/src/app/instance/instance.reducer.ts
+++ b/client/src/app/instance/instance.reducer.ts
@@ -15,6 +15,7 @@ import * as searchMultiple from './store/reducers/search-multiple.reducer';
 import * as samp from './store/reducers/samp.reducer';
 import * as coneSearch from './store/reducers/cone-search.reducer';
 import * as detail from './store/reducers/detail.reducer';
+import * as svomJsonKw from './store/reducers/svom-json-kw.reducer';
 
 /**
  * Interface for instance state.
@@ -26,7 +27,8 @@ export interface State {
     searchMultiple: searchMultiple.State,
     samp: samp.State,
     coneSearch: coneSearch.State
-    detail: detail.State
+    detail: detail.State,
+    svomJsonKw: svomJsonKw.State
 }
 
 const reducers = {
@@ -34,7 +36,8 @@ const reducers = {
     searchMultiple: searchMultiple.searchMultipleReducer,
     samp: samp.sampReducer,
     coneSearch: coneSearch.coneSearchReducer,
-    detail: detail.detailReducer
+    detail: detail.detailReducer,
+    svomJsonKw: svomJsonKw.svomJsonKwReducer
 };
 
 export const instanceReducer = combineReducers(reducers);
diff --git a/client/src/app/instance/search/components/criteria/criteria-by-family.component.html b/client/src/app/instance/search/components/criteria/criteria-by-family.component.html
index 6fa2b430..d7fa428f 100644
--- a/client/src/app/instance/search/components/criteria/criteria-by-family.component.html
+++ b/client/src/app/instance/search/components/criteria/criteria-by-family.component.html
@@ -149,6 +149,9 @@
                 [label]="attribute.form_label"
                 [criterion]="getCriterion(attribute.id)" 
                 [criteriaList]="criteriaList"
+                [svomKeywords]="svomKeywords"
+                (selectSvomAcronym)=selectSvomAcronym.emit($event)
+                (resetSvomKeywords)="resetSvomKeywords.emit()"
                 (addCriterion)="emitAdd($event)" 
                 (deleteCriterion)="emitDelete($event)">
             </app-svom-json-kw-criteria>
diff --git a/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts b/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
index d019066b..946737df 100644
--- a/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
@@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { CriteriaByFamilyComponent } from './criteria-by-family.component';
 import { Option } from '../../../../metamodel/models';
-import { Criterion, FieldCriterion } from '../../../store/models';
+import { Criterion, FieldCriterion, SvomKeyword } from '../../../store/models';
 
 describe('[Instance][Search][Component][Criteria] CriteriaByFamilyComponent', () => {
     @Component({ selector: 'app-field', template: '' })
@@ -123,6 +123,15 @@ describe('[Instance][Search][Component][Criteria] CriteriaByFamilyComponent', ()
         @Input() criterion: Criterion;
     }
 
+    @Component({ selector: 'app-svom-json-kw-criteria', template: '' })
+    class SvomJsonStubKwComponent {
+        @Input() id: number;
+        @Input() label: string;
+        @Input() criterion: Criterion;
+        @Input() criteriaList: Criterion[];
+        @Input() svomKeywords: SvomKeyword[];
+    }
+
     let component: CriteriaByFamilyComponent;
     let fixture: ComponentFixture<CriteriaByFamilyComponent>;
 
@@ -142,7 +151,8 @@ describe('[Instance][Search][Component][Criteria] CriteriaByFamilyComponent', ()
                 BetweenDateStubComponent,
                 TimeStubComponent,
                 DatetimeStubComponent,
-                JsonStubComponent
+                JsonStubComponent,
+                SvomJsonStubKwComponent
             ]
         });
         fixture = TestBed.createComponent(CriteriaByFamilyComponent);
diff --git a/client/src/app/instance/search/components/criteria/criteria-by-family.component.ts b/client/src/app/instance/search/components/criteria/criteria-by-family.component.ts
index ebfbed20..e64b8eb0 100644
--- a/client/src/app/instance/search/components/criteria/criteria-by-family.component.ts
+++ b/client/src/app/instance/search/components/criteria/criteria-by-family.component.ts
@@ -9,7 +9,7 @@
 
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
 
-import { Criterion } from '../../../store/models';
+import { Criterion, SvomKeyword } from '../../../store/models';
 import { Attribute, Option } from 'src/app/metamodel/models';
 
 /**
@@ -24,6 +24,9 @@ import { Attribute, Option } from 'src/app/metamodel/models';
 export class CriteriaByFamilyComponent {
     @Input() attributeList: Attribute[];
     @Input() criteriaList: Criterion[];
+    @Input() svomKeywords: SvomKeyword[];
+    @Output() selectSvomAcronym: EventEmitter<string> = new EventEmitter();
+    @Output() resetSvomKeywords: EventEmitter<{}> = new EventEmitter();
     @Output() addCriterion: EventEmitter<Criterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
diff --git a/client/src/app/instance/search/components/criteria/criteria-tabs.component.html b/client/src/app/instance/search/components/criteria/criteria-tabs.component.html
index fa9d3aef..d659ea65 100644
--- a/client/src/app/instance/search/components/criteria/criteria-tabs.component.html
+++ b/client/src/app/instance/search/components/criteria/criteria-tabs.component.html
@@ -16,6 +16,9 @@
             <app-criteria-by-family 
                 [attributeList]="attributeList | attributeListByFamily:family.id" 
                 [criteriaList]="criteriaList"
+                [svomKeywords]="svomKeywords"
+                (selectSvomAcronym)="selectSvomAcronym.emit($event)"
+                (resetSvomKeywords)="resetSvomKeywords.emit()"
                 (addCriterion)="emitAdd($event)" 
                 (deleteCriterion)="emitDelete($event)">
             </app-criteria-by-family>
diff --git a/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts b/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts
index b2557821..11b0bca6 100644
--- a/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts
@@ -6,7 +6,7 @@ import { AccordionModule } from 'ngx-bootstrap/accordion';
 
 import { CriteriaTabsComponent } from './criteria-tabs.component';
 import { Attribute } from '../../../../metamodel/models';
-import { Criterion, FieldCriterion } from '../../../store/models';
+import { Criterion, FieldCriterion, SvomKeyword } from '../../../store/models';
 import { AttributeListByFamilyPipe } from '../../../../shared/pipes/attribute-list-by-family.pipe';
 
 describe('[Instance][Search][Component][Criteria] CriteriaTabsComponent', () => {
@@ -14,6 +14,7 @@ describe('[Instance][Search][Component][Criteria] CriteriaTabsComponent', () =>
     class CriteriaByFamilyStubComponent {
         @Input() attributeList: Attribute[];
         @Input() criteriaList: Criterion[];
+        @Input() svomKeywords: SvomKeyword[];
     }
 
     let component: CriteriaTabsComponent;
diff --git a/client/src/app/instance/search/components/criteria/criteria-tabs.component.ts b/client/src/app/instance/search/components/criteria/criteria-tabs.component.ts
index a7c45275..7ed5b94d 100644
--- a/client/src/app/instance/search/components/criteria/criteria-tabs.component.ts
+++ b/client/src/app/instance/search/components/criteria/criteria-tabs.component.ts
@@ -9,7 +9,7 @@
 
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
 
-import { Criterion } from '../../../store/models';
+import { Criterion, SvomKeyword } from '../../../store/models';
 import { CriteriaFamily, Attribute } from 'src/app/metamodel/models';
 
 /**
@@ -25,6 +25,9 @@ export class CriteriaTabsComponent {
     @Input() attributeList: Attribute[];
     @Input() criteriaFamilyList: CriteriaFamily[];
     @Input() criteriaList: Criterion[];
+    @Input() svomKeywords: SvomKeyword[];
+    @Output() selectSvomAcronym: EventEmitter<string> = new EventEmitter();
+    @Output() resetSvomKeywords: EventEmitter<{}> = new EventEmitter();
     @Output() addCriterion: EventEmitter<Criterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
diff --git a/client/src/app/instance/search/components/criteria/search-type/json.component.html b/client/src/app/instance/search/components/criteria/search-type/json.component.html
index 8d69dac7..3780ed1f 100644
--- a/client/src/app/instance/search/components/criteria/search-type/json.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/json.component.html
@@ -8,14 +8,13 @@
                 </div>
                 <div class="w-100 d-block d-sm-none"></div>
                 <div class="col col-sm-auto mb-1 mb-sm-0 px-sm-0">
-                    <select class="custom-select" id="operator" name="operator" formControlName="operator">
-                        <option></option>
-                        <option value="eq">=</option>
-                        <option value="gt">></option>
-                        <option value="gte">>=</option>
-                        <option value="lt"><</option>
-                        <option value="lte"><=</option>
-                    </select>
+                    <ng-select [clearable]="false" [multiple]="false" [hideSelected]="true" class="ng-select-custom" formControlName="operator">
+                        <ng-option value="eq">=</ng-option >
+                        <ng-option value="gt">></ng-option >
+                        <ng-option value="gte">>=</ng-option >
+                        <ng-option value="lt"><</ng-option >
+                        <ng-option value="lte"><=</ng-option >
+                    </ng-select>
                 </div>
                 <div class="w-100 d-block d-sm-none"></div>
                 <div class="col">
diff --git a/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts
index 6941d117..673739f3 100644
--- a/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts
@@ -2,6 +2,8 @@ import { Component, ViewChild } from '@angular/core';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
+import { NgSelectModule } from '@ng-select/ng-select';
+
 import { JsonComponent } from './json.component';
 import { JsonCriterion } from '../../../../store/models/criterion';
 
@@ -35,7 +37,8 @@ describe('[Instance][Search][Component][Criteria][SearchType] JsonComponent', ()
             ],
             imports: [
                 FormsModule,
-                ReactiveFormsModule
+                ReactiveFormsModule,
+                NgSelectModule
             ]
         });
         testHostFixture = TestBed.createComponent(TestHostComponent);
diff --git a/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.html b/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.html
index 1f94ffca..7257b997 100644
--- a/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.html
@@ -4,26 +4,20 @@
             <label>{{ label }}</label>
             <div class="row">
                 <div class="col mb-1 mb-sm-0">
-                    <input *ngIf="!acronym" class="form-control" name="ext" placeholder="Ext" autocomplete="off" formControlName="ext">
-                    <select *ngIf="acronym" class="form-control" name="ext" formControlName="ext" (change)="extOnChange()">
-                        <option></option>
-                        <option *ngFor="let ext of exts" [ngValue]="ext">{{ ext }}</option>
-                    </select>
-                </div>
-                <div class="w-100 d-block d-sm-none"></div>
-                <div class="col mb-1 mb-sm-0 pl-sm-0 pl-md-0 pl-lg-0">
-                    <input *ngIf="!acronym" class="form-control" id="keyword" name="keyword" placeholder="Keyword" autocomplete="off" formControlName="keyword">
+                    <input *ngIf="svomKeywords.length < 1" class="form-control" id="path" name="path" placeholder="Path" autocomplete="off" formControlName="path">
+                    <ng-select *ngIf="svomKeywords.length > 0" [multiple]="false" [hideSelected]="true" placeholder="Select svom product keyword" class="ng-select-custom" formControlName="path">
+                        <ng-option *ngFor="let svomKeyword of svomKeywords" [value]="getKeywordValue(svomKeyword)">{{ svomKeyword.extension }}/{{ svomKeyword.name }}</ng-option>
+                    </ng-select>
                 </div>
                 <div class="w-100 d-block d-sm-none"></div>
                 <div class="col col-sm-auto mb-1 mb-sm-0 px-sm-0">
-                    <select class="custom-select" id="operator" name="operator" formControlName="operator">
-                        <option></option>
-                        <option value="eq">=</option>
-                        <option value="gt">></option>
-                        <option value="gte">>=</option>
-                        <option value="lt"><</option>
-                        <option value="lte"><=</option>
-                    </select>
+                    <ng-select [clearable]="false" [multiple]="false" [hideSelected]="true" class="ng-select-custom" formControlName="operator">
+                        <ng-option value="eq">=</ng-option >
+                        <ng-option value="gt">></ng-option >
+                        <ng-option value="gte">>=</ng-option >
+                        <ng-option value="lt"><</ng-option >
+                        <ng-option value="lte"><=</ng-option >
+                    </ng-select>
                 </div>
                 <div class="w-100 d-block d-sm-none"></div>
                 <div class="col">
diff --git a/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.spec.ts
new file mode 100644
index 00000000..05f55eec
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.spec.ts
@@ -0,0 +1,93 @@
+import { Component, ViewChild } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { NgSelectModule } from '@ng-select/ng-select';
+
+import { SvomJsonKwComponent } from './svom-json-kw.component';
+import { Criterion, JsonCriterion, SvomKeyword } from '../../../../store/models';
+
+describe('[Instance][Search][Component][Criteria][SearchType] SvomJsonKwComponent', () => {
+    @Component({
+        selector: `app-host`,
+        template: `
+            <app-svom-json-kw-criteria 
+                    [id]="id"
+                    [label]="label"
+                    [criterion]="criterion"
+                    [criteriaList]="criteriaList"
+                    [svomKeywords]="svomKeywords">
+            </app-svom-json-kw-criteria >`
+    })
+    class TestHostComponent {
+        @ViewChild(SvomJsonKwComponent, { static: false })
+        public testedComponent: SvomJsonKwComponent;
+        public id: number = undefined;
+        public label: string = undefined;
+        public criterion: JsonCriterion = undefined;
+        public criteriaList: Criterion[] = [];
+        public svomKeywords: SvomKeyword[] = [];
+    }
+
+    let testHostComponent: TestHostComponent;
+    let testHostFixture: ComponentFixture<TestHostComponent>;
+    let testedComponent: SvomJsonKwComponent;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                SvomJsonKwComponent,
+                TestHostComponent
+            ],
+            imports: [
+                FormsModule,
+                ReactiveFormsModule,
+                NgSelectModule
+            ]
+        });
+        testHostFixture = TestBed.createComponent(TestHostComponent);
+        testHostComponent = testHostFixture.componentInstance;
+        testHostFixture.detectChanges();
+        testedComponent = testHostComponent.testedComponent;
+    });
+
+    it('should create the component', () => {
+        expect(testedComponent).toBeTruthy();
+    });
+
+    it('should call ngOnChanges and apply changes', () => {
+        const spy = jest.spyOn(testedComponent, 'ngOnChanges');
+        testHostComponent.criterion = { id: testedComponent.id, type: 'json', path: 'myPath', operator: 'myOperator', value: 'myValue' } as JsonCriterion;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.path.value).toEqual('myPath');
+        expect(testedComponent.form.controls.operator.value).toEqual('myOperator');
+        expect(testedComponent.form.controls.value.value).toEqual('myValue');
+        expect(testedComponent.form.disabled).toBeTruthy();
+        testHostComponent.criterion = undefined;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.path.value).toBeNull();
+        expect(testedComponent.form.controls.operator.value).toBeNull();
+        expect(testedComponent.form.controls.value.value).toBeNull();
+        expect(testedComponent.form.enabled).toBeTruthy();
+        expect(spy).toHaveBeenCalledTimes(2);
+    });
+
+    it('raises the add criterion event when clicked', () => {
+        testedComponent.id = 1;
+        testedComponent.form.controls.path.setValue('myPath');
+        testedComponent.form.controls.operator.setValue('myOperator');
+        testedComponent.form.controls.value.setValue('myValue');
+        const expectedCriterion = { id: testedComponent.id, type: 'json', path: 'myPath', operator: 'myOperator', value: 'myValue' } as JsonCriterion;
+        testedComponent.addCriterion.subscribe((event: JsonCriterion) => expect(event).toEqual(expectedCriterion));
+        testedComponent.emitAdd();
+    });
+
+    it('Calculates the value of ng-option', () => {
+        expect(testedComponent.getKeywordValue({
+            extension: 'PrimaryHDU',
+            name: 'CARD',
+            data_type: 'string',
+            default: ''
+        })).toEqual('PrimaryHDU,CARD');
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.ts b/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.ts
index 57c2f4f1..9feb30f5 100644
--- a/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.ts
@@ -8,11 +8,9 @@
  */
 
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
 import { FormGroup, FormControl, Validators } from '@angular/forms';
  
-import { JsonCriterion, Criterion } from 'src/app/instance/store/models';
-import { AppConfigService } from 'src/app/app-config.service';
+import { JsonCriterion, Criterion, SvomKeyword } from 'src/app/instance/store/models';
 
 @Component({
     selector: 'app-svom-json-kw-criteria',
@@ -24,19 +22,14 @@ export class SvomJsonKwComponent implements OnChanges {
     @Input() label: string;
     @Input() criterion: Criterion;
     @Input() criteriaList: Criterion[];
+    @Input() svomKeywords: SvomKeyword[];
+    @Output() selectSvomAcronym: EventEmitter<string> = new EventEmitter();
+    @Output() resetSvomKeywords: EventEmitter<{}> = new EventEmitter();
     @Output() addCriterion: EventEmitter<JsonCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    acronym = false;
-    keywordsSearchable = null;
-    exts: string[] = [];
-    keywords: string[] = [];
-
-    public constructor(private httpClient: HttpClient, private config: AppConfigService) { }
-
     public form = new FormGroup({
-        ext: new FormControl('', [Validators.required]),
-        keyword: new FormControl('', [Validators.required]),
+        path: new FormControl('', [Validators.required]),
         operator: new FormControl('', [Validators.required]),
         value: new FormControl('', [Validators.required])
     });
@@ -52,25 +45,23 @@ export class SvomJsonKwComponent implements OnChanges {
             this.form.reset();
         }
 
-        if (changes.criteriaList && changes.criteriaList.currentValue.find((c: Criterion) => c.id === 3)) {
-            this.acronym = true;
-            this.getKwSearchable(changes.criteriaList.currentValue.find((c: Criterion) => c.id === 3).value);
+        if (changes.criteriaList && this.svomKeywords.length < 1 && changes.criteriaList.currentValue.find((c: Criterion) => c.id === 3)) {
+            this.selectSvomAcronym.emit(changes.criteriaList.currentValue.find((c: Criterion) => c.id === 3).value);
         }
-    }
 
-    getKwSearchable(acronym: string) {
-        this.httpClient.get(`${this.config.apiUrl}/search/sp_cards?a=8&c=1::eq::${acronym}`).subscribe(data => {
-            this.keywordsSearchable = data[0].search_kw;
-            this.exts = [...new Set<string>(this.keywordsSearchable.map(item => item.extension))];
-            console.log(data);
-            /* this.exts = this.keywordsSearchable
-                .map(item => item.extension)
-                .filter((value, index, self) => self.indexOf(value.) === index); */
-        });
+        if (changes.criteriaList && this.svomKeywords.length > 0 && !changes.criteriaList.currentValue.find((c: Criterion) => c.id === 3)) {
+            this.resetSvomKeywords.emit();
+        }
     }
 
-    extOnChange() {
-
+    /**
+     * Transform a SVOM json Keyword to as path value (anis json search)
+     * 
+     * @param svomKeyword Keyword selected by user
+     * @returns string path value
+     */
+    getKeywordValue(svomKeyword: SvomKeyword): string {
+        return `${svomKeyword.extension},${svomKeyword.name}`
     }
 
     /**
@@ -79,12 +70,7 @@ export class SvomJsonKwComponent implements OnChanges {
      * @fires EventEmitter<JsonCriterion>
      */
     emitAdd(): void {
-        const formValue = {
-            path: `${this.form.controls.ext.value},${this.form.controls.keyword.value}`,
-            operator: this.form.controls.operator.value,
-            value: this.form.controls.value.value
-        }
-        const js = { id: this.id, type: 'json', ...formValue };
+        const js = { id: this.id, type: 'json', ...this.form.value };
         this.addCriterion.emit(js);
     }
 }
diff --git a/client/src/app/instance/search/containers/criteria.component.html b/client/src/app/instance/search/containers/criteria.component.html
index 0afddb0a..c24a1901 100644
--- a/client/src/app/instance/search/containers/criteria.component.html
+++ b/client/src/app/instance/search/containers/criteria.component.html
@@ -22,6 +22,9 @@
             [attributeList]="attributeList | async | sortByCriteriaDisplay"
             [criteriaFamilyList]="criteriaFamilyList | async"
             [criteriaList]="criteriaList | async"
+            [svomKeywords]="svomKeywords | async"
+            (selectSvomAcronym)="selectSvomAcronym($event)"
+            (resetSvomKeywords)="resetSvomKeywords()"
             (addCriterion)="addCriterion($event)"
             (deleteCriterion)="deleteCriterion($event)">
         </app-criteria-tabs>
diff --git a/client/src/app/instance/search/containers/criteria.component.spec.ts b/client/src/app/instance/search/containers/criteria.component.spec.ts
index a1c1e2eb..035339c2 100644
--- a/client/src/app/instance/search/containers/criteria.component.spec.ts
+++ b/client/src/app/instance/search/containers/criteria.component.spec.ts
@@ -6,7 +6,7 @@ import { MockStore, provideMockStore } from '@ngrx/store/testing';
 
 import { CriteriaComponent } from './criteria.component';
 import { Attribute, CriteriaFamily, Dataset, OutputCategory, OutputFamily } from '../../../metamodel/models';
-import { ConeSearch, Criterion, Resolver, SearchQueryParams } from '../../store/models';
+import { ConeSearch, Criterion, Resolver, SearchQueryParams, SvomKeyword } from '../../store/models';
 import { SortByCriteriaDisplayPipe } from '../pipes/sort-by-criteria-display.pipe';
 import * as searchActions from '../../store/actions/search.actions';
 import { AbstractSearchComponent } from './abstract-search.component';
@@ -31,6 +31,7 @@ describe('[Instance][Search][Container] CriteriaComponent', () => {
         @Input() attributeList: Attribute[];
         @Input() criteriaFamilyList: CriteriaFamily[];
         @Input() criteriaList: Criterion[];
+        @Input() svomKeywords: SvomKeyword[];
     }
 
     @Component({ selector: 'app-summary', template: '' })
diff --git a/client/src/app/instance/search/containers/criteria.component.ts b/client/src/app/instance/search/containers/criteria.component.ts
index 2530489e..a73921a9 100644
--- a/client/src/app/instance/search/containers/criteria.component.ts
+++ b/client/src/app/instance/search/containers/criteria.component.ts
@@ -13,10 +13,12 @@ import { Store } from '@ngrx/store';
 import { Observable } from 'rxjs';
 
 import { AbstractSearchComponent } from './abstract-search.component';
-import { ConeSearch, Criterion, Resolver } from '../../store/models';
+import { ConeSearch, Criterion, Resolver, SvomKeyword } from '../../store/models';
 import * as searchActions from '../../store/actions/search.actions';
 import * as coneSearchActions from '../../store/actions/cone-search.actions';
 import * as coneSearchSelector from '../../store/selectors/cone-search.selector';
+import * as svomJsonKwActions from '../../store/actions/svom-json-kw.actions';
+import * as svomJsonKwSelector from '../../store/selectors/svom-json-kw.selector';
 
 /**
  * @class
@@ -30,12 +32,14 @@ export class CriteriaComponent extends AbstractSearchComponent {
     public resolver: Observable<Resolver>;
     public resolverIsLoading: Observable<boolean>;
     public resolverIsLoaded: Observable<boolean>;
+    public svomKeywords: Observable<SvomKeyword[]>;
 
     constructor(protected store: Store<{ }>) {
         super(store);
         this.resolver = this.store.select(coneSearchSelector.selectResolver);
         this.resolverIsLoading = this.store.select(coneSearchSelector.selectResolverIsLoading);
         this.resolverIsLoaded = this.store.select(coneSearchSelector.selectResolverIsLoaded);
+        this.svomKeywords = this.store.select(svomJsonKwSelector.selectSvomKeywords);
     }
 
     ngOnInit(): void {
@@ -88,4 +92,12 @@ export class CriteriaComponent extends AbstractSearchComponent {
     retrieveCoordinates(name: string): void {
         this.store.dispatch(coneSearchActions.retrieveCoordinates({ name }));
     }
+
+    selectSvomAcronym(acronymSelected: string): void {
+        this.store.dispatch(svomJsonKwActions.selectAcronym({ acronymSelected }));
+    }
+
+    resetSvomKeywords(): void {
+        Promise.resolve(null).then(() => this.store.dispatch(svomJsonKwActions.resetKw()));
+    }
 }
diff --git a/client/src/app/instance/store/actions/svom-json-kw.actions.ts b/client/src/app/instance/store/actions/svom-json-kw.actions.ts
new file mode 100644
index 00000000..4ce485e9
--- /dev/null
+++ b/client/src/app/instance/store/actions/svom-json-kw.actions.ts
@@ -0,0 +1,18 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createAction, props } from '@ngrx/store';
+
+import { SvomKeyword } from '../models';
+
+export const resetKw = createAction('[SVOM Json Kw] Reset Kw');
+export const selectAcronym = createAction('[SVOM Json Kw] Select Acronym', props<{ acronymSelected: string }>());
+export const loadKwSearchable = createAction('[SVOM Json Kw] Load Kw Searchable');
+export const loadKwSearchableSuccess = createAction('[SVOM Json Kw] Load Kw Searchable Success', props<{ svomKeywords: SvomKeyword[] }>());
+export const loadKwSearchableFail = createAction('[SVOM Json Kw] Load Kw Searchable Fail');
\ No newline at end of file
diff --git a/client/src/app/instance/store/effects/index.ts b/client/src/app/instance/store/effects/index.ts
index 582dd511..69b9a3b2 100644
--- a/client/src/app/instance/store/effects/index.ts
+++ b/client/src/app/instance/store/effects/index.ts
@@ -3,11 +3,13 @@ import { SearchEffects } from './search.effects';
 import { SearchMultipleEffects } from './search-multiple.effects';
 import { ConeSearchEffects } from './cone-search.effects';
 import { DetailEffects } from './detail.effects';
+import { SvomJsonKwEffects } from './svom-json-kw.effects';
 
 export const instanceEffects = [
     SampEffects,
     SearchEffects,
     SearchMultipleEffects,
     ConeSearchEffects,
-    DetailEffects
+    DetailEffects,
+    SvomJsonKwEffects
 ];
diff --git a/client/src/app/instance/store/effects/svom-json-kw.effects.spec.ts b/client/src/app/instance/store/effects/svom-json-kw.effects.spec.ts
new file mode 100644
index 00000000..ed3ca655
--- /dev/null
+++ b/client/src/app/instance/store/effects/svom-json-kw.effects.spec.ts
@@ -0,0 +1,44 @@
+import { TestBed } from '@angular/core/testing';
+
+import { provideMockActions } from '@ngrx/effects/testing';
+import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects';
+import { provideMockStore } from '@ngrx/store/testing';
+import { Observable } from 'rxjs';
+import { ToastrService } from 'ngx-toastr';
+
+import { SvomJsonKwEffects } from './svom-json-kw.effects';
+import { SvomJsonKwService } from '../services/svom-json-kw.service';
+import * as fromSvomJsonKw from '../reducers/svom-json-kw.reducer';
+
+describe('[Instance][Store] SvomJsonKwEffects', () => {
+    let actions = new Observable();
+    let effects: SvomJsonKwEffects;
+    let metadata: EffectsMetadata<SvomJsonKwEffects>;
+    let svomJsonKwService: SvomJsonKwService;
+    let toastr: ToastrService;
+    const initialState = {
+        instance: {
+            svomJsonKw: { ...fromSvomJsonKw.initialState }
+        }
+    };
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            providers: [
+                SvomJsonKwEffects,
+                { provide: SvomJsonKwService, useValue: { loadKwSearchable: jest.fn() }},
+                { provide: ToastrService, useValue: { success: jest.fn(), error: jest.fn() }},
+                provideMockActions(() => actions),
+                provideMockStore({ initialState })
+            ]
+        }).compileComponents();
+        effects = TestBed.inject(SvomJsonKwEffects);
+        metadata = getEffectsMetadata(effects);
+        svomJsonKwService = TestBed.inject(SvomJsonKwService);
+        toastr = TestBed.inject(ToastrService);
+    });
+
+    it('should be created', () => {
+        expect(effects).toBeTruthy();
+    });
+});
diff --git a/client/src/app/instance/store/effects/svom-json-kw.effects.ts b/client/src/app/instance/store/effects/svom-json-kw.effects.ts
new file mode 100644
index 00000000..c6d32c75
--- /dev/null
+++ b/client/src/app/instance/store/effects/svom-json-kw.effects.ts
@@ -0,0 +1,71 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Injectable } from '@angular/core';
+
+import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
+import { Store  } from '@ngrx/store';
+import { of } from 'rxjs';
+import { map, tap, mergeMap, catchError } from 'rxjs/operators';
+import { ToastrService } from 'ngx-toastr';
+
+import * as svomJsonKwActions from '../actions/svom-json-kw.actions';
+import * as svomJsonKwSelector from '../selectors/svom-json-kw.selector';
+
+import { SvomJsonKwService } from '../services/svom-json-kw.service';
+
+/**
+ * @class
+ * @classdesc Svom Json Kw effects.
+ */
+@Injectable()
+export class SvomJsonKwEffects {
+    selectAcronym$ = createEffect((): any =>
+        this.actions$.pipe(
+            ofType(svomJsonKwActions.selectAcronym),
+            map(() => svomJsonKwActions.loadKwSearchable())
+        )
+    );
+
+    loadKwSearchable$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(svomJsonKwActions.loadKwSearchable),
+            concatLatestFrom(() => this.store.select(svomJsonKwSelector.selectAcronymSelected)),
+            mergeMap(([action, acronymSelected]) => this.svomJsonKwService.loadKwSearchable(acronymSelected)
+                .pipe(
+                    map(svomKeywords => svomJsonKwActions.loadKwSearchableSuccess({ svomKeywords })),
+                    catchError(() => of(svomJsonKwActions.loadKwSearchableFail()))
+                )
+            )
+        )
+    );
+
+    loadKwSearchableSuccess$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(svomJsonKwActions.loadKwSearchableSuccess),
+            tap(() => {
+                this.toastr.success('SVOM Json Keywords was loaded successfully and are now available to product criteria', 'SVOM Json Keywords loaded')
+            })
+        ), { dispatch: false}
+    );
+
+    addDatabaseFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(svomJsonKwActions.loadKwSearchableFail),
+            tap(() => this.toastr.error('Failure to load Keywords', 'SVOM Json Keywords loaded failed'))
+        ), { dispatch: false}
+    );
+
+    constructor(
+        private actions$: Actions,
+        private store: Store<{ }>,
+        private svomJsonKwService: SvomJsonKwService,
+        private toastr: ToastrService
+    ) {}
+}
diff --git a/client/src/app/instance/store/models/index.ts b/client/src/app/instance/store/models/index.ts
index 962eede1..2570f163 100644
--- a/client/src/app/instance/store/models/index.ts
+++ b/client/src/app/instance/store/models/index.ts
@@ -7,3 +7,4 @@ export * from './cone-search.model';
 export * from './resolver.model';
 export * from './search-multiple-dataset-length';
 export * from './search-multiple-dataset-data';
+export * from './svom-keyword.model';
\ No newline at end of file
diff --git a/client/src/app/instance/store/models/svom-keyword.model.ts b/client/src/app/instance/store/models/svom-keyword.model.ts
new file mode 100644
index 00000000..1fabc699
--- /dev/null
+++ b/client/src/app/instance/store/models/svom-keyword.model.ts
@@ -0,0 +1,6 @@
+export interface SvomKeyword {
+    data_type: string
+    default: string
+    extension: string
+    name: string
+}
\ No newline at end of file
diff --git a/client/src/app/instance/store/reducers/svom-json-kw.reducer.spec.ts b/client/src/app/instance/store/reducers/svom-json-kw.reducer.spec.ts
new file mode 100644
index 00000000..b5c3ae74
--- /dev/null
+++ b/client/src/app/instance/store/reducers/svom-json-kw.reducer.spec.ts
@@ -0,0 +1,82 @@
+import * as fromSvomJsonKw from './svom-json-kw.reducer';
+import * as svomJsonKwActions from '../actions/svom-json-kw.actions';
+
+describe('[Instance][Store] Svom Json Kw reducer', () => {
+    it('unknown action should return the default state', () => {
+        const { initialState } = fromSvomJsonKw;
+        const action = { type: 'Unknown' };
+        const state = fromSvomJsonKw.svomJsonKwReducer(initialState, action);
+
+        expect(state).toBe(initialState);
+    });
+
+    it('resetKw action should return the default state', () => {
+        const { initialState } = fromSvomJsonKw;
+        const action = svomJsonKwActions.resetKw();
+        const state = fromSvomJsonKw.svomJsonKwReducer(initialState, action);
+
+        expect(state).toEqual(initialState);
+    });
+
+    it('selectAcronym action should change the acronymSelected', () => {
+        const { initialState } = fromSvomJsonKw;
+        const action = svomJsonKwActions.selectAcronym({ acronymSelected: 'OBLC_ECL' });
+        const state = fromSvomJsonKw.svomJsonKwReducer(initialState, action);
+
+        expect(state.acronymSelected).toBe('OBLC_ECL');
+        expect(state.svomKeywords).toEqual([]);
+        expect(state.svomKeywordsIsLoading).toBeFalsy();
+        expect(state.svomKeywordsIsLoaded).toBeFalsy();
+        expect(state).not.toBe(initialState);
+    });
+
+    it('loadKwSearchable action should change the svomKeywordsLoading and svomKeywordsLoaded', () => {
+        const { initialState } = fromSvomJsonKw;
+        const action = svomJsonKwActions.loadKwSearchable();
+        const state = fromSvomJsonKw.svomJsonKwReducer(initialState, action);
+
+        expect(state.acronymSelected).toBeNull();
+        expect(state.svomKeywords).toEqual([]);
+        expect(state.svomKeywordsIsLoading).toBeTruthy();
+        expect(state.svomKeywordsIsLoaded).toBeFalsy();
+        expect(state).not.toBe(initialState);
+    });
+
+    it('loadKwSearchableSuccess action should change the svomKeywords, svomKeywordsLoading and svomKeywordsLoaded', () => {
+        const { initialState } = fromSvomJsonKw;
+        const svomKeywords = [
+            {
+                data_type: 'string',
+                default: '',
+                extension: 'PrimaryHDU',
+                name: 'CARD'
+            },
+            {
+                data_type: 'string',
+                default: '',
+                extension: 'PrimaryHDU',
+                name: 'TIME'
+            }
+        ];
+        const action = svomJsonKwActions.loadKwSearchableSuccess({ svomKeywords });
+        const state = fromSvomJsonKw.svomJsonKwReducer(initialState, action);
+
+        expect(state.acronymSelected).toBeNull();
+        expect(state.svomKeywords).toEqual(svomKeywords);
+        expect(state.svomKeywordsIsLoading).toBeFalsy();
+        expect(state.svomKeywordsIsLoaded).toBeTruthy();
+        expect(state).not.toBe(initialState);
+    });
+
+    it('loadKwSearchableFail action should change the svomKeywordsLoading and svomKeywordsLoaded', () => {
+        const { initialState } = fromSvomJsonKw;
+        const action = svomJsonKwActions.loadKwSearchableFail();
+        const state = fromSvomJsonKw.svomJsonKwReducer(initialState, action);
+
+        expect(state.acronymSelected).toBeNull();
+        expect(state.svomKeywords).toEqual([]);
+        expect(state.svomKeywordsIsLoading).toBeFalsy();
+        expect(state.svomKeywordsIsLoaded).toBeFalsy();
+        expect(state).not.toBe(initialState);
+    });
+});
diff --git a/client/src/app/instance/store/reducers/svom-json-kw.reducer.ts b/client/src/app/instance/store/reducers/svom-json-kw.reducer.ts
new file mode 100644
index 00000000..37c85260
--- /dev/null
+++ b/client/src/app/instance/store/reducers/svom-json-kw.reducer.ts
@@ -0,0 +1,64 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createReducer, on } from '@ngrx/store';
+
+import { SvomKeyword } from '../models';
+import * as svomJsonKwActions from '../actions/svom-json-kw.actions';
+
+/**
+ * Interface for svom json kw state.
+ *
+ * @interface State
+ */
+export interface State {
+    acronymSelected: string;
+    svomKeywords: SvomKeyword[];
+    svomKeywordsIsLoading: boolean;
+    svomKeywordsIsLoaded: boolean;
+}
+
+export const initialState: State = {
+    acronymSelected: null,
+    svomKeywords: [],
+    svomKeywordsIsLoading: false,
+    svomKeywordsIsLoaded: false
+};
+
+export const svomJsonKwReducer = createReducer(
+    initialState,
+    on(svomJsonKwActions.resetKw, () => ({
+        ...initialState
+    })),
+    on(svomJsonKwActions.selectAcronym, (state, { acronymSelected }) => ({
+        ...state,
+        acronymSelected
+    })),
+    on(svomJsonKwActions.loadKwSearchable, (state) => ({
+        ...state,
+        svomKeywordsIsLoading: true,
+        svomKeywordsIsLoaded: false
+    })),
+    on(svomJsonKwActions.loadKwSearchableSuccess, (state, { svomKeywords }) => ({
+        ...state,
+        svomKeywords,
+        svomKeywordsIsLoading: false,
+        svomKeywordsIsLoaded: true
+    })),
+    on(svomJsonKwActions.loadKwSearchableFail, (state) => ({
+        ...state,
+        svomKeywordsIsLoading: false,
+        svomKeywordsIsLoaded: false
+    })),
+);
+
+export const selectAcronymSelected = (state: State) => state.acronymSelected;
+export const selectSvomKeywords = (state: State) => state.svomKeywords;
+export const selectSvomKeywordsIsLoading = (state: State) => state.svomKeywordsIsLoading;
+export const selectSvomKeywordsIsLoaded = (state: State) => state.svomKeywordsIsLoaded;
diff --git a/client/src/app/instance/store/selectors/svom-json-kw.selector.spec.ts b/client/src/app/instance/store/selectors/svom-json-kw.selector.spec.ts
new file mode 100644
index 00000000..e3e9604c
--- /dev/null
+++ b/client/src/app/instance/store/selectors/svom-json-kw.selector.spec.ts
@@ -0,0 +1,24 @@
+import * as svomJsonKwSelector from './svom-json-kw.selector';
+import * as fromSvomJsonKw from '../reducers/svom-json-kw.reducer';
+
+describe('[Instance][Store] Svom Json Kw selector', () => {
+    it('should get selectAcronymSelected', () => {
+        const state = { instance: { svomJsonKw: { ...fromSvomJsonKw.initialState }}};
+        expect(svomJsonKwSelector.selectAcronymSelected(state)).toBeNull();
+    });
+
+    it('should get selectSvomKeywords', () => {
+        const state = { instance: { svomJsonKw: { ...fromSvomJsonKw.initialState }}};
+        expect(svomJsonKwSelector.selectSvomKeywords(state)).toEqual([]);
+    });
+
+    it('should get selectSvomKeywordsIsLoading', () => {
+        const state = { instance: { svomJsonKw: { ...fromSvomJsonKw.initialState }}};
+        expect(svomJsonKwSelector.selectSvomKeywordsIsLoading(state)).toBeFalsy();
+    });
+
+    it('should get selectSvomKeywordsIsLoaded', () => {
+        const state = { instance: { svomJsonKw: { ...fromSvomJsonKw.initialState }}};
+        expect(svomJsonKwSelector.selectSvomKeywordsIsLoaded(state)).toBeFalsy();
+    });
+});
diff --git a/client/src/app/instance/store/selectors/svom-json-kw.selector.ts b/client/src/app/instance/store/selectors/svom-json-kw.selector.ts
new file mode 100644
index 00000000..c178ec62
--- /dev/null
+++ b/client/src/app/instance/store/selectors/svom-json-kw.selector.ts
@@ -0,0 +1,38 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createSelector } from '@ngrx/store';
+
+import * as reducer from '../../instance.reducer';
+import * as svomJsonKw from '../reducers/svom-json-kw.reducer';
+
+export const selectSvomJsonKwState = createSelector(
+    reducer.getInstanceState,
+    (state: reducer.State) => state.svomJsonKw
+);
+
+export const selectAcronymSelected = createSelector(
+    selectSvomJsonKwState,
+    svomJsonKw.selectAcronymSelected
+);
+
+export const selectSvomKeywords = createSelector(
+    selectSvomJsonKwState,
+    svomJsonKw.selectSvomKeywords
+);
+
+export const selectSvomKeywordsIsLoading = createSelector(
+    selectSvomJsonKwState,
+    svomJsonKw.selectSvomKeywordsIsLoading
+);
+
+export const selectSvomKeywordsIsLoaded = createSelector(
+    selectSvomJsonKwState,
+    svomJsonKw.selectSvomKeywordsIsLoaded
+);
diff --git a/client/src/app/instance/store/services/index.ts b/client/src/app/instance/store/services/index.ts
index cabc0796..795315e9 100644
--- a/client/src/app/instance/store/services/index.ts
+++ b/client/src/app/instance/store/services/index.ts
@@ -2,10 +2,12 @@ import { SearchService } from './search.service';
 import { SampService } from './samp.service';
 import { ConeSearchService } from './cone-search.service';
 import { DetailService } from './detail.service';
+import { SvomJsonKwService } from './svom-json-kw.service';
 
 export const instanceServices = [
     SearchService,
     SampService,
     ConeSearchService,
-    DetailService
+    DetailService,
+    SvomJsonKwService
 ];
diff --git a/client/src/app/instance/store/services/svom-json-kw.service.spec.ts b/client/src/app/instance/store/services/svom-json-kw.service.spec.ts
new file mode 100644
index 00000000..6a334fd2
--- /dev/null
+++ b/client/src/app/instance/store/services/svom-json-kw.service.spec.ts
@@ -0,0 +1,48 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { TestBed, inject } from '@angular/core/testing';
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+
+import { SvomJsonKwService } from './svom-json-kw.service';
+import { AppConfigService } from 'src/app/app-config.service';
+
+describe('[Instance][Store] SvomJsonKwService', () => {
+    let service: SvomJsonKwService;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            imports: [HttpClientTestingModule],
+            providers: [
+                { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } },
+                SvomJsonKwService
+            ]
+        });
+        service = TestBed.inject(SvomJsonKwService);
+    });
+
+    it('#retrieveData() should return an Observable<any[]>',
+        inject([HttpTestingController, SvomJsonKwService],(httpMock: HttpTestingController, svomJsonKwService: SvomJsonKwService) => {
+            const mockResponse = ['myData'];
+
+            svomJsonKwService.loadKwSearchable('OBLC_ECL').subscribe((event: any[]) => {
+                expect(event).toEqual(mockResponse);
+            });
+
+            const mockRequest = httpMock.expectOne('http://testing.com/search/sp_cards?a=8&c=1::eq::OBLC_ECL');
+
+            expect(mockRequest.cancelled).toBeFalsy();
+            expect(mockRequest.request.responseType).toEqual('json');
+            mockRequest.flush(mockResponse);
+
+            httpMock.verify();
+            }
+        )
+    );
+});
\ No newline at end of file
diff --git a/client/src/app/instance/store/services/svom-json-kw.service.ts b/client/src/app/instance/store/services/svom-json-kw.service.ts
new file mode 100644
index 00000000..7a990aa7
--- /dev/null
+++ b/client/src/app/instance/store/services/svom-json-kw.service.ts
@@ -0,0 +1,30 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { map } from 'rxjs/operators';
+
+import { AppConfigService } from 'src/app/app-config.service';
+import { SvomKeyword } from '../models';
+
+/**
+ * @class
+ * @classdesc Svom Json Kw service.
+ */
+@Injectable()
+export class SvomJsonKwService {
+    constructor(private http: HttpClient, private config: AppConfigService) { }
+
+    loadKwSearchable(acronym: string) {
+        return this.http.get<{search_kw: SvomKeyword[]}[]>(`${this.config.apiUrl}/search/sp_cards?a=8&c=1::eq::${acronym}`).pipe(
+            map(data => data[0].search_kw)
+        );
+    }
+}
diff --git a/client/src/styles.scss b/client/src/styles.scss
index db95f8b3..dc2941c0 100644
--- a/client/src/styles.scss
+++ b/client/src/styles.scss
@@ -44,6 +44,14 @@ main {
     margin-top: 100px;
 }
 
+.ng-select-container {
+    height: 38px !important;
+}
+
+.ng-select.ng-select-disabled>.ng-select-container {
+    background-color: #e9ecef !important;
+}
+
 .custom-switch label, .custom-radio label {
     cursor: pointer;
 }
-- 
GitLab