Commit fb1e923a176adbb9d007beba3747eef35e892d2a

Authored by Victor Costa
2 parents 8784fe3c 328a7a82

Merge branch 'events-hub' into 'master'

Events hub

Added a service called EventsHubService to serve as central point for events propagated through the Noosfero Angular Application.

See merge request !53
src/app/index.ts
1   -import {bootstrap} from "ng-forward";
  1 +import {bootstrap, provide} from "ng-forward";
2 2 import {noosferoModuleConfig} from "./index.config";
3 3 import {noosferoAngularRunBlock} from "./index.run";
4 4 import {MainComponent} from "./main/main.component";
... ... @@ -22,4 +22,13 @@ angular.module('noosfero.init', ['noosfero.templates.app', 'noosfero.templates.p
22 22 run(noosferoAngularRunBlock).
23 23 constant("moment", moment).
24 24 constant("AuthEvents", AuthEvents);
25   -bootstrap(MainComponent);
  25 +
  26 +
  27 +import { EVENTS_HUB_KNOW_EVENT_NAMES } from './shared/services/events-hub.service';
  28 +import { NoosferoKnownEvents } from './known-events';
  29 +
  30 +bootstrap(MainComponent,
  31 + [
  32 + provide(EVENTS_HUB_KNOW_EVENT_NAMES, { useClass: NoosferoKnownEvents })
  33 + ]
  34 +);
... ...
src/app/known-events.ts 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +import { EventsHubKnownEventNames } from './shared/services/events-hub.service';
  2 +
  3 +export class NoosferoKnownEvents implements EventsHubKnownEventNames {
  4 + IMAGE_PROFILE_UPDATED: string = 'IMAGE_PROFILE_UPDATED';
  5 + PROFILE_INFO_UPDATED: string = 'PROFILE_INFO_UPDATED';
  6 + ARTICLE_UPDATED: string = 'ARTICLE_UPDATED';
  7 +
  8 + constructor() {
  9 + }
  10 +
  11 + getNames() {
  12 + return Object.getOwnPropertyNames(this);
  13 + }
  14 +}
0 15 \ No newline at end of file
... ...
src/app/main/main.component.spec.ts
... ... @@ -4,8 +4,9 @@ import {TestComponentBuilder, ComponentFixture} from "ng-forward/cjs/testing/tes
4 4  
5 5 import {quickCreateComponent} from "../../spec/helpers";
6 6 import {getAngularServiceFactory} from "../../spec/helpers";
  7 +import { EVENTS_HUB_KNOW_EVENT_NAMES } from "../shared/services/events-hub.service";
7 8  
8   -describe("MainComponent", function() {
  9 +describe("MainComponent", function () {
9 10  
10 11 let localFixture: ComponentFixture;
11 12 let $state: angular.ui.IStateService;
... ... @@ -34,6 +35,14 @@ describe("MainComponent", function() {
34 35 {
35 36 useValue: environmentService
36 37 }),
  38 + provide(EVENTS_HUB_KNOW_EVENT_NAMES,
  39 + {
  40 + useValue: [
  41 + 'IMAGE_PROFILE_UPDATED',
  42 + 'PROFILE_INFO_UPDATED',
  43 + 'ARTICLE_UPDATED'
  44 + ]
  45 + }),
37 46 ]
38 47 })
39 48 class MainComponentParent {
... ... @@ -57,7 +66,7 @@ describe("MainComponent", function() {
57 66 // navigates to the environment home
58 67 $state.go("main.environment.home");
59 68 localFixture.detectChanges();
60   - // after changes were detected it checks the current $state route
  69 + // after changes were detected it checks the current $state route
61 70 expect($state.current.name).toEqual("main.environment.home");
62 71 done();
63 72 });
... ...
src/app/main/main.component.ts
1 1 import * as plugins from "../../plugins";
2   -import {bundle, Component, StateConfig, Inject} from "ng-forward";
3   -import {ArticleBlogComponent} from "./../article/types/blog/blog.component";
4   -
5   -import {ArticleViewComponent} from "./../article/article-default-view.component";
6   -
7   -import {ProfileComponent} from "../profile/profile.component";
8   -import {BoxesComponent} from "../layout/boxes/boxes.component";
9   -import {BlockContentComponent} from "../layout/blocks/block-content.component";
10   -import {BlockComponent} from "../layout/blocks/block.component";
11   -import {EnvironmentComponent} from "../environment/environment.component";
12   -import {EnvironmentHomeComponent} from "../environment/environment-home.component";
13   -import {PeopleBlockComponent} from "../layout/blocks/people/people-block.component";
14   -import {DisplayContentBlockComponent} from "../layout/blocks/display-content/display-content-block.component";
15   -import {LinkListBlockComponent} from "../layout/blocks/link-list/link-list-block.component";
16   -import {RecentDocumentsBlockComponent} from "../layout/blocks/recent-documents/recent-documents-block.component";
17   -import {ProfileImageBlockComponent} from "../layout/blocks/profile-image/profile-image-block.component";
18   -import {RawHTMLBlockComponent} from "../layout/blocks/raw-html/raw-html-block.component";
19   -import {StatisticsBlockComponent} from "../layout/blocks/statistics/statistics-block.component";
20   -import {PersonTagsPluginInterestsBlockComponent} from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component";
21   -import {TagsBlockComponent} from "../layout/blocks/tags/tags-block.component";
22   -import {CustomContentComponent} from "../profile/custom-content/custom-content.component";
23   -import {RecentActivitiesPluginActivitiesBlockComponent} from "../layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.component";
24   -
25   -import {MembersBlockComponent} from "../layout/blocks/members/members-block.component";
26   -import {CommunitiesBlockComponent} from "../layout/blocks/communities/communities-block.component";
27   -
28   -import {LoginBlockComponent} from "../layout/blocks/login-block/login-block.component";
29   -
30   -import {NoosferoTemplate} from "../shared/pipes/noosfero-template.filter";
31   -import {DateFormat} from "../shared/pipes/date-format.filter";
32   -
33   -import {AuthService} from "../login/auth.service";
34   -import {SessionService} from "../login/session.service";
35   -import {EnvironmentService} from "./../../lib/ng-noosfero-api/http/environment.service";
36   -import {NotificationService} from "../shared/services/notification.service";
37   -
38   -import {BodyStateClassesService} from "./../layout/services/body-state-classes.service";
39   -
40   -import {Navbar} from "../layout/navbar/navbar";
41   -
42   -import {SidebarComponent} from "../layout/sidebar/sidebar.component";
43   -
44   -import {MainBlockComponent} from "../layout/blocks/main/main-block.component";
45   -import {HtmlEditorComponent} from "../shared/components/html-editor/html-editor.component";
46   -import {PermissionDirective} from "../shared/components/permission/permission.directive";
47   -import {SearchComponent} from "../search/search.component";
48   -import {SearchFormComponent} from "../search/search-form/search-form.component";
49   -
  2 +import { bundle, Component, StateConfig, Inject } from "ng-forward";
  3 +import { ArticleBlogComponent } from "./../article/types/blog/blog.component";
  4 +
  5 +import { ArticleViewComponent } from "./../article/article-default-view.component";
  6 +
  7 +import { ProfileComponent } from "../profile/profile.component";
  8 +import { BoxesComponent } from "../layout/boxes/boxes.component";
  9 +import { BlockContentComponent } from "../layout/blocks/block-content.component";
  10 +import { BlockComponent } from "../layout/blocks/block.component";
  11 +import { EnvironmentComponent } from "../environment/environment.component";
  12 +import { EnvironmentHomeComponent } from "../environment/environment-home.component";
  13 +import { PeopleBlockComponent } from "../layout/blocks/people/people-block.component";
  14 +import { DisplayContentBlockComponent } from "../layout/blocks/display-content/display-content-block.component";
  15 +import { LinkListBlockComponent } from "../layout/blocks/link-list/link-list-block.component";
  16 +import { RecentDocumentsBlockComponent } from "../layout/blocks/recent-documents/recent-documents-block.component";
  17 +import { ProfileImageBlockComponent } from "../layout/blocks/profile-image/profile-image-block.component";
  18 +import { RawHTMLBlockComponent } from "../layout/blocks/raw-html/raw-html-block.component";
  19 +import { StatisticsBlockComponent } from "../layout/blocks/statistics/statistics-block.component";
  20 +import { PersonTagsPluginInterestsBlockComponent } from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component";
  21 +import { TagsBlockComponent } from "../layout/blocks/tags/tags-block.component";
  22 +import { CustomContentComponent } from "../profile/custom-content/custom-content.component";
  23 +import { RecentActivitiesPluginActivitiesBlockComponent } from "../layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.component";
  24 +
  25 +import { MembersBlockComponent } from "../layout/blocks/members/members-block.component";
  26 +import { CommunitiesBlockComponent } from "../layout/blocks/communities/communities-block.component";
  27 +
  28 +import { LoginBlockComponent } from "../layout/blocks/login-block/login-block.component";
  29 +
  30 +import { NoosferoTemplate } from "../shared/pipes/noosfero-template.filter";
  31 +import { DateFormat } from "../shared/pipes/date-format.filter";
  32 +
  33 +import { AuthService } from "../login/auth.service";
  34 +import { SessionService } from "../login/session.service";
  35 +import { EnvironmentService } from "./../../lib/ng-noosfero-api/http/environment.service";
  36 +import { NotificationService } from "../shared/services/notification.service";
  37 +
  38 +import { BodyStateClassesService } from "./../layout/services/body-state-classes.service";
  39 +
  40 +import { Navbar } from "../layout/navbar/navbar";
  41 +
  42 +import { SidebarComponent } from "../layout/sidebar/sidebar.component";
  43 +
  44 +import { MainBlockComponent } from "../layout/blocks/main/main-block.component";
  45 +import { HtmlEditorComponent } from "../shared/components/html-editor/html-editor.component";
  46 +import { PermissionDirective } from "../shared/components/permission/permission.directive";
  47 +import { SearchComponent } from "../search/search.component";
  48 +import { SearchFormComponent } from "../search/search-form/search-form.component";
  49 +
  50 +import { EVENTS_HUB_KNOW_EVENT_NAMES, EventsHubService } from "../shared/services/events-hub.service";
  51 +import { NoosferoKnownEvents } from "../known-events";
50 52 /**
51 53 * @ngdoc controller
52 54 * @name main.MainContentComponent
... ... @@ -62,12 +64,16 @@ import {SearchFormComponent} from "../search/search-form/search-form.component";
62 64 templateUrl: "app/main/main.html",
63 65 providers: [AuthService, SessionService]
64 66 })
65   -@Inject(BodyStateClassesService)
  67 +@Inject(BodyStateClassesService, EVENTS_HUB_KNOW_EVENT_NAMES)
66 68 export class MainContentComponent {
67 69  
68 70 public themeSkin: string = 'skin-whbl';
69 71  
70   - constructor(private bodyStateClassesService: BodyStateClassesService) {
  72 + constructor(
  73 + private bodyStateClassesService: BodyStateClassesService,
  74 + eventsNames: NoosferoKnownEvents,
  75 + eventsHubService: EventsHubService
  76 + ) {
71 77 bodyStateClassesService.start({
72 78 skin: this.themeSkin
73 79 });
... ...
src/app/shared/services/events-hub.service.spec.ts 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +import { OpaqueToken } from 'ng-forward';
  2 +import { EventsHubService, EventsHubKnownEventNames } from './events-hub.service';
  3 +
  4 +
  5 +describe("EventsHubService", () => {
  6 + let eventsHubService: EventsHubService;
  7 + let event1 = 'Event 1';
  8 + let eventsHubKnownEventNames = <EventsHubKnownEventNames>{ getNames: () => { return [ event1]; }};
  9 + it("emits events for the known events", (done) => {
  10 +
  11 + let eventListener = () => {
  12 + };
  13 + // creates the events hub service which known the event "Event1"
  14 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  15 + // subscribe to the event passing the done Function as the eventListener
  16 + // if the event emits works the done function is called and the
  17 + // test will pass
  18 + eventsHubService.subscribeToEvent<any>(event1, done);
  19 + // emits the event
  20 + eventsHubService.emitEvent(event1, null);
  21 + });
  22 +
  23 + it("throws error when trying to emit an unknow event", () => {
  24 + let eventListener = () => {
  25 + };
  26 + // creates the events hub service which known the event "Event1"
  27 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  28 +
  29 + // emits the event
  30 + expect(
  31 + () => { eventsHubService.emitEvent('NotKnownEvent', null); }
  32 + ).toThrowError('Unknown event named NotKnownEvent');
  33 + });
  34 +
  35 + it("throws error when trying to subscribe to an unknow event", () => {
  36 + let eventListener = () => {
  37 + };
  38 + // creates the events hub service which known the event "Event1"
  39 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  40 +
  41 + // emits the event
  42 + expect(
  43 + () => { eventsHubService.subscribeToEvent<void>('NotKnownEvent', () => {}); }
  44 + ).toThrowError('Unknown event named NotKnownEvent');
  45 + });
  46 +});
0 47 \ No newline at end of file
... ...
src/app/shared/services/events-hub.service.ts 0 → 100644
... ... @@ -0,0 +1,56 @@
  1 +import { Injectable, Inject, OpaqueToken, EventEmitter } from 'ng-forward';
  2 +
  3 +export const EVENTS_HUB_KNOW_EVENT_NAMES = new OpaqueToken('EVENTS_HUB_KNOW_EVENT_NAMES');
  4 +
  5 +export interface EventsHubKnownEventNames {
  6 + getNames(): string[];
  7 +}
  8 +
  9 +function isEventsHubKnownEventNames(object: any): object is EventsHubKnownEventNames {
  10 + return 'getNames' in object;
  11 +}
  12 +
  13 +@Injectable()
  14 +@Inject(EVENTS_HUB_KNOW_EVENT_NAMES)
  15 +export class EventsHubService {
  16 +
  17 + private emitters: Map<string, EventEmitter<any>>;
  18 + private knownEvents: string[] = [];
  19 +
  20 + constructor(private eventsHubKnownEventNames: EventsHubKnownEventNames | string[]) {
  21 + if (isEventsHubKnownEventNames(eventsHubKnownEventNames)) {
  22 + this.knownEvents = eventsHubKnownEventNames.getNames();
  23 + } else if (Array.isArray(eventsHubKnownEventNames)) {
  24 + this.knownEvents = eventsHubKnownEventNames;
  25 + }
  26 +
  27 + this.emitters = new Map<string, EventEmitter<any>>();
  28 + this.setupEmitters();
  29 + }
  30 +
  31 + emitEvent(eventType: string, payload?: any) {
  32 + this.checkKnownEvent(eventType);
  33 + let event = this.emitters.get(eventType);
  34 + if ( event ) this.emitters.get(eventType).next(payload);
  35 + }
  36 +
  37 + subscribeToEvent<T>(eventType: string, generatorOrNext?: ((p?: T) => void), error?: any, complete?: any) {
  38 + this.checkKnownEvent(eventType);
  39 + let event = this.emitters.get(eventType);
  40 + if (event) event.subscribe(generatorOrNext, error, complete);
  41 + }
  42 +
  43 + private setupEmitters() {
  44 + for (let i: number = 0; i < this.knownEvents.length; i++) {
  45 + this.emitters.set(this.knownEvents[i], new EventEmitter<any>());
  46 + }
  47 + }
  48 +
  49 + private checkKnownEvent(eventType: string) {
  50 + if (!this.emitters.has(eventType)) {
  51 + throw new Error('Unknown event named ' + eventType.toString());
  52 + }
  53 + }
  54 +
  55 +
  56 +}
0 57 \ No newline at end of file
... ...