JD-730: manually create party when creating organization (#54)

* JD-730: manually create party when creating organization
This commit is contained in:
mr-impossibru 2021-10-27 13:06:13 +03:00 committed by GitHub
parent 790b988e55
commit 2b6e48f696
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 281 additions and 82 deletions

13
pom.xml
View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.rbkmoney</groupId>
<artifactId>service-parent-pom</artifactId>
<version>2.0.8</version>
<version>2.0.11</version>
</parent>
<artifactId>org-manager</artifactId>
@ -81,12 +81,6 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -206,11 +200,6 @@
<artifactId>jackson-databind-nullable</artifactId>
<version>0.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.45</version>
</dependency>
<!--test-->

View File

@ -0,0 +1,26 @@
package com.rbkmoney.orgmanager.config;
import com.rbkmoney.damsel.payment_processing.PartyManagementSrv;
import com.rbkmoney.woody.thrift.impl.http.THSpawnClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import java.io.IOException;
@Configuration
public class PartyManagementConfig {
@Bean
public PartyManagementSrv.Iface partyManagementClient(
@Value("${party-management.url}") Resource resource,
@Value("${party-management.networkTimeout}") int networkTimeout
) throws IOException {
return new THSpawnClientBuilder()
.withNetworkTimeout(networkTimeout)
.withAddress(resource.getURI())
.build(PartyManagementSrv.Iface.class);
}
}

View File

@ -36,7 +36,10 @@ public class OrgsController implements OrgsApi {
organization);
resourceAccessService.checkRights();
AccessToken accessToken = keycloakService.getAccessToken();
return organizationService.create(accessToken.getSubject(), organization, idempotencyKey);
Organization createdOrganization = organizationService.create(accessToken, organization, idempotencyKey);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(createdOrganization);
}
@Override
@ -48,7 +51,11 @@ public class OrgsController implements OrgsApi {
.orgId(orgId)
.build();
resourceAccessService.checkRights(resource);
return organizationService.get(orgId);
return organizationService.get(orgId)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity
.status(HttpStatus.NOT_FOUND)
.build());
}
@Override
@ -166,7 +173,8 @@ public class OrgsController implements OrgsApi {
.orgId(orgId)
.build();
resourceAccessService.checkRights(resource);
return organizationService.modify(orgId, inlineObject.getName());
Organization modifiedOrganization = organizationService.modify(orgId, inlineObject.getName());
return ResponseEntity.ok(modifiedOrganization);
}
@Override

View File

@ -0,0 +1,12 @@
package com.rbkmoney.orgmanager.exception;
public class PartyManagementException extends RuntimeException {
public PartyManagementException() {
}
public PartyManagementException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -14,7 +14,7 @@ import com.rbkmoney.orgmanager.service.dto.MemberWithRoleDto;
import com.rbkmoney.swag.organizations.model.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.keycloak.representations.AccessToken;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -44,41 +44,37 @@ public class OrganizationService {
private final MemberContextRepository memberContextRepository;
private final InvitationService invitationService;
private final MemberRoleService memberRoleService;
private final PartyManagementService partyManagementService;
// TODO [a.romanov]: idempotency
public ResponseEntity<Organization> create(
String ownerId,
@Transactional
public Organization create(
AccessToken token,
Organization organization,
String idempotencyKey) {
OrganizationEntity entity = organizationConverter.toEntity(organization, ownerId);
String keycloakUserId = token.getSubject();
OrganizationEntity entity = organizationConverter.toEntity(organization, keycloakUserId);
OrganizationEntity savedEntity = organizationRepository.save(entity);
// TODO [v.hramov]: when org-manager will be fully operational party_id != keycloak_user_id
// most likely we will use organization_id as party_id
partyManagementService.createParty(keycloakUserId, keycloakUserId, token.getEmail());
Organization savedOrganization = organizationConverter.toDomain(savedEntity);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(savedOrganization);
savedOrganization.setParty(keycloakUserId);
return savedOrganization;
}
@Transactional
public ResponseEntity<Organization> modify(String orgId, String orgName) {
public Organization modify(String orgId, String orgName) {
OrganizationEntity organizationEntity = findById(orgId);
organizationEntity.setName(orgName);
Organization savedOrganization = organizationConverter.toDomain(organizationEntity);
return ResponseEntity.ok(savedOrganization);
return organizationConverter.toDomain(organizationEntity);
}
public ResponseEntity<Organization> get(String orgId) {
Optional<OrganizationEntity> entity = organizationRepository.findById(orgId);
if (entity.isPresent()) {
Organization organization = organizationConverter.toDomain(entity.get());
return ResponseEntity.ok(organization);
}
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.build();
@Transactional(readOnly = true)
public Optional<Organization> get(String orgId) {
return organizationRepository.findById(orgId)
.map(organizationConverter::toDomain);
}

View File

@ -0,0 +1,7 @@
package com.rbkmoney.orgmanager.service;
public interface PartyManagementService {
void createParty(String partyId, String userId, String email);
}

View File

@ -0,0 +1,40 @@
package com.rbkmoney.orgmanager.service;
import com.rbkmoney.damsel.domain.PartyContactInfo;
import com.rbkmoney.damsel.payment_processing.ExternalUser;
import com.rbkmoney.damsel.payment_processing.PartyExists;
import com.rbkmoney.damsel.payment_processing.PartyManagementSrv;
import com.rbkmoney.damsel.payment_processing.PartyParams;
import com.rbkmoney.damsel.payment_processing.UserInfo;
import com.rbkmoney.damsel.payment_processing.UserType;
import com.rbkmoney.orgmanager.exception.PartyManagementException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TException;
import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class PartyManagementServiceImpl implements PartyManagementService {
private final PartyManagementSrv.Iface partyManagementClient;
@Override
public void createParty(String partyId, String userId, String email) {
UserInfo userInfo = new UserInfo(userId, UserType.external_user(new ExternalUser()));
PartyParams partyParams = new PartyParams(new PartyContactInfo(email));
try {
partyManagementClient.create(userInfo, partyId, partyParams);
} catch (PartyExists ex) {
log.warn("Party already exists. (partyId: {}, userId: {}, email: {})", partyId, userId, email);
} catch (TException ex) {
throw new PartyManagementException(
String.format("Exception during party creation. (partyId: %s, userId: %s, email: %s)",
partyId, userId, email),
ex);
}
log.info("Created party. (partyId: {}, userId: {}, email: {})", partyId, userId, email);
}
}

View File

@ -82,6 +82,10 @@ dudoser:
url: http://dudoser:8022/dudos
networkTimeout: 10000
party-management:
url: http://party-management:8022/party/time
networkTimeout: 10000
dashboard:
url: https://dashboard.rbk.money/organization-section/accept-invitation/

View File

@ -45,10 +45,15 @@ public abstract class TestObjectFactory {
}
public static AccessToken testToken() {
return testToken(randomString(), randomString());
}
public static AccessToken testToken(String subject, String email) {
AccessToken token = new AccessToken();
token.exp(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC))
.subject(randomString())
.subject(subject)
.id(randomString());
token.setEmail(email);
return token;
}

View File

@ -5,6 +5,7 @@ import com.rbkmoney.orgmanager.converter.MemberConverter;
import com.rbkmoney.orgmanager.converter.OrganizationConverter;
import com.rbkmoney.orgmanager.entity.MemberEntity;
import com.rbkmoney.orgmanager.entity.OrganizationEntity;
import com.rbkmoney.orgmanager.exception.PartyManagementException;
import com.rbkmoney.orgmanager.exception.ResourceNotFoundException;
import com.rbkmoney.orgmanager.repository.MemberRepository;
import com.rbkmoney.orgmanager.repository.OrganizationRepository;
@ -17,14 +18,13 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static com.rbkmoney.orgmanager.TestObjectFactory.testToken;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
@ -32,44 +32,81 @@ import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class OrganizationServiceTest {
@Mock private OrganizationConverter organizationConverter;
@Mock private OrganizationRepository organizationRepository;
@Mock private MemberConverter memberConverter;
@Mock private MemberRepository memberRepository;
@Mock
private OrganizationConverter organizationConverter;
@Mock
private OrganizationRepository organizationRepository;
@Mock
private MemberConverter memberConverter;
@Mock
private MemberRepository memberRepository;
@Mock
private PartyManagementService partyManagementService;
@InjectMocks
private OrganizationService service;
private static final String OWNER_ID = "testOwnerId";
private static final String EMAIL = "email@email.org";
@Test
void shouldThrowPartyManagementExceptionOnCreate() {
Organization organization = new Organization();
OrganizationEntity entity = new OrganizationEntity();
OrganizationEntity savedEntity = new OrganizationEntity();
when(organizationConverter.toEntity(organization, OWNER_ID))
.thenReturn(entity);
when(organizationRepository.save(entity))
.thenReturn(savedEntity);
doThrow(new PartyManagementException())
.when(partyManagementService).createParty(anyString(), anyString(), anyString());
assertThrows(PartyManagementException.class,
() -> service.create(testToken(OWNER_ID, EMAIL), organization, ""));
verify(organizationConverter, times(1))
.toEntity(organization, OWNER_ID);
verify(organizationRepository, times(1))
.save(entity);
verify(partyManagementService, times(1))
.createParty(OWNER_ID, OWNER_ID, EMAIL);
verify(organizationConverter, times(0))
.toDomain(any(OrganizationEntity.class));
}
@Test
void shouldCreate() {
// Given
Organization organization = new Organization();
OrganizationEntity entity = new OrganizationEntity();
OrganizationEntity savedEntity = new OrganizationEntity();
Organization savedOrganization = new Organization();
when(organizationConverter.toEntity(organization, "testOwnerId"))
when(organizationConverter.toEntity(organization, OWNER_ID))
.thenReturn(entity);
when(organizationRepository.save(entity))
.thenReturn(savedEntity);
when(organizationConverter.toDomain(savedEntity))
.thenReturn(savedOrganization);
// When
ResponseEntity<Organization> response = service.create("testOwnerId", organization, "");
Organization response = service.create(testToken(OWNER_ID, EMAIL), organization, "");
// Then
verify(organizationConverter, times(1))
.toEntity(organization, OWNER_ID);
verify(organizationRepository, times(1))
.save(entity);
assertThat(response.getStatusCode())
.isEqualTo(HttpStatus.CREATED);
assertThat(response.getBody())
verify(partyManagementService, times(1))
.createParty(OWNER_ID, OWNER_ID, EMAIL);
verify(organizationConverter, times(1))
.toDomain(savedEntity);
assertThat(response)
.isEqualTo(savedOrganization);
assertThat(response.getParty())
.isEqualTo(OWNER_ID);
}
@Test
void shouldGet() {
// Given
String orgId = "orgId";
OrganizationEntity entity = new OrganizationEntity();
Organization organization = new Organization();
@ -79,37 +116,29 @@ public class OrganizationServiceTest {
when(organizationConverter.toDomain(entity))
.thenReturn(organization);
// When
ResponseEntity<Organization> response = service.get(orgId);
Optional<Organization> response = service.get(orgId);
// Then
assertThat(response.getStatusCode())
.isEqualTo(HttpStatus.OK);
assertThat(response.getBody())
assertThat(response.isPresent())
.isEqualTo(true);
assertThat(response.get())
.isEqualTo(organization);
}
@Test
void shouldReturnNotFound() {
// Given
String orgId = "orgId";
when(organizationRepository.findById(orgId))
.thenReturn(Optional.empty());
// When
ResponseEntity<Organization> response = service.get(orgId);
Optional<Organization> response = service.get(orgId);
// Then
assertThat(response.getStatusCode())
.isEqualTo(HttpStatus.NOT_FOUND);
assertThat(response.getBody())
.isNull();
assertThat(response.isPresent())
.isEqualTo(false);
}
@Test
void shouldListMembers() {
// Given
String orgId = TestObjectFactory.randomString();
Member member = new Member();
@ -123,10 +152,8 @@ public class OrganizationServiceTest {
when(memberConverter.toDomain(memberWithRoleList))
.thenReturn(List.of(member));
// When
MemberOrgListResult response = service.listMembers(orgId);
// Then
assertThat(response)
.isNotNull();
assertThat(response.getResult())
@ -174,49 +201,39 @@ public class OrganizationServiceTest {
@Test
void shouldReturnNotFoundIfNoOrganizationExistForMembersList() {
// Given
String orgId = "orgId";
// When
when(organizationRepository.existsById(orgId))
.thenReturn(false);
// Then
assertThrows(ResourceNotFoundException.class, () -> service.listMembers(orgId));
}
@Test
void shouldThrowExceptionIfOrganizationDoesNotExist() {
// Given
String orgId = TestObjectFactory.randomString();
String userId = TestObjectFactory.randomString();
// When
when(organizationRepository.findById(orgId))
.thenReturn(Optional.empty());
//Then
assertThrows(ResourceNotFoundException.class, () -> service.getOrgMember(userId, orgId));
}
@Test
void shouldThrowExceptionIfUserNotMemberOfOrganization() {
// Given
String orgId = TestObjectFactory.randomString();
OrganizationEntity organizationEntity = new OrganizationEntity();
organizationEntity.setId(orgId);
String userId = TestObjectFactory.randomString();
// When
when(organizationRepository.findById(orgId))
.thenReturn(Optional.of(organizationEntity));
//Then
assertThrows(ResourceNotFoundException.class, () -> service.getOrgMember(userId, orgId));
}
@Test
void shouldGetOrgMember() {
// Given
String orgId = TestObjectFactory.randomString();
OrganizationEntity organizationEntity = new OrganizationEntity();
organizationEntity.setId(orgId);
@ -231,10 +248,8 @@ public class OrganizationServiceTest {
when(memberConverter.toDomain(memberEntity, Collections.emptyList()))
.thenReturn(expectedMember);
// When
Member actualMember = service.getOrgMember(userId, orgId);
// Then
assertThat(actualMember)
.isEqualTo(expectedMember);
}

View File

@ -0,0 +1,97 @@
package com.rbkmoney.orgmanager.service;
import com.rbkmoney.damsel.payment_processing.InvalidUser;
import com.rbkmoney.damsel.payment_processing.PartyExists;
import com.rbkmoney.damsel.payment_processing.PartyManagementSrv;
import com.rbkmoney.damsel.payment_processing.PartyParams;
import com.rbkmoney.damsel.payment_processing.UserInfo;
import com.rbkmoney.orgmanager.exception.PartyManagementException;
import org.apache.thrift.TException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static com.rbkmoney.orgmanager.TestObjectFactory.randomString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class PartyManagementServiceImplTest {
private PartyManagementService partyManagementService;
@Mock
private PartyManagementSrv.Iface partyManagementClient;
@BeforeEach
void setUp() {
partyManagementService = new PartyManagementServiceImpl(partyManagementClient);
}
@Test
void shouldThrowPartyManagementExceptionOnCreateParty() throws TException {
doThrow(new InvalidUser())
.when(partyManagementClient).create(any(UserInfo.class), anyString(), any(PartyParams.class));
String partyId = randomString();
String userId = randomString();
String email = randomString();
PartyManagementException partyManagementException =
assertThrows(
PartyManagementException.class,
() -> partyManagementService.createParty(partyId, userId, email)
);
assertTrue(partyManagementException.getMessage().contains(String.format(
"Exception during party creation. (partyId: %s, userId: %s, email: %s)", partyId, userId, email)));
}
@Test
void shouldCreatePartyIfPartyExistThrown() throws TException {
doThrow(new PartyExists())
.when(partyManagementClient).create(any(UserInfo.class), anyString(), any(PartyParams.class));
String partyId = randomString();
String userId = randomString();
String email = randomString();
partyManagementService.createParty(partyId, userId, email);
verify(partyManagementClient, times(1))
.create(any(UserInfo.class), anyString(), any(PartyParams.class));
}
@Test
void shouldCreateParty() throws TException {
String partyId = randomString();
String userId = randomString();
String email = randomString();
partyManagementService.createParty(partyId, userId, email);
ArgumentCaptor<UserInfo> userInfoCaptor = ArgumentCaptor.forClass(UserInfo.class);
ArgumentCaptor<String> partyIdCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<PartyParams> partyParamsCaptor = ArgumentCaptor.forClass(PartyParams.class);
verify(partyManagementClient, times(1))
.create(userInfoCaptor.capture(), partyIdCaptor.capture(), partyParamsCaptor.capture());
assertEquals(1, userInfoCaptor.getAllValues().size());
assertEquals(userId, userInfoCaptor.getValue().getId());
assertTrue(userInfoCaptor.getValue().getType().isSetExternalUser());
assertEquals(1, partyIdCaptor.getAllValues().size());
assertEquals(partyId, partyIdCaptor.getValue());
assertEquals(1, partyParamsCaptor.getAllValues().size());
assertEquals(email, partyParamsCaptor.getValue().getContactInfo().getEmail());
}
}