From: Kai Moritz Date: Tue, 16 Sep 2025 20:14:01 +0000 (+0200) Subject: refactor: Moved services & model into the according domains -- MOVE X-Git-Tag: jest--2025-10-04--09-27~31 X-Git-Url: http://juplo.de/gitweb/?a=commitdiff_plain;h=bfc0ffa71b274e27106d6eeb8d543b2176fb5132;p=demos%2Fkafka%2Fchat refactor: Moved services & model into the according domains -- MOVE --- diff --git a/src/app/chatroom.service.spec.ts b/src/app/chatroom.service.spec.ts deleted file mode 100644 index af0ed87d..00000000 --- a/src/app/chatroom.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { ChatroomService } from './chatroom.service'; - -describe('ChatroomService', () => { - let service: ChatroomService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(ChatroomService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/src/app/chatroom.service.ts b/src/app/chatroom.service.ts deleted file mode 100644 index a2b039b3..00000000 --- a/src/app/chatroom.service.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { EventSourceMessage, fetchEventSource } from '@microsoft/fetch-event-source'; -import { Observable, Subscriber } from 'rxjs'; -import { Chatroom } from './chatroom'; -import { Message } from './message'; - -class RetriableError extends Error { } -class CanceledError extends Error { } -class FatalError extends Error { } - -@Injectable({ - providedIn: 'root' -}) -export class ChatroomService { - - private backendUri = 'http://localhost:8080/'; - - private channel: Subscriber = new Subscriber(); - private uri: string = "CLOSED"; - private canceled: boolean = false; - - constructor(private http: HttpClient) { } - - getChatrooms(): Observable { - return this.http.get(this.backendUri + 'list'); - } - - getChatroom(shard: string, id: string): Observable { - return this.http.get( - this.backendUri + id, - { headers: { 'X-Shard': shard }}); - } - - listen(shard: string, id: string): Observable { - let observable = new Observable( - (observer) => { - this.channel = observer; - }); - - if (this.uri !== 'CLOSED') { - console.log('Channel is still open, uncanceling ' + this.uri); - this.canceled = false; - return observable; - } - - let uri: string = this.backendUri + id + '/listen'; - let service = this; - - fetchEventSource(uri,{ - headers: { 'X-Shard': shard }, - async onopen(response) { - if (response.ok && response.status === 200) { - console.log('Opend channel ' + uri, response); - service.uri = uri; - service.canceled = false; - } - else if ( - response.status >= 400 && - response.status < 500 && - response.status !== 429 - ) { - console.error('Client side error when connecting to channel ' + uri, response); - throw new FatalError(); - } - }, - onmessage(event) { - console.debug('Received message on channel: ' + uri); - if (service.canceled) - throw new CanceledError(); - service.processEvent(event); - }, - onclose() { - console.log('Server closed channel ' + uri); - service.uri = "CLOSED"; - throw new RetriableError(); - }, - onerror(error) { - console.log('Error on channel ' + uri, error); - if (error instanceof CanceledError || error instanceof FatalError) { - service.uri = "CLOSED"; - throw error; // rethrow to stop the operation - } - else { - return 1000; // retry-intervall in ms - } - }, - openWhenHidden: true - }).then(() => console.debug('Promise fullfilled for ' + uri)); - - return observable; - } - - unlisten(): void { - console.log('Canceling channel ' + this.uri); - this.canceled = true; - } - - // Processes custom event types - private processEvent(message: EventSourceMessage): void { - const parsed = message.data ? JSON.parse(message.data) : {}; - switch (message.event) { - case 'message': { - this.channel.next(parsed); - break; - } - case 'error': { - // Not implemented server-side yet - console.error('Received error-message from server on channel ' + this.uri, message.data); - throw new FatalError(message.data); - } - // Add others if neccessary - default: { - console.error('Unhandled event:', message.event); - break; - } - } - } -} diff --git a/src/app/chatroom.ts b/src/app/chatroom.ts deleted file mode 100644 index 9539823f..00000000 --- a/src/app/chatroom.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Chatroom -{ - id: string, - name: string, - shard: number -} diff --git a/src/app/chatroom/chatroom.service.spec.ts b/src/app/chatroom/chatroom.service.spec.ts new file mode 100644 index 00000000..af0ed87d --- /dev/null +++ b/src/app/chatroom/chatroom.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ChatroomService } from './chatroom.service'; + +describe('ChatroomService', () => { + let service: ChatroomService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ChatroomService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/chatroom/chatroom.service.ts b/src/app/chatroom/chatroom.service.ts new file mode 100644 index 00000000..a2b039b3 --- /dev/null +++ b/src/app/chatroom/chatroom.service.ts @@ -0,0 +1,119 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { EventSourceMessage, fetchEventSource } from '@microsoft/fetch-event-source'; +import { Observable, Subscriber } from 'rxjs'; +import { Chatroom } from './chatroom'; +import { Message } from './message'; + +class RetriableError extends Error { } +class CanceledError extends Error { } +class FatalError extends Error { } + +@Injectable({ + providedIn: 'root' +}) +export class ChatroomService { + + private backendUri = 'http://localhost:8080/'; + + private channel: Subscriber = new Subscriber(); + private uri: string = "CLOSED"; + private canceled: boolean = false; + + constructor(private http: HttpClient) { } + + getChatrooms(): Observable { + return this.http.get(this.backendUri + 'list'); + } + + getChatroom(shard: string, id: string): Observable { + return this.http.get( + this.backendUri + id, + { headers: { 'X-Shard': shard }}); + } + + listen(shard: string, id: string): Observable { + let observable = new Observable( + (observer) => { + this.channel = observer; + }); + + if (this.uri !== 'CLOSED') { + console.log('Channel is still open, uncanceling ' + this.uri); + this.canceled = false; + return observable; + } + + let uri: string = this.backendUri + id + '/listen'; + let service = this; + + fetchEventSource(uri,{ + headers: { 'X-Shard': shard }, + async onopen(response) { + if (response.ok && response.status === 200) { + console.log('Opend channel ' + uri, response); + service.uri = uri; + service.canceled = false; + } + else if ( + response.status >= 400 && + response.status < 500 && + response.status !== 429 + ) { + console.error('Client side error when connecting to channel ' + uri, response); + throw new FatalError(); + } + }, + onmessage(event) { + console.debug('Received message on channel: ' + uri); + if (service.canceled) + throw new CanceledError(); + service.processEvent(event); + }, + onclose() { + console.log('Server closed channel ' + uri); + service.uri = "CLOSED"; + throw new RetriableError(); + }, + onerror(error) { + console.log('Error on channel ' + uri, error); + if (error instanceof CanceledError || error instanceof FatalError) { + service.uri = "CLOSED"; + throw error; // rethrow to stop the operation + } + else { + return 1000; // retry-intervall in ms + } + }, + openWhenHidden: true + }).then(() => console.debug('Promise fullfilled for ' + uri)); + + return observable; + } + + unlisten(): void { + console.log('Canceling channel ' + this.uri); + this.canceled = true; + } + + // Processes custom event types + private processEvent(message: EventSourceMessage): void { + const parsed = message.data ? JSON.parse(message.data) : {}; + switch (message.event) { + case 'message': { + this.channel.next(parsed); + break; + } + case 'error': { + // Not implemented server-side yet + console.error('Received error-message from server on channel ' + this.uri, message.data); + throw new FatalError(message.data); + } + // Add others if neccessary + default: { + console.error('Unhandled event:', message.event); + break; + } + } + } +} diff --git a/src/app/chatroom/chatroom.ts b/src/app/chatroom/chatroom.ts new file mode 100644 index 00000000..9539823f --- /dev/null +++ b/src/app/chatroom/chatroom.ts @@ -0,0 +1,6 @@ +export interface Chatroom +{ + id: string, + name: string, + shard: number +} diff --git a/src/app/chatroom/message.ts b/src/app/chatroom/message.ts new file mode 100644 index 00000000..f5451a67 --- /dev/null +++ b/src/app/chatroom/message.ts @@ -0,0 +1,8 @@ +export interface Message +{ + id: string, + serialNumber: number, + timestamp: string, + user: string, + text: string, +} diff --git a/src/app/message.ts b/src/app/message.ts deleted file mode 100644 index f5451a67..00000000 --- a/src/app/message.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface Message -{ - id: string, - serialNumber: number, - timestamp: string, - user: string, - text: string, -} diff --git a/src/app/user.service.spec.ts b/src/app/user.service.spec.ts deleted file mode 100644 index 3f804c9f..00000000 --- a/src/app/user.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { UserService } from './user.service'; - -describe('UserService', () => { - let service: UserService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(UserService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/src/app/user.service.ts b/src/app/user.service.ts deleted file mode 100644 index bc38fef2..00000000 --- a/src/app/user.service.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Router } from "@angular/router"; - -@Injectable({ - providedIn: 'root' -}) -export class UserService { - - private unknown: boolean = true; - private name = ''; - - constructor(private router: Router) { } - - assertUserisKnown(callback: Function): void { - if(this.unknown) { - this.router.navigate(['user']); - } - else { - callback(); - } - } - - setUser(name: string): void { - console.log("New user: " + name); - this.name = name; - this.unknown = false; - } - - getUser(): string { - return this.name; - } -} diff --git a/src/app/user/user.service.spec.ts b/src/app/user/user.service.spec.ts new file mode 100644 index 00000000..3f804c9f --- /dev/null +++ b/src/app/user/user.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { UserService } from './user.service'; + +describe('UserService', () => { + let service: UserService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(UserService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/user/user.service.ts b/src/app/user/user.service.ts new file mode 100644 index 00000000..bc38fef2 --- /dev/null +++ b/src/app/user/user.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { Router } from "@angular/router"; + +@Injectable({ + providedIn: 'root' +}) +export class UserService { + + private unknown: boolean = true; + private name = ''; + + constructor(private router: Router) { } + + assertUserisKnown(callback: Function): void { + if(this.unknown) { + this.router.navigate(['user']); + } + else { + callback(); + } + } + + setUser(name: string): void { + console.log("New user: " + name); + this.name = name; + this.unknown = false; + } + + getUser(): string { + return this.name; + } +}