diff --git a/src/Metadata/OperationDef.php b/src/Metadata/OperationDef.php index a4427f04d32eaf3aa43d0215b54291059cbb73da..a414781a0c000c13e3a196393f80c98e7572e90b 100644 --- a/src/Metadata/OperationDef.php +++ b/src/Metadata/OperationDef.php @@ -179,6 +179,19 @@ final class OperationDef implements \JsonSerializable, HasName return $this->isCollection; } + /** + * @return string + */ + public function __toString(): string + { + return sprintf('operation "%s" (method=%s, isCollection=%s, special=%s)', + $this->originalName, + $this->method, + $this->isCollection ? "yes" : "no", + $this->special + ); + } + /** * {@inheritdoc} */ diff --git a/src/OperationMapper.php b/src/OperationMapper.php index d820618be59a43a62323e02391c31f212fe7a4a7..9e3b1b872e77890924c6a8043885ef32a688e332 100644 --- a/src/OperationMapper.php +++ b/src/OperationMapper.php @@ -104,12 +104,9 @@ final class OperationMapper } $opParameters = $body = []; - $callParameters = [TypescriptHelper::quoteString($this->operation->getMethod())]; $callPath = $path = $this->pathParser->parse($this->operation->getPath(), $this->operation->getRequirements()); - $iriParameterName = null; - if ($this->iri) { $iriParameter = new Parameter('iri', Placeholder::get(sprintf('IRI<%s>', $this->operation->getClassName()))); @@ -118,14 +115,12 @@ final class OperationMapper if ($callPath === $this->iri) { $opParameters[] = $iriParameter; - $iriParameterName = $iriParameter->getName(); - $callParameters[] = $iriParameterName . ' as any'; + $urlParameter = $iriParameter->getName(); } else { foreach ($callPath->getParameters() as $parameter) { $opParameters[] = $parameter; } - $body[] = 'const path = ' . $callPath->getUsage() . ';'; - $callParameters[] = 'path'; + $urlParameter = $callPath->getUsage(); } $denormalization = $this->operation->getDenormalization(); @@ -137,7 +132,6 @@ final class OperationMapper } $opParameters[] = new Parameter('body', $requestBody); - $body[] = 'options.body = body;'; } $filterProperties = $this->getFilterProperties(); @@ -148,11 +142,10 @@ final class OperationMapper } $opParameters[] = new Parameter('options', Placeholder::get('RequestOptions'), false, '{}'); - $callParameters[] = 'options'; - $returnType = $responseBody ?: Placeholder::get('HttpResponseBase'); + $returnType = $responseBody ?: BuiltinType::get('void'); - $body[] = sprintf('return %s;', $this->buildClientCall($callParameters, $returnType, $iriParameterName)); + $body[] = sprintf('return %s;', $this->buildClientCall(TypescriptHelper::quoteString($this->operation->getMethod()), $urlParameter, 'body', 'options', $returnType)); return new Operation( $this->operation->getName(), @@ -186,41 +179,41 @@ final class OperationMapper } /** - * @param array $callParameters - * @param Type $returnType - * @param string|null $iriParameterName + * @param string $method + * @param string $path + * @param string $body + * @param string $options + * @param Type $returnType * * @return string */ - private function buildClientCall(array $callParameters, Type $returnType, ?string $iriParameterName): string + private function buildClientCall(string $method, string $path, string $body, string $options, Type $returnType): string { - $clientCall = sprintf( - 'this.client.request<%s>(%s)', - $returnType->getUsage(), - implode(', ', $callParameters) - ); - $opDef = $this->operation->getOpDef(); + if ($opDef->isDeleteItem()) { + return "this.client.deleteItem($path, $options)"; + } + + $type = $returnType->getUsage(); + if ($opDef->isGetCollection()) { - return "this.cache.getAll($clientCall)"; + return "this.client.getCollection<$type>($path, $options)"; } + if ($opDef->isCreateItem()) { - return "this.cache.post($clientCall)"; + return "this.client.postItem<$type>($path, $body, $options)"; } - if ($iriParameterName) { - if ($opDef->isGetItem()) { - return "this.cache.get($iriParameterName, () => $clientCall)"; - } - if ($opDef->isUpdateItem()) { - return "this.cache.put($iriParameterName, $clientCall)"; - } - if ($opDef->isDeleteItem()) { - return "this.cache.delete($iriParameterName, $clientCall)"; - } + + if ($opDef->isUpdateItem()) { + return "this.client.putItem<$type>($path, $body, $options)"; + } + + if ($opDef->isGetItem()) { + return "this.client.getItem<$type>($path, $options)"; } - return $clientCall; + return "this.client.request<$type>($method, $path, $options)"; } /** diff --git a/src/Resources/views/common.ts.twig b/src/Resources/views/common.ts.twig index cb466fa681f649859c2a5a1857110612cda7b04c..504de8a4a1fb576627156aa286fbb7ad9cab2f8f 100644 --- a/src/Resources/views/common.ts.twig +++ b/src/Resources/views/common.ts.twig @@ -1,7 +1,6 @@ {% extends '@NgModelGenerator/_layout.ts.twig' %} {% block content %} -import {HttpClient, HttpHeaders, HttpResponseBase} from '@angular/common/http'; import { forkJoin, Observable } from 'rxjs'; /** @@ -46,7 +45,7 @@ export interface Resource { readonly [IRI_PROPERTY]: IRI<any>; readonly [TYPE_PROPERTY]: string; [property: string]: any; -}; +} /** * Collection représente une collection de respoucres JSON-LD pour un type T donné. @@ -92,62 +91,48 @@ export type DateTime = string; * Options supplémentaires pour les requêtes. */ export interface RequestOptions { - body?: any; - headers?: HttpHeaders | { - [header: string]: string | string[]; - }; + headers?: { [header: string]: string | string[]; }; params?: { [param: string]: string | string[]; }; } /** - * AbstractResourceCache est une classe abstraite qui définit l'interface d'un cache de ressources. - * - * Elle doit être implémentée puis être fournie en provider d'un module core. Par exemple: - * - * final class ResourceCache<T extends Resource> extends AbstractResourceCache<T> { - * // Implémentation - * } - * - * providers: [ - * [ provider: AbstractResourceCache, useClass: ResourceCache ], - * ] + * Client d'API, à implémenter. */ -export abstract class AbstractResourceCache { - /** - * Récupère une ressource par son IRI. N'exécute la requête requestFactory que si on ne dispose - * pas d'une version en cache. +export abstract class APIClient { + /** + * Récupère une ressource par son IRI. */ - public abstract get<R extends Resource>( - iri: IRI<R>, - requestFactory: () => Observable<R> - ): Observable<R>; + public abstract getItem<R extends Resource>(iri: IRI<R>, options?: RequestOptions): Observable<R>; /** - * Met à jour une ressource existante, rafraîchit le cache local avec la réponse. + * Met à jour une ressource existante. */ - public abstract put<R extends Resource>(iri: IRI<R>, request: Observable<R>): Observable<R>; + public abstract putItem<R extends Resource>(iri: IRI<R>, item: any, options?: RequestOptions): Observable<R>; /** * Crée une nouvelle ressource et met la ressource créée dans le cache. */ - public abstract post<R extends Resource>(request: Observable<R>): Observable<R>; + public abstract postItem<R extends Resource>(iri: string, item: any, options?: RequestOptions): Observable<R>; /** * Supprime une ressource en distant et dans le cache. */ - public abstract delete<R extends Resource>(iri: IRI<R>, request: Observable<HttpResponseBase>): Observable<HttpResponseBase>; + public abstract deleteItem<R extends Resource>(iri: IRI<R>, options?: RequestOptions): Observable<void>; /** * Effectue une recherche et met en cache toutes les ressources récupérées. */ - public abstract getAll<R extends Resource>( - request: Observable<Collection<R>> - ): Observable<Collection<R>>; + public abstract getCollection<C extends Collection<Resource>>(iri: string, options?: RequestOptions): Observable<C>; /** * Invalide une ressource en cache. */ - public abstract invalidate<R extends Resource>(iri: IRI<R>): void; + public abstract invalidateItem<R extends Resource>(iri: IRI<R>): void; + + /** + * Effectue une requête arbitraire. + */ + public abstract request<T>(method: string, url: string, options?: RequestOptions): Observable<T>; } /** @@ -249,8 +234,7 @@ export class ResourceMetadata<R extends Resource, T extends string, P extends IR export abstract class AbstractRepository<R extends Resource, T extends string, P extends IRIParameters> { public constructor( public readonly metadata: ResourceMetadata<R, T, P>, - protected readonly client: HttpClient, - protected readonly cache: AbstractResourceCache + protected readonly client: APIClient ) {} /** @@ -379,8 +363,7 @@ export abstract class AbstractAPIService< public constructor( public readonly metadata: MR, public readonly repositories: RR, - private readonly cache: AbstractResourceCache, - private readonly client: HttpClient + private readonly client: APIClient ) {} public get<R extends Resource>(iri: IRI<R>, options?: RequestOptions): Observable<R>; @@ -398,7 +381,7 @@ export abstract class AbstractAPIService< iri = typeOrIRI as IRI<R>; options = parametersOrOptions as RequestOptions; } - return this.cache.get(iri, () => this.client.get<R>(iri as any, options)); + return this.client.getItem<R>(iri, options); } public getMany<R extends Resource>(iris: IRI<R>[], options?: RequestOptions): Observable<R[]> { @@ -410,7 +393,7 @@ export abstract class AbstractAPIService< } public invalidate<R extends Resource>(iri: IRI<R>): void { - this.cache.invalidate(iri); + this.client.invalidateItem(iri); } } diff --git a/src/Resources/views/metadata.ts.twig b/src/Resources/views/metadata.ts.twig index ae30a26c5f579d0d62b687e3fd3c4e9a3f45404d..164042134de91014cc4cab008e13e32ce03d33b9 100644 --- a/src/Resources/views/metadata.ts.twig +++ b/src/Resources/views/metadata.ts.twig @@ -5,11 +5,10 @@ {% block content %} {% autoescape false %} import { Injectable, Provider } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; import { AbstractAPIService, - AbstractResourceCache, + APIClient, APIMeta, APIRepositoryRegistry, IRIMetadata, @@ -93,13 +92,12 @@ export class AppMetadata extends LazyMetadataRegistry<AppAPI> { @Injectable() export class AppRepositories implements APIRepositoryRegistry<AppAPI> { {% for repo in repositories %} - public readonly {{ repo.resourceName }} = new {{ repo.name }}(this.metadata.{{ repo.resourceName }}, this.client, this.cache); + public readonly {{ repo.resourceName }} = new {{ repo.name }}(this.metadata.{{ repo.resourceName }}, this.client); {% endfor %} public constructor( public readonly metadata: AppMetadata, - private readonly cache: AbstractResourceCache, - private readonly client: HttpClient + private readonly client: APIClient ) {} public get<T extends keyof AppAPI>(type: T): AppAPI[T]['repository'] { @@ -112,14 +110,6 @@ export class AppRepositories implements APIRepositoryRegistry<AppAPI> { */ @Injectable() export class AppAPIService extends AbstractAPIService<AppAPI, AppMetadata, AppRepositories> { - public constructor( - metadata: AppMetadata, - repositories: AppRepositories, - cache: AbstractResourceCache, - client: HttpClient - ) { - super(metadata, repositories, cache, client); - } } /* Provider factories */ diff --git a/src/Resources/views/repositories.ts.twig b/src/Resources/views/repositories.ts.twig index 688e7c0c2da98845ebee723c966dc4328309b331..c13f258347c260ebcffc527e4f7e566b316a0362 100644 --- a/src/Resources/views/repositories.ts.twig +++ b/src/Resources/views/repositories.ts.twig @@ -5,7 +5,6 @@ {% block content %} {% autoescape false %} import { Observable } from 'rxjs'; -import { HttpResponseBase } from '@angular/common/http'; // @ts-ignore import { AbstractRepository, Collection, IRI, RequestOptions, UUID } from './common';