heracles
Version:
Hydra Core hypermedia-aware client library
183 lines (142 loc) • 5.43 kB
text/typescript
'use strict';
import {promises as jsonld} from 'jsonld';
import * as nonenumerable from 'core-decorators/lib/nonenumerable';
import {JsonLd, Core, MediaTypes} from './Constants';
import {
IOperation, ISupportedOperation, IHeracles, IHydraResource, IClass, IResource,
IPartialCollectionView, IApiDocumentation
} from "./interfaces";
import {IIncomingLink} from "./internals";
const _isProcessed = new WeakMap<IResource, boolean>();
const _apiDocumentation = new WeakMap<IResource, IApiDocumentation>();
const _incomingLinks = new WeakMap<IResource, IIncomingLink[]>();
const _heracles = new WeakMap<IResource, IHeracles>();
const _supportedOperation = new WeakMap<IOperation, ISupportedOperation>();
const _resource = new WeakMap<IOperation, IResource>();
export class Resource implements IResource {
constructor(actualResource:any) {
Object.assign(this, actualResource);
_isProcessed.set(this, false);
}
get id() {
return this[JsonLd.Id];
}
get types() {
let types = this[JsonLd.Type];
if(typeof types === 'string'){
return [ types ];
}
return types;
}
get _processed() {
return _isProcessed.get(this);
}
set _processed(val:boolean) {
_isProcessed.set(this, val);
}
compact(context:any = null) {
return jsonld.compact(this, context || Core.Context);
}
}
export class HydraResource extends Resource implements IHydraResource {
constructor(heracles:IHeracles, actualResource, apiDoc:IApiDocumentation, incomingLinks) {
super(actualResource);
_apiDocumentation.set(this, apiDoc);
_incomingLinks.set(this, incomingLinks);
_heracles.set(this, heracles);
}
get apiDocumentation(): IApiDocumentation {
return _apiDocumentation.get(this);
}
get _heracles() {
return _heracles.get(this);
}
getIncomingLinks():Array<IIncomingLink> {
return _incomingLinks.get(this);
}
get operations() {
let classOperations;
if(Array.isArray(this[JsonLd.Type])) {
classOperations = this[JsonLd.Type].map((type:string) => this.apiDocumentation.getOperations(type));
} else {
classOperations = [ this.apiDocumentation.getOperations(this[JsonLd.Type]) ];
}
const mappedLinks = this.getIncomingLinks()
.map(link => link.subject.types.map(type => ({type: type, predicate: link.predicate})));
const flattened = [].concat.apply([], mappedLinks);
const propertyOperations = flattened.map((link: any) => this.apiDocumentation.getOperations(link.type, link.predicate));
const operations = [].concat.apply([], [...classOperations, ...propertyOperations]);
return operations.map((supportedOperation:ISupportedOperation) => {
return new Operation(supportedOperation, this._heracles, this);
});
}
}
export class Operation implements IOperation {
constructor(supportedOperation: ISupportedOperation, heracles: IHeracles, resource: IHydraResource) {
if(!supportedOperation) {
throw new Error('Missing supportedOperation parameter');
}
if(!heracles) {
throw new Error('Missing heracles parameter');
}
_supportedOperation.set(this, supportedOperation);
_resource.set(this, resource);
_heracles.set(this, heracles);
}
get method():string {
return this._supportedOperation.method;
}
get expects():IClass {
return this._supportedOperation.expects;
}
get returns():IClass {
return this._supportedOperation.returns;
}
get requiresInput():boolean {
return this._supportedOperation.requiresInput;
}
get title():string {
return this._supportedOperation.title;
}
get description():string {
return this._supportedOperation.description;
}
get _supportedOperation():ISupportedOperation {
return _supportedOperation.get(this);
}
get _resource():IResource {
return _resource.get(this);
}
get _heracles():IHeracles {
return _heracles.get(this);
}
invoke(body:any, mediaType = MediaTypes.jsonLd) {
return this._heracles.invokeOperation(this, this._resource.id, body, mediaType);
}
}
export class PartialCollectionView extends HydraResource implements IPartialCollectionView {
get first() { return this[Core.Vocab.first] || null; }
get previous() { return this[Core.Vocab.previous] || null; }
get next() { return this[Core.Vocab.next] || null; }
get last() { return this[Core.Vocab.last] || null; }
get collection():IHydraResource {
const collectionLink = this.getIncomingLinks().find((linkArray: IIncomingLink) => {
return linkArray.predicate === Core.Vocab.view
});
return collectionLink ? collectionLink.subject : null
}
}