diff --git a/src/app/cms/cms.component.spec.ts b/src/app/cms/cms.component.spec.ts index 605bf91..6e6f9eb 100644 --- a/src/app/cms/cms.component.spec.ts +++ b/src/app/cms/cms.component.spec.ts @@ -26,7 +26,7 @@ describe("Components", () => { getCurrentProfileResponse.resolve({ id: 1 }); let articleCreate = $q.defer(); - articleCreate.resolve({ data: { article: { path: "path", profile: { identifier: "profile" } } } }); + articleCreate.resolve({ path: "path", profile: { identifier: "profile" } }); profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); articleServiceMock.create = jasmine.createSpy("create").and.returnValue(articleCreate.promise); diff --git a/src/app/cms/cms.component.ts b/src/app/cms/cms.component.ts index 4dad48b..fb85f8f 100644 --- a/src/app/cms/cms.component.ts +++ b/src/app/cms/cms.component.ts @@ -23,8 +23,8 @@ export class Cms { save() { this.profileService.getCurrentProfile().then((profile: Profile) => { return this.articleService.create(profile.id, this.article); - }).then((response: restangular.IResponse) => { - this.$state.transitionTo('main.profile.page', { page: response.data.article.path, profile: response.data.article.profile.identifier }); + }).then((article: noosfero.Article) => { + this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier }); this.SweetAlert.swal({ title: "Good job!", text: "Article saved!", diff --git a/src/app/components/noosfero-articles/blog/blog.component.spec.ts b/src/app/components/noosfero-articles/blog/blog.component.spec.ts index 23b04a5..5648579 100644 --- a/src/app/components/noosfero-articles/blog/blog.component.spec.ts +++ b/src/app/components/noosfero-articles/blog/blog.component.spec.ts @@ -1,21 +1,21 @@ import { - providers +providers } from 'ng-forward/cjs/testing/providers'; import { - Input, - Component +Input, +Component } from 'ng-forward'; import { - ArticleBlog +ArticleBlog } from './blog.component'; import { - createComponentFromClass, - quickCreateComponent, - provideEmptyObjects, - createProviderToValue, - provideFilters +createComponentFromClass, +quickCreateComponent, +provideEmptyObjects, +createProviderToValue, +provideFilters } from "../../../../spec/helpers.ts"; // this htmlTemplate will be re-used between the container components in this spec file @@ -23,7 +23,7 @@ const htmlTemplate: string = ' { - function promiseResultTemplate(response ? : {}) { + function promiseResultTemplate(response?: {}) { let thenFuncEmpty = (func: Function) => { // does nothing }; @@ -76,7 +76,7 @@ describe("Blog Component", () => { angular.mock.module("templates") providers((provide: any) => { - return [ + return [ provide('ArticleService', { useValue: articleService }) @@ -96,19 +96,16 @@ describe("Blog Component", () => { it("verify the blog data", (done: Function) => { + let articles = [{ + id: 1, + title: 'The article test' + }]; + + (articles)['_headers'] = { total: 1 }; + // defining a mock result to articleService.getChildren method articleService.getChildren = (article_id: number, filters: {}) => { - return promiseResultTemplate({ - headers: (headerName: string) => { - return 1; - }, - data: < any > { - articles: [{ - id: 1, - title: 'The article test' - }] - } - }); + return promiseResultTemplate(articles); }; createComponentFromClass(BlogContainerComponent).then((fixture) => { @@ -121,8 +118,8 @@ describe("Blog Component", () => { id: 1, title: 'The article test' }; - expect(( < any > articleBlog)["posts"][0]).toEqual(jasmine.objectContaining(post)); - expect(( < any > articleBlog)["totalPosts"]).toEqual(1); + expect((articleBlog)["posts"][0]).toEqual(jasmine.objectContaining(post)); + expect((articleBlog)["totalPosts"]).toEqual(1); done(); }); diff --git a/src/app/components/noosfero-articles/blog/blog.component.ts b/src/app/components/noosfero-articles/blog/blog.component.ts index bba66ba..72ad75d 100644 --- a/src/app/components/noosfero-articles/blog/blog.component.ts +++ b/src/app/components/noosfero-articles/blog/blog.component.ts @@ -33,9 +33,9 @@ export class ArticleBlog { this.articleService .getChildren(this.article.id, filters) - .then((response: restangular.IResponse) => { - this.totalPosts = (response.headers("total")); - this.posts = response.data.articles; + .then((articles: noosfero.Article[]) => { + this.totalPosts = (articles)["_headers"]["total"]; + this.posts = articles; }); } diff --git a/src/lib/ng-noosfero-api/http/article.service.spec.ts b/src/lib/ng-noosfero-api/http/article.service.spec.ts index 9d73d2f..dd51475 100644 --- a/src/lib/ng-noosfero-api/http/article.service.spec.ts +++ b/src/lib/ng-noosfero-api/http/article.service.spec.ts @@ -24,8 +24,8 @@ describe("Services", () => { it("should return article children", (done) => { let articleId = 1; $httpBackend.expectGET(`/api/v1/articles/${articleId}/children`).respond(200, { articles: [{ name: "article1" }] }); - articleService.getChildren(articleId).then((response: restangular.IResponse) => { - expect(response.data.articles).toEqual([{ name: "article1" }]); + articleService.getChildren(articleId).then((result: noosfero.ArticlesResult) => { + expect(result.articles).toEqual([{ name: "article1" }]); done(); }); $httpBackend.flush(); @@ -34,8 +34,8 @@ describe("Services", () => { it("should get articles by profile", (done) => { let profileId = 1; $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles`).respond(200, { articles: [{ name: "article1" }] }); - articleService.getByProfile(profileId).then((response: restangular.IResponse) => { - expect(response.data.articles).toEqual([{ name: "article1" }]); + articleService.getByProfile(profileId).then((result: noosfero.ArticlesResult) => { + expect(result.articles).toEqual([{ name: "article1" }]); done(); }); $httpBackend.flush(); @@ -44,8 +44,8 @@ describe("Services", () => { it("should get articles by profile with additional filters", (done) => { let profileId = 1; $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles?path=test`).respond(200, { articles: [{ name: "article1" }] }); - articleService.getByProfile(profileId, { path: 'test' }).then((response: restangular.IResponse) => { - expect(response.data.articles).toEqual([{ name: "article1" }]); + articleService.getByProfile(profileId, { path: 'test' }).then((result: noosfero.ArticlesResult) => { + expect(result.articles).toEqual([{ name: "article1" }]); done(); }); $httpBackend.flush(); @@ -54,8 +54,8 @@ describe("Services", () => { it("should get article children with additional filters", (done) => { let articleId = 1; $httpBackend.expectGET(`/api/v1/articles/${articleId}/children?path=test`).respond(200, { articles: [{ name: "article1" }] }); - articleService.getChildren(articleId, { path: 'test' }).then((response: restangular.IResponse) => { - expect(response.data.articles).toEqual([{ name: "article1" }]); + articleService.getChildren(articleId, { path: 'test' }).then((result: noosfero.ArticlesResult) => { + expect(result.articles).toEqual([{ name: "article1" }]); done(); }); $httpBackend.flush(); @@ -63,10 +63,10 @@ describe("Services", () => { it("should create an article in a profile", (done) => { let profileId = 1; - let article: Article = { id: null }; - $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/articles`, { article: article }).respond(200, { articles: [{ id: 2 }] }); - articleService.create(profileId, article).then((response: restangular.IResponse) => { - expect(response.data.articles).toEqual([{ id: 2 }]); + let article: noosfero.Article = { id: null}; + $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/articles`, { article: article }).respond(200, { id: 2 }); + articleService.create(profileId, article).then((result: noosfero.Article) => { + expect(article).toEqual({ id: 2 }); done(); }); $httpBackend.flush(); diff --git a/src/lib/ng-noosfero-api/http/article.service.ts b/src/lib/ng-noosfero-api/http/article.service.ts index 992431e..0911b41 100644 --- a/src/lib/ng-noosfero-api/http/article.service.ts +++ b/src/lib/ng-noosfero-api/http/article.service.ts @@ -1,48 +1,69 @@ import { Injectable, Inject } from "ng-forward"; -import {Article} from "../../../app/models/interfaces"; - +import {RestangularWrapperService} from "./restangular_wrapper_service"; @Injectable() @Inject("Restangular", "$q") +export class ArticleService extends RestangularWrapperService { -export class ArticleService { + constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService) { + super(Restangular, $q, $log); + } - constructor(private Restangular: restangular.IService, private $q: ng.IQService, private $log: ng.ILogService) { } + getPath() { + return "articles"; + } - create(profileId: number, article: Article) { - return this.Restangular.one('profiles', profileId).customPOST( - { article: article }, - 'articles', - {}, - { 'Content-Type': 'application/json' } - ); + getDataKeys() { + return { + singular: 'article', + plural: 'articles' + } } - // TODO create a handle ErrorFactory too and move handleSuccessFactory and handleErrorFactory - // to a base class (of course we will have to creates a base class too) - handleSuccessFactory(deferred: ng.IDeferred): (response: restangular.IResponse) => void { - let self = this; - let successFunction = (response: restangular.IResponse): void => { - this.$log.debug("Request successfull executed", this, response); - deferred.resolve(response.data); - }; - return successFunction; + create(profileId: number, article: noosfero.Article): ng.IPromise { + return this.post(this.Restangular.one('profiles', profileId), article); } + // // TODO create a handle ErrorFactory too and move handleSuccessFactory and handleErrorFactory + // // to a base class (of course we will have to creates a base class too) + // handleSuccessFactory(deferred: ng.IDeferred): (response: restangular.IResponse) => void { + // let self = this; + // let successFunction = (response: restangular.IResponse): void => { + // this.$log.debug("Request successfull executed", self, response); + // deferred.resolve(response.data); + // }; + // return successFunction; + // } + // + // handleErrorFactory(deferred: ng.IDeferred): (response: restangular.IResponse) => void { + // let self = this; + // let successFunction = (response: restangular.IResponse): void => { + // this.$log.error("Error executing request", self, response); + // deferred.reject(response.data); + // }; + // return successFunction; + // } + // TODO -> change all Restangular services to this approach "Return promise to a specific type" // it makes easy consume the service getByProfile(profileId: number, params?: any): ng.IPromise { let deferred = this.$q.defer(); - this.Restangular.one('profiles', profileId).customGET('articles', params).then(this.handleSuccessFactory(deferred)); + this.Restangular.one('profiles', profileId).customGET('articles', params) + .then(this.getHandleSuccessFunction(deferred, 'articles')) + .catch(this.getHandleErrorFunction(deferred)); return deferred.promise; } - getChildren(articleId: number, params?: any) { - return this.get(articleId).customGET('children', params); - } + getChildren(articleId: number, params?: any): ng.IPromise { + let deferred = this.$q.defer(); + + this.get(articleId).customGET('children', params) + .then(this.getHandleSuccessFunction(deferred, 'articles').bind(this)) + .catch(this.getHandleErrorFunction(deferred)); - private get(articleId: number) { - return this.Restangular.one('articles', articleId); + return deferred.promise; } + + } diff --git a/src/lib/ng-noosfero-api/http/http_client.ts b/src/lib/ng-noosfero-api/http/http_client.ts deleted file mode 100644 index 7866a12..0000000 --- a/src/lib/ng-noosfero-api/http/http_client.ts +++ /dev/null @@ -1,11 +0,0 @@ -namespace NgNoosferoAPI { - export class NoosferoHttpClient { - static $inject = ['$http', '$q']; - - constructor(private $http: ng.IHttpService, private $q: ng.IQService) { - - } - } - - NgNoosferoAPI.ngModule.service(NoosferoHttpClient); -} \ No newline at end of file diff --git a/src/lib/ng-noosfero-api/http/rest.decorator.ts b/src/lib/ng-noosfero-api/http/rest.decorator.ts new file mode 100644 index 0000000..2a10bbe --- /dev/null +++ b/src/lib/ng-noosfero-api/http/rest.decorator.ts @@ -0,0 +1,16 @@ +namespace noosfero.http { + export function Rest(config: { + path: string + } + ): Function { + let path = config.path; + return (t: Function) => { + if (!path) { + throw new Error(`Rest decorator error in ${(t).name}. Rest path should be provided`); + } + t.prototype.getPath = function() { + return path; + }; + }; + } +} \ No newline at end of file diff --git a/src/lib/ng-noosfero-api/http/restangular_wrapper_service.ts b/src/lib/ng-noosfero-api/http/restangular_wrapper_service.ts new file mode 100644 index 0000000..8866a50 --- /dev/null +++ b/src/lib/ng-noosfero-api/http/restangular_wrapper_service.ts @@ -0,0 +1,80 @@ +export abstract class RestangularWrapperService { + + private lastResponse: restangular.IResponse; + constructor(protected Restangular: restangular.IService, protected $q: ng.IQService, protected $log: ng.ILogService) { + + } + + protected abstract getPath(): string; + + protected abstract getDataKeys(): { singular: string, plural: string }; + + protected get(id: number): restangular.IElement { + return this.Restangular.one(this.getPath(), id); + } + + protected post(elementRoot: restangular.IElement, element?: any, path?: string, params?: any, headers?: any): ng.IPromise { + let deferred = this.$q.defer(); + + this.customPOST( + elementRoot, + element, + this.getPath(), + {} + ) + .then(this.getHandleSuccessFunction(deferred)) + .catch(this.getHandleErrorFunction(deferred)); + + return deferred.promise; + } + + protected customPOST(elementRoot: restangular.IElement, elem?: any, path?: string, params?: any, headers?: any) { + if (headers) { + headers['Content-Type'] = 'application/json'; + } else { + headers = { 'Content-Type': 'application/json' }; + } + return elementRoot.customPOST(elem, path, params, headers); + } + + // TODO create a handle ErrorFactory too and move handleSuccessFactory and handleErrorFactory + // to a base class (of course we will have to creates a base class too) + getHandleSuccessFunction(deferred: ng.IDeferred, responseKey?: string): (response: restangular.IResponse) => void { + let self = this; + let successFunction = (response: restangular.IResponse): void => { + if (self.$log) { + self.$log.debug("Request successfull executed", self, response); + } + let data = response.data; + + let dataKey: string; + + if (data && self.getDataKeys()) { + if ((data).hasOwnProperty(self.getDataKeys().singular)) { + data = data[self.getDataKeys().singular]; + dataKey = self.getDataKeys().singular; + } else if ((data).hasOwnProperty(self.getDataKeys().plural)) { + data = data[self.getDataKeys().plural]; + dataKey = self.getDataKeys().plural; + } + } + + let result: any = {}; + result[dataKey] = data; + result.headers = response.headers; + deferred.resolve(result); + }; + return successFunction; + } + + getHandleErrorFunction(deferred: ng.IDeferred): (response: restangular.IResponse) => void { + let self = this; + let successFunction = (response: restangular.IResponse): void => { + if (self.$log) { + self.$log.error("Error executing request", self, response); + } + deferred.reject(response); + }; + return successFunction; + } +} diff --git a/src/lib/ng-noosfero-api/interfaces/article.ts b/src/lib/ng-noosfero-api/interfaces/article.ts index 651d9da..b33efd1 100644 --- a/src/lib/ng-noosfero-api/interfaces/article.ts +++ b/src/lib/ng-noosfero-api/interfaces/article.ts @@ -1,6 +1,8 @@ namespace noosfero { - interface Article { + export interface Article { id: number; + path: string; + profile: Profile; } } \ No newline at end of file diff --git a/src/lib/ng-noosfero-api/interfaces/articles_result.ts b/src/lib/ng-noosfero-api/interfaces/articles_result.ts new file mode 100644 index 0000000..ae534e0 --- /dev/null +++ b/src/lib/ng-noosfero-api/interfaces/articles_result.ts @@ -0,0 +1,12 @@ + +namespace noosfero { + export interface ArticlesResult { + articles: Article[]; + headers: Function; + } + + export interface ArticleResult { + article: Article; + headers: Function; + } +} \ No newline at end of file -- libgit2 0.21.2