]> juplo.de Git - demos/kafka/chat/commitdiff
feat: Introduced method `createUser()` in `UserService`
authorKai Moritz <kai@juplo.de>
Sun, 12 Oct 2025 07:20:07 +0000 (09:20 +0200)
committerKai Moritz <kai@juplo.de>
Mon, 13 Oct 2025 18:30:50 +0000 (20:30 +0200)
* The method requests the creation of a new user and remembers the created
  instance of `User`, if the requests ist successful.
* The remembered `User` can be retrieved via `UserService.getUser()`.
* Also added a pact between the `UserService` and the
  `ChatBackendController`, that formulates the expectations of the
  `UserService`, that are not implemented yet by the backend.

pacts
src/app/chatroom/chatroom.model.ts
src/app/chatroom/chatroom.service.ts
src/app/user/user.component.spec.ts
src/app/user/user.component.ts
src/app/user/user.service.pact.spec.ts [new file with mode: 0644]
src/app/user/user.service.spec.ts
src/app/user/user.service.ts

diff --git a/pacts b/pacts
index a0e97a512e5e56f69035853d1510ae5efdde4d64..6d555b93a94e959095008ce51b4dacd986998eec 160000 (submodule)
--- a/pacts
+++ b/pacts
@@ -1 +1 @@
-Subproject commit a0e97a512e5e56f69035853d1510ae5efdde4d64
+Subproject commit 6d555b93a94e959095008ce51b4dacd986998eec
index 179bc586e2d5d1431c33ded69709766cbf32e879..9eaa9a8c691e8a9e19843283da5218630cd12de6 100644 (file)
@@ -1,3 +1,9 @@
+export interface User
+{
+  id: string,
+  shard: number
+}
+
 export interface Chatroom
 {
   id: string,
index f18f8e72999e7e36fdb397eaf67eadc9f228efa0..a50ba3c398b78f401950d981ed08b7cc80fca8b3 100644 (file)
@@ -14,8 +14,8 @@ class FatalError extends Error { }
 })
 export class ChatroomService {
 
-  private http = inject(HttpClient);
-  private backendUri: string;
+  private readonly http = inject(HttpClient);
+  private readonly backendUri: string;
 
   private chatroom?: Chatroom;
 
index b0a097d235dca7ddc04ac2ea416db802fcb15bbf..e69ff5cff2aabd6b8c2771e05fc41bdeab48b125 100644 (file)
@@ -2,6 +2,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { ReactiveFormsModule } from '@angular/forms';
 import { UserComponent } from './index';
 import { APP_PROPS } from '../app.tokens';
+import { provideHttpClientTesting } from '@angular/common/http/testing';
+import { provideHttpClient } from '@angular/common/http';
 
 describe('UserComponent', () => {
   let component: UserComponent;
@@ -14,6 +16,8 @@ describe('UserComponent', () => {
         UserComponent,
       ],
       providers: [
+        provideHttpClient(),
+        provideHttpClientTesting(),
         { provide: APP_PROPS, useValue: {} },
       ],
     })
index dcb4dad19224d5efbdbba51fc2ed1da117261099..4d7b3ab0747256c9319cdd83b8073e74f3e70c74 100644 (file)
@@ -22,7 +22,7 @@ export class UserComponent {
   updateName(): void {
     let input = this.usernameForm.getRawValue();
     if (input !== null) {
-      this.userService.setUser(input.trim());
+      this.userService.setUserName(input.trim());
       this.router.navigate(['chatrooms'])
     }
   }
diff --git a/src/app/user/user.service.pact.spec.ts b/src/app/user/user.service.pact.spec.ts
new file mode 100644 (file)
index 0000000..e4802f7
--- /dev/null
@@ -0,0 +1,61 @@
+import { Matchers, Pact, SpecificationVersion } from '@pact-foundation/pact';
+import { TestBed } from '@angular/core/testing';
+import { provideHttpClient } from '@angular/common/http';
+import { lastValueFrom } from 'rxjs';
+import { ChatroomService } from './chatroom.service';
+import { Chatroom } from './chatroom.model';
+import { APP_PROPS } from '../app.tokens';
+import { User } from '../chatroom';
+import { UserService } from './user.service';
+
+
+describe('Pact between the UserService and the backend', () => {
+
+  const provider = new Pact({
+    consumer: 'UserService',
+    provider: 'ChatBackendController',
+    spec: SpecificationVersion.SPECIFICATION_VERSION_V4,
+    dir: 'pacts',
+    logLevel: 'debug',
+  });
+
+
+  const EXAMPLE_USER: User = {
+    id: "5c73531c-6fc4-426c-adcb-afc5c140a0f7",
+    shard: 2
+  };
+
+
+  it('POST /user/create', async () => {
+
+    await provider
+      .addInteraction()
+      .given('there are 10 shards')
+      .given('the server is responsible for shard 2')
+      .uponReceiving('a request to create a user')
+      .withRequest('POST', '/user/create', (builder) => {
+        builder.headers({
+          Accept: Matchers.like('application/json'),
+        });
+      })
+      .willRespondWith(200, (builder) => {
+        builder.headers({ 'Content-Type': 'application/json' });
+        builder.jsonBody(EXAMPLE_USER);
+      })
+      .executeTest(async (mockserver) => {
+        await TestBed.configureTestingModule({
+          providers: [
+            UserService,
+            {provide: APP_PROPS, useValue: {backendUri: mockserver.url + '/'}},
+            provideHttpClient(),
+          ]
+        });
+
+        const service = TestBed.inject(UserService);
+        const user = await service.createUser();
+
+        expect(user).toEqual(EXAMPLE_USER);
+        expect(service.getUser()).toEqual(EXAMPLE_USER);
+      });
+  });
+});
index 4144cfd757b46da605acbe4f4adf859e193d2917..d439cf5f0c57731904ab129f3120a9f6d58c3933 100644 (file)
@@ -1,12 +1,21 @@
 import { TestBed } from '@angular/core/testing';
 
 import { UserService } from './index';
+import { provideHttpClient } from '@angular/common/http';
+import { provideHttpClientTesting } from '@angular/common/http/testing';
+import { APP_PROPS } from '../app.tokens';
 
 describe('UserService', () => {
   let service: UserService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    TestBed.configureTestingModule({
+      providers: [
+        provideHttpClient(),
+        provideHttpClientTesting(),
+        { provide: APP_PROPS, useValue: {} },
+      ],
+    });
     service = TestBed.inject(UserService);
   });
 
index bc38fef2c7d2f28a5fcdac987984daa771f1748d..24bb23e6d5c8b71277f76cdd330d8eed68d68635 100644 (file)
@@ -1,15 +1,29 @@
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
 import { Router } from "@angular/router";
+import { HttpClient } from '@angular/common/http';
+import { APP_PROPS } from '../app.tokens';
+import { User } from '../chatroom';
+import { firstValueFrom, Observable, tap } from 'rxjs';
 
 @Injectable({
   providedIn: 'root'
 })
 export class UserService {
 
+  private readonly router = inject(Router);
+
+  private readonly http = inject(HttpClient);
+  private readonly backendUri: string;
+
   private unknown: boolean = true;
+  private user: User|undefined = undefined;
   private name = '';
 
-  constructor(private router: Router) { }
+
+  constructor() {
+    const props = inject(APP_PROPS);
+    this.backendUri = props.backendUri;
+  }
 
   assertUserisKnown(callback: Function): void {
     if(this.unknown) {
@@ -20,13 +34,28 @@ export class UserService {
     }
   }
 
-  setUser(name: string): void {
+  async createUser(): Promise<User> {
+    return await firstValueFrom(
+      this
+        .http
+        .post<User>(this.backendUri + 'user/create', undefined)
+        .pipe(tap((user: User) => {
+          console.log('created a new user: ' + user);
+          this.user = user;
+        })));
+  }
+
+  getUser(): User|undefined {
+    return this.user;
+  }
+
+  setUserName(name: string): void {
     console.log("New user: " + name);
     this.name = name;
     this.unknown = false;
   }
 
-  getUser(): string {
+  getUserName(): string {
     return this.name;
   }
 }