I receive successful but EMPTY result from Drools Execution Server when call from Java application using RuleServiceClient although REST call from Postman returns expected result.
My question: what is incorrect in my Java code?
Please find details below.
I created sample rule (if field Message.MyField == 1 then set this field to 400) and I was able to fire it on KIE Execution Server using Postmen:
POST Request to http://SERVER:8080/kie-server-webc/services/rest/server/containers/instances/kie-container:
{
"lookup": "defaultStatelessKieSession",
"commands": [{
"insert": {
"object": {
"Message": {
"myField": 1
}
},
"disconnected": false,
"out-identifier": "Message",
"return-object": true,
"entry-point": "DEFAULT"
}
}, {
"fire-all-rules": {
"max": -1,
"out-identifier": null
}
}]
}
Response (please note "myField": 500):
{
"type": "SUCCESS",
"msg": "Container kie-container successfully called.",
"result": {
"execution-results": {
"results": [
{
"key": "Message",
"value": {
"bnym.test1.Message": {
"myField": 500
}
}
}
],
"facts": [
{
"key": "Message",
"value": {
"org.drools.core.common.DefaultFactHandle": {
"external-form": "0:1:1208207159:1208207159:2:DEFAULT:NON_TRAIT:myProj.test1.Message"
}
}
}
]
}
}
}
My Java client code borrowed from tutorial is:
public class Message{
public Integer myField;
}
. . .
private static String URL = "http://SERVER:8080/kie-server-webc/services/rest/server";
private static final String USER = "user";
private static final String PASSWORD = "pwd";
. . .
public void transform() throws Exception {
Message m = new Message();
m.myField = 1;
KieServicesConfiguration config = KieServicesFactory.newRestConfiguration(URL, USER, PASSWORD);
config.setMarshallingFormat(MarshallingFormat.JSON);
kieServicesClient = KieServicesFactory.newKieServicesClient(config);
RuleServicesClient rulesClient = kieServicesClient.getServicesClient(RuleServicesClient.class);
KieCommands commandsFactory = KieServices.Factory.get().getCommands();
Command<?> insert = commandsFactory.newInsert(m);
Command<?> fireAllRules = commandsFactory.newFireAllRules();
Command<?> batchCommand = commandsFactory.newBatchExecution(Arrays.asList(insert, fireAllRules)); //0
ServiceResponse<String> executeResponse = rulesClient.executeCommands("kie-container", batchCommand);
if(executeResponse.getType() == ResponseType.SUCCESS) {
System.out.println("Commands executed with success! Response: ");
System.out.println(executeResponse.getResult());
}
}
Result:
Commands executed with success! Response:
{
"results" : [ ],
"facts" : [ ]
}
My question: what is incorrect in my Java code so result is empty?
Thank you
Try with the following command:
Message m = new Message();
m.myField = 1;
Command<?> insert = commandsFactory.newInsert(m, "Message",true, "DEFAULT");
This is equivalent to the json request you are using:
"insert": {
"object": {
"Message": {
"myField": 1
}
},
"out-identifier": "Message",
"return-object": true,
"entry-point": "DEFAULT"
}
I suggest turn on debug logging for the client to see the sent request and compare it to your other one to find what's not correct with it.
For whatever logging system your app is using, set this logger to DEBUG (or just set everything to DEBUG if that's better for you):
org.kie.server.client.impl.AbstractKieServicesClientImpl
If you want to keep using stateless session, then you should create a stateless session, follow the example code:
public static StatelessKieSession getStatelessKieSession() {
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
StatelessKieSession kSession = kContainer.newStatelessKieSession("defaultStatelessKieSession");
return kSession;
}
public void runRulesStateless(List<Object> objects) {
getStatelessKieSession().execute(objects);
}
public static void main(String[] args) {
runRulesStateless(Arrays.asList(new Object[] { new Message() }));
}
If you want a differente stateless session, you can configure in a kmodule.xml
Related
I am using https://pub.dev/packages/flutter_web3 in order to connect my Flutter Web App with Solidity smart contract.
I could successfully connect with Metamask with my account
Future<bool> connectProvider() async {
debugPrint("connectProvider");
if (Ethereum.isSupported) {
final accs = await ethereum!.requestAccount();
if (accs.isNotEmpty) {
debugPrint("accounts not empty");
currentAddress = accs.first;
currentChain = await ethereum!.getChainId();
debugPrint("current chain is $currentChain");
debugPrint("current address $currentAddress");
} else {
debugPrint("accounts are empty");
}
} else {
debugPrint("not supported");
}
return false;
}
I could also get data about my balance:
Future<bool> getBalance() async {
final rpcProvider = JsonRpcProvider('https://bsc-dataseed1.defibit.io/');
final lastBlock = await rpcProvider.getLastestBlock();
final balance = await rpcProvider.getBalance(address);
BigInt balanceFormatted = BigInt.from(10).pow(18);
double b = balance / balanceFormatted;
debugPrint("balance = $b");
return false;
}
The problem appears when I am trying to fetch information from smart contract:
final contract = Contract(smartContactAddress, jsonAbi, rpcProvider);
final something = await contract.call<String>('address');
debugPrint("something = $something");
Where method in abi file is:
{
"inputs": [],
"name": "pick_address",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "pure",
"type": "function"
}
Here's the output:
js_util_patch.dart:80 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'apply')
at Object.callMethod$ [as callMethod] (js_util_patch.dart:80:66)
at ethers.Contract.__._call (contract.dart:193:43)
at _call.next (<anonymous>)
.....
Which is referring to this method:
#patch
T callMethod<T>(Object o, String method, List<Object?> args) {
assertInteropArgs(args);
return JS<dynamic>('Object|Null', '#[#].apply(#, #)', o, method, o, args);
}
Maybe anyone has a workaround for that or does know if I pass something in the wrong way.
EDIT
I have found some workaround to GET uint256 values from smart contract, but still cannot retrieve the value with type address. Post is edited. What is the equivalent type of address in flutter?
I am trying to implement security to my app and used Identity and JWT bearer token to authenticate but I always get the invalid token response in my swagger. Tried many solutions but I still get the same response.
[HttpPost("[action]")]
public async Task<IActionResult> Login(LoginBindingModel login)
{
IActionResult actionResult;
var user = await userManager.FindByEmailAsync(login.Email);
if (user == null)
{
actionResult = NotFound(new {errors = new[] {$"User with email {login.Email} is not found."}});
}
else if (await userManager.CheckPasswordAsync(user, login.Password))
{
if(!user.EmailConfirmed)
{
actionResult = BadRequest(new { errors = new[] { $"Email not confirmed." } });
}
else
{
var token = GenerateTokenAsync(user);
actionResult = Ok(token);
}
}
else
{
actionResult = BadRequest(new { errors = new[] { $"Password is not valid." } });
}
return actionResult;
}
private string GenerateTokenAsync(IdentityUser user)
{
IList<Claim> userClaims = new List<Claim>
{
new Claim("UserName", user.UserName),
new Claim("Email", user.Email)
};
var x = jwtOptions.SecurityKey;
return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
claims: userClaims,
expires: DateTime.UtcNow.AddMonths(1),
signingCredentials: new SigningCredentials(jwtOptions.SecurityKey, SecurityAlgorithms.HmacSha256)));
}
Program.cs
using ASPNetCoreMasterAPIAssignment2.Filters;
using DomainModels;
using Infrastructure.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Repositories;
using Services;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(x =>
{
x.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please insert JWT token with bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
x.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
builder.Services.AddScoped<IItemService, ItemService>();
builder.Services.AddScoped<IItemRepository, ItemRepository>();
builder.Services.AddDbContext<ItemDbContext>(opt =>
{
opt.UseSqlServer(builder.Configuration.GetConnectionString("default"));
});
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ItemDbContext>()
.AddDefaultTokenProviders();
SecurityKey key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration["jwt:secret"]));
builder.Services.Configure<JWTOptions>(_ => _.SecurityKey = key);
builder.Services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = "Bearer";
opt.DefaultChallengeScheme = "Bearer";
opt.DefaultScheme = "Bearer";
})
.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKey = key
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
}
else
{
app.UseExceptionHandler("/error");
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"default": "Server=localhost;Database=EfCoreDb;Trusted_Connection=True;"
},
"jwt": {
"secret": "5a927360-790b-4ba5-bae1-09aa98364090"
}
}
when i add the [Authorized] attribute to a controller, i get the following error
invalid_token can be caused by a lot of cases.
Try to view the root cause by handling the OnChallenge method (in particular the content of context.AuthenticateFailure):
return builder.AddJwtBearer(options =>
{
options.Authority = issuer;
options.Audience = audience;
options.TokenValidationParameters = new TokenValidationParameters()
{
ClockSkew = new System.TimeSpan(0, 0, 30)
};
options.Events = new JwtBearerEvents()
{
OnChallenge = context =>
{
context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Response.ContentType = "application/json";
// Ensure we always have an error and error description.
if (string.IsNullOrEmpty(context.Error))
context.Error = "invalid_token";
if (string.IsNullOrEmpty(context.ErrorDescription))
context.ErrorDescription = "This request requires a valid JWT access token to be provided";
// Add some extra context for expired tokens.
if (context.AuthenticateFailure != null && context.AuthenticateFailure.GetType() == typeof(SecurityTokenExpiredException))
{
var authenticationException = context.AuthenticateFailure as SecurityTokenExpiredException;
context.Response.Headers.Add("x-token-expired", authenticationException.Expires.ToString("o"));
context.ErrorDescription = $"The token expired on {authenticationException.Expires.ToString("o")}";
}
return context.Response.WriteAsync(JsonSerializer.Serialize(new
{
error = context.Error,
error_description = context.ErrorDescription
}));
}
};
});
source: https://sandrino.dev/blog/aspnet-core-5-jwt-authorization#configuring-jwt-bearer-authentication
In my case, it was resolved by updating some nuget, specially Microsoft.IdentityModel.Tokens : https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1792
We have an endpoint that masks card numbers. I want to mock this endpoint with Wiremock. Since it will work for more than one card number, it is not possible for me to prepare a separate mock file for each card. How can I mock this endpoint using a single file? The request sent and the response returned to this request are as follows:
Request:
{
"cardNumber": "1234561234561234"
}
Response:
{
"maskedCard": "123456******1234"
}
I prepared a Wiremock file that works only for 1 card number:
{
"request": {
"method": "POST",
"url": "/maskedCard",
"bodyPatterns": [
{
"matchesJsonPath": "[?(#.cardNumber == '1234561234561234')]"
}
]
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"maskedCard": "123456******1234"
}
}
}
How can I make this work for all incoming card numbers?
If the cardNumber attached to the request does not matter, then I would just exclude the bodyPatterns for matching.
{
"request": {
"method": "POST",
"url": "/maskedCard"
}
If you then need to take whatever the cardNumber in the request body is and mask it, I'm not aware of any out of the box solution to do that for you. Instead you'll have to write a Response Transformer to change the unmasked cardNumber to the masked CardNumber. Something like...
public class MaskCardNumbers extends ResponseTransformer {
public String getName() {
return "MaskCardNumbers";
}
#Override
public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
Pattern pattern = Pattern.compile("/maskedCard");
Matcher matcher = pattern.matcher(request.getUrl());
if(matcher.matches()) {
// Logic to extract cardNumber - my library of choice is JsonSimple
JSONObject responseBody = (JSONObject) parser.parse(response.getBodyAsString());
String cardNumber = responseBody.get("cardNumber").toString();
// Logic to replace cardNumber
String maskedCard = "";
for (int i = 0; i < cardNumber.length(); i++) {
if (i < 6 || i > 11) {
maskedCard += cardNumber.charAt(i);
} else {
maskedCard += "*";
}
}
// Create response JSON Object
JSONObject responseObject = new JSONObject();
responseObject.put("cardNumber", maskedCard)
// Return responseObject
return Response.Builder.like(response).but().body(responseBody.toJSONString()).build();
}
}
}
You'd then need to make sure that WireMock launches with the transformer attached. Finally, your mapping would look like:
{
"request": {
"method": "POST",
"url": "/maskedCard"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"maskedCard": "123456******1234"
}
}
}
You might need to play around with some specifics, but I think that should get the job done, or at least get you most of the way there.
I am trying to setup a BaseController to handle culture as part of the url (based on ASP.NET MVC 5 Internationalization). My implementation works properly as long as I disable my Areas' registration.
When One of my Area is registered, if I try to input a wrong/not supported culture (http://localhost:52639/zz/), I experience a 404 error with a request URL: http://localhost:52639/fr/Test/Post.
I have checked my routes are properly registered.
If I do the same while disabling the Areas registration, the base controller and routing behave correctly if I type the following URL: http://localhost:52639/zz/ I am redirected to http://localhost:52639/fr/ (default culture).
Those are my routes:
public static void RegisterRoutes(RouteCollection routes)
{
var namespaces = new[]{typeof(PostController).Namespace};
routes.IgnoreRoute("favicon.ico");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("PostToHack", "{culture}/Post/{idAndSlug}", new { culture = "", Controller = "Post", Action = "Show" }, namespaces);
routes.MapRoute("Post", "{culture}/Post/{id}-{slug}", new { culture = "", Controller = "Post", Action = "Show" }, namespaces);
routes.MapRoute("TagToHack", "{culture}/Tag/{idAndSlug}", new { culture = "", Controller = "Post", Action = "Tag" }, namespaces);
routes.MapRoute("Tag", "{culture}/Tag/{id}-{slug}", new { culture = "", Controller = "Post", Action = "Tag" }, namespaces);
routes.MapRoute("Logout", "{culture}/Logout", new { culture = "", Controller = "Authentication", Action = "Logout" }, namespaces);
routes.MapRoute("Login", "{culture}/Login", new { culture = "", Controller = "Authentication", Action = "Login" }, namespaces);
//Error routes
routes.MapRoute("Error404", "{culture}/errors/404", new { culture = "", Controller = "Errors", Action = "NotFound" }, namespaces);
routes.MapRoute("Error500", "{culture}/errors/500", new { culture = "", Controller = "Errors", Action = "Error" }, namespaces);
routes.MapRoute("Home", "{culture}", new { culture = "", Controller = "Post", Action = "Index"},namespaces);
//Never to be called by user which is why it comes after MapRoute Home so it is always overwritten by it
routes.MapRoute("Sidebar", "{culture}", new { culture = "", Controller = "Layout", Action = "Sidebar"},namespaces);//This is a "child-only" controller
routes.MapRoute("NavigationBar", "{culture}", new { culture = "", Controller = "Layout", Action = "NavigationBar"},namespaces);//This is a "child-only" controller
Area Route
public override void RegisterArea(AreaRegistrationContext context)
{
var namespaces = new[] { typeof(PostsController).Namespace };
context.MapRoute(
"admin_default",
"{culture}/admin/{controller}/{action}/{id}",
new { culture = "", action = "Index", id = UrlParameter.Optional }, namespaces
);
}
Base Controller:
public class BaseController : Controller
{
protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
{
var cultureName = RouteData.Values["culture"] as string;
// Attempt to read the culture cookie from Request
if (cultureName == null)
cultureName = (Request.UserLanguages != null) && (Request.UserLanguages.Length > 0)
? Request.UserLanguages[0]
: null; // obtain it from HTTP header AcceptLanguages
// Validate culture name
cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe
if (RouteData.Values["culture"] as string != cultureName)
{
// Force a valid culture in the URL
RouteData.Values["culture"] = cultureName.ToLowerInvariant(); // lower case too
// Redirect user
Response.RedirectToRoute(RouteData.Values);
}
// Modify current thread's cultures
Thread.CurrentThread.CurrentCulture = new CultureInfo(cultureName);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
return base.BeginExecuteCore(callback, state);
}
}
After some more digging I have found a solution that work for me. My issue was coming from the order in which I was registering my routes. I was registering my Area's routes first:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
I inverted the order and made sure that I was only registering my Area's routes after:
protected void Application_Start()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
AreaRegistration.RegisterAllAreas();
}
I have developed an OpenUI5 app ant it works fine!
But every time that I invoke the routing I have this message:
2015-07-15 16:15:45 hash format error! The current Hash: /line/01 -
log
error
onHashChange
detectHashChange
jQuery.event.dispatch
jQuery.event.add.elemData.handle
It is not a blocking problem but it is annoying because it dirty and fills thi debug console..!
To call the router I write:
this.router = sap.ui.core.UIComponent.getRouterFor(this);
this.router.navTo("activities", {
"id_line": '01'
});
and this is the routing file:
routes: [
...
{
pattern: "line/{id_line}",
name: "activities",
target: ["master_search", "detail_activities"]
},
...
],
targets: {
master_search: {
viewName: "UniversalMenu",
viewLevel: 1,
controlAggregation: "masterPages"
}
,
detail_activities: {
viewName: "DetailActivity",
viewLevel: 4
}
...
}
Edit: this is a snippet where I use jQuery.sap.history
jQuery.sap.require("jquery.sap.history");
jQuery.sap.require("sap.m.InstanceManager");
sap.ui.controller("ui5bp.view.App", {
getDefaultPage : function () {
return "Menu";
},
onInit : function () {
var historyDefaultHandler = function (navType) {
if (navType === jQuery.sap.history.NavType.Back) {
//this.navBack(this.getDefaultPage());
} else {
this.navTo(this.getDefaultPage(), null, false);
}
};
var historyPageHandler = function (params, navType) {
if (!params || !params.id) {
jQuery.sap.log.error("invalid parameter: " + params);
} else {
if (navType === jQuery.sap.history.NavType.Back) {
this.navBack(params.id);
} else {
this.navTo(params.id, params.data, false);
}
}
};
jQuery.sap.history({
routes: [{
// This handler is executed when you navigate back to the history state on the path "page"
path : "page",
handler : jQuery.proxy(historyPageHandler, this)
}],
// The default handler is executed when you navigate back to the history state with an empty hash
defaultHandler: jQuery.proxy(historyDefaultHandler, this)
});
// subscribe to event bus
var bus = sap.ui.getCore().getEventBus();
bus.subscribe("nav", "to", this.navHandler, this);
bus.subscribe("nav", "back", this.navHandler, this);
bus.subscribe("nav", "virtual", this.navHandler, this);
},
navHandler: function (channelId, eventId, data) {
if (eventId === "to") {
this.navTo(data.id, data.data, true);
} else if (eventId === "back") {
//**************************************************
// if(data && data.id){
// this.navBack(data.id);
// } else {
// jQuery.sap.history.back();
// }
var app = this.getView().app;
if(data.type==="master"){
app.backMaster();
}else if(data.type==="detail"){
app.backDetail();
}else{alert("back to master o detail?");};
//**************************************************
} else if (eventId === "virtual") {
jQuery.sap.history.addVirtualHistory();
} else {
jQuery.sap.log.error("'nav' event cannot be processed. There's no handler registered for event with id: " + eventId);
}
},
navTo : function (id, data, writeHistory) {
if (id === undefined) {
// invalid parameter
jQuery.sap.log.error("navTo failed due to missing id");
} else {
var app = this.getView().app;
// navigate in the app control
app.to(id, "slide", data);
}
},
/*
navBack : function (id) {
if (!id) {
// invalid parameter
jQuery.sap.log.error("navBack - parameters id must be given");
} else {
// close open popovers
if (sap.m.InstanceManager.hasOpenPopover()) {
sap.m.InstanceManager.closeAllPopovers();
}
// close open dialogs
if (sap.m.InstanceManager.hasOpenDialog()) {
sap.m.InstanceManager.closeAllDialogs();
jQuery.sap.log.info("navBack - closed dialog(s)");
}
// ... and navigate back
var app = this.getView().app;
var currentId = (app.getCurrentPage()) ? app.getCurrentPage().getId() : null;
if (currentId !== id) {
app.backToPage(id);
jQuery.sap.log.info("navBack - back to page: " + id);
}
}
}
*/
});
In Component.js I had 2 rows where I set up custom myNavBack and myNavToWithoutHash functions:
// 3a. monkey patch the router
var oRouter = this.getRouter();
oRouter.myNavBack = ui5bp.MyRouter.myNavBack; //to comment
oRouter.myNavToWithoutHash = ui5bp.MyRouter.myNavToWithoutHash; //to comment
I have started from an example of app skeleton for my app and then I have implemented the routing with the logic suggested from the framework.
This coexistence of two different methods to navigate produced the error in console. Tahnkyou #TimGerlach
After the comment of the two rows errors have vanished.