From 668dc184212ee28e701cc4b3e84ad8a662202222 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 9 Dec 2024 15:23:30 +0100 Subject: [PATCH] add endpoint /api/hs/office/memberships/M-{membershipNumber} and amend query to partnerNumber --- ...OfficeCoopAssetsTransactionController.java | 9 +- .../HsOfficeMembershipController.java | 46 ++++-- .../HsOfficeMembershipRepository.java | 19 ++- ...ice-memberships-with-membershipNumber.yaml | 58 +------ .../hs-office/hs-office-memberships.yaml | 10 +- .../api-definition/hs-office/hs-office.yaml | 1 - ...tsTransactionControllerAcceptanceTest.java | 10 +- ...opAssetsTransactionControllerRestTest.java | 2 +- ...sTransactionRepositoryIntegrationTest.java | 8 +- ...esTransactionControllerAcceptanceTest.java | 10 +- ...sTransactionRepositoryIntegrationTest.java | 8 +- ...iceMembershipControllerAcceptanceTest.java | 44 +++--- .../HsOfficeMembershipControllerRestTest.java | 147 +++++++++++++++++- ...ceMembershipRepositoryIntegrationTest.java | 36 ++++- 14 files changed, 277 insertions(+), 131 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java index fbb59788..9073e564 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java @@ -290,11 +290,10 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse if (adoptingMembershipMemberNumber != null) { final var adoptingMemberNumber = Integer.valueOf(adoptingMembershipMemberNumber.substring("M-".length())); final var adoptingMembership = membershipRepo.findMembershipByMemberNumber(adoptingMemberNumber); - if (adoptingMembership != null) { - return adoptingMembership; - } - throw new ValidationException("adoptingMembership.memberNumber='" + adoptingMembershipMemberNumber - + "' not found or not accessible"); + return adoptingMembership.orElseThrow( () -> + new ValidationException("adoptingMembership.memberNumber='" + adoptingMembershipMemberNumber + + "' not found or not accessible") + ); } throw new ValidationException( diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java index 3ba36f5c..84994ceb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeMembersh import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipResource; +import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.mapper.StandardMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -17,7 +18,7 @@ import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; -import static java.util.Optional.ofNullable; +import static net.hostsharing.hsadminng.errors.Validate.validate; import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag; @RestController @@ -39,16 +40,20 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { final String currentSubject, final String assumedRoles, final UUID partnerUuid, - final String memberNumber) { + final String partnerNumber) { context.define(currentSubject, assumedRoles); - final var entities = (memberNumber != null) - ? ofNullable(membershipRepo.findMembershipByMemberNumber( - cropTag(HsOfficeMembershipEntity.MEMBER_NUMBER_TAG, memberNumber))).stream() - .toList() - : membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid); + validate("partnerUuid, partnerNumber").atMaxOneNonNull(partnerUuid, partnerNumber); - final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class, + final var entities = partnerNumber != null + ? membershipRepo.findMembershipsByPartnerNumber( + cropTag(HsOfficePartnerEntity.PARTNER_NUMBER_TAG, partnerNumber)) + : partnerUuid != null + ? membershipRepo.findMembershipsByPartnerUuid(partnerUuid) + : membershipRepo.findAll(); + + final var resources = mapper.mapList( + entities, HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER); return ResponseEntity.ok(resources); } @@ -72,7 +77,8 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { .path("/api/hs/office/memberships/{id}") .buildAndExpand(saved.getUuid()) .toUri(); - final var mapped = mapper.map(saved, HsOfficeMembershipResource.class, + final var mapped = mapper.map( + saved, HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER); return ResponseEntity.created(uri).body(mapped); } @@ -91,7 +97,27 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { if (result.isEmpty()) { return ResponseEntity.notFound().build(); } - return ResponseEntity.ok(mapper.map(result.get(), HsOfficeMembershipResource.class, + return ResponseEntity.ok(mapper.map( + result.get(), HsOfficeMembershipResource.class, + SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER)); + } + + @Override + @Transactional(readOnly = true) + @Timed("app.office.membership.api.getSingleMembershipByMembershipNumber") + public ResponseEntity getSingleMembershipByMembershipNumber( + final String currentSubject, + final String assumedRoles, + final Integer membershipNumber) { + + context.define(currentSubject, assumedRoles); + + final var result = membershipRepo.findMembershipByMemberNumber(membershipNumber); + if (result.isEmpty()) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(mapper.map( + result.get(), HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER)); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java index 5a537b26..47a3608e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java @@ -22,12 +22,19 @@ public interface HsOfficeMembershipRepository extends Repository findMembershipsByOptionalPartnerUuid(UUID partnerUuid); + List findMembershipsByPartnerUuid(UUID partnerUuid); + + @Query(""" + SELECT membership FROM HsOfficeMembershipEntity membership + WHERE membership.partner.partnerNumber = :partnerNumber + ORDER BY membership.partner.partnerNumber, membership.memberNumberSuffix + """) + @Timed("app.office.membership.repo.findMembershipsByPartnerNumber") + List findMembershipsByPartnerNumber(Integer partnerNumber); @Query(""" SELECT membership FROM HsOfficeMembershipEntity membership @@ -35,12 +42,12 @@ public interface HsOfficeMembershipRepository extends Repository findMembershipByPartnerNumberAndSuffix( @NotNull Integer partnerNumber, @NotNull String suffix); - default HsOfficeMembershipEntity findMembershipByMemberNumber(Integer memberNumber) { + default Optional findMembershipByMemberNumber(final Integer memberNumber) { final var partnerNumber = memberNumber / 100; final String suffix = String.format("%02d", memberNumber % 100); final var result = findMembershipByPartnerNumberAndSuffix(partnerNumber, suffix); diff --git a/src/main/resources/api-definition/hs-office/hs-office-memberships-with-membershipNumber.yaml b/src/main/resources/api-definition/hs-office/hs-office-memberships-with-membershipNumber.yaml index 88cf784a..8e0f8a64 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-memberships-with-membershipNumber.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-memberships-with-membershipNumber.yaml @@ -2,7 +2,7 @@ get: tags: - hs-office-memberships description: 'Fetch a single membership by its membershipNumber, if visible for the current subject.' - operationId: getSingleMembershipByUuid + operationId: getSingleMembershipByMembershipNumber parameters: - $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/assumedRoles' @@ -27,59 +27,3 @@ get: $ref: 'error-responses.yaml#/components/responses/Unauthorized' "403": $ref: 'error-responses.yaml#/components/responses/Forbidden' - -patch: - tags: - - hs-office-memberships - description: 'Updates a single membership by its uuid, if permitted for the current subject.' - operationId: patchMembership - parameters: - - $ref: 'auth.yaml#/components/parameters/currentSubject' - - $ref: 'auth.yaml#/components/parameters/assumedRoles' - - name: membershipUUID - in: path - required: true - schema: - type: string - format: uuid - requestBody: - content: - 'application/json': - schema: - $ref: 'hs-office-membership-schemas.yaml#/components/schemas/HsOfficeMembershipPatch' - responses: - "200": - description: OK - content: - 'application/json': - schema: - $ref: 'hs-office-membership-schemas.yaml#/components/schemas/HsOfficeMembership' - "401": - $ref: 'error-responses.yaml#/components/responses/Unauthorized' - "403": - $ref: 'error-responses.yaml#/components/responses/Forbidden' - -delete: - tags: - - hs-office-memberships - description: 'Delete a single membership by its uuid, if permitted for the current subject.' - operationId: deleteMembershipByUuid - parameters: - - $ref: 'auth.yaml#/components/parameters/currentSubject' - - $ref: 'auth.yaml#/components/parameters/assumedRoles' - - name: membershipUUID - in: path - required: true - schema: - type: string - format: uuid - description: UUID of the membership to delete. - responses: - "204": - description: No Content - "401": - $ref: 'error-responses.yaml#/components/responses/Unauthorized' - "403": - $ref: 'error-responses.yaml#/components/responses/Forbidden' - "404": - $ref: 'error-responses.yaml#/components/responses/NotFound' diff --git a/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml b/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml index 9b2c27b4..f0da7c61 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml @@ -15,15 +15,15 @@ get: type: string format: uuid description: UUID of the business partner, exclusive to `memberNumber`. - - name: memberNumber + - name: partnerNumber in: query required: false schema: type: string - minLength: 9 - maxLength: 9 - pattern: 'M-[0-9]{7}' - description: Member number, exclusive to `partnerUuid`. + minLength: 7 + maxLength: 7 + pattern: 'P-[0-9]{5}' + description: partnerNumber of the partner the memberships belong to responses: "200": description: OK diff --git a/src/main/resources/api-definition/hs-office/hs-office.yaml b/src/main/resources/api-definition/hs-office/hs-office.yaml index 915afb61..40c0ce93 100644 --- a/src/main/resources/api-definition/hs-office/hs-office.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office.yaml @@ -85,7 +85,6 @@ paths: /api/hs/office/memberships/M-{membershipNumber}: $ref: "hs-office-memberships-with-membershipNumber.yaml" - /api/hs/office/memberships/{membershipUUID}: $ref: "hs-office-memberships-with-uuid.yaml" diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java index 6e54acfa..1239c2a4 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java @@ -79,7 +79,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canFindCoopAssetsTransactionsByMemberNumber() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); RestAssured // @formatter:off .given() @@ -202,7 +202,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canFindCoopAssetsTransactionsByMembershipUuidAndDateRange() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); RestAssured // @formatter:off .given() @@ -235,7 +235,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canPostNewCoopAssetTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var location = RestAssured // @formatter:off .given() @@ -280,7 +280,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canAddCoopAssetsReversalTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var givenTransaction = jpaAttempt.transacted(() -> { // TODO.impl: introduce something like transactedAsSuperuser / transactedAs("...", ...) context.define("superuser-alex@hostsharing.net"); @@ -348,7 +348,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canNotCancelMoreAssetsThanCurrentlySubscribed() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); RestAssured // @formatter:off .given() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java index 9409b856..c064a848 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java @@ -915,7 +915,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { AVAILABLE_MEMBER_ENTITY); final var availableMemberNumber = Integer.valueOf(AVAILABLE_TARGET_MEMBER_NUMBER.substring("M-".length())); - when(membershipRepo.findMembershipByMemberNumber(eq(availableMemberNumber))).thenReturn(AVAILABLE_MEMBER_ENTITY); + when(membershipRepo.findMembershipByMemberNumber(eq(availableMemberNumber))).thenReturn(Optional.of(AVAILABLE_MEMBER_ENTITY)); when(membershipRepo.findByUuid(eq(ORIGIN_MEMBERSHIP_UUID))).thenReturn(Optional.of(ORIGIN_TARGET_MEMBER_ENTITY)); when(membershipRepo.findByUuid(eq(AVAILABLE_TARGET_MEMBERSHIP_UUID))).thenReturn(Optional.of(AVAILABLE_MEMBER_ENTITY)); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java index 40f9d0a7..01248496 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java @@ -62,7 +62,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // given context("superuser-alex@hostsharing.net"); final var count = coopAssetsTransactionRepo.count(); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).load(); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow().load(); // when final var result = attempt(em, () -> { @@ -94,7 +94,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // when attempt(em, () -> { - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder() .membership(givenMembership) .transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT) @@ -166,7 +166,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuid() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); // when final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( @@ -189,7 +189,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuidAndValueDateRange() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); // when final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java index e7b1e52f..844e8db5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java @@ -87,7 +87,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canFindCoopSharesTransactionsByMemberNumber() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); RestAssured // @formatter:off .given().header("current-subject", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid()).then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" @@ -142,7 +142,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canFindCoopSharesTransactionsByMembershipUuidAndDateRange() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); RestAssured // @formatter:off .given().header("current-subject", "superuser-alex@hostsharing.net").port(port).when() @@ -167,7 +167,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canAddCoopSharesTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var location = RestAssured // @formatter:off .given().header("current-subject", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" @@ -198,7 +198,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canAddCoopSharesReversalTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var givenTransaction = jpaAttempt.transacted(() -> { // TODO.impl: introduce something like transactedAsSuperuser / transactedAs("...", ...) context.define("superuser-alex@hostsharing.net"); @@ -266,7 +266,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased void globalAdmin_canNotCancelMoreSharesThanCurrentlySubscribed() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); RestAssured // @formatter:off .given().header("current-subject", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java index 613ccc2b..d428a9d7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java @@ -61,7 +61,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // given context("superuser-alex@hostsharing.net"); final var count = coopSharesTransactionRepo.count(); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).load(); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow().load(); // when final var result = attempt(em, () -> { @@ -93,7 +93,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // when attempt(em, () -> { - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder() .membership(givenMembership) .transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION) @@ -159,7 +159,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuid() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); // when final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( @@ -180,7 +180,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuidAndValueDateRange() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); // when final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index 9f609e53..b0f99593 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -140,30 +140,30 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle } @Test - void globalAdmin_canViewMembershipsByMemberNumber() { + void globalAdmin_canViewMembershipsByPartnerNumber() { RestAssured // @formatter:off .given() - .header("current-subject", "superuser-alex@hostsharing.net") - .port(port) + .header("current-subject", "superuser-alex@hostsharing.net") + .port(port) .when() - .queryParam("memberNumber", "M-1000202" ) - .get("http://localhost/api/hs/office/memberships") + .queryParam("partnerNumber", "P-10002" ) + .get("http://localhost/api/hs/office/memberships") .then().log().all().assertThat() - .statusCode(200) - .contentType("application/json") - .body("", lenientlyEquals(""" - [ - { - "partner": { "partnerNumber": "P-10002" }, - "memberNumber": "M-1000202", - "memberNumberSuffix": "02", - "validFrom": "2022-10-01", - "validTo": null, - "status": "ACTIVE" - } - ] - """)); + .statusCode(200) + .contentType("application/json") + .body("", lenientlyEquals(""" + [ + { + "partner": { "partnerNumber": "P-10002" }, + "memberNumber": "M-1000202", + "memberNumberSuffix": "02", + "validFrom": "2022-10-01", + "validTo": null, + "status": "ACTIVE" + } + ] + """)); // @formatter:on } } @@ -220,7 +220,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle @Test void globalAdmin_canGetArbitraryMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).getUuid(); + final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow().getUuid(); RestAssured // @formatter:off .given() @@ -246,7 +246,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle @Test void normalUser_canNotGetUnrelatedMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).getUuid(); + final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow().getUuid(); RestAssured // @formatter:off .given() @@ -261,7 +261,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle @Test void partnerRelAgent_canGetRelatedMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000303).getUuid(); + final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000303).orElseThrow().getUuid(); RestAssured // @formatter:off .given() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java index 432a38e6..0d937904 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java @@ -19,11 +19,16 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import java.util.List; +import java.util.Optional; import java.util.UUID; +import static io.hypersistence.utils.hibernate.type.range.Range.localDateRange; +import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -33,6 +38,34 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @ActiveProfiles("test") public class HsOfficeMembershipControllerRestTest { + private static final HsOfficePartnerEntity PARTNER_12345 = HsOfficePartnerEntity.builder() + .partnerNumber(12345) + .build(); + public static final HsOfficeMembershipEntity MEMBERSHIP_1234501 = HsOfficeMembershipEntity.builder() + .partner(PARTNER_12345) + .memberNumberSuffix("01") + .validity(localDateRange("[2013-10-01,]")) + .status(HsOfficeMembershipStatus.ACTIVE) + .build(); + public static final HsOfficeMembershipEntity MEMBERSHIP_1234500 = HsOfficeMembershipEntity.builder() + .partner(PARTNER_12345) + .memberNumberSuffix("00") + .validity(localDateRange("[2011-04-01,2016-12-31]")) + .status(HsOfficeMembershipStatus.CANCELLED) + .build(); + public static final String MEMBERSHIP_1234501_JSON = """ + { + "partner": { + "partnerNumber":"P-12345" + }, + "memberNumber": "M-1234500", + "memberNumberSuffix": "00", + "validFrom": "2011-04-01", + "validTo": "2016-12-30", + "status":"CANCELLED" + } + """; + @Autowired MockMvc mockMvc; @@ -52,11 +85,11 @@ public class HsOfficeMembershipControllerRestTest { class GetListOfMemberships { @Test - void findMembershipByNonExistingMemberNumberReturnsEmptyList() throws Exception { + void findMembershipByNonExistingPartnerNumberReturnsEmptyList() throws Exception { // when mockMvc.perform(MockMvcRequestBuilders - .get("/api/hs/office/memberships?memberNumber=M-1234501") + .get("/api/hs/office/memberships?partnerNumber=P-12345") .header("current-subject", "superuser-alex@hostsharing.net") .contentType(MediaType.APPLICATION_JSON) .content(""" @@ -73,6 +106,116 @@ public class HsOfficeMembershipControllerRestTest { .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$", hasSize(0))); } + + @Test + void findMembershipByExistingPartnerNumberReturnsAllRelatedMemberships() throws Exception { + + // given + when(membershipRepo.findMembershipsByPartnerNumber(12345)) + .thenReturn(List.of( + MEMBERSHIP_1234500, + MEMBERSHIP_1234501 + )); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/hs/office/memberships?partnerNumber=P-12345") + .header("current-subject", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "partner.uuid": null, + "memberNumberSuffix": "01", + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" + } + """) + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$", hasSize(2))); + } + } + + @Nested + class GetSingleMembership { + + @Test + void byUuid() throws Exception { + + // given + final var givenUuid = UUID.randomUUID(); + when(membershipRepo.findByUuid(givenUuid)).thenReturn( + Optional.of(MEMBERSHIP_1234500) + ); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/hs/office/memberships/" + givenUuid) + .header("current-subject", "superuser-alex@hostsharing.net") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$", lenientlyEquals(MEMBERSHIP_1234501_JSON))); + } + + @Test + void byUnavailableUuid() throws Exception { + + // given + when(membershipRepo.findByUuid(any(UUID.class))).thenReturn( + Optional.empty() + ); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/hs/office/memberships/" + UUID.randomUUID()) + .header("current-subject", "superuser-alex@hostsharing.net") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isNotFound()); + } + + @Test + void byMemberNumber() throws Exception { + + // given + when(membershipRepo.findMembershipByMemberNumber(1234501)).thenReturn( + Optional.of(MEMBERSHIP_1234500) + ); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/hs/office/memberships/M-1234501") + .header("current-subject", "superuser-alex@hostsharing.net") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$", lenientlyEquals(MEMBERSHIP_1234501_JSON))); + } + + @Test + void byUnavailableMemberNumber() throws Exception { + + // given + when(membershipRepo.findMembershipByMemberNumber(any(Integer.class))).thenReturn( + Optional.empty() + ); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/hs/office/memberships/M-0000000") + .header("current-subject", "superuser-alex@hostsharing.net") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isNotFound()); + } + } @Nested diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index 0929f370..fc3599d2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -156,7 +156,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipsByOptionalPartnerUuid(null); + final var result = membershipRepo.findAll(); // then exactlyTheseMembershipsAreReturned( @@ -173,7 +173,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); // when - final var result = membershipRepo.findMembershipsByOptionalPartnerUuid(givenPartner.getUuid()); + final var result = membershipRepo.findMembershipsByPartnerUuid(givenPartner.getUuid()); // then exactlyTheseMembershipsAreReturned(result, @@ -186,7 +186,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipByMemberNumber(1000202); + final var result = membershipRepo.findMembershipByMemberNumber(1000202).orElseThrow(); // then assertThat(result) @@ -194,6 +194,34 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl .extracting(Object::toString) .isEqualTo("Membership(M-1000202, P-10002, [2022-10-01,), ACTIVE)"); } + + @Test + public void globalAdmin_withoutAssumedRole_canFindAllMembershipsByPartnerNumberAndSuffix() { + // given + context("superuser-alex@hostsharing.net"); + + // when + final var result = membershipRepo.findMembershipByPartnerNumberAndSuffix(10002, "02").orElseThrow(); + + // then + assertThat(result) + .isNotNull() + .extracting(Object::toString) + .isEqualTo("Membership(M-1000202, P-10002, [2022-10-01,), ACTIVE)"); + } + + @Test + public void globalAdmin_withoutAssumedRole_canFindAllMembershipsByPartnerNumber() { + // given + context("superuser-alex@hostsharing.net"); + + // when + final var result = membershipRepo.findMembershipsByPartnerNumber(10002); + + // then + exactlyTheseMembershipsAreReturned(result, + "Membership(M-1000202, P-10002, [2022-10-01,), ACTIVE)"); + } } @Nested @@ -339,7 +367,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl select currentTask, targetTable, targetOp, targetdelta->>'membernumbersuffix' from base.tx_journal_v where targettable = 'hs_office.membership'; - """); + """); // when @SuppressWarnings("unchecked") final List customerLogEntries = query.getResultList();