Halt the workflow and return the response to Controller - camunda

Create Order triggers the Rest End point and starts the workflow (Its a TASK ). CreateOrderController
Problem is CreateOrderController is always returning Success.I want to return ResponseEntity.ok("Not Success "); as shown in 2nd image and stop the call of Save Order Database
How to achieve it?
> #RestController
> public class CreateOrderController {
>
> #Autowired
> private RuntimeService runtimeService;
>
>
>
> #PostMapping("/rest/create/order")
> public ResponseEntity<?> createOrder(#RequestBody OrderInfo orderInfo) {
> Map<String, Object> inputData = new HashMap<String, Object>();
> inputData.put("orderInfo", orderInfo);
> ProcessInstance p = runtimeService.startProcessInstanceByKey("hello-world-process",inputData);
>
>
>
> return ResponseEntity.ok("Success");
>
> }

If you are executing the complete process in one transaction, then an exception along the way will create a rollback. However, you usually have a transaction boundary somewhere. You can query the status of the process instance after it has been started via the history endpoint.
The execute method returns void. Let the delegate write process data instead of returning a value. You can find a setVariable method on the delegateExecution you are getting in as a parameter.
You can get the data values in the REST response as shown in this example: https://docs.camunda.org/manual/7.18/reference/rest/process-definition/post-start-process-instance/#starting-a-process-instance-with-variables-in-return
Request:
{
"variables":{
"aVariable" : {
"value" : "aStringValue",
"type": "String"},
"anotherVariable" : {
"value" : true,
"type": "Boolean",
"valueInfo" : {
"transient" : true
}
}
},
"businessKey" : "myBusinessKey",
"withVariablesInReturn": true
}
Response
{
"links": [
{
"method": "GET",
"href": "http://localhost:8080/rest-test/process-instance/aProcInstId",
"rel": "self"
}
],
"id": "aProcInstId",
"definitionId": "aProcessDefinitionId",
"businessKey": "myBusinessKey",
"ended": false,
"suspended": false,
"tenantId": null,
"variables": {
"anotherVariable": {
"type": "Boolean",
"value": true,
"valueInfo": {
"transient" : true
}
},
"aVariable": {
"type": "String",
"value": "aStringValue",
"valueInfo": { }
}
}
}
Alternatively, error handling options in the delegate code / process include:
a) Simply throw an exception in your execute() method, for instance a new RuntimeException() and observe in Cockpit how Camunda creates a technical incident for the process (https://docs.camunda.org/manual/7.18/webapps/cockpit/bpmn/failed-jobs/).
b) You can also use custom exceptions and error codes, e.g. as shown here:
// Defining a custom exception.
public class MyException extends ProcessEngineException {
public MyException(String message, int code) {
super(message, code);
}
}
// Delegation code that throws MyException with a custom error code.
public class MyJavaDelegate implements JavaDelegate {
#Override
public void execute(DelegateExecution execution) {
String myErrorMessage = "My error message.";
int myErrorCode = 22_222;
throw new MyException(myErrorMessage, myErrorCode);
}
}
Src: https://docs.camunda.org/manual/7.18/user-guide/process-engine/delegation-code/#exception-codes
c) If you don't want to create e technical incident but prefer to throw a 'business' error which you can catch in the process model, so the process can take a different (error) path:
public class BookOutGoodsDelegate implements JavaDelegate {
public void execute(DelegateExecution execution) throws Exception {
try {
...
} catch (NotOnStockException ex) {
throw new BpmnError("Business issue");
}
}
}
src: https://docs.camunda.org/manual/7.18/user-guide/process-engine/delegation-code/#throw-bpmn-errors-from-delegation-code

Related

ValidationException on DynamoDB with AWS sdk for Java

I'm trying to put an item and get it back a few statements later, but I'm getting a cryptic error from AWS.
class DataStore<Payload extends EventPayload> {
private final Clock clock;
private final Table table;
private final ObjectMapper objectMapper = new ObjectMapper();
private final Class<Payload> payloadType;
public DynamoDBEventStore(final Clock clock, final Class<Payload> eventClass, final String dataStoreName) {
final AmazonDynamoDB client =
AmazonDynamoDBClientBuilder.standard().build();
final DynamoDB dynamoDB = new DynamoDB(client);
this.table = dynamoDB.getTable(dataStoreName);
this.clock = clock;
this.payloadType = eventClass;
}
public void persist(final UUID eventId, final String aggregateId, final Long version, final Payload eventPayload) {
final Map<String, Object> rawDomainEvent = Map.of(
"EventId", eventId.toString(),
"Timestamp", LocalDateTime.now(clock).toString(),
"AggregateId", eventPayload.getAggregateKey(),
"Version", version,
"Payload", objectMapper.convertValue(eventPayload, Map.class)
);
final Item domainEvent = Item.fromMap(rawDomainEvent);
table.putItem(domainEvent);
}
public void testEvent(final UUID eventId) {
table.getItem("EventId", eventId.toString();
}
}
If I save an item calling to persist, then the Item is saved as expected (see the JSON below from DynamoDB console).
{
"EventId": {
"S": "8a2c1733-887d-42e1-b720-2e87dcf46269"
},
"Timestamp": {
"S": "2021-10-10T04:18:56.465223700"
},
"Payload": {
"M": {
"transactions": {
"L": []
},
"aggregateKey": {
"S": "bc406432-37f1-440f-9157-b0ce8da814c1"
}
}
},
"Version": {
"N": "1"
},
"AggregateId": {
"S": "bc406432-37f1-440f-9157-b0ce8da814c1"
}
}
But when I call to testEvent it fails returning the following error:
"message": "Unable to unmarshall exception response with the unmarshallers provided (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException; Request ID: E6M3NI8CTF05ONUC5G25H53PT7VV4KQNSO5AEMVJF66Q9ASUAAJG; Proxy: null)",
Any thoughts about what could be wrong with my code? FYI, I'm using Java 11, Spring Boot 2.5.4 and AWS SDK 1.12.81:
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>1.12.81</version>
</dependency>
The error message isn't that cryptic! Ignore the unmarshalling part, and you are left with
Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException;
I would guess, your eventId can't be found in the database - but you need to debug from here

AWS API Gateway - Integration Response body mapping

In AWS API Gateway Integration Response body mapping I have following code:
#set($inputRoot = $input.path('$.Item'))
[
#foreach($elem in $inputRoot.Events)
{
"id": $elem.id,
"from" : $elem.from,
"to" : $elem.to,
"spent" : $elem.spent,
#if("$!elem.comment" != "")
"comment": $elem.comment,
#end
"project" : {
"id" : $elem.project.id,
"number" : $elem.project.number,
"name" : $elem.project.name
}
}
#if($foreach.hasNext),#end
#end
]
The data comes from a lambda functions which queries a DynamoDB Table
API gateway returns the data like this:
[
{
"id": 123443214,
"from" : 19:34,
"to" : 22:30,
"spent" : 02:56,
"project" : {
"id" : 4321,
"number" : CIB,
"name" : Backend
}
}
, {
"id": 12341234,
"from" : 19:34,
"to" : 22:30,
"spent" : 02:56,
"project" : {
"id" : 12341234,
"number" : CIB,
"name" : Backend
}
}
]
So it it's already formatted. How do I get APi Gateway to return the response unformatted? So that it's just pure json, without break lines, indentations etc.?
Thanks in advance!
(Small preliminary remark: you are missing some quotes around JSON string values).
It's possible to remove line breaks using ## and indentation using #**#, as follow, but the template will look a bit ugly:
#set($inputRoot = $input.path('$.Item'))##
[##
#foreach($elem in $inputRoot.Events)##
{##
#**#"id":$elem.id,##
#**#"from": $elem.from,##
#**#"to":$elem.to,##
#**#"spent":$elem.spent,##
#if("$!elem.comment" != "")##
#* *#"comment":$elem.comment,##
#end##
#**#"project":{##
#**#"id":$elem.project.id,##
#**#"number":"$elem.project.number",##
#**#"name":"$elem.project.name"##
}##
}##
#if($foreach.hasNext),#end##
#end##
]##
Since the only reason the indentation is in here at first is readability of the template, I would go another direction.
For instance, you can add a post-processing tidy formatter in your View servlet) using org.json:
import org.json.JSONObject;
....
Writer writer = new StringWriter();
getVelocityView().merge(template, context, writer);
String compactJSON = new JSONObject(writer.toString()).toString();
response.getWriter().write(compactJSON);
But this will only work for small JSON files since the response is buffered into memory, so let's keep on searching for a more elegant solution.
The way to go is to pre-process your template, using a custom ResouceLoader.
CompactJSONResourceLoader.java
package my.custom.loader;
import java.io.InputStream;
import java.io.IOException;
import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.resource.Resource;
import org.apache.velocity.runtime.resource.loader.ResourceLoader;
import org.apache.velocity.runtime.resource.loader.ResourceLoaderFactory;
public class CompactJSONResourceLoader extends ResourceLoader
{
protected ResourceLoader innerLoader = null;
#Override
public void init(ExtendedProperties configuration)
{
try
{
String innerLoaderID = configuration.getString("innerLoader") + ".resource.loader";
String innerLoaderClass = rsvc.getConfiguration().getString(innerLoaderID + ".class");
innerLoader = ResourceLoaderFactory.getLoader(rsvc, innerLoaderClass);
ExtendedProperties innerConfiguration = rsvc.getConfiguration().subset(innerLoaderID);
innerLoader.commonInit(rsvc, innerConfiguration);
innerLoader.init(innerConfiguration);
}
catch (Exception e)
{
log.error("could not initialize CompactJSONResourceLoader inner loader", e);
}
}
protected class CompactJSONInputStream extends InputStream
{
InputStream innerStream = null;
boolean insideQuotes = false;
public CompactJSONInputStream(InputStream innerStream)
{
this.innerStream = innerStream;
}
#Override
public int read() throws IOException
{
int ch;
do
{
ch = innerStream.read();
if (insideQuotes)
{
if (ch == '"') insideQuotes = false;
break;
}
else if (!Character.isWhitespace(ch))
{
if (ch == '"') insideQuotes = true;
break;
}
}
while (ch != -1);
return ch;
}
}
#Override
public InputStream getResourceStream(String source) throws ResourceNotFoundException
{
return new CompactJSONInputStream(innerLoader.getResourceStream(source));
}
#Override
public boolean isSourceModified(Resource resource)
{
return innerLoader.isSourceModified(resource);
}
#Override
public long getLastModified(Resource resource)
{
return innerLoader.getLastModified(resource);
}
}
And you would then need to configure Velocity with the following properties:
resource.loader = compact
compact.resource.loader.class = my.custom.loader.CompactJSONResourceLoader
compact.resource.loader.innerLoader = file
(you would replace file with the resource loader you are currently using, of course).

java.io.EOFException: End of input at line 1 column 1 in retrofit 2.0

I don't know where is problem
{
"success": "1",
"wallpapers": [
{
"id": "1",
"image": "http://cyphersol.com/apps/ringtona/uploads/gallery/1477685052.jpg"
},
{
"id": "2",
"image": "http://cyphersol.com/apps/ringtona/uploads/gallery/14776850521.jpg"
},
{
"id": "3",
"image": "http://cyphersol.com/apps/ringtona/uploads/gallery/14776850522.jpg"
},
{
"id": "4",
"image": "http://cyphersol.com/apps/ringtona/uploads/gallery/14776850523.jpg"
},
{
"id": "5",
"image": "http://cyphersol.com/apps/ringtona/uploads/gallery/14776850524.jpg"
}
]
}
I am using retrofit2.0
interface
public interface ApiInterface {
#POST("getImages")
Call<WallPaperResponse> getWallpapers(#Query("id") int apiKey);
}
Api Client
public class ApiClient {
public static final String BASE_URL = "http://cyphersol.com/apps/ringtona/webservice/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Call in to MainActivity
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<WallPaperResponse> call = apiService.getWallpapers(1);
call.enqueue(new Callback<WallPaperResponse>() {
#Override
public void onResponse(Call<WallPaperResponse> call, Response<WallPaperResponse> response) {
int statusCode = response.code();
List<WallpapersItem> wallpaper = response.body().getWallpapers();
for (int i = 0; i < wallpaper.size(); i++) {
Log.e(TAG, wallpaper.get(i).getImage());
}
// recyclerView.setAdapter(new MoviesAdapter(movies, R.layout.list_item_movie, getApplicationContext()));
}
#Override
public void onFailure(Call<WallPaperResponse> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
dependency
// retrofit, gson
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
I think This will help you.
KingController mWebController = KingController.getInstance(this);
String apiToken = "1"; mWebController.getMainCategories(apiToken);
#GET("getImages")
Call getWallpaperLis(#Header("id") String api_token);
Regargs
Rashid Ali
Your web service requires id to be sent as a HEADER
While you have rather sent it as a POST parameter.
Hence, Your web service did not return a valid response
and the error.
Let me know if this works.
public interface ApiInterface {
#GET("getImages")
Call<WallPaperResponse> getWallpapers(#Header("id") int apiKey);
}
P.S This site has solid documentation on retorfit
https://futurestud.io/tutorials/retrofit-2-manage-request-headers-in-okhttp-interceptor

How to extract JSON Array From json in mulesoft

I am getting xml output then i am converting that xml into json object.the format is given below.
{
"SOAP-ENV:Envelope": {
"#xmlns:SOAP-ENV": "http://schemas.xmlsoap.org/soap/envelope/",
"#xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"#xmlns:xsd": "http://www.w3.org/2001/XMLSchema",
"SOAP-ENV:Body": {
"rpc:TestExampleResponse": {
"#xmlns:rpc": "http://Test.com/asi/",
"TestMessage": {
"listOfTESTS": {
"#xmlns:xmlns": "http://www.Test.com/xml/TEST",
"TESTS": [{
"id": "1",
"lastSyncDate": "12/16/2015 07:06:38",
"listOfTESTsyncrealtimeChild": null
}, {
"id": "2",
"lastSyncDate": "12/16/2015 07:06:38",
"listOfTESTsyncrealtimeChild": null
}
]
}
}
}
}
}
}
i want to extract Test array from JSON Output in Mulesoft.I dont know how to extract that array in mulesoft.Thanks in advance
You can use Dataweave (Transform Message component in Anypoint Studio)
(Mule EE)
Take a look to the documentation:
https://docs.mulesoft.com/mule-user-guide/v/3.7/using-dataweave-in-studio
Sample script for your input:
%dw 1.0
%input payload application/json
%output application/json
---
TESTS: payload."SOAP-ENV:Envelope"."SOAP-ENV:Body"."rpc:TestExampleResponse".TestMessage.listOfTESTS.TESTS map ((tEST , indexOfTEST) -> {
id: tEST.id,
lastSyncDate: tEST.lastSyncDate,
listOfTESTsyncrealtimeChild: tEST.listOfTESTsyncrealtimeChild
})
Output when using %output application/json:
{
"TESTS": [
{
"id": "1",
"lastSyncDate": "12/16/2015 07:06:38",
"listOfTESTsyncrealtimeChild": null
},
{
"id": "2",
"lastSyncDate": "12/16/2015 07:06:38",
"listOfTESTsyncrealtimeChild": null
}
]
}
Output when using %output application/java:
{TESTS=[{id=1, lastSyncDate=12/16/2015 07:06:38, listOfTESTsyncrealtimeChild=null}, {id=2, lastSyncDate=12/16/2015 07:06:38, listOfTESTsyncrealtimeChild=null}]}
You can write a custom transformer like below. This transformer uses Jackson (com.fasterxml.jackson) dependency.
The transformer returns a list of strings where each string represents an element of your TESTS array.
public class JsonArrayExtractor extends AbstractTransformer {
private final ObjectMapper mapper = new ObjectMapper();
private final String testsNodeJsonPointer = "/SOAP-ENV:Envelope/SOAP-ENV:Body/rpc:TestExampleResponse/TestMessage/listOfTESTS/TESTS";
public JsonArrayExtractor() {
registerSourceType(DataTypeFactory.STRING);
}
#Override
protected Object doTransform(Object src, String enc) throws TransformerException {
String payload = Objects.toString(src);
JsonNode root;
try {
root = mapper.readTree(payload);
} catch (IOException e) {
throw new TransformerException(this, e);
}
List<String> testsList = new ArrayList<>();
JsonNode testsNode = root.at(JsonPointer.valueOf(testsNodeJsonPointer));
if (testsNode instanceof ArrayNode) {
ArrayNode testsArrayNode = (ArrayNode) testsNode;
for (JsonNode test : testsArrayNode) {
testsList.add(test.toString());
}
}
return testsList;
}
}
And you can use the above transformer in your flow as below.
<custom-transformer class="org.ram.JsonArrayExtractor" doc:name="extractTestsArray"/>

passing parameters to native reduce script of ElasticSearch facet script

I'm trying to use elasticsearch facet script, but when I get to the reduce phase's NativeScriptFactory the passed map parameter is empty.
Here's my query:
"facets": {
"myFacet": {
"script": {
"lang": "native",
"map_script": "MyMap",
"reduce_script": "MyReduce",
"params" : {
"facet" : {}
}
}
}
}
When I use the default reducer, I get this response:
"facets": {
"myFacet": {
"_type": "script",
"facet": [
{
"222790": 7,
"762984": 7
}
]
}
}
My map script looks like this:
public class MyMapScript extends AbstractSearchScript {
private Map<String, Double> _myScores;
public MyMapScript(Map<String, Object> stringObjectMap) {
_myScores = (Map<String, Double>) stringObjectMap.get("facet");
}
#Override
public Object run() {
ScriptDocValues.NumericLong tags = (ScriptDocValues.NumericLong) doc().get("tags");
for (Long t : tags.getValues()){
Double score = 7.0;
_myScores.put(t.toString(), score);
}
return _myScores;
}
}
and the reduce script factory, which gets an empty map as a parameter:
public class MyReduceScriptFactory implements NativeScriptFactory {
#Override
public ExecutableScript newScript(#Nullable Map<String, Object> stringObjectMap) {
return new MyReduceScript(stringObjectMap);
}
}
What do I have to do to get the mapper's output to the reducer?
Apparently this was fixed at latest version, I was using an older one.