Commit 9e3a58f8acfe2bd0dee59401bf8b84e8e4ccb875
1 parent
eef070ef
Exists in
master
and in
1 other branch
changes on article service
Showing
11 changed files
with
197 additions
and
80 deletions
Show diff stats
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("Blog Component", () => { | @@ -96,19 +96,16 @@ describe("Blog Component", () => { | ||
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("Blog Component", () => { | @@ -121,8 +118,8 @@ describe("Blog Component", () => { | ||
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("Services", () => { | @@ -24,8 +24,8 @@ describe("Services", () => { | ||
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("Services", () => { | @@ -34,8 +34,8 @@ describe("Services", () => { | ||
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("Services", () => { | @@ -44,8 +44,8 @@ describe("Services", () => { | ||
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("Services", () => { | @@ -54,8 +54,8 @@ describe("Services", () => { | ||
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("Services", () => { | @@ -63,10 +63,10 @@ describe("Services", () => { | ||
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 |
@@ -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
@@ -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 |