Skip to content

Commit

Permalink
[sc-7419] Support local NucliaDB as destination in Desktop app (#1050)
Browse files Browse the repository at this point in the history
* Allow to provide label sets as input in LabelField component

* [sc-7419] Support local NucliaDB as destination in Desktop app

* Fixes

* Fixes
  • Loading branch information
operramon authored Oct 9, 2023
1 parent e6dca90 commit fe27278
Show file tree
Hide file tree
Showing 17 changed files with 439 additions and 131 deletions.
62 changes: 62 additions & 0 deletions apps/desktop/desktop.babel
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,37 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>nucliadb-url</name>
<description/>
<comment/>
<translations>
<translation>
<language>ca-ES</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-ES</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>pt-PT</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>permanent-sync</name>
<description/>
Expand Down Expand Up @@ -1847,6 +1878,37 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>use-nucliadb</name>
<description/>
<comment/>
<translations>
<translation>
<language>ca-ES</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-ES</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>pt-PT</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/electron/src/service/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import express, { Express } from 'express';
import cors from 'cors';
import dns from 'node:dns';
import bodyParser from 'body-parser';
import { router } from './routes';
import { sync } from './sync';
Expand All @@ -23,6 +24,8 @@ export const initSyncService = () => {
});
});
}
// Allow requests to "localhost" domain (https://github.com/nodejs/node/issues/40702)
dns.setDefaultResultOrder('ipv4first');

app.use(
cors({
Expand Down
17 changes: 15 additions & 2 deletions apps/desktop/src/app/sync/destinations/nuclia-cloud.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Account, INuclia, Nuclia, NucliaOptions, WritableKnowledgeBox } from '@nuclia/core';
import { Account, IKnowledgeBoxItem, INuclia, Nuclia, NucliaOptions, WritableKnowledgeBox } from '@nuclia/core';
import { catchError, from, map, Observable, of, switchMap, tap } from 'rxjs';
import {
baseLogoPath,
Expand Down Expand Up @@ -114,7 +114,20 @@ class NucliaCloudKBImpl implements IDestinationConnector {
}

private getKbField(): Observable<Field> {
return this.nuclia.db.getKnowledgeBoxes(localStorage.getItem(ACCOUNT_KEY) || '').pipe(
const request: Observable<IKnowledgeBoxItem[]> = this.nuclia.options.standalone
? this.nuclia.db.getStandaloneKbs().pipe(
map((kbs) =>
kbs.map((kb) => ({
id: kb.uuid,
zone: '',
slug: kb.slug,
title: kb.slug,
role_on_kb: 'SOWNER',
})),
),
)
: this.nuclia.db.getKnowledgeBoxes(localStorage.getItem(ACCOUNT_KEY) || '');
return request.pipe(
map((kbs) => ({
id: 'kb',
label: 'Knowledge Box',
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/app/sync/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export interface SearchResults {
}

export interface ConnectorSettings {
[key: string]: string;
[key: string]: any;
}

export interface ConnectorParameters {
Expand Down
63 changes: 38 additions & 25 deletions apps/desktop/src/app/sync/sync.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ export class SyncService {
}
return (instances[instance] as ReplaySubject<ISourceConnector>).asObservable();
}
getDestination(id: string): Observable<IDestinationConnector> {
return this.destinations[id].definition.factory(this.destinations[id].settings);
getDestination(id: string, settings: ConnectorSettings = {}): Observable<IDestinationConnector> {
return this.destinations[id].definition.factory({ ...this.destinations[id].settings, ...settings });
}

setSourceData(sourceId: string, source: Source): Observable<void> {
Expand Down Expand Up @@ -232,30 +232,43 @@ export class SyncService {
);
}

setSourceDestination(sourceId: string, kbId: string, labels?: Classification[]): Observable<void> {
setSourceDestination(
sourceId: string,
kbId: string,
localBackend?: string,
labels?: Classification[],
): Observable<void> {
return this.getSourceData(sourceId).pipe(
switchMap((source) =>
this.getKb(kbId).pipe(
switchMap((kb) => {
if (source.kb && source.kb.knowledgeBox === kb.id && source.kb.apiKey) {
return of(source);
} else {
return this.getNucliaKey(kb).pipe(
map((data) => ({
...source,
labels,
kb: {
zone: this.sdk.nuclia.options.zone,
backend: this.sdk.nuclia.options.backend,
knowledgeBox: data.kbid,
apiKey: data.token,
} as NucliaOptions,
})),
);
}
}),
),
),
switchMap((source) => {
if (localBackend) {
return of({
...source,
labels,
kb: { standalone: true, zone: '', backend: localBackend, account: 'local', knowledgeBox: kbId },
});
} else {
return this.getKb(kbId).pipe(
switchMap((kb) => {
if (source.kb && source.kb.knowledgeBox === kb.id && source.kb.apiKey) {
return of(source);
} else {
return this.getNucliaKey(kb).pipe(
map((data) => ({
...source,
labels,
kb: {
zone: this.sdk.nuclia.options.zone,
backend: this.sdk.nuclia.options.backend,
knowledgeBox: data.kbid,
apiKey: data.token,
} as NucliaOptions,
})),
);
}
}),
);
}
}),
switchMap((source) => this.setSourceData(sourceId, source)),
);
}
Expand Down
115 changes: 75 additions & 40 deletions apps/desktop/src/app/upload/settings/settings.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,48 +79,83 @@ <h2 class="unit-test">{{ 'upload.source.settings' | translate }}</h2>

<div class="destination">
<div class="body-m">{{ 'upload.source.destination' | translate }}:</div>
<div
*ngIf="kbField.type === 'select'"
class="select-field-container">
<pa-select
formControlName="kb"
[label]="kbField.label"
(valueChange)="onSelectKb($event)">
<pa-option
*ngFor="let option of kbField.options"
[disabled]="option.disabled"
[value]="option.value">
{{ option.label }}
</pa-option>
</pa-select>
<pa-button
*ngIf="kbField.canBeRefreshed"
icon="refresh"
aspect="basic"
[paTooltip]="'upload.refresh' | translate: { field: kbField.label }"
(click)="refreshField(kbField.id)">
{{ 'upload.refresh' | translate: { field: kbField.label } }}
</pa-button>
</div>
<div
*ngIf="(hasLabelSets | async) === true"
class="field">
<div class="body-m">{{ 'upload.labels' | translate }}</div>
<app-label-field [(selection)]="selectedLabels"></app-label-field>
<div>
<pa-checkbox
formControlName="local"
(valueChange)="onToggleLocal($event)">
{{ 'upload.source.use-nucliadb' | translate }}
</pa-checkbox>
</div>
<div
*ngIf="(hasLabelSets | async) === false"
class="field">
<div class="body-m">
<a
[href]="dashboardUrl"
(click)="goToUrl($event, dashboardUrl)"
target="_blank"
rel="noopener noreferrer">
{{ 'upload.no-labels' | translate }}
</a>
<ng-container *ngIf="!local">
<div
*ngIf="kbField"
class="select-field-container">
<pa-select
formControlName="kb"
[label]="kbField.label">
<pa-option
*ngFor="let option of kbField.options"
[disabled]="option.disabled"
[value]="option.value">
{{ option.label }}
</pa-option>
</pa-select>
<pa-button
*ngIf="kbField.canBeRefreshed"
icon="refresh"
aspect="basic"
[paTooltip]="'upload.refresh' | translate : { field: kbField.label }"
(click)="refreshKbs(local)">
{{ 'upload.refresh' | translate : { field: kbField.label } }}
</pa-button>
</div>
</div>
</ng-container>
<ng-container *ngIf="local">
<div>
<pa-input
formControlName="localUrl"
placeholder="http://localhost:8080/api">
{{ 'upload.source.nucliadb-url' | translate }}
</pa-input>
</div>
<div
*ngIf="localKbField"
class="select-field-container">
<pa-select
formControlName="localKb"
[label]="localKbField.label">
<pa-option
*ngFor="let option of localKbField.options"
[disabled]="option.disabled"
[value]="option.value">
{{ option.label }}
</pa-option>
</pa-select>
</div>
</ng-container>
<ng-container *ngIf="(labelSets | async) !== null">
<div
*ngIf="(hasLabelSets | async) === true"
class="field">
<div class="body-m labels">{{ 'upload.labels' | translate }}</div>
<app-label-field
[labelSets]="labelSets | async"
[(selection)]="selectedLabels"></app-label-field>
</div>
<div
*ngIf="(hasLabelSets | async) === false"
class="field">
<div class="body-m">
<a
[href]="dashboardUrl"
(click)="goToUrl($event, dashboardUrl)"
target="_blank"
rel="noopener noreferrer">
{{ 'upload.no-labels' | translate }}
</a>
</div>
</div>
</ng-container>
</div>

<div class="buttons">
Expand Down
10 changes: 6 additions & 4 deletions apps/desktop/src/app/upload/settings/settings.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@ form {

.select-field-container {
display: flex;
gap: rhythm(2);
margin-bottom: $margin-bottom-field;

pa-select {
flex: 1 0 auto;
}
pa-button {
margin-left: rhythm(2);
}
}

.help {
Expand All @@ -54,10 +52,14 @@ form {
.destination {
display: flex;
flex-direction: column;
gap: rhythm(2);
gap: rhythm(3);
margin: rhythm(6) 0;
}

.labels {
margin-bottom: rhythm(1);
}

.buttons {
display: flex;
gap: rhythm(2);
Expand Down
Loading

0 comments on commit fe27278

Please sign in to comment.