quarkus reactive getting started PUT and DELETE operations - jax-rs

I'm going through the quarkus reactive getting started page and PUT and DELETE method implementations are missing.
Seem like it assumes we already know quarkus and are just reading the guide to switch from non-reactive to reactive. Why they don't provide a full example, I don't know. I mean where would you learn if not by a guide or someone showing you how it's done?
PUT should replace an entry and DELETE should delete one.
PUT /{id} = replace
DELETE /{id} = delete
Instead of Fruit my entity is named Profile.
package de.icod.reso.resources;
import de.icod.reso.entities.Profile;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.panache.common.Sort;
import io.smallrye.mutiny.Uni;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.List;
import java.util.UUID;
#Path("/profile")
#ApplicationScoped
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class ProfileResource {
#GET
public Uni<List<Profile>> get() {
return Profile.listAll(Sort.by("name"));
}
#GET
#Path("/{id}")
public Uni<Profile> getSingle(UUID id) {
return Profile.findById(id);
}
#POST
public Uni<Response> create(Profile profile) {
return Panache.<Profile>withTransaction(profile::persist)
.onItem().transform(inserted -> Response.created(URI.create("/profile" + inserted.id)).build());
}
#PUT
#Path("/{id}")
public Uni<Response> replace(UUID id, Profile profile) {
// error: incompatible types: incompatible parameter types in method reference
return Panache.<Profile>withTransaction(profile::update)
.onItem().transform(updated -> Response.ok(URI.create("/profile" + updated.id)).build());
}
#DELETE
#Path("/{id}")
public Uni<Response> delete(UUID id) {
// delete entity by id
}
}
Can you fill the 2 missing functions?
I'm aware there is Quarkus Getting Started (w/ Reactive): PostGres/Docker CRUD Application -- Missing POST, PUT, and DELETE REST URLs
but the contents are different from what's written in the getting started page.

Generally speaking, the Getting Started and the quickstarts might be slightly different. The reason is that the purpose of the Getting Started is to help a new user to set up a small working project quickly and the quickstart is a place to showcase different functionalities of the extensions involved.
We try to make sure that the documentation is always up to date but nothing beats a working example like the quickstart.
In this case I don't understand your complains.
An example of PUT and DELETE is available in the Hibernate Reactive with panache quickstart:
#PUT
#Path("{id}")
public Uni<Response> update(Long id, Fruit fruit) {
return Panache
.withTransaction(() -> Fruit.<Fruit> findById(id)
.onItem().ifNotNull().invoke(entity -> entity.name = fruit.name)
)
.onItem().ifNotNull().transform(entity -> Response.ok(entity).build())
.onItem().ifNull().continueWith(Response.ok().status(NOT_FOUND)::build);
}
#DELETE
#Path("{id}")
public Uni<Response> delete(Long id) {
return Panache.withTransaction(() -> Fruit.deleteById(id))
.map(deleted -> deleted
? Response.ok().status(NO_CONTENT).build()
: Response.ok().status(NOT_FOUND).build());
}
For your use case you need to use Profile instead of Fruit, and UUID instead of Long for the id. I don't think you need anything else to make it work.
// error: incompatible types: incompatible parameter types in method reference
The error message is telling you the cause of the problem: the syntax Panache.withTransaction(profile::update) is not correct.
This won't work because profile.update(...) expects additional parameters that you are not passing when using method reference with withTransaction.
On the other hand, one can use Panache.withTransaction(profile::persist) because profile.persist() is a method that doesn't require parameters.
This is how method reference works in Java.
That said, the documentation is never done and it will get better over time based on the feedback we receive.
Also, StackOverflow might not be the best place for this type of questions.
I would have probably asked this on the users stream on Zulip first. It's even faster to receive an answer if you push an example of the code that's giving you trouble somewhere, so that we can run it and give you precise help about what's wrong with it.
I'm aware there is Quarkus Getting Started (w/ Reactive): PostGres/Docker CRUD Application -- Missing POST, PUT, and DELETE REST URLs
The example in this question is 2 years old and things have changed since then. In fact, the same quickstart and tutorial referenced in that question now match the code I've used.

You can find the information you are asking for at https://quarkus.io/guides/hibernate-orm-panache#writing-a-jax-rs-resource.
You can also compare reactive vs. non-reactive versions of the same application by looking at the Hibernate Reactive Panache Quickstart and the Hibernate ORM Panache Quickstart

Related

UserActions logic is moved to #spartacus/user

Based on this upgrade suggestion I’m trying to import UserActions/UserDetailsAction from #spartacus/user lib but am getting the error has no exported member.
// TODO:Spartacus - UserActions - Following actions 'ForgotPasswordEmailRequestAction', 'ResetPasswordAction', 'EmailActions', 'UpdatePasswordAction', 'UserDetailsAction' were removed. Logic was moved to '#spartacus/user'
Module ‘“#spartacus/user”’ has no exported member ‘UserActions’.
import { UserActions } from '#spartacus/user';
this.store.dispatch(new UserActions.LoadUserDetails(user));
import { UserActions } from '#spartacus/user';
Tried to import UserActions from the user lib
According to the technical changes documentation, you should use the new recommended approach with commands and queries as mentioned in the link. But you can also try importing from #spartacus/user/account, #spartacus/user/profile or #spartacus/core bearing in mind that your previously used actions may not exist anymore.
In case it helps, a similar question was also asked here.
Based on technical changes in Spartacus 4.0, Some branches of the ngrx state for the User feature were removed and logic has been moved to facades in #spartacus/user library.
In your case, instead of dispatching an action, simply use the get method from UserAccountFacade that is part of #spartacus/user.

How can I mock a call to Spring's repository `saveAll()` method using mockk?

I am using Mockk as my mocking framework when testing my Spring Boot Data repository interfaces.
Actually I am doing the following
every { itemRepository.saveAll(listOf(any(), any())) } returns listOf<Item>(mockk())
which should mock the following behaviour
val loot: List<Item> = itemGenerator.generateLoot(lootTable)
itemRepository.saveAll(loot)
The error message I receive is the following:
Failed matching mocking signature for
SignedCall(retValue=, isRetValueMock=true, retType=class kotlin.collections.Iterable, self=ItemRepository(#28), method=saveAll(Iterable), args=[[com.barbarus.gameserver.item.Item#ea00de, com.barbarus.gameserver.item.Item#23ca36d]], invocationStr=ItemRepository(#28).saveAll([com.barbarus.gameserver.item.Item#ea00de, com.barbarus.gameserver.item.Item#23ca36d]))
left matchers: [any(), any()]
The error message says left matchers: [any(), any()] pointing out that I somehow am not defining the expected arguments right.
I could fully define the items by real implementations in my test logic but I'd like to stick with mockk() just to keep the test code slim and fast.
However I kinda am not able to define the List<Item> with two elements using listOf(any(),any()) here. I tried other API of Mockk without any luck.
Any idea what to use in this case?
You should type the any() when you are passing into saveAll().
For instance:
import com.barbarus.gameserver.item.Item
...
every { itemRepository.saveAll(any<List<Item>>() } returns listOf<Item>(mockk())
Solution from another post

Mulesoft Anypoint Studio DevKit - #Connector vs #Module ( No need for config-ref )

I am trying to create a component that does not need a connection to another source or for that fact a connection strategy... but no matter what I try I cannot prevent the component from having a "Basic Settings" > "Connector Configuration" dropdown/create/edit section in the "Mule Properties" section when working with the components settings.
Does anyone know how I create an Anypoint DevKit Component that does not need a "Connector Configuration" ?
My understanding is that annotations play a big role in defining this and that the connecotr uses the #Connector and that someone has mentioned to me the need to substitute this with the #Module annotation as this indicates no need for a connection... but even when doing this the config options remain on the component.
Please see the following code:
( note : I am trying to create a custom logger and need only to have several inputs and a dropdown operation which I can already get working. )
package au.com.company.companylogger;
import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.Module;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.param.Default;
import org.mule.api.annotations.param.Optional;
#Module(name="company-logger", friendlyName="companyLogger", schemaVersion="1.0.0-SNAPSHOT")
public class companyLoggerConnector {
#Processor(name="company-logger-trace", friendlyName="TRACE")
public String companyLoggerTrace(String strStep) {
/*
* MESSAGE PROCESSOR CODE GOES HERE
*/
return "TRACE : " + strStep;
}
#Processor(name="company-logger-debug", friendlyName="DEBUG")
public String companyLoggerDebug(String strStep) {
/*
* MESSAGE PROCESSOR CODE GOES HERE
*/
return "DEBUG : " + strStep;
}
}
I unfortunately do not have enough reputation to provide you with an image of the config I do not need.
Any help or suggestions are welcomed.
With Anypoint Devkit 3.6, the "config-ref" attribute on elements became required, so you NEED to create a global element definition for your connector. As far as I know, there isn't a way to get around this at this point.
HTH
The config element name can be changed using the configElementName attribute in #Connector or #Module
#Module(name="company-logger", friendlyName="companyLogger", configElementName="xx", schemaVersion="1.0.0-SNAPSHOT")
If you do not set this attribute, it will default to "config". May be if you provide an empty string or null then it will not show up. I haven't tried it before though.

Get pluginId stored in the IPreferenceNode in Eclipse

I am developing a plugin, in my plugin I want to get another plugin ID. I use the following code:
PreferenceManager pm = PlatformUI.getWorkbench( ).getPreferenceManager();
List<IPreferenceNode> list = pm.getElements(PreferenceManager.PRE_ORDER);
String pluginid;
// restoreDefValues("org.eclipse.ant.ui");
for(IPreferenceNode node : list){
the code to find the node related to the plugin;
}
When I debug the program, I can clearly see that in variable node(IPreferenceNode), it has the value of the pluginId. However, I check the document of IPreferenceNode, it seems that the neither IPreferenceNode nor the class PreferenceNode, provide a method to return the value of pluginId. I tried node.toString() as well, couldn't get the pluginId. So what should I do? Is there any other ways to get a plugin ID from another plugin?
Preference nodes created using the org.eclipse.ui.preferencePages extension point will actually be instances of org.eclipse.ui.internal.dialogs.WorkbenchPreferenceNode. The super class of this (WorkbenchPreferenceExtensionNode) contains the plugin id.
These classes are internal so you should not try to use them directly. However they implement org.eclipse.ui.IPluginContribution which can be used and has a getPluginId() method.
So something like:
if (node instanceof IPluginContribution) {
pluginId = ((IPluginContribution)node).getPluginId();
}
should work.

JAX-RS return a Map<String,String>

I want to retrieve a Map from a using JAX-RS (text/xml)
#GET
public Map<String,String> getMap(){
}
but I am getting the error below:
0000001e FlushResultHa E org.apache.wink.server.internal.handlers.FlushResultHandler handleResponse The system could not find a javax.ws.rs.ext.MessageBodyWriter or a DataSourceProvider class for the java.util.HashMap type and application/x-ms-application mediaType. Ensure that a javax.ws.rs.ext.MessageBodyWriter exists in the JAX-RS application for the type and media type specified.
[10:43:52:885 IST 07/02/12] 0000001e RequestProces I org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (500 - Internal Server Error) with message 'null' while processing GET request sent to http://localhost:9080/jaxrs_module/echo/upload/getSiteNames
The solution I choose is to wrap a Map and use it for the return param.
#XmlRootElement
public class JaxrsMapWrapper {
private Map<String,String> map;
public JaxrsMapWrapper(){
}
public void setMap(Map<String,String> map) {
this.map = map;
}
public Map<String,String> getMap() {
return map;
}
}
and the method signature will go like this
#GET
public JaxrsMapWrapper getMap()
Your problem is that the default serialization strategy (use JAXB) means that you can't serialize that map directly. There are two main ways to deal with this.
Write an XmlAdaptor
There are a number of questions on this on SO but the nicest explanation I've seen so far is on the CXF users mailing list from a few years ago. The one tricky bit (since you don't want an extra wrapper element) is that once you've got yourself a type adaptor, you've got to install it using a package-level annotation (on the right package, which might take some effort to figure out). Those are relatively exotic.
Write a custom MessageBodyWriter
It might well be easier to write your own code to do the serialization. To do this, you implement javax.ws.rs.ext.MessageBodyWriter and tag it with #Provider (assuming that you are using an engine that uses that to manage registration; not all do for complex reasons that don't matter too much here). This will let you produce exactly the document you want from any arbitrary type at a cost of more complexity when writing (but at least you won't be having complex JAXB problems). There are many ways to actually generate XML, with which ones to choose between depending on the data to be serialized
Note that if you were streaming the data out rather than assembling everything in memory, you'd have to implement this interface.
Using CXF 2.4.2, it supports returning Map from the api. I use jackson-jaxrs 1.9.6 for serialization.
#Path("participation")
#Consumes({"application/json"})
#Produces({"application/json"})
public interface SurveyParticipationApi {
#GET
#Path("appParameters")
Map<String,String> getAppParameters();
....
}
With CXF 2.7.x use
WebClient.postCollection(Object collection, Class<T> memberClass, Class<T> responseClass)
,like this in your rest client code.
(Map<String, Region>) client.postCollection(regionCodes, String.class,Map.class);
for other collections use WebClient.postAndGetCollection().