Created
July 26, 2016 11:57
-
-
Save syul/6d24c7a748835203074cb9536cb97fae to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Inject} from '../decorators/inject'; | |
import {Factory} from '../decorators/factory'; | |
export interface IModelFactory { | |
attributes: any; | |
id: string; | |
getRelation(relation?: string): ng.IPromise<any>; | |
update(): ng.IPromise<any>; | |
delete(model: IModelFactory): ng.IPromise<any>; | |
add(model?: IModelFactory): ng.IPromise<any>; | |
}; | |
export interface IModelFactoryConstructor { | |
new (modelData?: Object | string): IModelFactory; | |
}; | |
export interface IRelation { | |
name: string; | |
url: string; | |
}; | |
export interface ILink { | |
name: string; | |
url: string; | |
}; | |
export interface IModel { | |
relations: IRelation[]; | |
links: ILink[]; | |
type: string; | |
attributes: any; | |
parent: string; | |
self: string; | |
id: string; | |
}; | |
interface IModelData extends Object { | |
data?: Object | Object[]; | |
links?: Object[]; | |
} | |
/** | |
* | |
* | |
* @export | |
* @class ModelFactory | |
* @implements {IModelFactory} | |
*/ | |
@Factory() | |
export class ModelFactory implements IModelFactory { | |
@Inject | |
private $timeout: ng.ITimeoutService; | |
@Inject | |
private $q: ng.IQService; | |
@Inject | |
private $http: ng.IHttpService; | |
@Inject | |
private $log: ng.ILogService; | |
@Inject | |
private $rootScope: ng.IRootScopeService; | |
private type: string = ''; | |
public id: string = ''; | |
private parent: string = ''; | |
private self: string = ''; | |
private links: ILink[] = []; | |
public attributes: any = {}; | |
private relations: IRelation[] = []; | |
/** | |
* Creates an instance of ModelFactory. | |
* | |
* @param {(Object | string)} [modelData] | |
*/ | |
constructor(modelData?: Object | string) { | |
if (angular.isObject(modelData)) { | |
let result: IModel = this.parseModel(angular.copy(modelData)); | |
this.relations = result.relations; | |
this.type = result.type; | |
this.attributes = result.attributes; | |
this.parent = result.parent; | |
this.self = result.self; | |
this.id = result.id; | |
this.links = result.links; | |
} else if (angular.isString(modelData)) { | |
this.self = modelData as string; | |
} | |
}; | |
/** | |
* Get relations from the raw model data | |
* | |
* @private | |
* @param {*} modelData | |
* @returns {IRelation[]} | |
*/ | |
private getRelations(modelData: any): IRelation[] { | |
let relationships: IRelation[] = []; | |
angular.forEach(modelData.data.relationships, (value, key) => { | |
relationships.push({ | |
name: key, | |
url: value.links.related | |
}); | |
}); | |
return relationships; | |
}; | |
/** | |
* Get links from the raw model data | |
* | |
* @private | |
* @param {*} modelData | |
* @returns {ILink[]} | |
*/ | |
private getLinks(modelData: any): ILink[] { | |
let links: ILink[] = []; | |
angular.forEach(modelData.links, (value, key) => { | |
links.push({ | |
name: key, | |
url: value | |
}); | |
}); | |
return links; | |
} | |
/** | |
* Parces raw model data and fills out properties of the current instance | |
* | |
* @private | |
* @param {*} modelData | |
* @returns {IModel} | |
*/ | |
private parseModel(modelData: any): IModel { | |
return { | |
relations: this.getRelations(modelData), | |
links: this.getLinks(modelData), | |
type: modelData.data.type, | |
attributes: modelData.data.attributes, | |
parent: modelData.links.self, | |
self: modelData.data.links.self, | |
id: modelData.data.id | |
}; | |
}; | |
/** | |
* Gets model method url by given method name | |
* | |
* @param {string} linkName | |
* @returns {string} | |
*/ | |
public getLink(linkName: string): string { | |
let filtered: ILink[] = this.links.filter((el: ILink) => { | |
return el.name === linkName; | |
}); | |
if (filtered.length) { | |
return filtered[0].url; | |
} else { | |
return undefined; | |
} | |
}; | |
/** | |
* Every model has link property that represents all methods linked with this model. | |
* Current method invokes required method by given method name | |
* | |
* @param {string} methodName | |
* @param {(string | Object)} param1 | |
* @param {Object} [param2] | |
* @returns {ng.IPromise<any>} | |
*/ | |
public invokeMethod(methodName: string, param1: string | Object, param2?: Object): ng.IPromise<any> { | |
let deferred: ng.IDeferred<any> = this.$q.defer(); | |
let methodType: string = 'GET'; | |
let methodParameters: Object = {}; | |
//define the method type and method parameters regarding to passed arguments | |
if (param2) { | |
methodParameters = angular.isObject(param2) ? param2 : methodParameters; | |
methodType = angular.isString(param1) ? param1 as string : methodType; | |
} else if (param1) { | |
methodParameters = angular.isObject(param1) ? param1 : methodParameters; | |
methodType = angular.isString(param1) ? param1 as string : methodType; | |
}; | |
if (methodName) { | |
// find the required url by the given method name | |
let methodUrl: string = ''; | |
this.links.forEach((element: ILink) => { | |
if (element.name === methodName) { | |
methodUrl = element.url; | |
}; | |
}); | |
// if method was not found reject promise with error message otherwice procced http request | |
// according to passed parameters and method type | |
if (!methodUrl) { | |
deferred.reject({ message: `Could not find method with name ${methodName}` }); | |
} else { | |
this.$http({ | |
method: methodType, | |
url: methodUrl, | |
data: methodType != 'GET' ? methodParameters : {}, | |
params: methodType === 'GET' ? methodParameters : {} | |
}).then((result) => { | |
deferred.resolve(result.data); | |
}).catch((err) => { | |
deferred.reject(err); | |
}); | |
} | |
} else { | |
deferred.reject({ message: "Has not passed the method name parameter" }); | |
} | |
return deferred.promise; | |
}; | |
/** | |
* Create a single model according to the given raw model data | |
* | |
* @private | |
* @param {Object} modelData | |
* @returns {IModelFactory} | |
*/ | |
private createSingleModel(modelData: IModelData): IModelFactory { | |
let modelFactoryConstructor: IModelFactoryConstructor = (ModelFactory as Function)(); | |
return new modelFactoryConstructor(modelData); | |
}; | |
/** | |
* Create a collection of the models according to the passed array | |
* of raw models data | |
* | |
* @private | |
* @param {Object[]} collectionData | |
* @returns {IModelFactory[]} | |
*/ | |
private createCollection(collectionData: IModelData): IModelFactory[] { | |
let modelFactoryConstructor: IModelFactoryConstructor = (ModelFactory as Function)(); | |
return (collectionData.data as Object[]).map((el: Object) => { | |
let modelData: IModelData = { | |
data: el, | |
links: collectionData.links | |
} | |
return new modelFactoryConstructor(modelData); | |
}) as IModelFactory[]; | |
}; | |
/** | |
* According to the api realization there are some ways to get the model instance. | |
* The first way is to get the model by the relation name and the second way to | |
* build the model using direct url passed to the constructor. | |
* | |
* @param {(string | Object)} [param1] - relation name or parameters to get model | |
* using direct url | |
* @param {Object} [param2] - parameters to get model using relation name | |
* @returns {ng.IPromise<any>} | |
*/ | |
public getRelation(param1?: string | Object, param2?: Object): ng.IPromise<any> { | |
let deferred: ng.IDeferred<any> = this.$q.defer(); | |
//target url to get the data - it may be url related to current model | |
//as well as the url of the related to current model resource if the name | |
//of the resource is passed. By default we are working with current model | |
let targetUrl: string = this.self; | |
//parameters for http request by default is using empty object we do | |
//not have any additional parameters by default | |
let requestParameters: Object = {}; | |
//the name of the resource related to current model. By default it is | |
//an ampty string consider that we are working with current model | |
let relationName: string = ''; | |
//first parameter is an object in this case consider it as a filter for http request | |
if (angular.isObject(param1)) { | |
requestParameters = param1; | |
//first parameter is a string in this case consider it as a name of the related resource | |
} else if (angular.isString(param1)) { | |
relationName = param1 as string; | |
requestParameters = param2 ? param2 : undefined; | |
let filtered: IRelation[] = this.relations.filter((el): boolean => { | |
return el.name == relationName; | |
}); | |
if (filtered.length > 1) { | |
this.$log.warn(`Model ${this.self} has more than one relation with name ${relationName} the only first relation will be procceded.`); | |
} | |
let currentRelation: IRelation = filtered.length ? filtered[0] : undefined; | |
if (!currentRelation) { | |
throw new Error(`Could not find relation with name ${relationName}`); | |
} | |
targetUrl = currentRelation.url; | |
} | |
//make a request to get the model data according to passed parameters | |
this.$http.get(targetUrl, { params: requestParameters }).then((response) => { | |
let modelData: IModelData = response.data; | |
//create collection | |
if (angular.isArray(modelData.data)) { | |
deferred.resolve(this.createCollection(modelData)); | |
} else if (angular.isObject(modelData.data)) { | |
deferred.resolve(this.createSingleModel(modelData)); | |
} else { | |
throw new Error(`Incorrect structure of the model data of relation ${relationName}`); | |
} | |
}).catch((err) => { | |
deferred.reject(err); | |
}); | |
//return promise | |
return deferred.promise; | |
}; | |
/** | |
* Gets named relation by it's id | |
* | |
* @param {string} relationName | |
* @param {string} relationId | |
* @returns {ng.IPromise<any>} | |
*/ | |
public getRelationById(relationName: string, relationId: string): ng.IPromise<any> { | |
let deferred: ng.IDeferred<any> = this.$q.defer(); | |
let filtered: IRelation[] = this.relations.filter((el): boolean => { | |
return el.name == relationName; | |
}); | |
if (filtered.length > 1) { | |
this.$log.warn(`Model ${this.self} has more than one relation with name ${relationName} the only first relation will be procceded.`); | |
} | |
let currentRelation: IRelation = filtered.length ? filtered[0] : undefined; | |
if (!currentRelation) { | |
throw new Error(`Could not find relation with name ${relationName}`); | |
} | |
let targetUrl: string = currentRelation.url + `/${relationId}`; | |
this.$http.get(targetUrl).then((response) => { | |
let modelData: IModelData = response.data; | |
//create collection | |
if (angular.isArray(modelData.data)) { | |
deferred.resolve(this.createCollection(modelData)); | |
} else if (angular.isObject(modelData.data)) { | |
console.log(`http request ${this.$rootScope.$$phase}`); | |
deferred.resolve(this.createSingleModel(modelData)); | |
} else { | |
throw new Error(`Incorrect structure of the model data of relation ${relationName}`); | |
} | |
}).catch((err) => { | |
deferred.reject(err); | |
}); | |
//return promise | |
return deferred.promise; | |
} | |
public serialize(): any { | |
return { | |
data: { | |
type: this.type, | |
id: this.id, | |
attributes: this.attributes | |
} | |
}; | |
}; | |
/** | |
* Updates the current model | |
* | |
* @returns {ng.IPromise<any>} | |
*/ | |
public update(): ng.IPromise<any> { | |
let deferred = this.$q.defer(); | |
this.$http.patch(this.self, this.serialize()).then((result: any) => { | |
deferred.resolve(result); | |
}).catch((err: any) => { | |
deferred.reject(err); | |
}); | |
return deferred.promise; | |
}; | |
/** | |
* Adds new element to the model collection | |
* Is not implemented yet | |
* | |
* @param {IModelFactory} model | |
* @returns {ng.IPromise<any>} | |
*/ | |
public add(model: IModelFactory): ng.IPromise<any> { | |
let deferred = this.$q.defer(); | |
deferred.resolve(); | |
return deferred.promise; | |
}; | |
/** | |
* Removes current element from the collection | |
* Is not implemented yet | |
* | |
* @param {IModelFactory} [model] | |
* @returns {ng.IPromise<any>} | |
*/ | |
public delete(model?: IModelFactory): ng.IPromise<any> { | |
let deferred = this.$q.defer(); | |
deferred.resolve(); | |
return deferred.promise; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment