nakedobjects.spa
Version:
Single Page Application client for a Naked Objects application.
283 lines • 12.9 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Injectable } from '@angular/core';
import { Request, RequestOptions, Headers, RequestMethod, ResponseContentType } from '@angular/http';
import * as Models from './models';
import { Subject } from 'rxjs/Subject';
import { ConfigService } from './config.service';
import { SimpleLruCache } from './simple-lru-cache';
import { AuthHttp } from 'angular2-jwt';
import 'rxjs/add/operator/toPromise';
import each from 'lodash/each';
import reduce from 'lodash/reduce';
var RepLoaderService = (function () {
function RepLoaderService(http, configService) {
var _this = this;
this.http = http;
this.configService = configService;
this.loadingCount = 0;
this.loadingCountSource = new Subject();
this.loadingCount$ = this.loadingCountSource.asObservable();
// use our own LRU cache
this.cache = new SimpleLruCache(this.configService.config.httpCacheDepth);
this.populate = function (model, ignoreCache) {
var response = model;
var config = new RequestOptions({
withCredentials: true,
url: model.getUrl(),
method: model.method,
body: model.getBody()
});
return _this.httpPopulate(config, !!ignoreCache, response);
};
this.retrieve = function (map, rc, digest) {
var response = new rc();
var config = _this.setConfigFromMap(map, digest);
return _this.httpPopulate(config, true, response);
};
this.validate = function (map, digest) {
var config = _this.setConfigFromMap(map, digest);
return _this.httpValidate(config);
};
this.retrieveFromLink = function (link, parms) {
if (link) {
var response = link.getTarget();
var urlParms = "";
if (parms) {
var urlParmString = reduce(parms, function (result, n, key) { return (result === "" ? "" : result + "&") + key + "=" + n; }, "");
urlParms = urlParmString !== "" ? "?" + urlParmString : "";
}
var config = new RequestOptions({
method: link.method(),
url: link.href() + urlParms,
withCredentials: true
});
return _this.httpPopulate(config, true, response);
}
return Promise.reject("link must not be null");
};
this.invoke = function (action, parms, urlParms) {
var invokeMap = action.getInvokeMap();
if (invokeMap) {
each(urlParms, function (v, k) { return invokeMap.setUrlParameter(k, v); });
each(parms, function (v, k) { return invokeMap.setParameter(k, v); });
return _this.retrieve(invokeMap, Models.ActionResultRepresentation);
}
return Promise.reject("attempting to invoke uninvokable action " + action.actionId());
};
this.clearCache = function (url) {
_this.cache.remove(url);
};
this.addToCache = function (url, m) {
_this.cache.add(url, m);
};
this.getFile = function (url, mt, ignoreCache) {
if (ignoreCache) {
// clear cache of existing values
_this.cache.remove(url);
}
else {
var blob = _this.cache.get(url);
if (blob) {
return Promise.resolve(blob);
}
}
var config = new RequestOptions({
method: "GET",
url: url,
responseType: ResponseContentType.Blob,
headers: new Headers({ "Accept": mt })
});
var request = new Request(config);
return _this.http.request(request)
.toPromise()
.then(function (r) {
var blob = r.blob();
_this.cache.add(config.url, blob);
return blob;
})
.catch(function (r) {
var originalUrl = config.url || "Unknown url";
r.url = r.url || originalUrl;
return _this.handleError(r, originalUrl);
});
};
this.uploadFile = function (url, mt, file) {
var config = new RequestOptions({
method: "POST",
url: url,
body: file,
headers: new Headers({ "Content-Type": mt })
});
var request = new Request(config);
return _this.http.request(request)
.toPromise()
.then(function () {
return Promise.resolve(true);
})
.catch(function () {
return Promise.resolve(false);
});
};
}
RepLoaderService.prototype.addIfMatchHeader = function (config, digest) {
if (digest && (config.method === RequestMethod.Post || config.method === RequestMethod.Put || config.method === RequestMethod.Delete)) {
config.headers = new Headers({ "If-Match": digest });
}
};
RepLoaderService.prototype.handleInvalidResponse = function (rc) {
var rr = new Models.ErrorWrapper(rc, Models.ClientErrorCode.ConnectionProblem, "The response from the client was not parseable as a RestfulObject json Representation ");
return Promise.reject(rr);
};
RepLoaderService.prototype.isObjectUrl = function (url) {
var segments = url.split('/');
return segments.length >= 4 && segments[3] === "objects";
};
RepLoaderService.prototype.handleError = function (response, originalUrl) {
var category;
var error;
if (response.status === Models.HttpStatusCode.InternalServerError) {
// this error should contain an error representatation
if (Models.isErrorRepresentation(response.json())) {
var errorRep = new Models.ErrorRepresentation();
errorRep.populate(response.json());
category = Models.ErrorCategory.HttpServerError;
error = errorRep;
}
else {
return this.handleInvalidResponse(Models.ErrorCategory.HttpServerError);
}
}
else if (response.status <= 0) {
// failed to connect
category = Models.ErrorCategory.ClientError;
error = "Failed to connect to server: " + (response.url || "unknown");
}
else {
category = Models.ErrorCategory.HttpClientError;
var message = (response.headers && response.headers.get("warning")) || "Unknown client HTTP error";
if (response.status === Models.HttpStatusCode.BadRequest ||
response.status === Models.HttpStatusCode.UnprocessableEntity) {
// these errors should contain a map
error = new Models.ErrorMap(response.json(), response.status, message);
}
else if (response.status === Models.HttpStatusCode.NotFound && this.isObjectUrl(originalUrl)) {
// were looking for an object an got not found - object may be deleted
// treat as http problem.
category = Models.ErrorCategory.HttpClientError;
error = "Failed to connect to server: " + (response.url || "unknown");
}
else if (response.status === Models.HttpStatusCode.NotFound) {
// general not found other than object - assume client programming error
category = Models.ErrorCategory.ClientError;
error = "Failed to connect to server: " + (response.url || "unknown");
}
else {
error = message;
}
}
var rr = new Models.ErrorWrapper(category, response.status, error, originalUrl);
return Promise.reject(rr);
};
RepLoaderService.prototype.httpValidate = function (config) {
var _this = this;
this.loadingCountSource.next(++(this.loadingCount));
return this.http.request(new Request(config))
.toPromise()
.then(function () {
_this.loadingCountSource.next(--(_this.loadingCount));
return Promise.resolve(true);
})
.catch(function (r) {
_this.loadingCountSource.next(--(_this.loadingCount));
var originalUrl = config.url || "Unknown url";
r.url = r.url || originalUrl;
return _this.handleError(r, originalUrl);
});
};
// special handler for case where we receive a redirected object back from server
// instead of an actionresult. Wrap the object in an actionresult and then handle normally
RepLoaderService.prototype.handleRedirectedObject = function (response, data) {
if (response instanceof Models.ActionResultRepresentation && Models.isIDomainObjectRepresentation(data)) {
var actionResult = {
resultType: "object",
result: data,
links: [],
extensions: {}
};
return actionResult;
}
return data;
};
RepLoaderService.prototype.isValidResponse = function (data) {
return Models.isResourceRepresentation(data);
};
RepLoaderService.prototype.httpPopulate = function (config, ignoreCache, response) {
var _this = this;
if (!config.url) {
throw new Error("Request must have a URL");
}
var requestUrl = config.url;
if (ignoreCache) {
// clear cache of existing values
this.cache.remove(requestUrl);
}
else {
var cachedValue = this.cache.get(requestUrl);
if (cachedValue) {
response.populate(cachedValue);
response.keySeparator = this.configService.config.keySeparator;
return Promise.resolve(response);
}
}
this.loadingCountSource.next(++(this.loadingCount));
return this.http.request(new Request(config))
.toPromise()
.then(function (r) {
_this.loadingCountSource.next(--(_this.loadingCount));
var asJson = r.json();
if (!_this.isValidResponse(asJson)) {
return _this.handleInvalidResponse(Models.ErrorCategory.ClientError);
}
var representation = _this.handleRedirectedObject(response, asJson);
_this.cache.add(requestUrl, representation);
response.populate(representation);
response.etagDigest = (r.headers && r.headers.get("ETag")) || "";
response.keySeparator = _this.configService.config.keySeparator;
return Promise.resolve(response);
})
.catch(function (r) {
_this.loadingCountSource.next(--(_this.loadingCount));
r.url = r.url || requestUrl;
return _this.handleError(r, requestUrl);
});
};
RepLoaderService.prototype.setConfigFromMap = function (map, digest) {
var config = new RequestOptions({
withCredentials: true,
url: map.getUrl(),
method: map.method,
body: map.getBody()
});
this.addIfMatchHeader(config, digest);
return config;
};
RepLoaderService.prototype.logoff = function () {
this.cache.removeAll();
};
return RepLoaderService;
}());
RepLoaderService = __decorate([
Injectable(),
__metadata("design:paramtypes", [AuthHttp,
ConfigService])
], RepLoaderService);
export { RepLoaderService };
//# sourceMappingURL=rep-loader.service.js.map