I have the following code in Java 8
List<CategoryTranslation> categoriesTranslation = categoriesRepository.findByLanguageId(languageId);
List<CategoryDTO> categoryList = categoriesTranslation
.stream()
.map(x -> categoriesAdapter.category2DTO(x))
.collect(Collectors.toList());
This works correctly , but I need to convert like this.
List<CategoryTranslation> categoriesTranslation = categoriesRepository.findByLanguageId(languageId);
List<CategoryDTO> categoryList = new ArrayList<CategoryDTO>();
for (CategoryTranslation categoryTranslation : categoriesTranslation) {
CategoryDTO categoryDTO = categoriesAdapter.category2DTO(categoryTranslation);
categoryDTO.setTotal(categoryRepository.countTotalArticlesByCategory(categoryDTO.getCategoryId(), languageId));
categoryList.add(categoryDTO);
}
I know that I could use the adapter but I don't like to use a JPA in Adapter.
Just create a method categorty2DTOWithTotal(CategoryTranslation ct, Long? languiageId).
Otherwise you'd have to call forEach, but it's a terminating method so you couldn't group it into a list. In theory if setting total would result in sensible mapping, you could introduce a method which does that, but here it seems like a bit of a stretch.
void aMethod(Long? languageId) {
List<CategoryTranslation> categoriesTranslation = categoriesRepository
.findByLanguageId(languageId);
List<CategoryDTO> categoryList = categoriesTranslation
.stream()
.map(x -> category2DTOWithTotal(x, languageId))
.collect(Collectors.toList());
}
CategoryDTO category2DTOWithTotal(CategoryTranslation ct, Long? languageId) {
CategoryDTO categoryDTO = categoriesAdapter.category2DTO(categoryTranslation);
categoryDTO.setTotal(
categoryRepository.countTotalArticlesByCategory(
categoryDTO.getCategoryId(), languageId
)
);
return categoryDTO;
}
Or, you could set total later:
void aMethod(Long? languageId) {
List<CategoryDTO> categoryList = categoriesTranslation
.stream()
.map(categoriesAdapter::category2DTO)
.collect(Collectors.toList());
categoryList.forEach(dto -> dto.setTotal(
categoryRepository.countTotalArticlesByCategory(
categoryDTO.getCategoryId(), languageId
)
);
}
And for completeness, a mappable version of setting total:
void aMethod(Long? languageId) {
List<CategoryTranslation> categoriesTranslation = categoriesRepository
.findByLanguageId(languageId);
List<CategoryDTO> categoryList = categoriesTranslation
.stream()
.map(categoriesAdapter::category2DTO)
.map(x -> setTotal(x, languageId))
.collect(Collectors.toList());
}
CategoryDTO setTotal(CategoryDTO ctd, Long? languageId) {
ctd.setTotal(
categoryRepository.countTotalArticlesByCategory(ctd.getCategoryId(), languageId)
);
return ctd;
}
Related
To manage swagger documentations I am using custom annotations for the methods which call the API
#SwagRef(method = POST, url = "/my/api/{pathParam1}")
public Response callMyAPI(
#MyParam(name = "pathParam1", required = true, in = PATH) String p1,
#MyParam(name = "param2", required = false, in = QUERY) String p2) {
return given()
.pathParam("pathParam1", p1)
.queryParam("param2", p2)
.get();
}
There is a separate piece of code which validates the Swagger/api/docs vs the annotations.
However I'm wondering is it possible to somehow use all this already presented data in the annotations and have a common code where I can pass the method reference or the parameter reference and the RequestSpecification can be built using the annotations.
I tried with reflection, but I'm unable to fetch the value of parameters using reflection from method
I was only able to deduce the method type and API since it's constant using the methodName and stackTrace
private SwagRef defineSwaggerInfo() {
List<StackTraceElement> stackTrace = asList(currentThread().getStackTrace());
return stackTrace.stream()
.map(tryOrNull(element -> Pair.with(element.getMethodName(), forName(element.getClassName()))))
.filter(Objects::nonNull)
.filter(pair -> MyAPI.class.isAssignableFrom(pair.getValue1()))
.map(pair -> with(pair.getValue0(), asList(pair.getValue1().getDeclaredMethods())))
.flatMap(
tryOrNull(
pair ->
pair.getValue1().stream()
.filter(method -> Objects.equals(method.getName(), pair.getValue0()))
.peek(method -> method.setAccessible(true))
.map(method -> method.getAnnotation(SwagRef.class))))
.filter(Objects::nonNull)
.findFirst()
.orElseThrow();
}
But I'm not able to come up with a generic function for Building the request spec using method parameters
I tried looking at AspectJ but wasn't able to embed it properly
There is no way to get the actual parameter values from the stack via Reflection. In fact, there’s not even a guaranty that the parameter values of an ongoing invocation are still on the stack at that point.
The closest you can get to perform automated parameter processing, is to declare the methods in an interface and generate a proxy:
interface FrontEnd {
public static FrontEnd get() {
return (FrontEnd)Proxy.newProxyInstance(FrontEnd.class.getClassLoader(),
new Class<?>[]{FrontEnd.class}, (proxy, method, args) -> {
if(method.getDeclaringClass() == Object.class) {
switch(method.getName()) {
case "toString": return
FrontEnd.class.getName()+'#'+System.identityHashCode(proxy);
case "equals": return proxy == args[0];
case "hashCode": return System.identityHashCode(proxy);
default: throw new AssertionError();
}
}
SwagRef swagRef = method.getAnnotation(SwagRef.class);
if(swagRef == null) throw new IncompatibleClassChangeError();
MyParam[] p = Arrays.stream(method.getParameterAnnotations())
.map(pa -> Arrays.stream(pa)
.filter(a -> a.annotationType() == MyParam.class)
.findFirst().orElseThrow(
() -> new IllegalStateException("missing required #MyParam")))
.toArray(MyParam[]::new);
Map<String,String> map = IntStream.range(0, args.length).boxed()
.filter(i -> p[i].required() || args[i] != null)
.collect(Collectors.toMap(i -> p[i].name(), i -> args[i].toString()));
// do actual invocation logic here
System.out.println(
"operation: "+swagRef.method()+' '+swagRef.url()+", "+map);
return null;
});
}
#SwagRef(method = POST, url = "/my/api/{pathParam1}")
public Response callMyAPI(
#MyParam(name = "pathParam1", required = true, in = PATH) String p1,
#MyParam(name = "param2", required = false, in = QUERY) String p2);
}
You may add more methods to that interface, to be handled the same way, assuming that they all have the necessary annotations.
Starting with Java 9, you can use a private method in the interface, which I would prefer here.
interface FrontEnd {
public static FrontEnd get() {
return (FrontEnd)Proxy.newProxyInstance(FrontEnd.class.getClassLoader(),
new Class<?>[]{FrontEnd.class}, FrontEnd::callImpl);
}
#SwagRef(method = POST, url = "/my/api/{pathParam1}")
public Response callMyAPI(
#MyParam(name = "pathParam1", required = true, in = PATH) String p1,
#MyParam(name = "param2", required = false, in = QUERY) String p2);
private static Object callImpl(Object proxy, Method method, Object[] args) {
if(method.getDeclaringClass() == Object.class) {
switch(method.getName()) {
case "toString": return
FrontEnd.class.getName()+'#'+System.identityHashCode(proxy);
case "equals": return proxy == args[0];
case "hashCode": return System.identityHashCode(proxy);
default: throw new AssertionError();
}
}
SwagRef swagRef = method.getAnnotation(SwagRef.class);
if(swagRef == null) throw new IncompatibleClassChangeError();
MyParam[] p = Arrays.stream(method.getParameterAnnotations())
.map(pa -> Arrays.stream(pa)
.filter(a -> a.annotationType() == MyParam.class)
.findFirst().orElseThrow(
() -> new IllegalStateException("missing required #MyParam")))
.toArray(MyParam[]::new);
Map<String,String> map = IntStream.range(0, args.length).boxed()
.filter(i -> p[i].required() || args[i] != null)
.collect(Collectors.toMap(i -> p[i].name(), i -> args[i].toString()));
// do actual invocation logic here
System.out.println("operation: "+swagRef.method()+' '+swagRef.url()+", "+map);
return null;
}
}
Alternatively, you may split up the logic between the interface and a, possibly non-public, helper class.
I'm trying to rewrite code with nested conditions using Optional and Stream. That's how he looked:
if (transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION") != null) {
editObj = (EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION");
} else {
if (editObj != null) {
editObj = editObj.getEditInstance(transaction);
} else {
editObj = HOME.newEmployeeWorkstation(compId);
}
}
I tried to rewrite so:
editObj =
ofNullable(
(EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.orElse(
editObj != null
? editObj.getEditInstance(transaction)
: HOME.newEmployeeWorkstation(compId));
And it works fine but my mentor said that it can be simplified
then I tried so:
editObj =
Optional.ofNullable(
(EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.map(obj -> obj.getEditInstance(transaction))
.orElse(HOME.newEmployeeWorkstation(compId));
I understand that my .map() does not work as described above in the first versions. How can I rewrite .map so that it works as described above?
You can use a nested Optional:
EmployeeWorkstation edit = Optional.ofNullable((EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.orElseGet(() -> Optional.ofNullable(editObj)
.map(e -> e.getEditInstance(transaction))
.orElseGet(() -> HOME.newEmployeeWorkstation(compId)));
If you are using Java 9 or higher you can use Optional.or():
EmployeeWorkstation edit = Optional.ofNullable((EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.or(() -> Optional.ofNullable(editObj).map(edit -> edit.getEditInstance(transaction)))
.orElseGet(() -> HOME.newEmployeeWorkstation(compId));
I want to return JSON from Rest API endpoint as keys with values. Example:
{
"terminal 1":
{"date":"2018-10-06T00:00:00.000+0000","volume":111,"count":1},
"terminal 2":
{"date":"2018-11-06T00:00:00.000+0000","volume":122,"count":1}
}
How I can add the keys? It should be I suppose like this:
List<String<List<TopTerminalsDTO>>>>
Can you give me some code example?
Latest attempt to clean the final code:
#GetMapping("/terminals")
public ResponseEntity<Map<Integer, List<TopTerminalsDTO>>> getTopTerminalsVolumes(
#RequestParam(value = "start_date", required = true) String start_date,
#RequestParam(value = "end_date", required = true) String end_date) {
LocalDateTime start_datel = LocalDateTime.now(Clock.systemUTC());
LocalDateTime end_datel = LocalDateTime.now(Clock.systemUTC());
final List<PaymentTransactionsDailyFacts> list = dashboardRepository.top_daily_transactions(start_datel, end_datel);
final Collector<PaymentTransactionsDailyFacts, List<TopTerminalsDTO>, List<TopTerminalsDTO>> terminalsCollector =
Collector.of(
ArrayList::new,
(terminals, p) -> terminals.add(mapper.toTopTerminalsDTO(p)),
(accumulator, terminals) -> {
accumulator.addAll(terminals);
return accumulator;
}
);
final Map<Integer, List<TopTerminalsDTO>> final_map =
list.stream()
.filter(p -> p.getTerminal_id() != null)
.collect(Collectors.groupingBy(p -> p.getTerminal_id(), terminalsCollector));
return ResponseEntity.ok(final_map);
}
Following your JSON, testDate() should return Map<String, TopTerminalsDTO> instead of List.
Map<String, TopTerminalsDTO> result = newHashMap();
for (int i = 0; i <= 10; i++) {
TopTerminalsDTO ttDto = new TopTerminalsDTO();
ttDto.setCount(ThreadLocalRandom.current().nextInt(20, 500 + 1));
LocalDate localDate = LocalDate.now().minus(Period.ofDays((new Random().nextInt(365 * 70))));
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
ttDto.setDate(date);
ttDto.setVolume(ThreadLocalRandom.current().nextInt(300, 5000 + 1));
result.put("terminal "+i, ttDto)
}
return result;
And, of course, change response type of rest method to ResponseEntity<Map<String, TopTerminalsDTO>>
This is what a Javascript dictionary looks like.
In Java, the correct representation is a Map<String, TopTerminalDto>.
Say you have an ordered List, and you want to return a Map with generated keys terminal{index}.
final List<TopTerminalDto> list = ...
final Map<String, TopTerminalDto> map =
IntStream.range(0, list.size())
.boxed()
.collect(toMap(i -> "terminal" + i, i -> list.get(i)));
The Spring endpoint would become:
#GetMapping("terminals")
Map<String, TopTerminalDto> getTopTerminalVolumes() { ... }
The ResponseEntity is not mandatory in Spring.
Remember to work as much as possible via Stream(s), to produce results without intermediate temporary state.
Additional example:
final List<PaymentTransactionsDailyFacts> list =
dashboardRepository.top_daily_transactions(start_datel, end_datel);
final Map<String, TopTerminalDto> map =
list.stream()
.collect(toMap(p -> p.getTerminal(), this::toDto))
// Conversion method
private TopTerminalDto toDto(final PaymentTransactionsDailyFacts p) {
// Implement conversion to Dto
}
For multiple values associated with a terminal:
final Map<Integer, List<TopTerminalDto>> map =
list.stream()
.filter(p -> p.getTerminal() != null)
.collect(groupingBy(
p -> p.getTerminal(),
Collector.of(
ArrayList::new,
(terminals, p) -> terminals.add(toDto(p)),
(accumulator, terminals) -> {
accumulator.addAll(terminals);
return accumulator;
}
)
));
You can clean the code by extracting the Collector.
final Collector<Integer, List<TopTerminalDto>, List<TopTerminalDto>> terminalsCollector =
Collector.of(
ArrayList::new,
(terminals, p) -> terminals.add(toDto(p)),
(accumulator, terminals) -> {
accumulator.addAll(terminals);
return accumulator;
}
)
final Map<Integer, List<TopTerminalDto>> map =
list.stream()
.filter(p -> p.getTerminal() != null)
.collect(groupingBy(p -> p.getTerminal(), terminalsCollector));
I am newbie in RxJava and need help to improve my code. Here is what I've done:
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io()).blockingAwait();
List<MenuItemsBlocks> blocks = new ArrayList<>();
Set<String> aliasList = getAliasFromMenuItems();
for (String alias : aliasList) {
List<MenuItemsBlocks> itemsBlocks = ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
.toList()
.blockingGet();
blocks.addAll(itemsBlocks);
}
return Single.just(blocks);
}
There is no problem at runtime with this code, but I want to improve it in rx style, I've tried to rewrite it something like this (but it's not working):
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io()).blockingAwait();
Set<String> aliasList = getAliasFromMenuItems();
return Observable.fromIterable(aliasList)
.switchMap(alias -> ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
.toList()
);
}
And I am stuck with it and need your help!
First of all, if you have blockingAwait in non-test code, you are doing it wrong. Second, you probably need concatMap instead of switchMap as it will just keep switching to later list elements, cancelling the outstanding API calls.
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
return Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io())
.andThen(Single.defer(() -> {
Set<String> aliasList = getAliasFromMenuItems();
return Observable.fromIterable(aliasList)
.concatMap(alias -> ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
)
.toList();
}));
}
I want to apply/or not filter for users if condition is satisfied (condition: if principal.isAdministrator() ) for this code sequence.
for (User oneUser : subcontractorUserRole.getUsers()
.filter(new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor()))
.filter(Identity.DELETED, false))
users.add(new GenericItem(oneUser.getId(), oneUser.getName()));
I want to do something like this:
if ( principal.isAdministrator() {
for ( User oneUser : subcontractorUserRole.getUsers()
//.filter(new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor()))
.filter(Identity.DELETED, false))
users.add(new GenericItem(oneUser.getId(), oneUser.getName()));
} else {
for ( User oneUser : subcontractorUserRole.getUsers()
.filter(new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor()))
.filter(Identity.DELETED, false))
users.add(new GenericItem(oneUser.getId(), oneUser.getName()));
}
Do you know an elegant way to write this without duplicate the loop? I can use else java 8 ...
Thank you.
Try this
subcontractorUserRole.getUsers()
.stream()
.filter(i -> principal.isAdministrador() || new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor()))
.filter(Identity.DELETED, false)
.forEach(oneUser -> users.add(new GenericItem(oneUser.getId(), oneUser.getName())));
If you operate a complex logic it is better to have streaming based on predicates.
Predicate<User> isAdmin = u -> principal.isAdministrator();
Predicate<User> isOTMFpositive = new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor());
Predicate<User> notDeleted = ...
Predicate<User> fullPredicate = isAdmin.or(isOTMFpositive).and(notDeleted);
subcontractorUserRole.getUsers()
.stream()
.filter(fullPredicate)
.map(u -> new GenericItem(u.getId(), u.getName()))
.collect(collect(Collectors.toList()));