stoiveyp / alexa.net.apl Goto Github PK
View Code? Open in Web Editor NEWSmall helper library to allow Alexa.NET skills to work with APL
License: MIT License
Small helper library to allow Alexa.NET skills to work with APL
License: MIT License
I created a simple APL document using Amazon Developer console that looks like this:
{
"type": "APL",
"version": "1.8",
"license": "",
"settings": {},
"theme": "dark",
"import": [],
"resources": [],
"styles": {},
"onMount": [],
"graphics": {},
"commands": {},
"layouts": {},
"mainTemplate": {
"parameters": [
"payload"
],
"items": [
{
"type": "Container",
"height": "100vh",
"width": "100vw",
"items": [
{
"type": "Container",
"width": "100%",
"grow": 1,
"shrink": 1,
"alignItems": "center",
"data": "${payload.categories}",
"numbered": false,
"items": [
{
"type": "Text",
"text": "${data.name}",
"color": "blue"
}
]
}
]
}
]
}
}
In my function code, I'm reading this JSON file and adding to JsonDirective as APLDocument with the code below:
string aplDocumentRaw = File.ReadAllText("./Documents/list-categories.json");
APLDocument aplDocument = JsonConvert.DeserializeObject<APLDocument>(aplDocumentRaw);
var payloadObj = new { categories = categories.Select(category => new { name = category }).ToList() };
response.Response.Directives.Add(new JsonDirective(RenderDocumentDirective.APLDirectiveType)
{
Properties = new Dictionary<string, object>()
{
["document"] = aplDocument,
["datasources"] = payloadObj
}
});
The problem is, even though items in the original JSON document is an array, it's deserialized as a single item. When I log the entire response before I return I can see it looks like this (redacted for brevity):
"mainTemplate": {
"parameters": [
"payload"
],
"items": {
"type": "Container",
"items": {
"type": "Container",
"alignItems": "center",
"data": "${payload.categories}",
"items": {
"type": "Text",
"text": "${data.name}",
"color": "blue"
},
"numbered": false,
"width": "100%",
"grow": "1",
"shrink": 1
},
"height": "100vh",
"width": "100vw"
}
},
Because now items is a single object trying to bind an array causes an error.
Is there a way to deserialize "items" object as an array?
Many thanks for your hard work in putting this library together.
Kind regards,
Volkan
If you go here and try the sample "Long text" then export the code.
when you try to deserialize that json you get an error of
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IList`1
for the property "fontWeight" as it is a single element and not a list element.
I tried to puta JsonObject attribute on the class, but it didn't resolve the issue.
There is an issue in pager, when we set "data" property of pager and assign some datasource and desrialize the json template to RenderDocumentDirective, the data come up as null. Where as in sequence the data come up. I saw the code the "data" property of Pager is different than the "data" property of sequence. I extend the Pager and override the data property and that worked, but can you plz reply if this is a bug or if there is some other technique to use "data" property of pager when deserializing?
Hello,
Thank you for the library. It really makes APL easier for me.
I have an issue on AbsoluteDimension.
AbsoluteDimension is serialized as
"left": {
"Unit": "vw",
"Number": 16
}
but it is unlikely compatible with APL. It should be serialized as
"left": "16vw"
I checked with the following code and it worked.
[JsonConverter(typeof(AbsoluteDimensionExConverter))]
internal class AbsoluteDimensionEx : AbsoluteDimension
{
public AbsoluteDimensionEx(int number, string unit) : base(number, unit)
{
}
}
class AbsoluteDimensionExConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(AbsoluteDimensionEx);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var raw = (string)reader.Value;
var match = Regex.Match(raw, @"^(?<num\d+>)(?<unit>.+)$");
if (match.Success)
{
return new AbsoluteDimensionEx(int.Parse(match.Groups["num"].Value), match.Groups["unit"].Value);
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dim = value as AbsoluteDimensionEx;
if (dim != null)
writer.WriteValue($"{dim.Number}{dim.Unit}");
else
writer.WriteNull();
}
}
BTW, APL likely accepts floating point number for the value.
The documentation around linked documents could be clearer. Please add an example screenshot of the multimodal section of the console so the link to the document is more obvious.
Hey!
I'm trying to elicit slots manually from the user so that I can also present them with APL documents for each turn to allow them to type the information instead of speak it if they wish. I send an ElicitSlot request and display the APL document which works fine. When the user types a value and presses the button I'm using SendEvent which returns me a UserEvent request which contains the information which they have typed. However I'm a little unsure of how to set the slot value to that of the value the user has entered since I don't have a handle on the Slots dictionary in the UserEvent request.. I guess i could use the session attributes and save it for the next turn until i have all the info but this didn't quite feel right.
I guess i either need to let the ElicitSlot request that the value is coming from either speech or the text input or handle the UserEvent and populate the slot value manually but I'm not too sure how to do either of these.. any ideas?
Not sure if this is a Alexa.NET.APL or Alexa.NET question so sorry if it's not directed at the right place.
Hi,
i had problems using ExecuteCommandsDirective with SpeakItem after updating to version 4.0.10 of the APL library. It is also possible that my problem has to do something with a change done by amazon.
I always got an response error when i had just one command added to the ExecuteCommandsDirective. I think the problem is that the Commands enumeration is not serialized as array when there is just one command in it. My Workaround is to add a second SpeakItem command with a not existing componentid property.
Workaround:
{
"type": "Alexa.Presentation.APL.ExecuteCommands",
"token": "WasteTipsDirective",
"commands": [
{
"type": "SpeakItem",
"componentId": "fake"
},
{
"type": "SpeakItem",
"align": "center",
"componentId": "talker",
"highlightMode": "line"
}
]
}
NOT WORKING:
{
"type": "Alexa.Presentation.APL.ExecuteCommands",
"token": "WasteTipsDirective",
"commands": {
"type": "SpeakItem",
"align": "center",
"componentId": "talker",
"highlightMode": "line"
}
}
Shouldn't the Style
property of Components be a string, since according to their documentation it is supposed to refer to a named style, rather than it being the style object directly. Currently I'm resorting to CustomerComponent
with a custom style
property which is a string value.
Is there something I've missed?
One of the data source types that Alexa supports is "list", and there doesn't seem to be a way to support it currently. Here's a json example of one of the list data source types from their carousel sample:
{
"type": "list",
"listId": "lt2Sample",
"totalNumberOfItems": 10,
"hintText": "Try, \"Alexa, select number 1\"",
"listPage": {
"listItems": [
{
"listItemIdentifier": "brie",
"ordinalNumber": 1,
"textContent": {
"primaryText": {
"type": "PlainText",
"text": "Brie"
},
"secondaryText": {
"type": "PlainText",
"text": "Origin: France"
}
},
"image": {
"contentDescription": null,
"smallSourceUrl": null,
"largeSourceUrl": null,
"sources": [
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/md_brie.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/md_brie.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"token": "brie"
},
{
"listItemIdentifier": "gruyere",
"ordinalNumber": 2,
"textContent": {
"primaryText": {
"type": "PlainText",
"text": "Gruyere"
},
"secondaryText": {
"type": "RichText",
"text": "Origin: Switzerland"
}
},
"image": {
"contentDescription": null,
"smallSourceUrl": null,
"largeSourceUrl": null,
"sources": [
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/md_gruyere.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/md_gruyere.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"token": "gruyere"
},
{
"listItemIdentifier": "gorgonzola",
"ordinalNumber": 3,
"textContent": {
"primaryText": {
"type": "PlainText",
"text": "Gorgonzola"
},
"secondaryText": {
"type": "RichText",
"text": "Origin: Italy"
}
},
"image": {
"contentDescription": null,
"smallSourceUrl": null,
"largeSourceUrl": null,
"sources": [
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/md_gorgonzola.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/md_gorgonzola.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"token": "gorgonzola"
},
{
"listItemIdentifier": "brie",
"ordinalNumber": 1,
"textContent": {
"primaryText": {
"type": "PlainText",
"text": "Brie"
},
"secondaryText": {
"type": "PlainText",
"text": "Origin: France"
}
},
"image": {
"contentDescription": null,
"smallSourceUrl": null,
"largeSourceUrl": null,
"sources": [
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/tl_brie.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/tl_brie.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"token": "tl_brie"
},
{
"listItemIdentifier": "gruyere",
"ordinalNumber": 2,
"textContent": {
"primaryText": {
"type": "PlainText",
"text": "Gruyere"
},
"secondaryText": {
"type": "RichText",
"text": "Origin: Switzerland"
}
},
"image": {
"contentDescription": null,
"smallSourceUrl": null,
"largeSourceUrl": null,
"sources": [
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/tl_gruyere.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/tl_gruyere.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"token": "tl_gruyere"
},
{
"listItemIdentifier": "gorgonzola",
"ordinalNumber": 3,
"textContent": {
"primaryText": {
"type": "PlainText",
"text": "Gorgonzola"
},
"secondaryText": {
"type": "RichText",
"text": "Origin: Italy"
}
},
"image": {
"contentDescription": null,
"smallSourceUrl": null,
"largeSourceUrl": null,
"sources": [
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/tl_gorgonzola.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://d2o906d8ln7ui1.cloudfront.net/images/tl_gorgonzola.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"token": "tl_gorgonzola"
}
]
}
I have a question on how to fill a ListDataSource (which I see has been added fairly recently).
I am currently trying to recreate a Layout I did with the alexa authoring tool in .NET .
So the ListDataSource has different properties like "ListId", while the data is supposed to be stored in the "ListPage", however the ListPage property is readonly and doesn't have a constructor that takes a parameter.
Can you describe me how it is supposed to be used like? Sorry if this is a trivial question I am no programming pro.
public class ListPage
{
public ListPage()
ListItems = new List();
[JsonProperty("listItems")]
public List<object> ListItems { get; set; }
}
I guess I could just write my own DataSource type, but I'd rather stick to your classes.
According to the APL documentation, parameters now seem to be simple strings, rather than an object with a name property. Could the json serialization for them be updated to reflect this?
Hi,
Our code just broke with a call to skillRequest.APLInterfaceDetails()
The device had updated to APL v1.2 and there was an internal Newtonsoft error mapping 1.2 to the APLDocumentVersions.
As skills can be deployed and device updates can happen post deployment, can this section be made safer, avoiding a republish of the skill with a new Alexa.NET.APL package?
Hi! I found that it is not convenient to define document layout, resources etc. in C# code. I use the online authoring tool https://developer.amazon.com/alexa/console/ask/displays/ that produces JSON document as a result. How can I use that JSON in RenderDocumentDirective as an APLDocument?
The code example for "Sending a RenderDocument Directive" does not compile because Component
does not have a constructor that takes an enumerable.
A better implementation is to make Component
implement IEnumerable<IComponent>
. This allows the following:
new Container
{
new Text("APL in C#")
{
FontSize = "24dp",
TextAlign = "Center",
},
new Image("https://images.example.com/photos/2143/lights-party-dancing-music.jpg?cs=srgb&dl=cheerful-club-concert-2143.jpg&fm=jpg")
{
Width = 400,
Height = 400,
},
}
The compiler will "automagically" call Add()
for each IComponent
. Please check the working example on sharplab.io.
It will also make possible to enumerate the components in the container.
The sample code under the "Creating a Layout Document" heading has mismatching parenthesis. Took me an embarrassing amount of time to figure out where the parenthesis should be!
Currently, the Sequence
component has the Data
property defined as APLValue<List<object>>
. This will work great when I want to provide the data as a list of objects. But for the scenario where I want to reference a datasource because I'm using a transformer, this won't work because I need to be able to provide a string. Something like this:
{
"type": "Sequence",
"id": "leaderboardSequence",
"width": "100%",
"height": "80%",
"data": "${payload.leaderboardData.properties.listItems}",
"numbered": true,
"items": [
Ideally it would be nice to be able to support both a list of objects and a string so I can define this either way.
We see that this library doesnt covers all the commands, like i wanted to use SetState command but could not found in the library.
I like the strongly-typed way of creating the layout but only being able visualize after deploying makes is very unproductive.
I'm trying to figure out how to use the code sample from the README:
var directive = new JsonDirective(RenderDocumentDirective.APLDirectiveType);
directive.Properties.Add("document",aplDocumentJson);
What is aplDocumentJson
?
How to send both the APL and the data files?
I have a small voice-only skill and would like to upgrade to use APL.
The current function signature is:
[FunctionName("MyFunction")]
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]SkillRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
return req.Request switch
{
LaunchRequest _ => new OkObjectResult(ResponseBuilder.Ask("How can I help?", new Reprompt())),
_ => new OkObjectResult(ResponseBuilder.Empty())
};
}
Do I only need to change the first parameter type to APLSkillRequest
? I tried but didn't work.
To my best understanding, both the code snippets in the README and the sample use AWS.
public StyleValue(IDictionary<string,string> dictionary) { }
May I remove the parameter?
preferably with a few samples
The method Convert() in Alexa.NET.APL.UserEventRequestHandler just throws a NotImplementedException.
APLComponent.When was changed by PR #50 to be a boolean type, but this is not what APL expects. It is a string type that will be evaluated on the device, not on the skill backend.
This is clearly documented here:
https://developer.amazon.com/en-US/docs/alexa/alexa-presentation-language/understand-apl.html#conditional-logic-and-responsive-documents
Using a boolean prevents the device-side evaluation.
Footer does not work, in ResponsiveTemplate.cs, possibly the below property needs have it's jsonproperty set to "footerHintText", and also change the property name to FooterHintText ideally too:
[JsonProperty("hintText", NullValueHandling = NullValueHandling.Ignore)]
public APLValue HintText { get; set; }
to
[JsonProperty("footerHintText", NullValueHandling = NullValueHandling.Ignore)]
public APLValue FooterHintText { get; set; }
Hey man, thank you for doing this project.
Could you add the base component property "opacity" to the APLcomponent class?
Thank you!
I am using the ssmlToSpeech APLTransformer, therefore I do not need (?) to use ResponseBuilder.Ask
or ResponseBuilder.Tell
. So, what kind of response I have to build? I am asking because I do not know why my transformation does not work.
"datasources": {
"article": {
"type": "object",
"properties": {
"title": "...",
"subtitle": "...",
"ssml": "<speak>I have a dream that one day this nation will rise up and live out the true meaning of its creed: “We hold these truths to be self-evident, that all men are created equal.</speak>"
},
"transformers": [
{
"inputPath": "ssml",
"outputName": "speech",
"transformer": "ssmlToSpeech"
},
{
"inputPath": "ssml",
"outputName": "text",
"transformer": "ssmlToText"
}
]
}
}
When I use an empty Ask, Alexa simply waits for a command. If I send an empty Tell, Alexa shuts down the skill.
Migrating to APLSkillRequest means simplifying code with .APLSupported() rather than
request.Context?.System.Device.SupportedInterfaces.ContainsKey(InterfaceName)
However, in our tests Context can be null which is not handled by APLInterface.cs code because there is no ? null check
Assuming there are valid cases where .Context may be null (and System? and Device?) consider revising the code. See also checks in APLInterfaceDetails()
Events (onPress
in my case) appear to expect an array of actions now, rather than a single object. The documentation doesn't appear to reflect this change in all places, however the APL designer does give warnings when you input something other than an array. The onPress documentation of TouchWrapper does refer to command(s) to execute though. The fix in my case seems to be making it an array of a single object, rather than the single object its self.
In the multimodal creator the values of opacity is the type double
Only accepts integers
I've just updated to 4.1.0, and gone from
Top = "{Top}"
to
Top = APLValue.To<APLAbsoluteDimensionValue>("${Top}")
This doesn't work however as the implicit conversion takes the Value
property, which is null meaning nothing is output in JSON.
Since APL seems to allow Top
(and probably others) to use an expression I guess the type should be something like APLValue<APLAbsoluteDimensionValue>
.
In the following example, the data binding expression wouldn't be accepted as the data
property isn't an APLValue
{
"type": "Container",
"data": "${payload.data.issues}"
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.