Business Central: custome api getById does not work - api

My problem is that I can't call my custom API for a specific itemId , however when I call the normal API it works as expected.
Currently I can successfully call the normal API for a specific item like so:
myBcServer:Port/bc/api/v1.0/companies(666e508d-9abb-ea11-bbac-000d3a492c82)/items(a5dc88b9-9abb-ea11-bbac-000d3a492c82)
But when I try to do the same with my custom API:
myBcServer:Port/bc/api/contoso/app1/v2.0/companies(666e508d-9abb-ea11-bbac-000d3a492c82)/items(a5dc88b9-9abb-ea11-bbac-000d3a492c82)
I get an error:
"error": {
"code": "BadRequest_NotFound",
"message": "Bad Request - Error in query syntax. CorrelationId: f7bc0b59-45ac-4293-9f94-108d6436272c."
}
I can successfully call both API versions at /items. Where I get a list of all the items.
The custom API page that I made looks like this:
page 50101 ItemsCustomApi
{
PageType = API;
Caption = 'API: Items';
APIPublisher = 'contoso';
APIGroup = 'app1';
APIVersion = 'v2.0';
EntityName = 'item';
EntitySetName = 'items';
SourceTable = Item;
DelayedInsert = true;
Editable = false;
layout
{
area(Content)
{
field(id; SystemId)
{
Caption = 'ID';
}
field("No"; "No.")
{
Caption = 'No.';
}
field("UnitPrice"; "Unit Price")
{
Caption = 'Unit Price';
}
field("VendorNo"; "Vendor No.")
{
Caption = 'Vendor No.';
}
}
}
}
I suspect I need to add a property to the page like CanGetById = true. However, I don't know.
BC version run in docker: mcr.microsoft.com/businesscentral/sandbox:dk
Docker version: V19.03.12

You need to set the page property ODataKeyFields to be able to select by id:
ODataKeyFields = SystemId;

Related

Logging from inside a Customized Bad Request Response

I'm capturing model validation errors using the code below and outputting a custom 400 response from the CustomProblemDetails object which works great. My question is, I want to log from within the CustomProblemDetails object but don't see how I can use DI. I've passed in context which gives me access to the services but is this the way to go? If so it appears I can only get access to the ILoggerFactory how do I log using ILoggerFactory?
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new CustomProblemDetails(context)
{
Type = "https://contoso.com/probs/modelvalidation",
Title = "One or more model validation errors occurred.",
Status = StatusCodes.Status400BadRequest,
Detail = "See the errors property for details.",
Instance = context.HttpContext.Request.Path
};
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json" }
};
};
});
For logging in InvalidModelStateResponseFactory, you could try code like:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Logger From Invalid Model");
var problemDetails = new CustomProblemDetails(context)
{
Type = "https://contoso.com/probs/modelvalidation",
Title = "One or more model validation errors occurred.",
Status = StatusCodes.Status400BadRequest,
Detail = "See the errors property for details.",
Instance = context.HttpContext.Request.Path
};
logger.LogError(JsonConvert.SerializeObject(problemDetails));
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json" }
};
};
});

Interactive button doesn't work properly when using pub/sub

I'm writing a Hangouts Chat bot in C# that uses pub/sub so I can host the bot on our side of a firewall. Everything seems to work well except interactive buttons within cards. If I create a button with a specific action method name, the bot does receive the CARD_CLICKED message with the appropriate action method name. However, it doesn't seem like the card in the Hangouts Chat app knows a response was sent because the bot ends up getting the CARD_CLICKED message three times before the Hangouts Chat app finally says "Unable to contact Bot. Try again later". I've been using the Google.Apis.HangoutsChat.v1 and Google.Cloud.PubSub.V1 packages from NuGet for the bot.
This is speculation, but it seems like the issue might be that interactive buttons don't work properly through pub/sub. Any help would be appreciated.
Here is a snippet of the code I have:
SubscriptionName subscriptionName = new SubscriptionName(PROJECT_ID, SUBSCRIPTION_ID);
SubscriberServiceApiClient client = SubscriberServiceApiClient.Create();
GoogleCredential credential = GoogleCredential.FromFile(CREDENTIALS_PATH_ENV_PROPERTY).CreateScoped(HANGOUTS_CHAT_API_SCOPE);
HangoutsChatService chatService = new HangoutsChatService(new BaseClientService.Initializer
{
ApplicationName = "My Bot",
HttpClientInitializer = credential
});
while (true)
{
PullResponse response = client.Pull(subscriptionName, false, 3, CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(TimeSpan.FromSeconds(90)))));
if ((response.ReceivedMessages == null) || (response.ReceivedMessages.Count == 0))
Console.WriteLine("Pulled no messages.");
else
{
foreach (ReceivedMessage message in response.ReceivedMessages)
{
try
{
byte[] jsonBytes = message.Message.Data.ToByteArray();
JObject json = JObject.Parse(Encoding.UTF8.GetString(jsonBytes));
string messageType = (string)json["type"];
switch (messageType)
{
case "MESSAGE":
{
// Get text
string messageText = (string)json["message"]["text"];
Console.WriteLine($"[{messageType}] {messageText}");
// Send response
string spaceName = (string)json["space"]["name"];
SpacesResource.MessagesResource.CreateRequest request = chatService.Spaces.Messages.Create(new Message
{
Cards = new[]
{
new Card
{
Header = new CardHeader
{
Title = "Message Received!"
},
Sections = new[]
{
new Section
{
Widgets = new[]
{
new WidgetMarkup
{
Buttons = new[]
{
new Button
{
TextButton = new TextButton
{
Text = "Click Me!",
OnClick = new OnClick
{
Action = new FormAction
{
ActionMethodName = "ClickedAction"
}
}
}
}
}
}
}
}
}
}
},
Thread = new Thread
{
Name = (string)json["message"]["thread"]["name"]
}
}, spaceName);
Message responseMsg = request.Execute();
break;
}
case "CARD_CLICKED":
{
string actionMethodName = (string)json["action"]["actionMethodName"];
Console.WriteLine($"[{messageType}] {actionMethodName} at {((DateTime)json["message"]["createTime"]).ToString()}");
// Send response
string spaceName = (string)json["space"]["name"];
SpacesResource.MessagesResource.CreateRequest request = chatService.Spaces.Messages.Create(new Message
{
ActionResponse = new ActionResponse
{
Type = "UPDATE_MESSAGE"
},
Text = $"You clicked on '{actionMethodName}'.",
Thread = new Thread
{
Name = (string)json["message"]["thread"]["name"]
}
}, spaceName);
Message responseMsg = request.Execute();
break;
}
default:
{
Console.WriteLine($"[{messageType}]");
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error parsing message: {ex}");
}
}
// Acknowledge the message so we don't see it again.
string[] ackIds = new string[response.ReceivedMessages.Count];
for (int i = 0; i < response.ReceivedMessages.Count; ++i)
ackIds[i] = response.ReceivedMessages[i].AckId;
client.Acknowledge(subscriptionName, ackIds);
}
}
Using buttons with Hangouts Chat API requires a custom answer including:
{
'thread': {
'name': thread_id
},
'actionResponse': {
'type': 'UPDATE_MESSAGE'
}
}
I'd recommend using Hangouts Chat API with a bot URL.

Display dynamic data with JSON API Jquery

I've been reading about Javascript for 2 days now and I'm still confused. I'm familiar with HTML and CSS. My goal is to display a dynamic value on my website. The data will be from an API call using JSON.
The URL i'm using displays info like this
{
"error" : 0,
"error_message" : "-",
"amount" : 35.63000
}
What I want to do is take the "amount" variable and display it on my website. So far I have managed to jumble a bunch of code together that barely makes sense to me. I'm probably using all the wrong syntax so I will continue to try and figure this out myself. All this does it displays a static variable in "div1". What is the best way to convert the variable from the API call to show instead.
$.getJSON('https://www.amdoren.com/api/currency.php?api_key=jbqe5fH8AykJTFbnyR7Hf3d2n3KVQR&from=USD&to=THB&amount=1', function(data) {
//data is the JSON string
});
////
var $items = $('#amount')
var obj = {}
$items.each(function() {
obj[this.id] = $(this).val();
})
var json = JSON.stringify(obj);
///
var obj = [ {
"error": 0,
"error_message": "-",
"amount": 35.60000 ///THIS IS OBVIOUSLY STATIC...
}]
var tbl = $("<table/>").attr("id", "mytable");
$("#div1").append(tbl);
for (var i = 0; i < obj.length; i++) {
var tr = "<tr>";
var td3 = "<td>" + obj[i]["amount"] + "</td></tr>";
$("#mytable").append(tr + td3);
}

Worklight JsonStore advanced find

How to use advanced find in worklight JSONStore using QueryPart?
I have tried the following code but its not working properly, I doubt if I am calling advancedFind correctly.
var query = WL.JSONStore.QueryPart().equal('age', 35);
var collectionName = "people";
WL.JSONStore.get(collectionName).find(query).then(function(arrayResults) {
// if data not present , get the data from DB
if (arrayResults.length == 0) {
} else {
}
}).fail(function(errorObject) {
alert("fail" + errorObject);
// handle failure
});
You are calling the find() method. The one you want to call is advancedFind(). Also, advancedFind receives an array of query parts, not just one query part. Your code should look like this:
var queryPart = WL.JSONStore.QueryPart().equal('age', 35);
var collectionName = "people";
WL.JSONStore.get(collectionName).advancedFind([queryPart]).then(function(arrayResults) {
// if data not present , get the data from DB
if (arrayResults.length == 0) {
} else {
}
}).fail(function(errorObject) {
alert("fail" + errorObject);
// handle failure
});
For future reference, here is the API and some examples on how to use the Javascript JSONStore API.

BlackBerry 10 Cascades: How do I load data into a DropDown?

I have managed to load data from a remote Json web service into a QML ListView, but there doesn't seem to be any such thing for the DropDown control.
Does someone have an example or an alternative method to accomplish a DropDown bound to attachedObjects data sources in Cascades?
I have an alternate method for you, Do note that I have used google's web service here for demonstration purpose, you need to replace it with your url & parse response according to that.
import bb.cascades 1.0
Page {
attachedObjects: [
ComponentDefinition {
id: optionControlDefinition
Option {
}
}
]
function getData() {
var request = new XMLHttpRequest()
request.onreadystatechange = function() {
if (request.readyState == 4) {
var response = request.responseText
response = JSON.parse(response)
var addressComponents = response.results[0].address_components
for (var i = 0; i < addressComponents.length; i ++) {
var option = optionControlDefinition.createObject();
option.text = addressComponents[i].long_name
dropDown.add(option)
}
}
}
// I have used goole's web service url, you can replace with your url
request.open("GET", "http://maps.googleapis.com/maps/api/geocode/json?address=" + "Ahmedabad" + "&sensor=false", true)
request.send()
}
Container {
DropDown {
id: dropDown
}
Button {
onClicked: getData()
}
}
}
Hope this helps.