retriever renamed, added token to calls

This commit is contained in:
Lucio Lelii 2026-03-06 15:00:09 +01:00
parent 8a8f486eb5
commit dbe1ab4588
16 changed files with 177 additions and 42 deletions

View File

@ -61,6 +61,15 @@
"with": "src/environments/environment.development.ts"
}
]
},
"staging": {
"outputHashing": "all",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.staging.ts"
}
]
}
},
"defaultConfiguration": "production"
@ -73,6 +82,9 @@
},
"development": {
"buildTarget": "humainFlow-gui-a21:build:development"
},
"staging": {
"buildTarget": "humainFlow-gui-a21:build:staging"
}
},
"defaultConfiguration": "development"

View File

@ -1,11 +1,14 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideRouter } from '@angular/router';
import { authTokenInterceptor } from './interceptors/auth-token.interceptor';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideHttpClient(withInterceptors([authTokenInterceptor])),
provideRouter(routes)
]
};

View File

@ -0,0 +1,42 @@
import { HttpInterceptorFn } from '@angular/common/http';
import { Authorization } from '@services/authorization/authorization';
export const authTokenInterceptor: HttpInterceptorFn = (req, next) => {
const token = getToken();
if (!token) return next(req);
const requestPath = req.url.split('?')[0];
const isAuthEndpoint =
requestPath.endsWith('/auth/login') ||
requestPath.endsWith('/auth/register');
if (isAuthEndpoint) {
return next(req);
}
if (req.headers.has('Authorization')) {
return next(req);
}
return next(
req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
})
);
};
function getToken(): string | null {
const directToken = localStorage.getItem(Authorization.TOKEN_STORAGE_KEY);
if (directToken) return directToken;
const rawUser = localStorage.getItem(Authorization.USER_STORAGE_KEY);
if (!rawUser) return null;
try {
const parsed = JSON.parse(rawUser) as { token?: unknown };
return typeof parsed?.token === 'string' && parsed.token.length > 0 ? parsed.token : null;
} catch {
return null;
}
}

View File

@ -1,9 +1,10 @@
export type User = {
username: string;
email: string;
token?: string;
};
export type UserRegistration = User & {
password: string;
fullname: string;
}
}

View File

@ -1,15 +1,48 @@
import { AuthorizationCallServiceBase } from "./authorization-call.base";
import { User, UserRegistration } from "@models/user";
import { Observable } from "rxjs";
import { map, Observable } from "rxjs";
import { HttpClient } from "@angular/common/http";
import { inject } from "@angular/core";
import { environment } from "@environment";
export class AuthorizationCallService extends AuthorizationCallServiceBase {
override login(_username: string, _password: string): Observable<User> {
throw new Error("Method not implemented.");
}
override register(_userRegistration: UserRegistration): Observable<void> {
throw new Error("Method not implemented.");
}
private readonly http = inject(HttpClient);
override login(username: string, password: string): Observable<User> {
return this.http
.post<unknown>(`${environment.apiUrl}/auth/login`, { username, password })
.pipe(
map((raw) => {
const payload = (raw ?? {}) as Record<string, unknown>;
const userSource =
(payload["user"] as Record<string, unknown> | undefined)
?? (payload["profile"] as Record<string, unknown> | undefined)
?? payload;
const token = this.extractToken(payload, userSource);
return {
username: String(userSource["username"] ?? username),
email: String(userSource["email"] ?? ''),
token: typeof token === "string" && token.length > 0 ? token : undefined
} satisfies User;
})
);
}
override register(userRegistration: UserRegistration): Observable<void> {
return this.http.post<void>(`${environment.apiUrl}/auth/register`, userRegistration);
}
private extractToken(...sources: Array<Record<string, unknown> | undefined>): unknown {
const tokenKeys = ['token', 'accessToken', 'access_token', 'jwt', 'id_token'];
for (const source of sources) {
if (!source) continue;
for (const key of tokenKeys) {
const value = source[key];
if (typeof value === 'string' && value.length > 0) {
return value;
}
}
}
return undefined;
}
}

View File

@ -8,6 +8,8 @@ import { take, tap } from 'rxjs';
providedIn: 'root',
})
export class Authorization {
static readonly USER_STORAGE_KEY = 'loggedInUser';
static readonly TOKEN_STORAGE_KEY = 'authToken';
private user = signal<User | null>(null);
@ -20,7 +22,12 @@ export class Authorization {
take(1),
tap(res => {
this.user.set(res);
localStorage.setItem('loggedInUser', JSON.stringify(res));
localStorage.setItem(Authorization.USER_STORAGE_KEY, JSON.stringify(res));
if (res.token) {
localStorage.setItem(Authorization.TOKEN_STORAGE_KEY, res.token);
} else {
localStorage.removeItem(Authorization.TOKEN_STORAGE_KEY);
}
})
);
}
@ -32,12 +39,17 @@ export class Authorization {
}
logout() {
localStorage.removeItem('loggedInUser');
localStorage.removeItem(Authorization.USER_STORAGE_KEY);
localStorage.removeItem(Authorization.TOKEN_STORAGE_KEY);
this.user.set(null);
}
token(): string | null {
return localStorage.getItem(Authorization.TOKEN_STORAGE_KEY);
}
isLoggedIn(): boolean {
const storedUser = localStorage.getItem('loggedInUser');
const storedUser = localStorage.getItem(Authorization.USER_STORAGE_KEY);
this.user.set(storedUser != null ? JSON.parse(storedUser) : null);
return storedUser != null;
}

View File

@ -34,8 +34,15 @@ export function flowFromApi(raw: unknown): Flow {
};
}
export function toFlowCreateRequest(name: string) {
return { name };
export function toFlowCreateRequest(name: string, description?: string, flow?: FlowData) {
return {
name,
description: description ?? '',
flow: flow ?? {
blocks: [],
connections: []
}
};
}
export function toFlowUpdateRequest(flow: Flow) {

View File

@ -1,6 +1,6 @@
import { Observable } from "rxjs";
export abstract class FieldRetreiverCallServiceBase {
export abstract class FieldRetrieverCallServiceBase {
abstract retrieveValues(
blockType: string,
key: string,

View File

@ -1,7 +1,7 @@
import { Observable, of } from "rxjs";
import { FieldRetreiverCallServiceBase } from "./field-retreiver-call.base";
import { FieldRetrieverCallServiceBase } from "./field-retriever-call.base";
export class FieldRetreiverCallServiceFake extends FieldRetreiverCallServiceBase {
export class FieldRetrieverCallServiceFake extends FieldRetrieverCallServiceBase {
private readonly providersByBlockType: Record<string, string[]> = {
LLM: ["OpenAI", "Anthropic", "OllamaTestProvider"],
};

View File

@ -2,21 +2,34 @@ import { HttpClient, HttpParams } from "@angular/common/http";
import { inject } from "@angular/core";
import { environment } from "@environment";
import { Observable } from "rxjs";
import { FieldRetreiverCallServiceBase } from "./field-retreiver-call.base";
import { FieldRetrieverCallServiceBase } from "./field-retriever-call.base";
export class FieldRetreiverCallService extends FieldRetreiverCallServiceBase {
export class FieldRetrieverCallService extends FieldRetrieverCallServiceBase {
private readonly http = inject(HttpClient);
private buildParams(context?: Record<string, string>) {
let params = new HttpParams();
const entries = Object.entries(context ?? {});
if (!entries.length) {
// New API marks `params` as required even when empty.
return params.set('params', '{}');
}
for (const [ctxKey, ctxValue] of entries) {
params = params.set(`params[${ctxKey}]`, ctxValue);
}
return params;
}
override retrieveValues(
blockType: string,
key: string,
context?: Record<string, string>
): Observable<string[]> {
const url = `${environment.apiUrl}/retriever/${encodeURIComponent(blockType)}/${encodeURIComponent(key)}`;
let params = new HttpParams();
for (const [ctxKey, ctxValue] of Object.entries(context ?? {})) {
params = params.set(ctxKey, ctxValue);
}
const params = this.buildParams(context);
return this.http.get<string[]>(url, { params });
}
@ -26,10 +39,7 @@ export class FieldRetreiverCallService extends FieldRetreiverCallServiceBase {
context?: Record<string, string>
): Observable<boolean> {
const url = `${environment.apiUrl}/retriever/${encodeURIComponent(blockType)}/${encodeURIComponent(key)}/required`;
let params = new HttpParams();
for (const [ctxKey, ctxValue] of Object.entries(context ?? {})) {
params = params.set(ctxKey, ctxValue);
}
const params = this.buildParams(context);
return this.http.get<boolean>(url, { params });
}
}

View File

@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';
import { FieldRetreiver } from './field-retreiver';
import { FieldRetriever } from './field-retriever';
describe('FieldRetreiver', () => {
let service: FieldRetreiver;
describe('FieldRetriever', () => {
let service: FieldRetriever;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(FieldRetreiver);
service = TestBed.inject(FieldRetriever);
});
it('should be created', () => {

View File

@ -1,20 +1,20 @@
import { Injectable } from '@angular/core';
import { environment } from '@environment';
import { catchError, throwError } from 'rxjs';
import { FieldRetreiverCallServiceBase } from './field-retreiver-call.base';
import { FieldRetrieverCallServiceBase } from './field-retriever-call.base';
@Injectable({
providedIn: 'root',
})
export class FieldRetreiver {
fieldRetreiverCallService: FieldRetreiverCallServiceBase = new environment.fieldRetreiverCallService();
export class FieldRetriever {
fieldRetrieverCallService: FieldRetrieverCallServiceBase = new environment.fieldRetrieverCallService();
retrieveValues(
blockType: string,
key: string,
context?: Record<string, string>
) {
return this.fieldRetreiverCallService.retrieveValues(blockType, key, context).pipe(
return this.fieldRetrieverCallService.retrieveValues(blockType, key, context).pipe(
catchError((err) => {
console.error('Field retrieval failed', err);
return throwError(() => err);
@ -27,7 +27,7 @@ export class FieldRetreiver {
key: string,
context?: Record<string, string>
) {
return this.fieldRetreiverCallService.isFieldRequired(blockType, key, context).pipe(
return this.fieldRetrieverCallService.isFieldRequired(blockType, key, context).pipe(
catchError((err) => {
console.error('Field required check failed', err);
return throwError(() => err);

View File

@ -5,7 +5,7 @@ import { ClassicPreset } from 'rete';
import { ReteModule } from 'rete-angular-plugin/21';
import { NodeSettingsDialogService } from '@services/dialogs/node-settings-dialog';
import { EditorStateHolder } from '@stores/flow-editor';
import { FieldRetreiver as FieldRetriever } from '@services/retreiver/field-retreiver';
import { FieldRetriever } from '@services/retriever/field-retriever';
import { BlocksService } from '@services/blocks/blocks';
import { firstValueFrom } from 'rxjs';
import { ConditionalRequiredField, extractSchemaRequirements, SchemaRequirements } from '../schema-requirements';

View File

@ -1,7 +1,7 @@
import { AuthorizationCallFakeService } from "@services/authorization/authorization-call.fake";
import { BlocksCallServiceFake } from "@services/blocks/blocks-call.fake";
import { FlowsCallServiceFake } from "@services/flows/flows-call.fake";
import { FieldRetreiverCallServiceFake } from "@services/retreiver/field-retreiver-call.fake";
import { FieldRetrieverCallServiceFake } from "@services/retriever/field-retriever-call.fake";
import { TaskExecutionsCallServiceFake } from "@services/task-executions/task-executions-call.fake";
export const environment = {
@ -10,6 +10,6 @@ export const environment = {
authorizationCallService: AuthorizationCallFakeService, // Assign the appropriate service here
flowsCallService: FlowsCallServiceFake,
blocksCallService: BlocksCallServiceFake,
fieldRetreiverCallService: FieldRetreiverCallServiceFake,
fieldRetrieverCallService: FieldRetrieverCallServiceFake,
taskExecutionsCallService: TaskExecutionsCallServiceFake
};

View File

@ -0,0 +1,15 @@
import { AuthorizationCallService } from "@services/authorization/authorization-call";
import { BlocksCallService } from "@services/blocks/blocks-call";
import { FlowsCallService } from "@services/flows/flows-call";
import { FieldRetrieverCallService } from "@services/retriever/field-retriever-call";
import { TaskExecutionsCallService } from "@services/task-executions/task-executions-call";
export const environment = {
production: false,
apiUrl: 'http://localhost:8080',
authorizationCallService: AuthorizationCallService,
flowsCallService: FlowsCallService,
blocksCallService: BlocksCallService,
fieldRetrieverCallService: FieldRetrieverCallService,
taskExecutionsCallService: TaskExecutionsCallService
};

View File

@ -1,7 +1,7 @@
import { AuthorizationCallService } from "@services/authorization/authorization-call";
import { BlocksCallService } from "@services/blocks/blocks-call";
import { FlowsCallService } from "@services/flows/flows-call";
import { FieldRetreiverCallService } from "@services/retreiver/field-retreiver-call";
import { FieldRetrieverCallService } from "@services/retriever/field-retriever-call";
import { TaskExecutionsCallService } from "@services/task-executions/task-executions-call";
export const environment = {
@ -10,6 +10,6 @@ export const environment = {
authorizationCallService: AuthorizationCallService,
flowsCallService: FlowsCallService,
blocksCallService: BlocksCallService,
fieldRetreiverCallService: FieldRetreiverCallService,
fieldRetrieverCallService: FieldRetrieverCallService,
taskExecutionsCallService: TaskExecutionsCallService
};