Commit 211a8948 authored by Guillaume Perréal's avatar Guillaume Perréal
Browse files

Génération d'un APIClient au lieu d'utiliser une AbstractResourceCache.

No related merge requests found
Showing with 65 additions and 87 deletions
+65 -87
......@@ -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}
*/
......
......@@ -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)";
}
/**
......
{% 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);
}
}
......
......@@ -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 */
......
......@@ -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';
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment