Skip to content

Commit 23ecded

Browse files
committed
define asset categories
1 parent 002f050 commit 23ecded

File tree

7 files changed

+303
-177
lines changed

7 files changed

+303
-177
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.backbase.stream.investment;
2+
3+
import com.backbase.investment.api.service.v1.model.AssetCategory;
4+
import com.backbase.investment.api.service.v1.model.AssetTypeEnum;
5+
import com.backbase.investment.api.service.v1.model.StatusA10Enum;
6+
import com.fasterxml.jackson.annotation.JsonProperty;
7+
import java.net.URI;
8+
import java.util.HashMap;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.UUID;
12+
13+
/**
14+
* Lightweight projection of {@link com.backbase.investment.api.service.v1.model.Asset} that keeps the DTO immutable
15+
* while providing helpers to translate to/from the generated model.
16+
*/
17+
public record Asset(
18+
UUID uuid,
19+
String name,
20+
String isin,
21+
String ticker,
22+
StatusA10Enum status,
23+
String market,
24+
String currency,
25+
@JsonProperty("extra_data")
26+
Map<String, Object> extraData,
27+
@JsonProperty("asset_type")
28+
AssetTypeEnum assetType,
29+
List<String> categories,
30+
URI logo,
31+
String externalId,
32+
String description
33+
) {
34+
35+
/**
36+
* Creates a record from the generated API model.
37+
*/
38+
public static Asset fromModel(com.backbase.investment.api.service.v1.model.Asset asset) {
39+
if (asset == null) {
40+
return null;
41+
}
42+
List<AssetCategory> categories = asset.getCategories();
43+
Map<String, Object> extraData = asset.getExtraData();
44+
return new Asset(
45+
asset.getUuid(),
46+
asset.getName(),
47+
asset.getIsin(),
48+
asset.getTicker(),
49+
asset.getStatus(),
50+
asset.getMarket(),
51+
asset.getCurrency(),
52+
extraData == null ? Map.of() : Map.copyOf(extraData),
53+
asset.getAssetType(),
54+
categories == null ? List.of() : categories.stream().map(AssetCategory::getCode).toList(),
55+
asset.getLogo(),
56+
asset.getExternalId(),
57+
asset.getDescription()
58+
);
59+
}
60+
61+
/**
62+
* Ensures the record keeps defensive copies of mutable collections.
63+
*/
64+
public Asset {
65+
Map<String, Object> safeExtraData = extraData == null ? Map.of() : Map.copyOf(extraData);
66+
List<String> safeCategories = categories == null ? List.of() : List.copyOf(categories);
67+
extraData = safeExtraData;
68+
categories = safeCategories;
69+
}
70+
}

stream-investment/investment-core/src/main/java/com/backbase/stream/investment/InvestmentData.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.backbase.stream.investment;
22

3-
import com.backbase.investment.api.service.v1.model.Asset;
43
import com.backbase.investment.api.service.v1.model.InvestorModelPortfolio;
54
import com.backbase.investment.api.service.v1.model.Market;
65
import com.backbase.investment.api.service.v1.model.MarketSpecialDay;

stream-investment/investment-core/src/main/java/com/backbase/stream/investment/InvestmentTask.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.backbase.stream.investment;
22

3-
import com.backbase.investment.api.service.v1.model.Asset;
43
import com.backbase.investment.api.service.v1.model.Market;
54
import com.backbase.investment.api.service.v1.model.MarketSpecialDay;
65
import com.backbase.stream.worker.model.StreamTask;
@@ -24,18 +23,19 @@ public String getName() {
2423
return "investment";
2524
}
2625

27-
public InvestmentTask data(ClientUser clientUser) {
28-
29-
return null;
30-
}
31-
3226
public void data(List<ClientUser> clients) {
3327
data.setClientUsers(clients);
3428
}
3529

36-
public void setMarkets(List<Market> markets) { data.setMarkets(markets); }
30+
public void setMarkets(List<Market> markets) {
31+
data.setMarkets(markets);
32+
}
3733

38-
public void setMarketSpecialDays(List<MarketSpecialDay> marketSpecialDays) { data.setMarketSpecialDays(marketSpecialDays); }
34+
public void setMarketSpecialDays(List<MarketSpecialDay> marketSpecialDays) {
35+
data.setMarketSpecialDays(marketSpecialDays);
36+
}
3937

40-
public void setAssets(List<Asset> assets) { data.setAssets(assets); }
38+
public void setAssets(List<Asset> assets) {
39+
data.setAssets(assets);
40+
}
4141
}

stream-investment/investment-core/src/main/java/com/backbase/stream/investment/saga/InvestmentSaga.java

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package com.backbase.stream.investment.saga;
22

3-
import com.backbase.investment.api.service.v1.model.AssetCategory;
43
import com.backbase.investment.api.service.v1.model.ClientCreateRequest;
54
import com.backbase.investment.api.service.v1.model.MarketRequest;
65
import com.backbase.investment.api.service.v1.model.MarketSpecialDayRequest;
7-
import com.backbase.investment.api.service.v1.model.OASAssetRequestDataRequest;
86
import com.backbase.investment.api.service.v1.model.Status836Enum;
97
import com.backbase.stream.investment.InvestmentData;
108
import com.backbase.stream.investment.InvestmentTask;
@@ -15,7 +13,6 @@
1513
import com.backbase.stream.worker.StreamTaskExecutor;
1614
import com.backbase.stream.worker.model.StreamTask;
1715
import com.backbase.stream.worker.model.StreamTask.State;
18-
import java.io.IOException;
1916
import java.util.List;
2017
import java.util.Map;
2118
import java.util.UUID;
@@ -489,55 +486,27 @@ public Mono<InvestmentTask> createAssets(final InvestmentTask investmentTask) {
489486
investmentTask.setState(State.COMPLETED);
490487
return Mono.just(investmentTask);
491488
}
492-
493489
// Process each asset: create or get from asset universe service
494-
return Flux.fromIterable(investmentData.getAssets())
495-
.flatMap(asset -> {
496-
try {
497-
// Build asset request and invoke service
498-
return assetUniverseService.getOrCreateAsset(
499-
new OASAssetRequestDataRequest()
500-
.name(asset.getName())
501-
.isin(asset.getIsin())
502-
.ticker(asset.getTicker())
503-
.market(asset.getMarket())
504-
.currency(asset.getCurrency())
505-
.status(asset.getStatus())
506-
.description(asset.getDescription())
507-
.extraData(asset.getExtraData())
508-
.assetType(asset.getAssetType())
509-
.categories(asset.getCategories() == null
510-
? List.of()
511-
: asset.getCategories().stream().map(AssetCategory::getUuid).toList())
512-
.externalId(asset.getExternalId())
513-
);
514-
} catch (IOException e) {
515-
final String assetIdentifier =
516-
asset.getIsin() + "_" + asset.getMarket() + "_" + asset.getCurrency();
517-
log.error("Failed to create asset with asset identifier {} : {}", assetIdentifier, e.getMessage(),
518-
e);
519-
return Mono.error(e);
520-
}
521-
})
522-
.collectList() // Collect all created/retrieved assets into a list
523-
.map(assets -> {
490+
return assetUniverseService.createAssets(investmentData.getAssets())
491+
.collectList()
492+
.doOnSuccess(assets -> {
524493
investmentTask.setAssets(assets);
525-
// Log completion and set task state to COMPLETED
526494
investmentTask.info(INVESTMENT, OP_CREATE, RESULT_CREATED, investmentTask.getName(),
527495
investmentTask.getId(),
528496
RESULT_CREATED + " " + assets.size() + " Investment Assets");
529497
investmentTask.setState(State.COMPLETED);
530498
log.info("Successfully created all assets: taskId={}, assetCount={}",
531499
investmentTask.getId(), assets.size());
532-
return investmentTask;
500+
533501
})
534502
.doOnError(throwable -> {
535503
log.error("Failed to create investment assets: taskId={}, assetCount={}",
536504
investmentTask.getId(), assetCount, throwable);
537505
investmentTask.error(INVESTMENT, OP_CREATE, RESULT_FAILED, investmentTask.getName(),
538506
investmentTask.getId(),
539507
"Failed to create investment assets: " + throwable.getMessage());
540-
});
508+
})
509+
.map(a -> investmentTask);
541510
}
542511

543512
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.backbase.stream.investment.service;
2+
3+
import com.backbase.investment.api.service.v1.model.AssetCategory;
4+
import com.backbase.investment.api.service.v1.model.OASAssetRequestDataRequest;
5+
import com.backbase.stream.investment.Asset;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Objects;
10+
import java.util.UUID;
11+
import org.mapstruct.AfterMapping;
12+
import org.mapstruct.Mapper;
13+
import org.mapstruct.Mapping;
14+
import org.mapstruct.MappingTarget;
15+
import org.mapstruct.Named;
16+
17+
@Mapper
18+
public interface AssetMapper {
19+
20+
@Mapping(target = "categories", ignore = true)
21+
OASAssetRequestDataRequest map(Asset asset, Map<String, UUID> categoryIdByCode);
22+
23+
@Mapping(target = "categories", source = "categories", qualifiedByName = "mapCategories")
24+
Asset map(com.backbase.investment.api.service.v1.model.Asset asset);
25+
26+
@AfterMapping
27+
default void postMap(@MappingTarget OASAssetRequestDataRequest requestDataRequest, Asset asset,
28+
Map<String, UUID> categoryIdByCode) {
29+
if (requestDataRequest == null) {
30+
return;
31+
}
32+
requestDataRequest.setCategories(Objects.requireNonNullElse(asset.categories(), new ArrayList<AssetCategory>())
33+
.stream().filter(Objects::nonNull).map(categoryIdByCode::get)
34+
.filter(Objects::nonNull).toList());
35+
}
36+
37+
@Named("mapCategories")
38+
default List<String> mapCategories(List<AssetCategory> categories) {
39+
return Objects.requireNonNullElse(categories, new ArrayList<AssetCategory>())
40+
.stream().map(AssetCategory::getCode).toList();
41+
}
42+
43+
}

stream-investment/investment-core/src/main/java/com/backbase/stream/investment/service/InvestmentAssetUniverseService.java

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,36 @@
22

33
import com.backbase.investment.api.service.v1.AssetUniverseApi;
44
import com.backbase.investment.api.service.v1.model.Asset;
5+
import com.backbase.investment.api.service.v1.model.AssetCategory;
56
import com.backbase.investment.api.service.v1.model.Market;
67
import com.backbase.investment.api.service.v1.model.MarketRequest;
78
import com.backbase.investment.api.service.v1.model.MarketSpecialDay;
89
import com.backbase.investment.api.service.v1.model.MarketSpecialDayRequest;
910
import com.backbase.investment.api.service.v1.model.OASAssetRequestDataRequest;
10-
import lombok.RequiredArgsConstructor;
11-
import lombok.extern.slf4j.Slf4j;
12-
import org.springframework.web.reactive.function.client.WebClientResponseException;
13-
import reactor.core.publisher.Mono;
14-
11+
import com.backbase.investment.api.service.v1.model.PaginatedAssetCategoryList;
1512
import java.io.IOException;
1613
import java.time.LocalDate;
1714
import java.util.List;
15+
import java.util.Map;
16+
import java.util.Objects;
1817
import java.util.Optional;
18+
import java.util.UUID;
19+
import java.util.stream.Collectors;
20+
import lombok.RequiredArgsConstructor;
21+
import lombok.extern.slf4j.Slf4j;
22+
import org.mapstruct.factory.Mappers;
23+
import org.springframework.util.CollectionUtils;
24+
import org.springframework.web.reactive.function.client.WebClientResponseException;
25+
import reactor.core.publisher.Flux;
26+
import reactor.core.publisher.Mono;
1927

2028
@Slf4j
2129
@RequiredArgsConstructor
2230
public class InvestmentAssetUniverseService {
2331

2432
private final AssetUniverseApi assetUniverseApi;
2533
private final CustomIntegrationApiService customIntegrationApiService;
34+
private final AssetMapper assetMapper = Mappers.getMapper(AssetMapper.class);
2635

2736
/**
2837
* Gets an existing market by code, or creates it if not found (404). Handles 404 NOT_FOUND from getMarket by
@@ -64,7 +73,7 @@ public Mono<Market> getOrCreateMarket(MarketRequest marketRequest) {
6473
* @return Mono<Asset> representing the existing or newly created asset
6574
* @throws IOException if an I/O error occurs
6675
*/
67-
public Mono<Asset> getOrCreateAsset(final OASAssetRequestDataRequest assetRequest) throws IOException {
76+
public Mono<Asset> getOrCreateAsset(final OASAssetRequestDataRequest assetRequest) {
6877
log.debug("Creating asset: {}", assetRequest);
6978

7079
// Build a unique asset identifier using ISIN, market, and currency
@@ -155,4 +164,44 @@ public Mono<MarketSpecialDay> getOrCreateMarketSpecialDay(MarketSpecialDayReques
155164
})
156165
);
157166
}
167+
168+
public Flux<com.backbase.stream.investment.Asset> createAssets(List<com.backbase.stream.investment.Asset> assets) {
169+
if (CollectionUtils.isEmpty(assets)) {
170+
return Flux.empty();
171+
}
172+
return assetUniverseApi.listAssetCategories(null,
173+
null, null, null, null, null)
174+
.filter(Objects::nonNull)
175+
.map(PaginatedAssetCategoryList::getResults)
176+
.filter(Objects::nonNull)
177+
.flatMapMany(categories -> {
178+
Map<String, UUID> categoryIdByCode = categories.stream()
179+
.collect(Collectors.toMap(AssetCategory::getCode, AssetCategory::getUuid));
180+
181+
return Flux.fromIterable(assets)
182+
.flatMap(asset -> {
183+
OASAssetRequestDataRequest assetRequest = assetMapper.map(asset, categoryIdByCode);
184+
return this.getOrCreateAsset(assetRequest)
185+
.doOnSuccess(
186+
createdMarketSpecialDay -> log.info("Created market special day: {}",
187+
createdMarketSpecialDay))
188+
.doOnError(error -> {
189+
String assetIdentifier = "1";
190+
// asset.getIsin() + "_" + asset.getMarket() + "_" + asset.getCurrency();
191+
if (error instanceof WebClientResponseException) {
192+
WebClientResponseException w = (WebClientResponseException) error;
193+
log.error("Error creating market special day : {} : HTTP {} -> {}",
194+
assetRequest, w.getStatusCode(), w.getResponseBodyAsString());
195+
} else {
196+
log.error("Failed to create asset with asset identifier {} : {}", 1,
197+
error.getMessage(),
198+
error);
199+
}
200+
201+
})
202+
.map(assetMapper::map);
203+
});
204+
});
205+
}
206+
158207
}

0 commit comments

Comments
 (0)