Skip to content

Commit 1f7e66c

Browse files
committed
Refactor clan creation to be done via Elide POST call
+ other cleanups
1 parent 9ce4b0e commit 1f7e66c

File tree

11 files changed

+307
-195
lines changed

11 files changed

+307
-195
lines changed

src/main/java/com/faforever/api/clan/ClanService.java

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,75 @@
1010
import com.faforever.api.error.ApiException;
1111
import com.faforever.api.error.Error;
1212
import com.faforever.api.error.ErrorCode;
13-
import com.faforever.api.error.ProgrammingError;
14-
import com.faforever.api.player.PlayerRepository;
1513
import com.faforever.api.player.PlayerService;
1614
import com.faforever.api.security.JwtService;
1715
import com.fasterxml.jackson.databind.ObjectMapper;
1816
import lombok.RequiredArgsConstructor;
1917
import lombok.SneakyThrows;
20-
import org.springframework.security.core.Authentication;
2118
import org.springframework.security.jwt.Jwt;
2219
import org.springframework.stereotype.Service;
20+
import org.springframework.transaction.annotation.Transactional;
21+
import org.springframework.util.Assert;
2322

2423
import java.time.Instant;
2524
import java.time.temporal.ChronoUnit;
2625
import java.util.Collections;
26+
import java.util.List;
2727

2828
@Service
2929
@RequiredArgsConstructor
3030
public class ClanService {
3131

3232
private final ClanRepository clanRepository;
33-
private final PlayerRepository playerRepository;
3433
private final FafApiProperties fafApiProperties;
3534
private final JwtService jwtService;
3635
private final ObjectMapper objectMapper;
3736
private final PlayerService playerService;
3837
private final ClanMembershipRepository clanMembershipRepository;
3938

39+
@Transactional
40+
public void preCreate(Clan clan) {
41+
Assert.isNull(clan.getId(), "Clan payload with id can not be used for creation.");
42+
43+
Player player = playerService.getCurrentPlayer();
44+
45+
if (player.getClanMembership() != null) {
46+
throw ApiException.of(ErrorCode.CLAN_CREATE_FOUNDER_IS_IN_A_CLAN);
47+
}
48+
49+
if (!player.equals(clan.getFounder())) {
50+
throw ApiException.of(ErrorCode.CLAN_INVALID_FOUNDER);
51+
}
52+
53+
clanRepository.findOneByName(clan.getName()).ifPresent(c -> {
54+
throw ApiException.of(ErrorCode.CLAN_NAME_EXISTS, clan.getName());
55+
});
56+
57+
clanRepository.findOneByTag(clan.getTag()).ifPresent(c -> {
58+
throw ApiException.of(ErrorCode.CLAN_TAG_EXISTS, clan.getTag());
59+
});
60+
61+
clan.setLeader(player);
62+
clan.setMemberships(List.of(new ClanMembership()
63+
.setClan(clan)
64+
.setPlayer(player)));
65+
}
66+
4067
@SneakyThrows
41-
Clan create(String name, String tag, String description, Player creator) {
68+
@Transactional
69+
@Deprecated
70+
// use preCreate instead
71+
Clan create(String name, String tag, String description) {
72+
Player creator = playerService.getCurrentPlayer();
73+
4274
if (creator.getClanMembership() != null) {
43-
throw new ApiException(new Error(ErrorCode.CLAN_CREATE_FOUNDER_IS_IN_A_CLAN));
75+
throw ApiException.of(ErrorCode.CLAN_CREATE_FOUNDER_IS_IN_A_CLAN);
4476
}
4577
if (clanRepository.findOneByName(name).isPresent()) {
46-
throw new ApiException(new Error(ErrorCode.CLAN_NAME_EXISTS, name));
78+
throw ApiException.of(ErrorCode.CLAN_NAME_EXISTS, name);
4779
}
4880
if (clanRepository.findOneByTag(tag).isPresent()) {
49-
throw new ApiException(new Error(ErrorCode.CLAN_TAG_EXISTS, tag));
81+
throw ApiException.of(ErrorCode.CLAN_TAG_EXISTS, tag);
5082
}
5183

5284
Clan clan = new Clan();
@@ -70,16 +102,18 @@ Clan create(String name, String tag, String description, Player creator) {
70102
}
71103

72104
@SneakyThrows
73-
String generatePlayerInvitationToken(Player requester, int newMemberId, int clanId) {
105+
@Transactional
106+
String generatePlayerInvitationToken(int newMemberId, int clanId) {
107+
Player requester = playerService.getCurrentPlayer();
108+
74109
Clan clan = clanRepository.findById(clanId)
75110
.orElseThrow(() -> new ApiException(new Error(ErrorCode.CLAN_NOT_EXISTS, clanId)));
76111

77112
if (requester.getId() != clan.getLeader().getId()) {
78-
throw new ApiException(new Error(ErrorCode.CLAN_NOT_LEADER, clanId));
113+
throw ApiException.of(ErrorCode.CLAN_NOT_LEADER, clanId);
79114
}
80115

81-
Player newMember = playerRepository.findById(newMemberId)
82-
.orElseThrow(() -> new ApiException(new Error(ErrorCode.CLAN_GENERATE_LINK_PLAYER_NOT_FOUND, newMemberId)));
116+
Player newMember = playerService.getById(newMemberId);
83117

84118
long expire = Instant.now()
85119
.plus(fafApiProperties.getClan().getInviteLinkExpireDurationMinutes(), ChronoUnit.MINUTES)
@@ -92,28 +126,27 @@ String generatePlayerInvitationToken(Player requester, int newMemberId, int clan
92126
}
93127

94128
@SneakyThrows
95-
void acceptPlayerInvitationToken(String stringToken, Authentication authentication) {
129+
@Transactional
130+
void acceptPlayerInvitationToken(String stringToken) {
96131
Jwt token = jwtService.decodeAndVerify(stringToken);
97132
InvitationResult invitation = objectMapper.readValue(token.getClaims(), InvitationResult.class);
98133

99134
if (invitation.isExpired()) {
100-
throw new ApiException(new Error(ErrorCode.CLAN_ACCEPT_TOKEN_EXPIRE));
135+
throw ApiException.of(ErrorCode.CLAN_ACCEPT_TOKEN_EXPIRE);
101136
}
102137

103138
final Integer clanId = invitation.getClan().getId();
104-
Player player = playerService.getPlayer(authentication);
139+
Player player = playerService.getCurrentPlayer();
105140
Clan clan = clanRepository.findById(clanId)
106141
.orElseThrow(() -> new ApiException(new Error(ErrorCode.CLAN_NOT_EXISTS, clanId)));
107142

108-
Player newMember = playerRepository.findById(invitation.getNewMember().getId())
109-
.orElseThrow(() -> new ProgrammingError("ClanMember does not exist: " + invitation.getNewMember().getId()));
110-
143+
Player newMember = playerService.getById(invitation.getNewMember().getId());
111144

112145
if (player.getId() != newMember.getId()) {
113-
throw new ApiException(new Error(ErrorCode.CLAN_ACCEPT_WRONG_PLAYER));
146+
throw ApiException.of(ErrorCode.CLAN_ACCEPT_WRONG_PLAYER);
114147
}
115148
if (newMember.getClan() != null) {
116-
throw new ApiException(new Error(ErrorCode.CLAN_ACCEPT_PLAYER_IN_A_CLAN));
149+
throw ApiException.of(ErrorCode.CLAN_ACCEPT_PLAYER_IN_A_CLAN);
117150
}
118151

119152
ClanMembership membership = new ClanMembership();

src/main/java/com/faforever/api/clan/ClansController.java

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,17 @@
66
import com.faforever.api.data.domain.Clan;
77
import com.faforever.api.data.domain.Player;
88
import com.faforever.api.player.PlayerService;
9-
import com.google.common.collect.ImmutableMap;
109
import io.swagger.annotations.ApiOperation;
1110
import io.swagger.annotations.ApiResponse;
1211
import io.swagger.annotations.ApiResponses;
1312
import lombok.RequiredArgsConstructor;
1413
import org.springframework.security.access.prepost.PreAuthorize;
15-
import org.springframework.security.core.Authentication;
16-
import org.springframework.transaction.annotation.Transactional;
1714
import org.springframework.web.bind.annotation.GetMapping;
1815
import org.springframework.web.bind.annotation.PostMapping;
1916
import org.springframework.web.bind.annotation.RequestMapping;
2017
import org.springframework.web.bind.annotation.RequestParam;
2118
import org.springframework.web.bind.annotation.RestController;
2219

23-
import java.io.IOException;
2420
import java.io.Serializable;
2521
import java.util.Map;
2622

@@ -41,8 +37,9 @@ public class ClansController {
4137
@ApiResponse(code = 200, message = "Success with JSON { player: {id: ?, login: ?}, clan: { id: ?, name: ?, tag: ?}}"),
4238
@ApiResponse(code = 400, message = "Bad Request")})
4339
@GetMapping(path = "/me", produces = APPLICATION_JSON_VALUE)
44-
public MeResult me(Authentication authentication) {
45-
Player player = playerService.getPlayer(authentication);
40+
@Deprecated // use regular /me route instead
41+
public MeResult me() {
42+
Player player = playerService.getCurrentPlayer();
4643

4744
Clan clan = player.getClan();
4845
ClanResult clanResult = null;
@@ -60,14 +57,12 @@ public MeResult me(Authentication authentication) {
6057
@ApiResponse(code = 400, message = "Bad Request")})
6158
@PostMapping(path = "/create", produces = APPLICATION_JSON_VALUE)
6259
@PreAuthorize("hasRole('ROLE_USER')")
63-
@Transactional
60+
@Deprecated // use POST /data/clans instead (with a founder in relationships)
6461
public Map<String, Serializable> createClan(@RequestParam(value = "name") String name,
6562
@RequestParam(value = "tag") String tag,
66-
@RequestParam(value = "description", required = false) String description,
67-
Authentication authentication) throws IOException {
68-
Player player = playerService.getPlayer(authentication);
69-
Clan clan = clanService.create(name, tag, description, player);
70-
return ImmutableMap.of("id", clan.getId(), "type", "clan");
63+
@RequestParam(value = "description", required = false) String description) {
64+
Clan clan = clanService.create(name, tag, description);
65+
return Map.of("id", clan.getId(), "type", "clan");
7166
}
7267

7368
@ApiOperation("Generate invitation link")
@@ -77,19 +72,14 @@ public Map<String, Serializable> createClan(@RequestParam(value = "name") String
7772
@GetMapping(path = "/generateInvitationLink", produces = APPLICATION_JSON_VALUE)
7873
public Map<String, Serializable> generateInvitationLink(
7974
@RequestParam(value = "clanId") int clanId,
80-
@RequestParam(value = "playerId") int newMemberId,
81-
Authentication authentication) throws IOException {
82-
Player player = playerService.getPlayer(authentication);
83-
String jwtToken = clanService.generatePlayerInvitationToken(player, newMemberId, clanId);
84-
return ImmutableMap.of("jwtToken", jwtToken);
75+
@RequestParam(value = "playerId") int newMemberId) {
76+
String jwtToken = clanService.generatePlayerInvitationToken(newMemberId, clanId);
77+
return Map.of("jwtToken", jwtToken);
8578
}
8679

87-
@ApiOperation("Check invitation link and add Member to Clan")
80+
@ApiOperation("Check invitation link and add member to Clan")
8881
@PostMapping(path = "/joinClan", produces = APPLICATION_JSON_VALUE)
89-
@Transactional
90-
public void joinClan(
91-
@RequestParam(value = "token") String stringToken,
92-
Authentication authentication) throws IOException {
93-
clanService.acceptPlayerInvitationToken(stringToken, authentication);
82+
public void joinClan(@RequestParam(value = "token") String stringToken) {
83+
clanService.acceptPlayerInvitationToken(stringToken);
9484
}
9585
}

src/main/java/com/faforever/api/data/domain/Clan.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ public class Clan extends AbstractEntity implements OwnableEntity {
4747
private String description;
4848
private String tagColor;
4949
private String websiteUrl;
50+
private Boolean requiresInvitation = Boolean.TRUE;
5051
private List<ClanMembership> memberships;
51-
private Boolean requiresInvitation;
5252

5353
@Column(name = "name")
5454
@NotNull

src/main/java/com/faforever/api/data/listeners/ClanEnricherListener.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
package com.faforever.api.data.listeners;
22

3+
import com.faforever.api.clan.ClanService;
34
import com.faforever.api.config.FafApiProperties;
45
import com.faforever.api.data.domain.Clan;
56
import org.springframework.stereotype.Component;
67

78
import javax.inject.Inject;
89
import javax.persistence.PostLoad;
10+
import javax.persistence.PrePersist;
911

1012
@Component
1113
public class ClanEnricherListener {
1214

1315
private static FafApiProperties fafApiProperties;
16+
private static ClanService clanService;
1417

1518
@Inject
16-
public void init(FafApiProperties fafApiProperties) {
19+
public void init(FafApiProperties fafApiProperties, ClanService clanService) {
1720
ClanEnricherListener.fafApiProperties = fafApiProperties;
21+
ClanEnricherListener.clanService = clanService;
22+
}
23+
24+
@PrePersist
25+
public void prePersist(Clan clan) {
26+
if (clan.getId() == null) {
27+
clanService.preCreate(clan);
28+
}
1829
}
1930

2031
@PostLoad

src/main/java/com/faforever/api/error/ErrorCode.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public enum ErrorCode {
6161
CLAN_ACCEPT_PLAYER_IN_A_CLAN(152, "Player is in a clan", "You are already in a clan"),
6262
CLAN_NOT_LEADER(153, "You Permission", "You are not the leader of the clan"),
6363
CLAN_NOT_EXISTS(154, "Cannot find Clan", "Clan with id {0, number} is not available"),
64-
CLAN_GENERATE_LINK_PLAYER_NOT_FOUND(155, "Player not found", "Cannot find player with id {0, number} who should be invited to the clan"),
64+
PLAYER_NOT_FOUND(155, "Player not found", "Cannot find player with id {0, number}."),
6565
CLAN_NAME_EXISTS(156, "Clan Name already in use", "The clan name ''{0}'' is already in use. Please choose a different clan name."),
6666
CLAN_TAG_EXISTS(157, "Clan Tag already in use", "The clan tag ''{0}'' is already in use. Please choose a different clan tag."),
6767
VALIDATION_FAILED(158, "Validation failed", "{0}"),
@@ -99,7 +99,8 @@ public enum ErrorCode {
9999
PARSING_LUA_FILE_FAILED(189, "Parsing lua files failed", "During the parsing of the lua file an error occured: {0}"),
100100
NO_RUSH_RADIUS_MISSING(190, "No rush radius missing", "The scenario file must specify a no rush radius"),
101101
INVALID_FEATURED_MOD(191, "Invalid featured mod name", "The featured mod name ''{0}'' is not allowed in this context."),
102-
API_KEY_INVALID(192, "Api key is invalid", "The api key is invalid.");
102+
API_KEY_INVALID(192, "Api key is invalid", "The api key is invalid."),
103+
CLAN_INVALID_FOUNDER(193, "Invalid clan founder", "If you create a clan you must be the founder of it.");
103104

104105
private final int code;
105106
private final String title;

src/main/java/com/faforever/api/map/MapsController.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import io.swagger.annotations.ApiResponses;
1515
import lombok.AllArgsConstructor;
1616
import lombok.extern.slf4j.Slf4j;
17-
import org.springframework.security.core.Authentication;
1817
import org.springframework.web.bind.annotation.RequestMapping;
1918
import org.springframework.web.bind.annotation.RequestMethod;
2019
import org.springframework.web.bind.annotation.RequestParam;
@@ -77,8 +76,7 @@ public void validateScenarioLua(@RequestParam(name = "scenarioLua") String scena
7776
@ApiResponse(code = 500, message = "Failure")})
7877
@RequestMapping(path = "/upload", method = RequestMethod.POST, produces = APPLICATION_JSON_UTF8_VALUE)
7978
public void uploadMap(@RequestParam("file") MultipartFile file,
80-
@RequestParam("metadata") String jsonString,
81-
Authentication authentication) throws IOException {
79+
@RequestParam("metadata") String jsonString) throws IOException {
8280
if (file == null) {
8381
throw new ApiException(new Error(ErrorCode.UPLOAD_FILE_MISSING));
8482
}
@@ -97,7 +95,7 @@ public void uploadMap(@RequestParam("file") MultipartFile file,
9795
throw new ApiException(new Error(ErrorCode.INVALID_METADATA, e.getMessage()));
9896
}
9997

100-
Player player = playerService.getPlayer(authentication);
98+
Player player = playerService.getCurrentPlayer();
10199
mapService.uploadMap(file.getInputStream(), file.getOriginalFilename(), player, ranked);
102100
}
103101
}

src/main/java/com/faforever/api/mod/ModsController.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import com.faforever.api.player.PlayerService;
88
import com.google.common.io.Files;
99
import io.swagger.annotations.ApiOperation;
10-
import org.springframework.security.core.Authentication;
1110
import org.springframework.web.bind.annotation.RequestMapping;
1211
import org.springframework.web.bind.annotation.RequestMethod;
1312
import org.springframework.web.bind.annotation.RequestParam;
@@ -35,7 +34,7 @@ public ModsController(PlayerService playerService, ModService modService, FafApi
3534

3635
@ApiOperation("Upload a mod")
3736
@RequestMapping(path = "/upload", method = RequestMethod.POST, produces = APPLICATION_JSON_UTF8_VALUE)
38-
public void uploadMod(@RequestParam("file") MultipartFile file, Authentication authentication) throws IOException {
37+
public void uploadMod(@RequestParam("file") MultipartFile file) throws IOException {
3938
if (file == null) {
4039
throw new ApiException(new Error(ErrorCode.UPLOAD_FILE_MISSING));
4140
}
@@ -48,6 +47,6 @@ public void uploadMod(@RequestParam("file") MultipartFile file, Authentication a
4847
Path tempFile = java.nio.file.Files.createTempFile("mod", ".tmp");
4948
file.transferTo(tempFile.getFileName().toFile());
5049

51-
modService.processUploadedMod(tempFile, playerService.getPlayer(authentication));
50+
modService.processUploadedMod(tempFile, playerService.getCurrentPlayer());
5251
}
5352
}

src/main/java/com/faforever/api/player/PlayerService.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,34 @@
22

33
import com.faforever.api.data.domain.Player;
44
import com.faforever.api.error.ApiException;
5-
import com.faforever.api.error.Error;
65
import com.faforever.api.error.ErrorCode;
76
import com.faforever.api.security.FafUserDetails;
87
import lombok.RequiredArgsConstructor;
98
import org.springframework.security.core.Authentication;
9+
import org.springframework.security.core.context.SecurityContextHolder;
1010
import org.springframework.stereotype.Service;
1111

12-
import static com.faforever.api.error.ErrorCode.TOKEN_INVALID;
1312

1413
@Service
1514
@RequiredArgsConstructor
1615
public class PlayerService {
1716

1817
private final PlayerRepository playerRepository;
1918

20-
public Player getPlayer(Authentication authentication) {
19+
public Player getCurrentPlayer() {
20+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
21+
2122
if (authentication != null
2223
&& authentication.getPrincipal() != null
2324
&& authentication.getPrincipal() instanceof FafUserDetails) {
2425
return playerRepository.findById(((FafUserDetails) authentication.getPrincipal()).getId())
25-
.orElseThrow(() -> new ApiException(new Error(TOKEN_INVALID)));
26+
.orElseThrow(() -> ApiException.of(ErrorCode.TOKEN_INVALID));
2627
}
27-
throw new ApiException(new Error(TOKEN_INVALID));
28+
throw ApiException.of(ErrorCode.TOKEN_INVALID);
2829
}
2930

3031
public Player getById(Integer playerId) {
3132
return playerRepository.findById(playerId)
32-
.orElseThrow(() -> new ApiException(new Error(ErrorCode.ENTITY_NOT_FOUND, playerId)));
33+
.orElseThrow(() -> ApiException.of(ErrorCode.PLAYER_NOT_FOUND, playerId));
3334
}
3435
}

0 commit comments

Comments
 (0)