diff --git a/pom.xml b/pom.xml
index cf2ea0b..e9d9ff3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.rbkmoney
service-parent-pom
- 2.0.8
+ 2.0.11
org-manager
@@ -81,12 +81,6 @@
org.springframework.boot
spring-boot-starter-web
-
-
- org.apache.tomcat.embed
- tomcat-embed-core
-
-
org.springframework.boot
@@ -206,11 +200,6 @@
jackson-databind-nullable
0.2.1
-
- org.apache.tomcat.embed
- tomcat-embed-core
- 9.0.45
-
diff --git a/src/main/java/com/rbkmoney/orgmanager/config/PartyManagementConfig.java b/src/main/java/com/rbkmoney/orgmanager/config/PartyManagementConfig.java
new file mode 100644
index 0000000..0a91a12
--- /dev/null
+++ b/src/main/java/com/rbkmoney/orgmanager/config/PartyManagementConfig.java
@@ -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);
+ }
+
+}
diff --git a/src/main/java/com/rbkmoney/orgmanager/controller/OrgsController.java b/src/main/java/com/rbkmoney/orgmanager/controller/OrgsController.java
index bc1a94f..4c764fd 100644
--- a/src/main/java/com/rbkmoney/orgmanager/controller/OrgsController.java
+++ b/src/main/java/com/rbkmoney/orgmanager/controller/OrgsController.java
@@ -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
diff --git a/src/main/java/com/rbkmoney/orgmanager/exception/PartyManagementException.java b/src/main/java/com/rbkmoney/orgmanager/exception/PartyManagementException.java
new file mode 100644
index 0000000..95a9ab0
--- /dev/null
+++ b/src/main/java/com/rbkmoney/orgmanager/exception/PartyManagementException.java
@@ -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);
+ }
+
+}
diff --git a/src/main/java/com/rbkmoney/orgmanager/service/OrganizationService.java b/src/main/java/com/rbkmoney/orgmanager/service/OrganizationService.java
index 2d940cb..f9eed4e 100644
--- a/src/main/java/com/rbkmoney/orgmanager/service/OrganizationService.java
+++ b/src/main/java/com/rbkmoney/orgmanager/service/OrganizationService.java
@@ -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 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 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 get(String orgId) {
- Optional 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 get(String orgId) {
+ return organizationRepository.findById(orgId)
+ .map(organizationConverter::toDomain);
}
diff --git a/src/main/java/com/rbkmoney/orgmanager/service/PartyManagementService.java b/src/main/java/com/rbkmoney/orgmanager/service/PartyManagementService.java
new file mode 100644
index 0000000..5019759
--- /dev/null
+++ b/src/main/java/com/rbkmoney/orgmanager/service/PartyManagementService.java
@@ -0,0 +1,7 @@
+package com.rbkmoney.orgmanager.service;
+
+public interface PartyManagementService {
+
+ void createParty(String partyId, String userId, String email);
+
+}
diff --git a/src/main/java/com/rbkmoney/orgmanager/service/PartyManagementServiceImpl.java b/src/main/java/com/rbkmoney/orgmanager/service/PartyManagementServiceImpl.java
new file mode 100644
index 0000000..57051ca
--- /dev/null
+++ b/src/main/java/com/rbkmoney/orgmanager/service/PartyManagementServiceImpl.java
@@ -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);
+ }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 558b274..91a443a 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -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/
diff --git a/src/test/java/com/rbkmoney/orgmanager/TestObjectFactory.java b/src/test/java/com/rbkmoney/orgmanager/TestObjectFactory.java
index 83fcc05..e10e94f 100644
--- a/src/test/java/com/rbkmoney/orgmanager/TestObjectFactory.java
+++ b/src/test/java/com/rbkmoney/orgmanager/TestObjectFactory.java
@@ -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;
}
diff --git a/src/test/java/com/rbkmoney/orgmanager/service/OrganizationServiceTest.java b/src/test/java/com/rbkmoney/orgmanager/service/OrganizationServiceTest.java
index 16c4d72..928c369 100644
--- a/src/test/java/com/rbkmoney/orgmanager/service/OrganizationServiceTest.java
+++ b/src/test/java/com/rbkmoney/orgmanager/service/OrganizationServiceTest.java
@@ -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 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 response = service.get(orgId);
+ Optional 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 response = service.get(orgId);
+ Optional 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);
}
diff --git a/src/test/java/com/rbkmoney/orgmanager/service/PartyManagementServiceImplTest.java b/src/test/java/com/rbkmoney/orgmanager/service/PartyManagementServiceImplTest.java
new file mode 100644
index 0000000..9fe92b6
--- /dev/null
+++ b/src/test/java/com/rbkmoney/orgmanager/service/PartyManagementServiceImplTest.java
@@ -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 userInfoCaptor = ArgumentCaptor.forClass(UserInfo.class);
+ ArgumentCaptor partyIdCaptor = ArgumentCaptor.forClass(String.class);
+ ArgumentCaptor 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());
+ }
+}