Skip to content

Commit

Permalink
Member edit form split into two (#249)
Browse files Browse the repository at this point in the history
member edit form split into two:
- `{{host}}/members/1/editOwnMemberInfoForm` => form for members to edit own info
- `{{host}}/members/1/editByAdminForm` => form for admins to edit info about other members

* Updated API Specs for splitted members form
* Added forms for edit own and another member details
* Corrected format of birth certificate number for initial loaded data
* Oris proxy controller bug fix - HTTP 500 for unknown regnum
* ArchUnit failures fix
* Validations for existing birthday certificate and one of email/phone contacts doesn't respond with HTTP 500 now
  • Loading branch information
dapolach authored Oct 6, 2024
1 parent a41202f commit 5625aca
Show file tree
Hide file tree
Showing 23 changed files with 498 additions and 35 deletions.
50 changes: 50 additions & 0 deletions backend/api.http
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,56 @@ Authorization: Bearer {{$auth.token("frontend")}}
"sex": "male"
}

### Get form data for Member Edit by Admin
GET {{host}}/members/1/editByAdminForm
Authorization: Bearer {{$auth.token("frontend")}}

### Update member info by ADMIN
PUT {{host}}/members/1/editByAdminForm
Content-Type: application/json
Authorization: Bearer {{$auth.token("frontend")}}

{
"firstName": "Dave",
"lastName": "Pol",
"dateOfBirth": "1982-04-26",
"birthCertificateNumber": "123456/780",
"nationality": "CZ",
"sex": "male"
}

###
GET {{host}}/members/1/editOwnMemberInfoForm
Authorization: Bearer {{$auth.token("frontend")}}

###
PUT {{host}}/members/1/editOwnMemberInfoForm
Authorization: Bearer {{$auth.token("frontend")}}
Content-Type: application/json

{
"identityCard": null,
"nationality": "CZ",
"address": {
"streetAndNumber": "Nekde jinde 12",
"city": "Brno",
"postalCode": "63212",
"country": "CZ"
},
"contact": {
"email": "test@testx.com",
"phone": "888888888",
"note": null
},
"guardians": [],
"siCard": 12345678,
"bankAccount": "CZ1234567891000",
"dietaryRestrictions": null,
"drivingLicence": [],
"medicCourse": false
}


### Membership suspension info
GET {{host}}/members/1/suspendMembershipForm
Authorization: Bearer {{$auth.token("frontend")}}
Expand Down
10 changes: 6 additions & 4 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ description = "klabis"
java.sourceCompatibility = JavaVersion.VERSION_21

val recordbuilderVersion = "41"
val mapstructVersion = "1.6.0.Beta1"
val mapstructSpringExtensionsVersion = "1.1.1"
val mapstructVersion = "1.6.2"
val mapstructSpringExtensionsVersion = "1.1.2"

dependencies {

Expand Down Expand Up @@ -132,8 +132,10 @@ openApiGenerate {
//"hateoas" to "false",
"booleanGetterPrefix" to "is",
"interfaceOnly" to "true",
"defaultInterfaces" to "false",
"useBeanValidation" to "true"
"skipDefaultInterface" to "true",
"useBeanValidation" to "true",
"apiFirst" to "false"
//"useOptional" to "false"
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
import club.klabis.domain.members.Member;
import club.klabis.domain.members.MemberNotFoundException;
import club.klabis.domain.members.MemberService;
import club.klabis.domain.members.forms.EditAnotherMemberInfoByAdminForm;
import club.klabis.domain.members.forms.EditOwnMemberInfoForm;
import club.klabis.domain.members.forms.MemberEditForm;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;
Expand Down Expand Up @@ -66,7 +69,7 @@ public ResponseEntity<MembershipSuspensionInfoApiDto> membersMemberIdSuspendMemb
}

@Override
public ResponseEntity<Void> membersMemberIdSuspendMembershipFormPost(Integer memberId, Boolean force) {
public ResponseEntity<Void> membersMemberIdSuspendMembershipFormPut(Integer memberId, Boolean force) {
service.suspendMembershipForMember(memberId, force);
return ResponseEntity.ok(null);
}
Expand Down Expand Up @@ -98,4 +101,25 @@ public ResponseEntity<Void> updateMemberGrants(Integer memberId, MemberGrantsFor
return ResponseEntity.ok(null);
}

@Override
public ResponseEntity<EditAnotherMemberDetailsFormApiDto> getMemberEditByAdminForm(Integer memberId) {
return ResponseEntity.ok(conversionService.convert(service.getEditAnotherMemberForm(memberId), EditAnotherMemberDetailsFormApiDto.class));
}

@Override
public ResponseEntity<Void> putMemberEditByAdminForm(Integer memberId, EditAnotherMemberDetailsFormApiDto editAnotherMemberDetailsFormApiDto) {
service.editMember(memberId, conversionService.convert(editAnotherMemberDetailsFormApiDto, EditAnotherMemberInfoByAdminForm.class));
return ResponseEntity.ok(null);
}

@Override
public ResponseEntity<EditMyDetailsFormApiDto> membersMemberIdEditOwnMemberInfoFormGet(Integer memberId) {
return ResponseEntity.ok(conversionService.convert(service.getEditOwnMemberInfoForm(memberId), EditMyDetailsFormApiDto.class));
}

@Override
public ResponseEntity<Void> membersMemberIdEditOwnMemberInfoFormPut(Integer memberId, EditMyDetailsFormApiDto editMyDetailsFormApiDto) {
service.editMember(memberId, conversionService.convert(editMyDetailsFormApiDto, EditOwnMemberInfoForm.class));
return ResponseEntity.ok(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public MembersRegistrationController(MemberService service, ConversionService co
}

@Override
public ResponseEntity<Void> memberRegistrationsPost(MemberRegistrationFormApiDto memberRegistrationFormApiDto) {
public ResponseEntity<Void> memberRegistrationsPut(MemberRegistrationFormApiDto memberRegistrationFormApiDto) {
Member createdMember = service.registerMember(conversionService.convert(memberRegistrationFormApiDto, RegistrationForm.class));
return ResponseEntity.created(URI.create("/members/%s".formatted(createdMember.getId()))).header("MemberId", "%d".formatted(createdMember.getId())).build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package club.klabis.adapters.api.members.mappers;

import club.klabis.api.dto.EditAnotherMemberDetailsFormApiDto;
import club.klabis.common.mapstruct.DomainFormMapperConfiguration;
import club.klabis.domain.members.Member;
import club.klabis.domain.members.forms.EditAnotherMemberInfoByAdminForm;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.extensions.spring.DelegatingConverter;
import org.springframework.core.convert.converter.Converter;

@Mapper(config = DomainFormMapperConfiguration.class, unmappedSourcePolicy = ReportingPolicy.IGNORE)
interface EditAnotherMemberInfoByAdminFormMappers extends Converter<EditAnotherMemberInfoByAdminForm, EditAnotherMemberDetailsFormApiDto> {

@Override
EditAnotherMemberDetailsFormApiDto convert(EditAnotherMemberInfoByAdminForm source);

@InheritInverseConfiguration
@DelegatingConverter
EditAnotherMemberInfoByAdminForm convertReverse(EditAnotherMemberDetailsFormApiDto source);

@DelegatingConverter
EditAnotherMemberInfoByAdminForm fromDomain(Member member);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package club.klabis.adapters.api.members.mappers;

import club.klabis.api.dto.EditMyDetailsFormApiDto;
import club.klabis.common.mapstruct.DomainFormMapperConfiguration;
import club.klabis.domain.members.Member;
import club.klabis.domain.members.forms.EditOwnMemberInfoForm;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.extensions.spring.DelegatingConverter;
import org.springframework.core.convert.converter.Converter;

@Mapper(config = DomainFormMapperConfiguration.class)
interface MemberEditOwnInfoFormApiDtoFromMemberDomainMapper extends Converter<EditOwnMemberInfoForm, EditMyDetailsFormApiDto> {
@Override
EditMyDetailsFormApiDto convert(EditOwnMemberInfoForm source);

@InheritInverseConfiguration
@DelegatingConverter
EditOwnMemberInfoForm convertReverse(EditMyDetailsFormApiDto source);

@Mapping(target = "guardians", source = "legalGuardians")
@Mapping(target = "contact", source = "contact")
@DelegatingConverter
EditOwnMemberInfoForm fromDomain(Member member);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package club.klabis.domain.oris;
package club.klabis.adapters.api.oris;

import club.klabis.adapters.oris.OrisApiClient;
import club.klabis.api.OrisApi;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package club.klabis.domain.oris;
package club.klabis.adapters.api.oris;

import club.klabis.adapters.oris.OrisApiClient;
import club.klabis.common.mapstruct.DomainToDtoMapperConfiguration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

public interface OrisApiClient {

@GetExchange("/API/?format=xml&method=getUser")
@GetExchange("/API/?format=json&method=getUser")
OrisResponse<OrisUserInfo> getUserInfo(@RequestParam("rgnum") String registrationNumber);

record OrisResponse<T> (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package club.klabis.common.mapstruct;

import club.klabis.common.ConversionServiceAdapter;
import org.mapstruct.MapperConfig;
import org.mapstruct.ReportingPolicy;

/**
* Mapstruct configuration for API DTO mappers.
* These mappers shall implement `Converter<DOMAIN, APIDTO>` (APIDTO needs to be target, DOMAIN object needs to be source)
*/
@MapperConfig(componentModel = "spring", uses = {ConversionServiceAdapter.class, OptionalMapstructSupport.class}, unmappedSourcePolicy = ReportingPolicy.IGNORE, unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface DomainFormMapperConfiguration {
}
33 changes: 33 additions & 0 deletions backend/src/main/java/club/klabis/domain/members/Member.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package club.klabis.domain.members;

import club.klabis.api.dto.EditAnotherMemberDetailsFormApiDto;
import club.klabis.api.dto.EditMyDetailsFormApiDto;
import club.klabis.domain.members.events.MemberCreatedEvent;
import club.klabis.domain.members.events.MemberEditedEvent;
import club.klabis.domain.members.events.MemberWasSuspendedEvent;
import club.klabis.domain.members.forms.EditAnotherMemberInfoByAdminForm;
import club.klabis.domain.members.forms.EditOwnMemberInfoForm;
import club.klabis.domain.members.forms.MemberEditForm;
import club.klabis.domain.members.forms.RegistrationForm;
import org.jmolecules.ddd.annotation.AggregateRoot;
Expand Down Expand Up @@ -117,6 +121,34 @@ public void edit(MemberEditForm form) {
this.andEvent(new MemberEditedEvent(this));
}

public void edit(EditOwnMemberInfoForm form) {
this.identityCard = form.identityCard();
this.nationality = form.nationality();
this.address = form.address();
this.contact.clear();
this.contact.addAll(form.contact());
this.legalGuardians.clear();
this.legalGuardians.addAll(form.guardians());
this.siCard = form.siCard();
this.bankAccount = form.bankAccount();
this.dietaryRestrictions = form.dietaryRestrictions();
this.drivingLicence.clear();
this.drivingLicence.addAll(form.drivingLicence());
this.medicCourse = form.medicCourse();
this.andEvent(new MemberEditedEvent(this));
}

public void edit(EditAnotherMemberInfoByAdminForm form) {
this.firstName = form.firstName();
this.lastName = form.lastName();
this.nationality = form.nationality();
this.dateOfBirth = form.dateOfBirth();
this.birthCertificateNumber = form.birthCertificateNumber();
this.sex = form.sex();
this.andEvent(new MemberEditedEvent(this));

}

public void suspend() {
if (!this.suspended) {
this.suspended = true;
Expand Down Expand Up @@ -211,4 +243,5 @@ public Optional<String> getDietaryRestrictions() {
public boolean isSuspended() {
return suspended;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package club.klabis.domain.members;

import club.klabis.api.dto.EditMyDetailsFormApiDto;
import club.klabis.domain.members.forms.EditAnotherMemberInfoByAdminForm;
import club.klabis.domain.members.forms.EditOwnMemberInfoForm;
import club.klabis.domain.members.forms.MemberEditForm;
import club.klabis.domain.members.forms.RegistrationForm;
import jakarta.validation.Valid;
Expand All @@ -21,9 +24,17 @@ public interface MemberService {

RegistrationNumber suggestRegistrationNumber(LocalDate dateOfBirth, Sex sex);

Member editMember(Integer memberId, @Valid MemberEditForm editForm);

Optional<MembershipSuspensionInfo> getSuspensionInfoForMember(int memberId);

void suspendMembershipForMember(int memberId, boolean forceSuspension);

Member editMember(Integer memberId, @Valid MemberEditForm editForm);

EditAnotherMemberInfoByAdminForm getEditAnotherMemberForm(Integer memberId);

Member editMember(Integer memberId, @Valid EditAnotherMemberInfoByAdminForm form);

EditOwnMemberInfoForm getEditOwnMemberInfoForm(Integer memberId);

Member editMember(Integer memberId, @Valid EditOwnMemberInfoForm form);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package club.klabis.domain.members;

import club.klabis.domain.members.forms.EditAnotherMemberInfoByAdminForm;
import club.klabis.domain.members.forms.EditOwnMemberInfoForm;
import club.klabis.domain.members.forms.MemberEditForm;
import club.klabis.domain.members.forms.RegistrationForm;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -12,9 +15,11 @@
@Service
class MemberServiceImpl implements MemberService {
private final MembersRepository membersRepository;
private final ConversionService conversionService;

MemberServiceImpl(MembersRepository membersRepository) {
MemberServiceImpl(MembersRepository membersRepository, ConversionService conversionService) {
this.membersRepository = membersRepository;
this.conversionService = conversionService;
}

@Override
Expand Down Expand Up @@ -88,4 +93,39 @@ public void suspendMembershipForMember(int memberId, boolean forceSuspension) {
throw new MembershipCannotBeSuspendedException(memberId, "member is already suspended");
}
}

@Transactional
@Override
public Member editMember(Integer memberId, EditOwnMemberInfoForm form) {
Member member = membersRepository.findById(memberId)
.orElseThrow(() -> new MemberNotFoundException(memberId));

member.edit(form);
return membersRepository.save(member);
}

@Override
public EditAnotherMemberInfoByAdminForm getEditAnotherMemberForm(Integer memberId) {
return findById(memberId)
.map(m -> conversionService.convert(m, EditAnotherMemberInfoByAdminForm.class))
.orElseThrow(() -> new MemberNotFoundException(memberId));
}

@Override
public Member editMember(Integer memberId, EditAnotherMemberInfoByAdminForm form) {
Member member = membersRepository.findById(memberId)
.orElseThrow(() -> new MemberNotFoundException(memberId));

member.edit(form);
return membersRepository.save(member);
}

@Override
public EditOwnMemberInfoForm getEditOwnMemberInfoForm(Integer memberId) {
return findById(memberId)
.map(m -> conversionService.convert(m, EditOwnMemberInfoForm.class))
.orElseThrow(() -> new MemberNotFoundException(memberId));
}


}
Loading

0 comments on commit 5625aca

Please sign in to comment.