From 56b07ad986f5b96cfdf5638bd172fa7f5908a05d Mon Sep 17 00:00:00 2001
From: OZGCloud <ozgcloud@mgm-tp.com>
Date: Wed, 29 May 2024 19:43:41 +0200
Subject: [PATCH] OZG-5012 remove or convert "get native data" functions of
 resource service; adjust bescheid service

---
 .../src/lib/bescheid.service.spec.ts          |  56 ++--
 .../src/lib/bescheid.service.ts               |  45 ++-
 .../src/lib/command-resource.service.ts       |   1 -
 .../lib/resource/api-resource.service.spec.ts |  41 ---
 .../src/lib/resource/api-resource.service.ts  |   5 -
 .../src/lib/resource/resource.service.spec.ts | 261 +++++++++---------
 .../src/lib/resource/resource.service.ts      | 113 +++-----
 7 files changed, 243 insertions(+), 279 deletions(-)

diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts
index e60c95076e..844c5769fd 100644
--- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts
+++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts
@@ -11,12 +11,12 @@ import {
 } from '@alfa-client/command-shared';
 import {
   ApiError,
-  createEmptyStateResource,
-  createErrorStateResource,
-  createStateResource,
   EMPTY_STRING,
   HttpError,
   StateResource,
+  createEmptyStateResource,
+  createErrorStateResource,
+  createStateResource,
 } from '@alfa-client/tech-shared';
 import { Mock, mock, useFromMock } from '@alfa-client/test-utils';
 import {
@@ -27,12 +27,12 @@ import {
 } from '@alfa-client/vorgang-shared';
 import { fakeAsync, tick } from '@angular/core/testing';
 import faker from '@faker-js/faker';
-import { getUrl, ResourceUri } from '@ngxp/rest';
+import { ResourceUri, getUrl } from '@ngxp/rest';
 import { cold } from 'jest-marbles';
 import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel';
 import { createApiError } from 'libs/tech-shared/test/error';
 import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang';
-import { first, Observable, of } from 'rxjs';
+import { Observable, first, of } from 'rxjs';
 import {
   createBinaryFileListResource,
   createBinaryFileResource,
@@ -148,7 +148,7 @@ describe('BescheidService', () => {
 
     beforeEach(() => {
       facade.getBescheidCommand.mockReturnValue(of(commandStateResource));
-      service.bescheidDraftService.setResourceByUri = jest.fn();
+      service.bescheidDraftService.loadByResourceUri = jest.fn();
     });
 
     it('should call facade', () => {
@@ -163,7 +163,7 @@ describe('BescheidService', () => {
     it('should set resource by uri', () => {
       service.createBescheid(vorgangWithEingang).pipe(first()).subscribe();
 
-      expect(service.bescheidDraftService.setResourceByUri).toHaveBeenCalledWith(
+      expect(service.bescheidDraftService.loadByResourceUri).toHaveBeenCalledWith(
         getUrl(command, CommandLinkRel.EFFECTED_RESOURCE),
       );
     });
@@ -368,8 +368,9 @@ describe('BescheidService', () => {
     });
 
     it('should return command', () => {
-      const command$: Observable<StateResource<CommandResource>> =
-        service.bescheidErstellungUeberspringen(vorgangWithEingangResource);
+      const command$: Observable<StateResource<CommandResource>> = service.vorgangAbschliesen(
+        vorgangWithEingangResource,
+      );
 
       expect(command$).toBeObservable(cold('(a|)', { a: commandStateResource }));
     });
@@ -421,14 +422,15 @@ describe('BescheidService', () => {
         .mockReturnValue(createCommandProps);
       service.bescheidDraftService.stateResource.next(createStateResource(bescheidResource));
       commandService.createCommandByProps.mockReturnValue(of(commandStateResource));
-      service.bescheidDraftService.setResourceByUri = jest.fn();
+      service.bescheidDraftService.loadByResourceUri = jest.fn();
+      service.getResource = jest.fn().mockReturnValue(createBescheidResource());
     });
 
     it('should build update bescheid command props', () => {
       service.updateBescheid(bescheid);
 
       expect(buildUpdateBescheidCommandPropsSpy).toHaveBeenCalledWith(
-        service.bescheidDraftService.getResource(),
+        service.getResource(),
         bescheid,
       );
     });
@@ -451,7 +453,7 @@ describe('BescheidService', () => {
         .updateBescheid(bescheid)
         .pipe(first())
         .subscribe((commandStateResource: StateResource<CommandResource>) => {
-          expect(service.bescheidDraftService.setResourceByUri).toHaveBeenCalledWith(
+          expect(service.bescheidDraftService.loadByResourceUri).toHaveBeenCalledWith(
             getUrl(commandStateResource.resource, CommandLinkRel.EFFECTED_RESOURCE),
           );
           done();
@@ -956,7 +958,7 @@ describe('BescheidService', () => {
 
     beforeEach(() => {
       commandService.createCommandByProps.mockReturnValue(of(commandStateResource));
-      service.bescheidDraftService.getResource = jest.fn().mockReturnValue(bescheidResource);
+      service.getResource = jest.fn().mockReturnValue(bescheidResource);
       buildCreateBescheidDocumentCommandPropsSpy = jest
         .spyOn(BescheidUtil, 'buildCreateBescheidDocumentCommandProps')
         .mockReturnValue(createCommandProps);
@@ -1087,12 +1089,28 @@ describe('BescheidService', () => {
   });
 
   describe('exists bescheid draft', () => {
-    it('should call resource service', () => {
-      service.bescheidDraftService.exists = jest.fn();
+    beforeEach(() => {
+      service.bescheidDraftService.existResource = jest.fn().mockReturnValue(of(true));
+    });
 
+    it('should call bescheid draft service', () => {
       service.existsBescheidDraft();
 
-      expect(service.bescheidDraftService.exists).toHaveBeenCalled();
+      expect(service.bescheidDraftService.existResource).toHaveBeenCalled();
+    });
+
+    it('should return false on missing resource', () => {
+      service.bescheidDraftService.existResource = jest.fn().mockReturnValue(of(false));
+
+      const exists: boolean = service.existsBescheidDraft();
+
+      expect(exists).toBeFalsy();
+    });
+
+    it('should return true on existing resource', () => {
+      const exists: boolean = service.existsBescheidDraft();
+
+      expect(exists).toBeTruthy();
     });
   });
 
@@ -1103,14 +1121,14 @@ describe('BescheidService', () => {
     beforeEach(() => {
       service.deleteBescheid = jest.fn().mockReturnValue(of(commandStateResource));
       service.deleteBescheidDocument = jest.fn();
+
+      service.getResource = jest.fn().mockReturnValue(createBescheidResource());
     });
 
     it('should get resource', () => {
-      service.bescheidDraftService.getResource = jest.fn();
-
       service.bescheidVerwerfen().pipe(first()).subscribe();
 
-      expect(service.bescheidDraftService.getResource).toHaveBeenCalled();
+      expect(service.getResource).toHaveBeenCalled();
     });
 
     it('should delete bescheid', (done) => {
diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts
index 90dbfa4346..42b888cbfa 100644
--- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts
+++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts
@@ -13,17 +13,17 @@ import {
   tapOnCommandSuccessfullyDone,
 } from '@alfa-client/command-shared';
 import {
+  HttpError,
+  ResourceListService,
+  StateResource,
   createEmptyStateResource,
   createStateResource,
   filterIsLoadedOrHasError,
   getEmbeddedResources,
   hasStateResourceError,
-  HttpError,
   isLoaded,
   isNotEmpty,
-  ResourceListService,
   sortByGermanDateStr,
-  StateResource,
 } from '@alfa-client/tech-shared';
 import {
   VorgangCommandService,
@@ -33,16 +33,17 @@ import {
 } from '@alfa-client/vorgang-shared';
 import { getEmpfaenger } from '@alfa-client/vorgang-shared-ui';
 import { Injectable } from '@angular/core';
-import { getUrl, hasLink, ResourceUri } from '@ngxp/rest';
+import { ResourceUri, getUrl, hasLink } from '@ngxp/rest';
 import {
   BehaviorSubject,
+  Observable,
+  Subscription,
   filter,
   first,
   map,
-  Observable,
   startWith,
-  Subscription,
   switchMap,
+  take,
   tap,
 } from 'rxjs';
 import {
@@ -223,7 +224,7 @@ export class BescheidService {
   }
 
   public updateBescheid(bescheid: Bescheid): Observable<StateResource<CommandResource>> {
-    return this.doUpdateBescheid(this.bescheidDraftService.getResource(), bescheid).pipe(
+    return this.doUpdateBescheid(this.getResource(), bescheid).pipe(
       tapOnCommandSuccessfullyDone((commandStateResource: StateResource<CommandResource>) => {
         this.updateBescheidDraft(commandStateResource.resource);
         this.clearCreateBescheidDocumentInProgress();
@@ -268,7 +269,7 @@ export class BescheidService {
   }
 
   private updateBescheidDraft(command: CommandResource): void {
-    this.bescheidDraftService.setResourceByUri(getEffectedResourceUrl(command));
+    this.bescheidDraftService.loadByResourceUri(getEffectedResourceUrl(command));
   }
 
   public getAttachments(): Observable<BinaryFileResource[]> {
@@ -422,7 +423,7 @@ export class BescheidService {
 
   doCreateBescheidDocument(): Observable<StateResource<CommandResource>> {
     return this.commandService.createCommandByProps(
-      buildCreateBescheidDocumentCommandProps(this.bescheidDraftService.getResource()),
+      buildCreateBescheidDocumentCommandProps(this.getResource()),
     );
   }
 
@@ -453,11 +454,16 @@ export class BescheidService {
   }
 
   public existsBescheidDraft(): boolean {
-    return this.bescheidDraftService.exists();
+    let exists: boolean;
+    this.bescheidDraftService
+      .existResource()
+      .pipe(take(1))
+      .subscribe((existsDraft: boolean) => (exists = existsDraft));
+    return exists;
   }
 
   public bescheidVerwerfen(): Observable<StateResource<CommandResource>> {
-    return this.deleteBescheid(this.bescheidDraftService.getResource()).pipe(
+    return this.deleteBescheid(this.getResource()).pipe(
       tapOnCommandSuccessfullyDone(() => {
         this.deleteBescheidDocument();
         this.vorgangService.reloadCurrentVorgang();
@@ -469,6 +475,23 @@ export class BescheidService {
     return this.commandService.createCommandByProps(buildDeleteBescheidCommandProps(bescheid));
   }
 
+  /**
+   * @returns @deprecated Don't use this function, instead passing data to function if necessarry.
+   */
+  getResource(): BescheidResource {
+    let resource: StateResource<BescheidResource> = undefined;
+    const selected = this.bescheidDraftService.get().pipe(
+      filter((stateResource: StateResource<BescheidResource>) => !stateResource.loading),
+      take(1),
+    );
+    const sub = selected.subscribe(
+      (stateResource: StateResource<BescheidResource>) => (resource = stateResource),
+    );
+    sub.unsubscribe();
+
+    return resource.resource;
+  }
+
   public reloadCurrentVorgang(): void {
     this.vorgangService.reloadCurrentVorgang();
   }
diff --git a/alfa-client/libs/command-shared/src/lib/command-resource.service.ts b/alfa-client/libs/command-shared/src/lib/command-resource.service.ts
index 0eb9f03aa4..9a564442fd 100644
--- a/alfa-client/libs/command-shared/src/lib/command-resource.service.ts
+++ b/alfa-client/libs/command-shared/src/lib/command-resource.service.ts
@@ -32,7 +32,6 @@ export class CommandResourceService<B extends Resource, T extends Resource> exte
   }
 
   public delete(): Observable<StateResource<CommandResource>> {
-    this.verifyDeleteLinkRel();
     return this.commandService.createCommandByProps(this.buildDeleteCommandProps());
   }
 
diff --git a/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.spec.ts
index 1a742a30f0..8b559b8b81 100644
--- a/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.spec.ts
+++ b/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.spec.ts
@@ -46,14 +46,6 @@ describe('ApiResourceService', () => {
 
     const resourceWithEditLinkRel: Resource = createDummyResource([editLinkRel]);
 
-    it('should throw error if edit link not exists', () => {
-      service.stateResource.next(createStateResource(createDummyResource()));
-
-      expect(() => service.save(dummyToSave)).toThrowError(
-        'No edit link exists on current stateresource.',
-      );
-    });
-
     it('should call repository', fakeAsync(() => {
       service.stateResource.next(createStateResource(resourceWithEditLinkRel));
       repository.save.mockReturnValue(of(loadedResource));
@@ -99,37 +91,4 @@ describe('ApiResourceService', () => {
       expect(service.stateResource.value).toEqual(createStateResource(loadedResource));
     }));
   });
-
-  describe('delete', () => {
-    const resourceWithDeleteLinkRel: Resource = createDummyResource([deleteLinkRel]);
-    const stateResourceWithDeleteLink: StateResource<Resource> =
-      createStateResource(resourceWithDeleteLinkRel);
-
-    beforeEach(() => {
-      service.stateResource.next(stateResourceWithDeleteLink);
-    });
-
-    it('should throw error if delete linkRel not exists on current stateresource', () => {
-      service.stateResource.next(createStateResource(createDummyResource()));
-
-      expect(() => service.delete()).toThrowError(
-        'No delete link exists on current stateresource.',
-      );
-    });
-
-    it('should call repository', () => {
-      service.delete();
-
-      expect(repository.delete).toHaveBeenCalledWith(resourceWithDeleteLinkRel, deleteLinkRel);
-    });
-
-    it('should return value', () => {
-      const deleteResource: Resource = createDummyResource();
-      repository.delete.mockReturnValue(singleHot(deleteResource));
-
-      const deletedResource: Observable<Resource> = service.delete();
-
-      expect(deletedResource).toBeObservable(singleCold(deleteResource));
-    });
-  });
 });
diff --git a/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.ts
index 2f328bcfd4..d19c4a6fdc 100644
--- a/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.ts
+++ b/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.ts
@@ -22,9 +22,4 @@ export class ApiResourceService<B extends Resource, T extends Resource> extends
       toSave,
     });
   }
-
-  public delete(): Observable<Resource> {
-    this.verifyDeleteLinkRel();
-    return this.repository.delete(this.getResource(), this.config.delete.linkRel);
-  }
 }
diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts
index 8c6b5ae509..d61b71c6e5 100644
--- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts
+++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts
@@ -11,6 +11,7 @@ import { createDummyResource } from '../../../test/resource';
 import { HttpError, ProblemDetail } from '../tech.model';
 import { LinkRelationName, ResourceServiceConfig } from './resource.model';
 import { ResourceRepository } from './resource.repository';
+import { ResourceService } from './resource.service';
 import {
   StateResource,
   createEmptyStateResource,
@@ -18,7 +19,6 @@ import {
   createStateResource,
 } from './resource.util';
 
-import { ResourceService } from './resource.service';
 import * as ResourceUtil from './resource.util';
 
 describe('ResourceService', () => {
@@ -31,6 +31,9 @@ describe('ResourceService', () => {
   const configStateResource: StateResource<Resource> = createStateResource(configResource);
   const configStateResource$: Observable<StateResource<Resource>> = of(configStateResource);
 
+  const dummyResource: Resource = createDummyResource();
+  const dummyStateResource: StateResource<Resource> = createStateResource(dummyResource);
+
   const editLinkRel: string = 'dummyEditLinkRel';
   const getLinkRel: LinkRelationName = 'dummyGetLinkRel';
   const deleteLinkRel: LinkRelationName = 'dummyDeleteLinkRel';
@@ -58,8 +61,7 @@ describe('ResourceService', () => {
     beforeEach(() => {
       service.stateResource.next(stateResource);
 
-      service.handleNullConfigResource = jest.fn();
-      service.handleConfigResourceChanged = jest.fn();
+      service.handleResourceChanges = jest.fn();
       isInvalidResourceCombinationSpy = jest
         .spyOn(ResourceUtil, 'isInvalidResourceCombination')
         .mockReturnValue(true);
@@ -69,17 +71,7 @@ describe('ResourceService', () => {
       service.get().subscribe();
       tick();
 
-      expect(service.handleConfigResourceChanged).toHaveBeenCalledWith(
-        stateResource,
-        configResource,
-      );
-    }));
-
-    it('should handle null configresource', fakeAsync(() => {
-      service.get().subscribe();
-      tick();
-
-      expect(service.handleNullConfigResource).toHaveBeenCalledWith(configResource);
+      expect(service.handleResourceChanges).toHaveBeenCalledWith(stateResource, configResource);
     }));
 
     it('should call isInvalidResourceCombinationSpy', fakeAsync(() => {
@@ -102,25 +94,21 @@ describe('ResourceService', () => {
     });
   });
 
-  describe('handle config resource changed', () => {
+  describe('handle resource changes', () => {
     const stateResource: StateResource<Resource> = createStateResource(createDummyResource());
     const changedConfigResource: Resource = createDummyResource();
 
     describe('on different config resource', () => {
-      it('should update config resource if is different', () => {
-        service.configResource = createDummyResource();
-
-        service.handleConfigResourceChanged(stateResource, changedConfigResource);
-
-        expect(service.configResource).toBe(changedConfigResource);
+      beforeEach(() => {
+        service.handleConfigResourceChanges = jest.fn();
       });
 
-      it('should set state resource reload', () => {
+      it('should update state resource by config resource', () => {
         service.configResource = createDummyResource();
 
-        service.handleConfigResourceChanged(stateResource, changedConfigResource);
+        service.handleResourceChanges(stateResource, changedConfigResource);
 
-        expect(service.stateResource.value.reload).toBeTruthy();
+        expect(service.handleConfigResourceChanges).toHaveBeenCalled();
       });
     });
 
@@ -134,7 +122,7 @@ describe('ResourceService', () => {
       it('should call shouldLoadResource', () => {
         service.shouldLoadResource = jest.fn();
 
-        service.handleConfigResourceChanged(stateResource, configResource);
+        service.handleResourceChanges(stateResource, configResource);
 
         expect(service.shouldLoadResource).toHaveBeenCalledWith(stateResource, configResource);
       });
@@ -143,71 +131,146 @@ describe('ResourceService', () => {
         service.shouldLoadResource = jest.fn().mockReturnValue(true);
         service.loadResource = jest.fn();
 
-        service.handleConfigResourceChanged(stateResource, configResource);
+        service.handleResourceChanges(stateResource, configResource);
 
         expect(service.loadResource).toHaveBeenCalledWith(configResource);
       });
 
-      it('should NOT load resource on shouldLoadResource false', () => {
+      it('should NOT load resource', () => {
         service.loadResource = jest.fn();
         service.shouldLoadResource = jest.fn().mockReturnValue(false);
 
-        service.handleConfigResourceChanged(stateResource, configResource);
+        service.handleResourceChanges(stateResource, configResource);
 
         expect(service.loadResource).not.toHaveBeenCalled();
       });
     });
   });
 
-  describe('handle null config resource', () => {
-    const resource: Resource = createDummyResource();
-    const stateResource: StateResource<Resource> = createStateResource(resource);
+  describe('handle config resource changes', () => {
+    const stateResource: StateResource<Resource> = createStateResource(createDummyResource());
 
-    beforeEach(() => {
-      service.shouldClearStateResource = jest.fn();
-      service.stateResource.next(stateResource);
+    describe('on stable stateresource', () => {
+      beforeEach(() => {
+        jest.spyOn(ResourceUtil, 'isStateResoureStable').mockReturnValue(true);
+        service.updateStateResourceByConfigResource = jest.fn();
+      });
+
+      it('should update configresource', () => {
+        service.configResource = createDummyResource();
+
+        service.handleResourceChanges(stateResource, configResource);
+
+        expect(service.configResource).toBe(configResource);
+      });
+
+      it('should update stateresource by configresource', () => {
+        service.handleResourceChanges(stateResource, configResource);
+
+        expect(service.updateStateResourceByConfigResource).toHaveBeenCalledWith(
+          stateResource,
+          configResource,
+        );
+      });
+    });
+
+    describe('on instable stateresource', () => {
+      beforeEach(() => {
+        jest.spyOn(ResourceUtil, 'isStateResoureStable').mockReturnValue(false);
+      });
+
+      it('should NOT update configresource', () => {
+        const currentConfigResource = createDummyResource();
+        service.configResource = currentConfigResource;
+
+        service.handleResourceChanges(stateResource, configResource);
+
+        expect(service.configResource).toBe(currentConfigResource);
+      });
+
+      it('should NOT update stateresource by configresource', () => {
+        service.updateStateResourceByConfigResource = jest.fn();
+
+        service.handleResourceChanges(stateResource, configResource);
+
+        expect(service.updateStateResourceByConfigResource).not.toHaveBeenCalled();
+      });
     });
-    it('should call shouldClearStateResource', () => {
-      service.handleNullConfigResource(null);
+  });
+
+  describe('update stateresource by configresource', () => {
+    it('should check if should clear stateresource', () => {
+      service.shouldClearStateResource = jest.fn();
+
+      service.updateStateResourceByConfigResource(dummyStateResource, configResource);
 
-      expect(service.shouldClearStateResource).toHaveBeenCalledWith(null);
+      expect(service.shouldClearStateResource).toHaveBeenCalled();
     });
 
-    it('should clear stateresource if shouldClearStateResource is true', () => {
+    it('should clear resource if should', () => {
+      service.stateResource.next(createStateResource(createDummyResource()));
       service.shouldClearStateResource = jest.fn().mockReturnValue(true);
 
-      service.handleNullConfigResource(null);
+      service.updateStateResourceByConfigResource(dummyStateResource, configResource);
 
       expect(service.stateResource.value).toEqual(createEmptyStateResource());
     });
 
-    it('should keep stateresource if shouldClearStateResource is false', () => {
-      service.shouldClearStateResource = jest.fn().mockReturnValue(false);
+    describe('on NOT clearing stateresource', () => {
+      beforeEach(() => {
+        service.shouldClearStateResource = jest.fn().mockReturnValue(false);
+      });
+
+      it('should load resource if link exists', () => {
+        service.hasGetLink = jest.fn();
 
-      service.handleNullConfigResource(null);
+        service.updateStateResourceByConfigResource(dummyStateResource, configResource);
 
-      expect(service.stateResource.value).toBe(stateResource);
+        expect(service.hasGetLink).toHaveBeenCalledWith(configResource);
+      });
     });
   });
 
   describe('should clear stateresource', () => {
-    const resource: Resource = createDummyResource();
-    const stateResource: StateResource<Resource> = createStateResource(resource);
+    describe('on existing stateresource', () => {
+      beforeEach(() => {
+        service.stateResource.next(dummyStateResource);
+      });
 
-    it('should return true on null configresource and filled stateresource', () => {
-      service.stateResource.next(stateResource);
+      it('should return true if configresource is null', () => {
+        const shouldClear: boolean = service.shouldClearStateResource(dummyStateResource, null);
 
-      const shouldClear: boolean = service.shouldClearStateResource(null);
+        expect(shouldClear).toBeTruthy();
+      });
 
-      expect(shouldClear).toBeTruthy();
+      it('should return true if configresource has no get link', () => {
+        const shouldClear: boolean = service.shouldClearStateResource(
+          dummyStateResource,
+          createDummyResource(),
+        );
+
+        expect(shouldClear).toBeTruthy();
+      });
     });
 
-    it('should return false on null configresource and empty stateresource', () => {
-      service.stateResource.next(createEmptyStateResource());
+    describe('on empty stateresource', () => {
+      it('should return false', () => {
+        const shouldClear: boolean = service.shouldClearStateResource(
+          createEmptyStateResource(),
+          null,
+        );
 
-      const shouldClear: boolean = service.shouldClearStateResource(null);
+        expect(shouldClear).toBeFalsy();
+      });
+
+      it('should return false if configresource has no get link', () => {
+        const shouldClear: boolean = service.shouldClearStateResource(
+          createEmptyStateResource(),
+          createDummyResource(),
+        );
 
-      expect(shouldClear).toBeFalsy();
+        expect(shouldClear).toBeFalsy();
+      });
     });
   });
 
@@ -247,12 +310,6 @@ describe('ResourceService', () => {
   describe('load resource', () => {
     const configResourceWithGetLinkRel: Resource = createDummyResource([getLinkRel]);
 
-    it('should throw error if getLinkRel not exists', () => {
-      expect(() => service.loadResource(configResource)).toThrowError(
-        'No get link exists on configresource.',
-      );
-    });
-
     it('should call do load resource', () => {
       service.doLoadResource = jest.fn();
 
@@ -314,12 +371,12 @@ describe('ResourceService', () => {
     });
   });
 
-  describe('setResourceByUri', () => {
+  describe('loadByResourceUri', () => {
     it('should do load resource', () => {
       service.doLoadResource = jest.fn();
       const resourceUri: ResourceUri = faker.internet.url();
 
-      service.setResourceByUri(resourceUri);
+      service.loadByResourceUri(resourceUri);
 
       expect(service.doLoadResource).toHaveBeenCalledWith(resourceUri);
     });
@@ -343,14 +400,6 @@ describe('ResourceService', () => {
 
     const resourceWithEditLinkRel: Resource = createDummyResource([editLinkRel]);
 
-    it('should throw error if edit link not exists', () => {
-      service.stateResource.next(createStateResource(createDummyResource()));
-
-      expect(() => service.save(dummyToSave)).toThrowError(
-        'No edit link exists on current stateresource.',
-      );
-    });
-
     it('should do save', fakeAsync(() => {
       const stateResource: StateResource<Resource> = createStateResource(resourceWithEditLinkRel);
       service.stateResource.next(stateResource);
@@ -434,77 +483,21 @@ describe('ResourceService', () => {
     });
   });
 
-  describe('can edit', () => {
-    it('should return true if link is present', () => {
-      const resource: StateResource<Resource> = createStateResource(
-        createDummyResource([editLinkRel]),
-      );
-      service.stateResource.next(resource);
-
-      const canEdit: boolean = service.canEdit();
-
-      expect(canEdit).toBeTruthy();
-    });
-
-    it('should return false if link is NOT present', () => {
-      const resource: StateResource<Resource> = createStateResource(createDummyResource());
-      service.stateResource.next(resource);
-
-      const canEdit: boolean = service.canEdit();
-
-      expect(canEdit).toBeFalsy();
-    });
-  });
-
-  describe('can delete', () => {
-    it('should return true if link is present', () => {
-      const resource: StateResource<Resource> = createStateResource(
-        createDummyResource([deleteLinkRel]),
-      );
-      service.stateResource.next(resource);
-
-      const canEdit: boolean = service.canDelete();
-
-      expect(canEdit).toBeTruthy();
-    });
-
-    it('should return false if link is NOT present', () => {
-      const resource: StateResource<Resource> = createStateResource(createDummyResource());
-      service.stateResource.next(resource);
-
-      const canEdit: boolean = service.canDelete();
-
-      expect(canEdit).toBeFalsy();
-    });
-  });
-
-  describe('get resource', () => {
-    it('should return resource from stateResource', () => {
-      const resource: Resource = createDummyResource();
-      const stateResource: StateResource<Resource> = createStateResource(resource);
-      service.stateResource.next(stateResource);
-
-      const result: Resource = service.getResource();
-
-      expect(result).toBe(resource);
-    });
-  });
-
-  describe('exists', () => {
-    it('should return true', () => {
-      service.updateStateResource(createDummyResource());
+  describe('exist resource', () => {
+    it('should return true on existing resource', () => {
+      service.stateResource.next(createStateResource(createDummyResource()));
 
-      const exists = service.exists();
+      const existResource$: Observable<boolean> = service.existResource();
 
-      expect(exists).toBeTruthy();
+      expect(existResource$).toBeObservable(singleCold(true));
     });
 
-    it('should return false', () => {
-      service.updateStateResource(null);
+    it('should return false on null resource', () => {
+      service.stateResource.next(createEmptyStateResource());
 
-      const exists = service.exists();
+      const existResource$: Observable<boolean> = service.existResource();
 
-      expect(exists).toBeFalsy();
+      expect(existResource$).toBeObservable(singleCold(false));
     });
   });
 });
diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts
index b87ef49e8d..8a05ace866 100644
--- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts
+++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts
@@ -26,8 +26,8 @@ import {
   createStateResource,
   isInvalidResourceCombination,
   isLoadingRequired,
+  isStateResoureStable,
   StateResource,
-  throwErrorOn,
 } from './resource.util';
 
 /**
@@ -49,9 +49,8 @@ export abstract class ResourceService<B extends Resource, T extends Resource> {
   public get(): Observable<StateResource<T>> {
     return combineLatest([this.stateResource.asObservable(), this.getConfigResource()]).pipe(
       tap(([stateResource, configResource]) =>
-        this.handleConfigResourceChanged(stateResource, configResource),
+        this.handleResourceChanges(stateResource, configResource),
       ),
-      tap(([, configResource]) => this.handleNullConfigResource(configResource)),
       filter(
         ([stateResource]) => !isInvalidResourceCombination(stateResource, this.configResource),
       ),
@@ -70,33 +69,61 @@ export abstract class ResourceService<B extends Resource, T extends Resource> {
     );
   }
 
-  handleConfigResourceChanged(stateResource: StateResource<T>, configResource: B): void {
+  handleResourceChanges(stateResource: StateResource<T>, configResource: B): void {
     if (!isEqual(this.configResource, configResource)) {
-      this.configResource = configResource;
-      this.stateResource.next({ ...this.stateResource.value, reload: true });
+      this.handleConfigResourceChanges(stateResource, configResource);
     } else if (this.shouldLoadResource(stateResource, configResource)) {
       this.loadResource(configResource);
     }
   }
 
+  handleConfigResourceChanges(stateResource: StateResource<T>, configResource: B) {
+    if (isStateResoureStable(stateResource)) {
+      this.configResource = configResource;
+      this.updateStateResourceByConfigResource(stateResource, configResource);
+    }
+  }
+
+  updateStateResourceByConfigResource(stateResource: StateResource<T>, configResource: B): void {
+    if (this.shouldClearStateResource(stateResource, configResource)) {
+      this.clearResource();
+    } else if (this.hasGetLink(configResource)) {
+      this.loadResource(configResource);
+    }
+  }
+
+  shouldClearStateResource(stateResource: StateResource<T>, configResource: B): boolean {
+    return (
+      (isNull(configResource) || this.hasNotGetLink(configResource)) &&
+      !this.isStateResourceEmpty(stateResource)
+    );
+  }
+
+  private hasNotGetLink(configResource: B): boolean {
+    return !this.hasGetLink(configResource);
+  }
+
+  private isStateResourceEmpty(stateResource: StateResource<T>): boolean {
+    return isEqual(stateResource, createEmptyStateResource());
+  }
+
+  private clearResource(): void {
+    this.stateResource.next(createEmptyStateResource());
+  }
+
+  hasGetLink(configResource: B): boolean {
+    return isNotNull(configResource) && hasLink(configResource, this.config.getLinkRel);
+  }
+
   shouldLoadResource(stateResource: StateResource<T>, configResource: B): boolean {
     return isNotNull(configResource) && isLoadingRequired(stateResource);
   }
 
   loadResource(configResource: B): void {
-    this.verifyGetLink(configResource);
     this.doLoadResource(getUrl(configResource, this.config.getLinkRel));
   }
 
-  private verifyGetLink(configResource: B): void {
-    throwErrorOn(
-      !hasLink(configResource, this.config.getLinkRel),
-      'No get link exists on configresource.',
-    );
-  }
-
-  //TODO rename to reloadByResourceUri
-  public setResourceByUri(resourceUri: ResourceUri): void {
+  public loadByResourceUri(resourceUri: ResourceUri): void {
     this.doLoadResource(resourceUri);
   }
 
@@ -116,18 +143,7 @@ export abstract class ResourceService<B extends Resource, T extends Resource> {
     this.stateResource.next(createStateResource(resource));
   }
 
-  handleNullConfigResource(configResource: B): void {
-    if (this.shouldClearStateResource(configResource)) {
-      this.stateResource.next(createEmptyStateResource());
-    }
-  }
-
-  shouldClearStateResource(configResource: B): boolean {
-    return isNull(configResource) && !isEqual(this.stateResource.value, createEmptyStateResource());
-  }
-
   public save(toSave: unknown): Observable<StateResource<T | HttpError>> {
-    this.verifyEditLinkRel();
     const previousResource: T = this.stateResource.value.resource;
     return this.doSave(previousResource, toSave).pipe(
       tap((loadedResource: T) => this.stateResource.next(createStateResource(loadedResource))),
@@ -143,52 +159,13 @@ export abstract class ResourceService<B extends Resource, T extends Resource> {
     return throwError(() => errorResponse);
   }
 
-  public verifyEditLinkRel(): void {
-    throwErrorOn(
-      !this.hasLinkRel(this.config.edit.linkRel),
-      'No edit link exists on current stateresource.',
-    );
-  }
-
   abstract doSave(resource: T, toSave: unknown): Observable<T>;
 
   public refresh(): void {
     this.stateResource.next({ ...this.stateResource.value, reload: true });
   }
 
-  /**
-   * @deprecated
-   */
-  public canEdit(): boolean {
-    return this.hasLinkRel(this.config.edit.linkRel);
-  }
-
-  protected hasLinkRel(linkRel: string): boolean {
-    return hasLink(this.getResource(), linkRel);
-  }
-
-  /**
-   * @deprecated
-   */
-  public getResource(): T {
-    return this.stateResource.value.resource;
-  }
-
-  /**
-   * @deprecated
-   */
-  public canDelete(): boolean {
-    return this.hasLinkRel(this.config.delete.linkRel);
-  }
-
-  protected verifyDeleteLinkRel(): void {
-    throwErrorOn(!this.canDelete(), 'No delete link exists on current stateresource.');
-  }
-
-  /**
-   * @deprecated
-   */
-  public exists(): boolean {
-    return isNotNull(this.stateResource.value.resource);
+  public existResource(): Observable<boolean> {
+    return this.stateResource.asObservable().pipe(mapToResource<T>(), map(isNotNull));
   }
 }
-- 
GitLab