Commit 9e3a58f8acfe2bd0dee59401bf8b84e8e4ccb875

Authored by Ábner Oliveira
1 parent eef070ef
Exists in master and in 1 other branch dev-fixes

changes on article service

src/app/cms/cms.component.spec.ts
@@ -26,7 +26,7 @@ describe("Components", () => { @@ -26,7 +26,7 @@ describe("Components", () => {
26 getCurrentProfileResponse.resolve({ id: 1 }); 26 getCurrentProfileResponse.resolve({ id: 1 });
27 27
28 let articleCreate = $q.defer(); 28 let articleCreate = $q.defer();
29 - articleCreate.resolve({ data: { article: { path: "path", profile: { identifier: "profile" } } } }); 29 + articleCreate.resolve({ path: "path", profile: { identifier: "profile" } });
30 30
31 profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); 31 profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise);
32 articleServiceMock.create = jasmine.createSpy("create").and.returnValue(articleCreate.promise); 32 articleServiceMock.create = jasmine.createSpy("create").and.returnValue(articleCreate.promise);
src/app/cms/cms.component.ts
@@ -23,8 +23,8 @@ export class Cms { @@ -23,8 +23,8 @@ export class Cms {
23 save() { 23 save() {
24 this.profileService.getCurrentProfile().then((profile: Profile) => { 24 this.profileService.getCurrentProfile().then((profile: Profile) => {
25 return this.articleService.create(profile.id, this.article); 25 return this.articleService.create(profile.id, this.article);
26 - }).then((response: restangular.IResponse) => {  
27 - this.$state.transitionTo('main.profile.page', { page: response.data.article.path, profile: response.data.article.profile.identifier }); 26 + }).then((article: noosfero.Article) => {
  27 + this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier });
28 this.SweetAlert.swal({ 28 this.SweetAlert.swal({
29 title: "Good job!", 29 title: "Good job!",
30 text: "Article saved!", 30 text: "Article saved!",
src/app/components/noosfero-articles/blog/blog.component.spec.ts
1 import { 1 import {
2 - providers 2 +providers
3 } from 'ng-forward/cjs/testing/providers'; 3 } from 'ng-forward/cjs/testing/providers';
4 4
5 import { 5 import {
6 - Input,  
7 - Component 6 +Input,
  7 +Component
8 } from 'ng-forward'; 8 } from 'ng-forward';
9 import { 9 import {
10 - ArticleBlog 10 +ArticleBlog
11 } from './blog.component'; 11 } from './blog.component';
12 12
13 import { 13 import {
14 - createComponentFromClass,  
15 - quickCreateComponent,  
16 - provideEmptyObjects,  
17 - createProviderToValue,  
18 - provideFilters 14 +createComponentFromClass,
  15 +quickCreateComponent,
  16 +provideEmptyObjects,
  17 +createProviderToValue,
  18 +provideFilters
19 } from "../../../../spec/helpers.ts"; 19 } from "../../../../spec/helpers.ts";
20 20
21 // this htmlTemplate will be re-used between the container components in this spec file 21 // this htmlTemplate will be re-used between the container components in this spec file
@@ -23,7 +23,7 @@ const htmlTemplate: string = '<noosfero-blog [article]="ctrl.article" [profile]= @@ -23,7 +23,7 @@ const htmlTemplate: string = '<noosfero-blog [article]="ctrl.article" [profile]=
23 23
24 describe("Blog Component", () => { 24 describe("Blog Component", () => {
25 25
26 - function promiseResultTemplate(response ? : {}) { 26 + function promiseResultTemplate(response?: {}) {
27 let thenFuncEmpty = (func: Function) => { 27 let thenFuncEmpty = (func: Function) => {
28 // does nothing 28 // does nothing
29 }; 29 };
@@ -76,7 +76,7 @@ describe("Blog Component", () => { @@ -76,7 +76,7 @@ describe("Blog Component", () => {
76 angular.mock.module("templates") 76 angular.mock.module("templates")
77 77
78 providers((provide: any) => { 78 providers((provide: any) => {
79 - return <any > [ 79 + return <any>[
80 provide('ArticleService', { 80 provide('ArticleService', {
81 useValue: articleService 81 useValue: articleService
82 }) 82 })
@@ -96,19 +96,16 @@ describe(&quot;Blog Component&quot;, () =&gt; { @@ -96,19 +96,16 @@ describe(&quot;Blog Component&quot;, () =&gt; {
96 96
97 it("verify the blog data", (done: Function) => { 97 it("verify the blog data", (done: Function) => {
98 98
  99 + let articles = [{
  100 + id: 1,
  101 + title: 'The article test'
  102 + }];
  103 +
  104 + (<any>articles)['_headers'] = { total: 1 };
  105 +
99 // defining a mock result to articleService.getChildren method 106 // defining a mock result to articleService.getChildren method
100 articleService.getChildren = (article_id: number, filters: {}) => { 107 articleService.getChildren = (article_id: number, filters: {}) => {
101 - return promiseResultTemplate({  
102 - headers: (headerName: string) => {  
103 - return 1;  
104 - },  
105 - data: < any > {  
106 - articles: [{  
107 - id: 1,  
108 - title: 'The article test'  
109 - }]  
110 - }  
111 - }); 108 + return promiseResultTemplate(articles);
112 }; 109 };
113 110
114 createComponentFromClass(BlogContainerComponent).then((fixture) => { 111 createComponentFromClass(BlogContainerComponent).then((fixture) => {
@@ -121,8 +118,8 @@ describe(&quot;Blog Component&quot;, () =&gt; { @@ -121,8 +118,8 @@ describe(&quot;Blog Component&quot;, () =&gt; {
121 id: 1, 118 id: 1,
122 title: 'The article test' 119 title: 'The article test'
123 }; 120 };
124 - expect(( < any > articleBlog)["posts"][0]).toEqual(jasmine.objectContaining(post));  
125 - expect(( < any > articleBlog)["totalPosts"]).toEqual(1); 121 + expect((<any>articleBlog)["posts"][0]).toEqual(jasmine.objectContaining(post));
  122 + expect((<any>articleBlog)["totalPosts"]).toEqual(1);
126 123
127 done(); 124 done();
128 }); 125 });
src/app/components/noosfero-articles/blog/blog.component.ts
@@ -33,9 +33,9 @@ export class ArticleBlog { @@ -33,9 +33,9 @@ export class ArticleBlog {
33 33
34 this.articleService 34 this.articleService
35 .getChildren(this.article.id, filters) 35 .getChildren(this.article.id, filters)
36 - .then((response: restangular.IResponse) => {  
37 - this.totalPosts = <number>(<any>response.headers("total"));  
38 - this.posts = response.data.articles; 36 + .then((articles: noosfero.Article[]) => {
  37 + this.totalPosts = <number>(<any>articles)["_headers"]["total"];
  38 + this.posts = articles;
39 }); 39 });
40 } 40 }
41 41
src/lib/ng-noosfero-api/http/article.service.spec.ts
@@ -24,8 +24,8 @@ describe(&quot;Services&quot;, () =&gt; { @@ -24,8 +24,8 @@ describe(&quot;Services&quot;, () =&gt; {
24 it("should return article children", (done) => { 24 it("should return article children", (done) => {
25 let articleId = 1; 25 let articleId = 1;
26 $httpBackend.expectGET(`/api/v1/articles/${articleId}/children`).respond(200, { articles: [{ name: "article1" }] }); 26 $httpBackend.expectGET(`/api/v1/articles/${articleId}/children`).respond(200, { articles: [{ name: "article1" }] });
27 - articleService.getChildren(articleId).then((response: restangular.IResponse) => {  
28 - expect(response.data.articles).toEqual([{ name: "article1" }]); 27 + articleService.getChildren<noosfero.ArticlesResult>(articleId).then((result: noosfero.ArticlesResult) => {
  28 + expect(result.articles).toEqual([{ name: "article1" }]);
29 done(); 29 done();
30 }); 30 });
31 $httpBackend.flush(); 31 $httpBackend.flush();
@@ -34,8 +34,8 @@ describe(&quot;Services&quot;, () =&gt; { @@ -34,8 +34,8 @@ describe(&quot;Services&quot;, () =&gt; {
34 it("should get articles by profile", (done) => { 34 it("should get articles by profile", (done) => {
35 let profileId = 1; 35 let profileId = 1;
36 $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles`).respond(200, { articles: [{ name: "article1" }] }); 36 $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles`).respond(200, { articles: [{ name: "article1" }] });
37 - articleService.getByProfile(profileId).then((response: restangular.IResponse) => {  
38 - expect(response.data.articles).toEqual([{ name: "article1" }]); 37 + articleService.getByProfile<noosfero.ArticlesResult>(profileId).then((result: noosfero.ArticlesResult) => {
  38 + expect(result.articles).toEqual([{ name: "article1" }]);
39 done(); 39 done();
40 }); 40 });
41 $httpBackend.flush(); 41 $httpBackend.flush();
@@ -44,8 +44,8 @@ describe(&quot;Services&quot;, () =&gt; { @@ -44,8 +44,8 @@ describe(&quot;Services&quot;, () =&gt; {
44 it("should get articles by profile with additional filters", (done) => { 44 it("should get articles by profile with additional filters", (done) => {
45 let profileId = 1; 45 let profileId = 1;
46 $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles?path=test`).respond(200, { articles: [{ name: "article1" }] }); 46 $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles?path=test`).respond(200, { articles: [{ name: "article1" }] });
47 - articleService.getByProfile(profileId, { path: 'test' }).then((response: restangular.IResponse) => {  
48 - expect(response.data.articles).toEqual([{ name: "article1" }]); 47 + articleService.getByProfile<noosfero.ArticlesResult>(profileId, { path: 'test' }).then((result: noosfero.ArticlesResult) => {
  48 + expect(result.articles).toEqual([{ name: "article1" }]);
49 done(); 49 done();
50 }); 50 });
51 $httpBackend.flush(); 51 $httpBackend.flush();
@@ -54,8 +54,8 @@ describe(&quot;Services&quot;, () =&gt; { @@ -54,8 +54,8 @@ describe(&quot;Services&quot;, () =&gt; {
54 it("should get article children with additional filters", (done) => { 54 it("should get article children with additional filters", (done) => {
55 let articleId = 1; 55 let articleId = 1;
56 $httpBackend.expectGET(`/api/v1/articles/${articleId}/children?path=test`).respond(200, { articles: [{ name: "article1" }] }); 56 $httpBackend.expectGET(`/api/v1/articles/${articleId}/children?path=test`).respond(200, { articles: [{ name: "article1" }] });
57 - articleService.getChildren(articleId, { path: 'test' }).then((response: restangular.IResponse) => {  
58 - expect(response.data.articles).toEqual([{ name: "article1" }]); 57 + articleService.getChildren<noosfero.ArticlesResult>(articleId, { path: 'test' }).then((result: noosfero.ArticlesResult) => {
  58 + expect(result.articles).toEqual([{ name: "article1" }]);
59 done(); 59 done();
60 }); 60 });
61 $httpBackend.flush(); 61 $httpBackend.flush();
@@ -63,10 +63,10 @@ describe(&quot;Services&quot;, () =&gt; { @@ -63,10 +63,10 @@ describe(&quot;Services&quot;, () =&gt; {
63 63
64 it("should create an article in a profile", (done) => { 64 it("should create an article in a profile", (done) => {
65 let profileId = 1; 65 let profileId = 1;
66 - let article: Article = { id: null };  
67 - $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/articles`, { article: article }).respond(200, { articles: [{ id: 2 }] });  
68 - articleService.create(profileId, article).then((response: restangular.IResponse) => {  
69 - expect(response.data.articles).toEqual([{ id: 2 }]); 66 + let article: noosfero.Article = <any>{ id: null};
  67 + $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/articles`, { article: article }).respond(200, { id: 2 });
  68 + articleService.create(profileId, article).then((result: noosfero.Article) => {
  69 + expect(article).toEqual({ id: 2 });
70 done(); 70 done();
71 }); 71 });
72 $httpBackend.flush(); 72 $httpBackend.flush();
src/lib/ng-noosfero-api/http/article.service.ts
1 import { Injectable, Inject } from "ng-forward"; 1 import { Injectable, Inject } from "ng-forward";
2 -import {Article} from "../../../app/models/interfaces";  
3 - 2 +import {RestangularWrapperService} from "./restangular_wrapper_service";
4 @Injectable() 3 @Injectable()
5 @Inject("Restangular", "$q") 4 @Inject("Restangular", "$q")
6 5
  6 +export class ArticleService extends RestangularWrapperService<noosfero.Article> {
7 7
8 -export class ArticleService { 8 + constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService) {
  9 + super(Restangular, $q, $log);
  10 + }
9 11
10 - constructor(private Restangular: restangular.IService, private $q: ng.IQService, private $log: ng.ILogService) { } 12 + getPath() {
  13 + return "articles";
  14 + }
11 15
12 - create(profileId: number, article: Article) {  
13 - return this.Restangular.one('profiles', profileId).customPOST(  
14 - { article: article },  
15 - 'articles',  
16 - {},  
17 - { 'Content-Type': 'application/json' }  
18 - ); 16 + getDataKeys() {
  17 + return {
  18 + singular: 'article',
  19 + plural: 'articles'
  20 + }
19 } 21 }
20 22
21 - // TODO create a handle ErrorFactory too and move handleSuccessFactory and handleErrorFactory  
22 - // to a base class (of course we will have to creates a base class too)  
23 - handleSuccessFactory<T>(deferred: ng.IDeferred<T>): (response: restangular.IResponse) => void {  
24 - let self = this;  
25 - let successFunction = (response: restangular.IResponse): void => {  
26 - this.$log.debug("Request successfull executed", this, response);  
27 - deferred.resolve(response.data);  
28 - };  
29 - return successFunction; 23 + create(profileId: number, article: noosfero.Article): ng.IPromise<noosfero.Article> {
  24 + return this.post(this.Restangular.one('profiles', profileId), article);
30 } 25 }
31 26
  27 + // // TODO create a handle ErrorFactory too and move handleSuccessFactory and handleErrorFactory
  28 + // // to a base class (of course we will have to creates a base class too)
  29 + // handleSuccessFactory<T>(deferred: ng.IDeferred<T>): (response: restangular.IResponse) => void {
  30 + // let self = this;
  31 + // let successFunction = (response: restangular.IResponse): void => {
  32 + // this.$log.debug("Request successfull executed", self, response);
  33 + // deferred.resolve(response.data);
  34 + // };
  35 + // return successFunction;
  36 + // }
  37 + //
  38 + // handleErrorFactory<T>(deferred: ng.IDeferred<T>): (response: restangular.IResponse) => void {
  39 + // let self = this;
  40 + // let successFunction = (response: restangular.IResponse): void => {
  41 + // this.$log.error("Error executing request", self, response);
  42 + // deferred.reject(response.data);
  43 + // };
  44 + // return successFunction;
  45 + // }
  46 +
32 // TODO -> change all Restangular services to this approach "Return promise to a specific type" 47 // TODO -> change all Restangular services to this approach "Return promise to a specific type"
33 // it makes easy consume the service 48 // it makes easy consume the service
34 getByProfile<T>(profileId: number, params?: any): ng.IPromise<T> { 49 getByProfile<T>(profileId: number, params?: any): ng.IPromise<T> {
35 let deferred = this.$q.defer<T>(); 50 let deferred = this.$q.defer<T>();
36 - this.Restangular.one('profiles', profileId).customGET('articles', params).then(this.handleSuccessFactory(deferred)); 51 + this.Restangular.one('profiles', profileId).customGET('articles', params)
  52 + .then(this.getHandleSuccessFunction<T>(deferred, 'articles'))
  53 + .catch(this.getHandleErrorFunction(deferred));
37 return deferred.promise; 54 return deferred.promise;
38 } 55 }
39 56
40 - getChildren(articleId: number, params?: any) {  
41 - return this.get(articleId).customGET('children', params);  
42 - } 57 + getChildren<T>(articleId: number, params?: any): ng.IPromise<T> {
  58 + let deferred = this.$q.defer<T>();
  59 +
  60 + this.get(articleId).customGET('children', params)
  61 + .then(this.getHandleSuccessFunction<T>(deferred, 'articles').bind(this))
  62 + .catch(this.getHandleErrorFunction(deferred));
43 63
44 - private get(articleId: number) {  
45 - return this.Restangular.one('articles', articleId); 64 + return deferred.promise;
46 } 65 }
47 66
  67 +
  68 +
48 } 69 }
src/lib/ng-noosfero-api/http/http_client.ts
@@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
1 -namespace NgNoosferoAPI {  
2 - export class NoosferoHttpClient {  
3 - static $inject = ['$http', '$q'];  
4 -  
5 - constructor(private $http: ng.IHttpService, private $q: ng.IQService) {  
6 -  
7 - }  
8 - }  
9 -  
10 - NgNoosferoAPI.ngModule.service(NoosferoHttpClient);  
11 -}  
12 \ No newline at end of file 0 \ No newline at end of file
src/lib/ng-noosfero-api/http/rest.decorator.ts 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +namespace noosfero.http {
  2 + export function Rest(config: {
  3 + path: string
  4 + }
  5 + ): Function {
  6 + let path = config.path;
  7 + return (t: Function) => {
  8 + if (!path) {
  9 + throw new Error(`Rest decorator error in ${(<any>t).name}. Rest path should be provided`);
  10 + }
  11 + t.prototype.getPath = function() {
  12 + return path;
  13 + };
  14 + };
  15 + }
  16 +}
0 \ No newline at end of file 17 \ No newline at end of file
src/lib/ng-noosfero-api/http/restangular_wrapper_service.ts 0 → 100644
@@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
  1 +export abstract class RestangularWrapperService<T> {
  2 +
  3 + private lastResponse: restangular.IResponse;
  4 + constructor(protected Restangular: restangular.IService, protected $q: ng.IQService, protected $log: ng.ILogService) {
  5 +
  6 + }
  7 +
  8 + protected abstract getPath(): string;
  9 +
  10 + protected abstract getDataKeys(): { singular: string, plural: string };
  11 +
  12 + protected get(id: number): restangular.IElement {
  13 + return this.Restangular.one(this.getPath(), id);
  14 + }
  15 +
  16 + protected post(elementRoot: restangular.IElement, element?: any, path?: string, params?: any, headers?: any): ng.IPromise<T> {
  17 + let deferred = this.$q.defer<T>();
  18 +
  19 + this.customPOST(
  20 + elementRoot,
  21 + element,
  22 + this.getPath(),
  23 + {}
  24 + )
  25 + .then(this.getHandleSuccessFunction(deferred))
  26 + .catch(this.getHandleErrorFunction(deferred));
  27 +
  28 + return deferred.promise;
  29 + }
  30 +
  31 + protected customPOST(elementRoot: restangular.IElement, elem?: any, path?: string, params?: any, headers?: any) {
  32 + if (headers) {
  33 + headers['Content-Type'] = 'application/json';
  34 + } else {
  35 + headers = { 'Content-Type': 'application/json' };
  36 + }
  37 + return elementRoot.customPOST(elem, path, params, headers);
  38 + }
  39 +
  40 + // TODO create a handle ErrorFactory too and move handleSuccessFactory and handleErrorFactory
  41 + // to a base class (of course we will have to creates a base class too)
  42 + getHandleSuccessFunction<C>(deferred: ng.IDeferred<C>, responseKey?: string): (response: restangular.IResponse) => void {
  43 + let self = this;
  44 + let successFunction = (response: restangular.IResponse): void => {
  45 + if (self.$log) {
  46 + self.$log.debug("Request successfull executed", self, response);
  47 + }
  48 + let data = response.data;
  49 +
  50 + let dataKey: string;
  51 +
  52 + if (data && self.getDataKeys()) {
  53 + if ((<Object>data).hasOwnProperty(self.getDataKeys().singular)) {
  54 + data = data[self.getDataKeys().singular];
  55 + dataKey = self.getDataKeys().singular;
  56 + } else if ((<Object>data).hasOwnProperty(self.getDataKeys().plural)) {
  57 + data = data[self.getDataKeys().plural];
  58 + dataKey = self.getDataKeys().plural;
  59 + }
  60 + }
  61 +
  62 + let result: any = {};
  63 + result[dataKey] = data;
  64 + result.headers = response.headers;
  65 + deferred.resolve(result);
  66 + };
  67 + return successFunction;
  68 + }
  69 +
  70 + getHandleErrorFunction<T>(deferred: ng.IDeferred<T>): (response: restangular.IResponse) => void {
  71 + let self = this;
  72 + let successFunction = (response: restangular.IResponse): void => {
  73 + if (self.$log) {
  74 + self.$log.error("Error executing request", self, response);
  75 + }
  76 + deferred.reject(response);
  77 + };
  78 + return successFunction;
  79 + }
  80 +}
src/lib/ng-noosfero-api/interfaces/article.ts
1 1
2 namespace noosfero { 2 namespace noosfero {
3 - interface Article { 3 + export interface Article {
4 id: number; 4 id: number;
  5 + path: string;
  6 + profile: Profile;
5 } 7 }
6 } 8 }
7 \ No newline at end of file 9 \ No newline at end of file
src/lib/ng-noosfero-api/interfaces/articles_result.ts 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +
  2 +namespace noosfero {
  3 + export interface ArticlesResult {
  4 + articles: Article[];
  5 + headers: Function;
  6 + }
  7 +
  8 + export interface ArticleResult {
  9 + article: Article;
  10 + headers: Function;
  11 + }
  12 +}
0 \ No newline at end of file 13 \ No newline at end of file