Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Topic search refactor #603

Merged
merged 38 commits into from
Jan 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
be0ed50
Refactor v3 aggregate autocomplete search serializers
amanji Dec 14, 2020
7b24e21
Adds new index fields for topic
amanji Dec 14, 2020
9702094
Expand topic search indexes
amanji Dec 15, 2020
e5db06f
Adds sub types to autocomplete results
amanji Dec 15, 2020
063b43b
Refactor v3 search autocomplete into inheritable classes
amanji Dec 16, 2020
d99652f
Remove duplicated serializers
amanji Dec 16, 2020
4c8205b
Adds v4 app, scaffolding and url config
amanji Dec 17, 2020
e37568b
Credential search based on v3 without facetingP
amanji Dec 17, 2020
4997f30
Refactor CredentialTopicSearchView into a subclass of CredentialSearc…
amanji Dec 17, 2020
a197f74
First pass of v4 topic search with faceting
amanji Dec 18, 2020
8077e29
Re-order filtering operations for v4 credential search
amanji Dec 18, 2020
6c961f1
Reorg some code
amanji Dec 18, 2020
663f468
Merge branch 'master' into topic-search-refactor
amanji Dec 18, 2020
d1e61a4
Format v4 topic search responses
amanji Dec 18, 2020
b9f113c
Fix v4 topic search filtering
amanji Dec 18, 2020
9565116
Format v4 topic facet search responses
amanji Dec 19, 2020
325f04d
v4 credential search iherits from v3 credential search
amanji Dec 21, 2020
b81f329
Adds None checks on TopicIndex creation
amanji Dec 23, 2020
c074df8
Merge branch 'master' into topic-search-refactor
amanji Dec 24, 2020
867ef98
Merge branch 'master' into topic-search-refactor
amanji Dec 24, 2020
c1cdb89
Memoizes field name lookups when formatting facet counts
amanji Dec 24, 2020
0cf7014
Adds topic fields required for client
amanji Dec 24, 2020
fb8f3c8
Adds topic fields required for client
amanji Dec 24, 2020
27a7ae3
Update search to v4. Fix UI issues in search result list
amanji Dec 25, 2020
bab0a12
Fix search query in basic search
amanji Dec 25, 2020
61e08fe
Fix category facet filtering for v4 search
amanji Dec 25, 2020
f33a981
Query params map between basic and advanced search
amanji Dec 25, 2020
2dd17a7
Historical facet filtering on advanced search aligns with basic search
amanji Dec 25, 2020
790c728
Re-organizes indexes. Adds new topic index
amanji Dec 27, 2020
1b86485
Adds faceting for credential type on advanced search
amanji Dec 28, 2020
c7edbdd
Fixes facet narrowing on topic category query paramters
amanji Dec 28, 2020
5b18343
All query parameters map over from basic search to advanced search
amanji Dec 28, 2020
ce0f03e
Update client autocomplete to v3
amanji Dec 28, 2020
e1e299c
Advanced search form values map from query params on page load
amanji Dec 28, 2020
2ca7b4c
Ensure inactive and revoked tags show in search results
amanji Dec 29, 2020
428d06b
Declare abstract serializer methods
amanji Dec 29, 2020
2e2a698
Fix credential links and labels
amanji Dec 30, 2020
3d65c6f
Select FormControl should not be private
amanji Jan 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions client/src/app/data-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,11 @@ export namespace Model {
addresses: Address[];
local_name: Name;
remote_name: Name;
credential_type: CredentialType;
credential_set: CredentialSet;
effective_date: string;
inactive: boolean;
revoked: boolean;

static resourceName = "topic";

Expand Down Expand Up @@ -498,6 +503,11 @@ export namespace Model {
return ["/topic", this.type, this.source_id];
}

get issuer(): Issuer {
return this.credential_type && this.credential_type.issuer;
}
set issuer(val: Issuer) {}

extLink(...args): string[] {
return this.link.concat(args);
}
Expand All @@ -515,12 +525,12 @@ export namespace Model {
static childResource = "related_to";
}

export class TopicSearchResult extends Credential {
static resourceName = "v3/search/topic";
export class TopicSearchResult extends Topic {
static resourceName = "v4/search/topic";
}

export class TopicFacetSearchResult extends Credential {
static resourceName = "v3/search/topic/facets";
export class TopicFacetSearchResult extends Topic {
static resourceName = "v4/search/topic/facets";
}

export class TopicRelationship extends BaseModel {
Expand Down
33 changes: 11 additions & 22 deletions client/src/app/general-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

import { BehaviorSubject, from, Observable, Observer, of, Subscription , throwError as _throw } from 'rxjs';
import { BehaviorSubject, from, Observable, Observer, of, Subscription, throwError as _throw } from 'rxjs';
import { catchError, map, mergeMap, shareReplay } from 'rxjs/operators';

import { Fetch, Filter, Model } from './data-types';
Expand Down Expand Up @@ -148,27 +148,14 @@ export class GeneralDataService {
return from([]);
}
let params = new HttpParams().set('q', term).append('inactive', inactive.toString());

return this.loadFromApi('search/autocomplete', params).pipe(
map(response => {
let ret = [];
for (let row of response['results']) {
let found = null;
for (let name of row.names) {
if (~name.text.toLowerCase().indexOf(term.toLowerCase())) {
found = name.text;
break;
} else if (found === null) {
found = name.text;
}
}
if (found !== null) {
ret.push({ id: row.id, term: found });
}
}
return ret;
}),
);
return this.loadFromApi('v3/search/autocomplete', params)
.pipe(
map(({ results }: { results? }) => results || []),
map(results => results.map(row => ({
id: row.id,
term: row.value
})))
);
}

makeHttpParams(query?: { [key: string]: string } | HttpParams) {
Expand Down Expand Up @@ -270,6 +257,8 @@ export class GeneralDataService {
count: optitem.count,
};
}
} else if (optname === 'credential_type_id') {
optval.label = this._translate.instant(`name.${optval.label}`)
}
if (optidx in options) {
options[optidx].push(optval);
Expand Down
40 changes: 24 additions & 16 deletions client/src/app/search/advanced-search.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export interface IAdvancedSearchOption {

const FilterSpec = [
{
name: "name",
name: "q",
alias: "query",
hidden: true
},
{
Expand All @@ -26,7 +27,7 @@ const FilterSpec = [
label: "cred.issuer"
},
{
name: "topic_credential_type_id",
name: "credential_type_id",
label: "cred.cred-type"
},
{
Expand All @@ -43,7 +44,7 @@ const FilterSpec = [
options: [
{
tlabel: "general.show-inactive",
value: "true"
value: "any"
}
],
defval: "false",
Expand Down Expand Up @@ -77,7 +78,8 @@ export class AdvancedSearchComponent implements OnInit, OnDestroy {
credentialTypeOptions$: Observable<ISelectOption[]>;

yesNoOptions: ISelectOption[] = [
{ value: 'true', description: 'Yes' }
{ value: 'false', description: 'No' },
{ value: 'any', description: 'Yes' }
];

/* TODO: Parameterize these to include a method of defining the input option */
Expand All @@ -87,9 +89,6 @@ export class AdvancedSearchComponent implements OnInit, OnDestroy {
{ label: 'historical credentials', helper: 'Include results that have expired or are no longer active.' },
];

yesNoSelected: ISelectOption = { value: 'false', description: 'No' }
credentialTypeSelected: ISelectOption = { value: '', description: 'any' };

fg: FormGroup = new FormGroup({
text: new FormControl(''),
type: new FormControl(''),
Expand Down Expand Up @@ -121,6 +120,15 @@ export class AdvancedSearchComponent implements OnInit, OnDestroy {
return parseInt(this._currentPage, 10);
}

private get _queryParams(): { text: string, type: string, archived: string } {
const queryParamMap: any = this.route.snapshot.queryParamMap;
const queryParams: any = this.route.snapshot.queryParams;
const q = queryParamMap.get('q') || queryParamMap.get('query') || '';
const credential_type_id = queryParamMap.get('credential_type_id') || '';
const inactive = queryParamMap.get('inactive') || 'false';
return { ...queryParams, q, credential_type_id, inactive };
}

get filters(): Filter.FieldSet {
return this._filters;
}
Expand All @@ -139,7 +147,11 @@ export class AdvancedSearchComponent implements OnInit, OnDestroy {
map(res => res.data.map(type => ({
value: type.id,
description: type.description
})))
}))),
map(credentialTypeOptions => [
{ value: '', description: 'any' },
...credentialTypeOptions
])
);

this.credentials$ = this._cLoader.stream
Expand Down Expand Up @@ -176,17 +188,13 @@ export class AdvancedSearchComponent implements OnInit, OnDestroy {
}

private patchForm(): void {
const queryParamMap: any = this.route.snapshot.queryParamMap;
this.fg.patchValue({
text: queryParamMap.get('name') || '',
type: queryParamMap.get('topic_credential_type_id') || '',
archived: queryParamMap.get('inactive') || 'false'
});
const { q: text, credential_type_id: type, inactive: archived }: any = this._queryParams;
this.fg.patchValue({ text, type, archived });
}

private updateFilters(): void {
const { text: name, archived: inactive, type: topic_credential_type_id } = this.fg.value;
this._filters.update({ name, inactive, topic_credential_type_id, page: this._currentPage });
const { text: q, archived: inactive, type: credential_type_id } = this.fg.value;
this._filters.update({ ...this._queryParams, q, inactive, credential_type_id, page: this._currentPage });
}

private updateUrl() {
Expand Down
32 changes: 7 additions & 25 deletions client/src/app/search/form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Subscription } from 'rxjs';

const FilterSpec = [
{
name: "name",
name: "q",
alias: "query",
hidden: true
},
Expand All @@ -22,10 +22,6 @@ const FilterSpec = [
name: "issuer_id",
label: "cred.issuer"
},
{
name: "topic_credential_type_id",
label: "cred.cred-type"
},
{
name: "category:entity_type",
label: "attribute.entity_type"
Expand All @@ -36,26 +32,12 @@ const FilterSpec = [
options: [
{
tlabel: "general.show-inactive",
value: ""
}
],
defval: "false",
blank: true
}
/*
{
name: "revoked",
label: "cred.status",
options: [
{
tlabel: "general.show-revoked",
value: ""
value: "any"
}
],
defval: "false",
blank: true
}
*/
];

@Component({
Expand Down Expand Up @@ -131,7 +113,7 @@ export class SearchComponent implements OnInit, OnDestroy, AfterViewInit {

ngAfterViewInit() {
if(this._searchInput) {
this._searchInput.value = this._filters.getFieldValue('name');
this._searchInput.value = this._filters.getFieldValue('q');
}
}

Expand Down Expand Up @@ -198,26 +180,26 @@ export class SearchComponent implements OnInit, OnDestroy, AfterViewInit {
this._loader.reset();
this._refresh = true;
this._filters.update({
name: this._searchInput.value,
q: this._searchInput.value,
page: '1'
});
}
}

public initSearch() {
if(this._searchInput)
this._searchInput.value = this._filters.getFieldValue('name');
this._searchInput.value = this._filters.getFieldValue('q');
this._inited = true;
this._performSearch();
}

get blankQuery() {
return this._filters.getFieldValue('name') === '';
return this._filters.getFieldValue('q') === '';
}

_performSearch() {
let query = this._filters.values;
if('name' in query && query['name'] !== '') {
if('q' in query && query['q'] !== '') {
this._dataService.loadList(this._loader, {query});
} else {
this._loader.reset();
Expand Down
10 changes: 6 additions & 4 deletions client/src/app/search/input.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Component, AfterViewInit, ElementRef, EventEmitter, Input, Output, Renderer2, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { catchError, debounceTime, distinctUntilChanged, map, tap, switchMap } from 'rxjs/operators';
import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
import { GeneralDataService } from 'app/general-data.service';
import { HttpService } from 'app/core/services/http.service';
import { ICredentialTypeResult } from 'app/core/interfaces/icredential-type-results.interface';
import { Router } from '@angular/router';
import { AppConfigService } from 'app/app-config.service';

export interface ICredentialTypeOption {
Expand Down Expand Up @@ -86,9 +86,10 @@ export class SearchInputComponent implements AfterViewInit {
private _renderer: Renderer2,
private _dataService: GeneralDataService,
private httpSvc: HttpService,
private route: ActivatedRoute,
private router: Router,
private configSvc: AppConfigService,
) {}
) { }

async ngOnInit() {
const $categories = this.httpSvc
Expand Down Expand Up @@ -185,10 +186,11 @@ export class SearchInputComponent implements AfterViewInit {
}

advancedSearch() {
const queryParams = this.route.snapshot.queryParams;
const query = this._input.nativeElement.value;
const lang = this._dataService.language;

const nav = `/${lang}/advanced-search`;
query ? this.router.navigate([nav], { queryParams: { name: query } }) : this.router.navigate([nav]);
this.router.navigate([nav], { queryParams: { ...queryParams, query: query } });
}
}
34 changes: 16 additions & 18 deletions client/src/app/shared/components/select/select.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, OnInit, Input, forwardRef } from '@angular/core';
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Component, Input, forwardRef } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';

export interface ISelectOption {
value: string | boolean | number;
Expand All @@ -19,36 +19,34 @@ export interface ISelectOption {
},
],
})
export class SelectComponent implements OnInit, ControlValueAccessor {
export class SelectComponent implements ControlValueAccessor {
select = new FormControl('');

@Input() options: ISelectOption[];
@Input() selected: ISelectOption;
@Input() translateSelector: string;

value: any;
disabled: boolean;
onChange: (any) => void;
onTouch: (any) => void;

writeValue(obj: any): void {
this.value = obj;
constructor(public translate: TranslateService) { }

writeValue(value: any): void {
value && this.select.setValue(value);
}

registerOnChange(fn: any): void {
this.onChange = fn;
this.select.valueChanges.subscribe(fn);
}

registerOnTouched(fn: any): void {
this.onTouch = fn;
}

setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
isDisabled ? this.select.disable() : this.select.enable();
}

constructor(public translate: TranslateService) { }

ngOnInit() { }

changeEvent(value: string) {
this.value = value;
this.onChange(value);
this.select.setValue(value);
this.onTouch(value);
}
}
Loading