New-AzEventGridSubscription "The attempt to validate the provided endpoint" failed - azure

I created an endpoint in my C# code at /events path. If the event is of type "Microsoft.EventGrid.SubscriptionValidationEvent", i extract the ValidationCode and return 200 with a json object with validationResponse. I tested this using Postman, and everything appears to work, but when using "New-AzEventGridSubscription", it fails. Any ideas?
Here's the command I used, scrubbed of course.
New-AzEventGridSubscription -ResourceGroup MyResourceGroupName -Endpoint https://requestb.in/19qlscd1/events -EventSubscriptionName EventSubscription1 -TopicName Topic1
Sample Post:
[
{
"id": "531d4a96-d4c7-43d6-8b4c-e1ff9351b869",
"topic": "scrubbed",
"subject": "",
"data": {
"validationCode": "7062AAC0-656D-4C4C-BDD6-0FA673676D95",
"validationUrl": "scrubbed"
},
"eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
"eventTime": "2020-03-30T17:46:15.1827678Z",
"metadataVersion": "1",
"dataVersion": "2"
}
]
Sample Response:
{
"validationResponse": "7062AAC0-656D-4C4C-BDD6-0FA673676D95"
}
Here is my code to handle the Event. I've tested this using Ngrok, and it appears to work.
private void HandleEvent(IApplicationBuilder app)
{
app.Run(async context =>
{
string content;
using (var reader = new StreamReader(context.Request.Body))
{
content = reader.ReadToEnd();
}
var eventGridEvents = JsonConvert.DeserializeObject<EventGridEvent[]>(content);
foreach (var eventGridEvent in eventGridEvents)
{
if (eventGridEvent.EventType == "Microsoft.EventGrid.SubscriptionValidationEvent")
{
var eventDataJson = JsonConvert.SerializeObject(eventGridEvent.Data);
var eventData = JsonConvert.DeserializeObject<SubscriptionValidationEventData>(eventDataJson);
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = eventData.ValidationCode
};
context.Response.StatusCode = 200;
await context.Response.WriteAsync(JsonConvert.SerializeObject(responseData));
}
else
{
var deserialized = JsonConvert.DeserializeObject((string)eventGridEvent.Data);
if (typeof(IEnumerable).IsAssignableFrom(deserialized.GetType()))
{
eventGridEvent.Data = (IEnumerable<object>)deserialized;
}
else
{
eventGridEvent.Data = new List<object>() { deserialized };
}
await HandleMessageListAsync(eventGridEvents.ToList());
}
}
context.Response.StatusCode = 200;
await context.Response.WriteAsync(string.Empty);
});
}
Thank you!

Turns out the issue was my API was returned as "Chunked". Microsoft applied a patch to support "Chunked" responses, and now it works. :)

Related

AWS PUT request met with "Provided key element does not match schema."

(Edited to incorporate comments)
So I apologize in advance for the long question. I don't know how else to ask it.
I'm trying to finish up a full-stack web app using React, Node, and DynamoDB. POST and GET requests are working fine, but I'm stuck on PUT. My mock PUT request works fine, but once I try it from the front end in React, I get the error mentioned in the title. I'll show the back end code first, then the mock update, and then the front end.
import handler from "./libs/handler-lib";
import dynamoDb from "./libs/dynamodb-lib";
export const main = handler(async (event, context) => {
const data = JSON.parse(event.body);
const params = {
TableName: process.env.tableName,
Key: {
userId: event.requestContext.identity.cognitoIdentityId,
activityId: event.pathParameters.activityId
},
UpdateExpression: "SET title = :title, activityType = :activityType, activityRoutine = :activityRoutine, activityComment = :activityComment",
ExpressionAttributeValues: {
":title": data.title || null,
":activityType": data.activityType || null,
// ":activityRoutine": data.activityRoutine == '' ? "None" : data.activityRoutine,
// ":activityComment": data.activityComment == '' ? "None" : data.activityComment
":activityRoutine": data.activityRoutine || null,
":activityComment": data.activityComment || null
},
ReturnValues: "ALL_NEW"
};
await dynamoDb.update(params);
return { status: true };
This mock update event works without issue:
{
"body": "{\"title\":\"test\",\"activityType\":\"testing\",\"activityRoutine\":\"\",\"activityComment\":\"\"}",
"pathParameters": {
"activityId": "long-alphanumeric-id"
},
"requestContext": {
"identity": {
"cognitoIdentityId": "us-east-and-so-on"
}
}
}
But this code, which produces the exact same Javascript object as the mock, is not okay with AWS:
function saveActivity(activity) {
try {
return API.put("activities", `/activities/${id}`, {
body: activity
});
} catch(e) {
console.log("saveActivity error:", e);
}
}
async function handleSubmit(event) {
event.preventDefault();
setIsLoading(true)
try {
await saveActivity({
title: title, activityType: activityType, activityRoutine: activityRoutine, activityComment: activityComment
// "key": {userId: userId, activityId: activityId}
// "pathParameters": {"id": activityId},
// "requestContext": {"identity": {"cognitoIdentityId": userId}}
});
} catch(e) {
console.log(e)
setIsLoading(false)
}
}
If anyone needs to see more of the code, I'm happy to share, but I figured this question is already getting very long. Any code you see commented out has been tried before without success.
I'd also be happy if someone could point me in the right direction as far as the AWS documentation is concerned. I've been going off of a tutorial and modifying it where need be.
Any help is appreciated!

Unable to write item(s) to DynamoDB table utilizing DocumentClient - Nodejs

I'm absolutely brand new to DynamoDb and I'm trying to simply write an object from a NodeJS Lambda. Based on what I've read and researched I should probably be using DocumentClient from the aws-sdk. I also found the following question here regarding issues with DocumentClient, but it doesn't seem to address my specific issue....which I can't really find/pinpoint unfortunately. I've set up a debugger to help with SAM local development, but it appears to be only providing some of the errors.
The code's implementation is shown here.
var params = {
TableName: "March-Madness-Teams",
Item: {
"Id": {"S": randstring.generate(9)},
"School":{"S": team_name},
"Seed": {"S": seed},
"ESPN_Id": {"S": espn_id}
}
}
console.log(JSON.stringify(params))
dynamodb.put(params, (error,data) => {
if (error) {
console.log("Error ", error)
} else {
console.log("Success! ", data)
}
})
Basically I'm scrubbing a website utilizing cheerio library and cherry picking values from the DOM and saving them into the json object shown below.
{
"TableName": "March-Madness-Teams",
"Item": {
"Id": {
"S": "ED311Oi3N"
},
"School": {
"S": "BAYLOR"
},
"Seed": {
"S": "1"
},
"ESPN_Id": {
"S": "239"
}
}
}
When I attempt to push this json object to Dynamo, I get errors says
Error MultipleValidationErrors: There were 2 validation errors:
* MissingRequiredParameter: Missing required key 'TableName' in params
* MissingRequiredParameter: Missing required key 'Item' in params
The above error is all good in well....I assume it didn't like the fact that I had wrapped those to keys in strings, so I removed the quotes and sent the following
{
TableName: "March-Madness-Teams",
Item: {
"Id": {
"S": "ED311Oi3N"
},
"School": {
"S": "BAYLOR"
},
"Seed": {
"S": "1"
},
"ESPN_Id": {
"S": "239"
}
}
}
However, when I do that...I kind of get nothing.
Here is a larger code snippet.
return new Promise((resolve,reject) => {
axios.get('http://www.espn.com/mens-college-basketball/bracketology')
.then(html => {
const dynamodb = new aws.DynamoDB.DocumentClient()
let $ = cheerio.load(html.data)
$('.region').each(async function(index, element){
var preregion = $(element).children('h3,b').text()
var region = preregion.substr(0, preregion.indexOf('(') - 1)
$(element).find('a').each(async function(index2, element2){
var seed = $(element2).siblings('span.rank').text()
if (seed.length > 2){
seed = $(element2).siblings('span.rank').text().substring(0, 2)
}
var espn_id = $(element2).attr('href').split('/').slice(-2)[0]
var team_name = $(element2).text()
var params = {
TableName: "March-Madness-Teams",
Item: {
"Id": randstring.generate(9),
"School":team_name,
"Seed": seed,
"ESPN_Id": espn_id
}
}
console.log(JSON.stringify(params))
// dynamodb.put(params)
// .then(function(data) {
// console.log(`Success`, data)
// })
})
})
})
})
Can you try without the type?
Instead of
"School":{"S": team_name},
for example, use
"School": team_name,
From your code, I can see the mis promise on the dynamodb request. Try to change your lines :
dynamodb.put(params).then(function(data) {
console.log(`Success`, data)
})
to be :
dynamodb.put(params).promise().then(function(data) {
console.log(`Success`, data)
})
you can combine with await too :
await dynamodb.put(params).promise().then(function(data) {
console.log(`Success`, data)
})
exports.lambdaHandler = async (event, context) => {
const html = await axios.get('http://www.espn.com/mens-college-basketball/bracketology')
let $ = cheerio.load(html.data)
const schools = buildCompleteSchoolObject(html, $)
try {
await writeSchoolsToDynamo(schools)
return { statusCode: 200 }
} catch (error) {
return { statusCode: 400, message: error.message }
}
}
const writeSchoolsToDynamo = async (schools) => {
const promises = schools.map(async school => {
await dynamodb.put(school).promise()
})
await Promise.all(promises)
}
const buildCompleteSchoolObject = (html, $) => {
const schools = []
$('.region').each(loopThroughSubRegions(schools, $))
return schools
}
const loopThroughSubRegions = (schools, $) => {
return (index, element) => {
var preregion = $(element).children('h3,b').text()
var region = preregion.substr(0, preregion.indexOf('(') - 1)
$(element).find('a').each(populateSchoolObjects(schools, $))
}
}
const populateSchoolObjects = (schools, $) => {
return (index, element) => {
var seed = $(element).siblings('span.rank').text()
if (seed.length > 2) {
seed = $(element).siblings('span.rank').text().substring(0, 2)
}
var espn_id = $(element).attr('href').split('/').slice(-2)[0]
var team_name = $(element).text()
schools.push({
TableName: "March-Madness-Teams",
Item: {
"Id": randstring.generate(9),
"School": team_name,
"Seed": seed,
"ESPN_Id": espn_id
}
})
}
}
I know this is drastically different from what I started with but I did some more digging and kind of kind of worked to this...I'm not sure if this is the best way, but I seemed to get it to work...Let me know if something should change!
Oh I understand what you want.
Maybe you can see the code above works, but there is one concept you have to improve here about async - await and promise especially on lambda function.
I have some notes here from your code above, maybe can be your consideration to improve your lambda :
Using await for every promise in lambda is not the best approach because we know the lambda time limitation. But sometimes we can do that for other case.
Maybe you can change the dynamodb.put method to be dynamodb.batchWriteItem :
The BatchWriteItem operation puts or deletes multiple items in one or more tables.
Or If you have to use dynamodb.put instead, try to get improve the code to be like so :
const writeSchoolsToDynamo = async (schools) => {
const promises = schools.map(school => {
dynamodb.put(school).promise()
})
return Promise.all(promises)
}

How to get response body using custom middle ware?

problem
How to get response body on invoke next context using custom middle ware ??
After reach to line of await _next.Invoke(context) from debug;
not return json data from action result getusermenu
[HttpGet(Contracts.ApiRoutes.Security.GetUserMenus)]
public IActionResult GetUserMenu(string userId)
{
string strUserMenus = _SecurityService.GetUserMenus(userId);
return Ok(strUserMenus);
}
I need to get response body from action result above
header :
key : Authorization
value : ehhhheeedff .
my code i try it :
public async Task InvokeAsync(HttpContext context, DataContext dataContext)
{
// than you logic to validate token
if (!validKey)
{
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
await context.Response.WriteAsync("Invalid Token");
}
//if valid than next middleware Invoke
else
{
await _next.Invoke(context);
// i need after that get result on last of thread meaning return data of usermenu
}
}
}
public static class TokenExtensions
{
public static IApplicationBuilder UseTokenAuth(this IApplicationBuilder builder)
{
return builder.UseMiddleware<TokenValidateMiddleware>();
}
}
[no return data from access token][1]
https://i.stack.imgur.com/PHUMs.png
if(validtoken)
{
continue display getusermenuaction and show result
}
when valid token it return data like below on browser googlechrom
[
{
"form_name": "FrmAddPrograms",
"title": "Adding Screens",
"url": "",
"permissions": {
"Insert": "True",
"Edit": "True",
"Read": "True",
"Delete": "True",
"Print": "True",
"Excel": "False",
"RecordList": "False"
}
},
but on my app browser return
not valid token
Try to get response body in custom middleware using below code:
public class CustomMiddleware
{
private readonly RequestDelegate next;
public CustomMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (!validKey)
{
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
await context.Response.WriteAsync("Invalid Token");
}
//if valid than next middleware Invoke
else
{
Stream originalBody = context.Response.Body;
try
{
using (var memStream = new MemoryStream())
{
context.Response.Body = memStream;
await next(context);
memStream.Position = 0;
string responseBody = new StreamReader(memStream).ReadToEnd();//get response body here after next.Invoke()
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
}
finally
{
context.Response.Body = originalBody;
}
}
}
}

Azure device twin add new reported property

I am following the azure device twin tutorial https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-csharp-node-twin-how-to-configure
I can make this work to update an existing reported property. What I am not sure about is how can I add a new reported property.
Specifically the code looks snippet looks like:
var currentTelemetryConfig = twin.properties.reported.telemetryConfig;
currentTelemetryConfig.pendingConfig =
twin.properties.desired.telemetryConfig;
currentTelemetryConfig.status = "Pending";
telemetryConfig: currentTelemetryConfig
var patch = {
telemetryConfig: currentTelemetryConfig
};
twin.properties.reported.update(patch, function(err) {
if (err) {
console.log(err);
} else {
console.log('success');
}
}
I can easily understand how this works for the existing property update (in this case the telemetryConfig) but what would it look like if the change I was trying to make was to a entirely new property?
How would it work if I decide at some point the I want a a new desired property called "favourite_colour" : "blue"?
In the azure backend I can add this but how do I dynamically build the var patch variable?
I tried this but it returned an error:
twin.properties.reported.update(twin.properties.desired, function(err) {
if (err) {
console.log('Could not report properties');
} else {
console.log('Success');
}
});
This is what my twin looks like:
"properties": {
"desired": {
"active": true,
"pws": "xyz",
"$metadata": {
"$lastUpdated": "2018-03-27T18:21:57.010036Z",
"$lastUpdatedVersion": 5,
"active": {
"$lastUpdated": "2018-03-27T18:21:57.010036Z",
"$lastUpdatedVersion": 5
},
"pws": {
"$lastUpdated": "2018-03-27T18:21:57.010036Z",
"$lastUpdatedVersion": 5
}
},
"$version": 5
},
"reported": {
"telemetryConfig": 6,
"$metadata": {
"$lastUpdated": "2018-03-27T18:56:05.2445399Z",
"telemetryConfig": {
"$lastUpdated": "2018-03-27T18:56:05.2445399Z"
}
},
"$version": 5
}
}
}
I'm guessing you want to:
Add new reported property on device side (favourite_colour)
I first recommend you to read this sample in GitHub.
Main this sample shows different way to listen for updates in twin on different level. The top most, where any update in device twin will trigger an event; or specific property (favourite_color).
I've edited the sample from the Microsoft document you provided to work with favorite_color.
'use strict';
var Client = require('azure-iot-device').Client;
var Protocol = require('azure-iot-device-mqtt').Mqtt;
var connectionString = '{iot hub connection string}';
var client = Client.fromConnectionString(connectionString, Protocol);
var initConfigChange = function(twin, patch) {
twin.properties.reported.update(patch, function(err) {
if (err) {
console.log('Could not report properties');
} else {
console.log('Reported pending config change: ' + JSON.stringify(patch));
setTimeout(function() {completeConfigChange(twin, patch);}, 30000);
}
});
}
var completeConfigChange = function(twin, patch) {
if (patch.telemetryConfig) {
// Same as Sample
} else if (patch.favourite_colour) {
var currentfavourite_colour = twin.properties.reported.favourite_colour;
currentfavourite_colour.color = currentfavourite_colour.pendingConfig.color;
currentfavourite_colour.status = "Success";
delete currentfavourite_colour.pendingConfig;
var patch = {
favourite_colour: currentfavourite_colour
};
patch.favourite_colour.pendingConfig = null;
}
twin.properties.reported.update(patch, function(err) {
if (err) {
console.error('Error reporting properties: ' + err);
} else {
console.log('Reported completed config change: ' + JSON.stringify(patch));
}
});
};
client.open(function(err) {
if (err) {
console.error('could not open IotHub client');
} else {
client.getTwin(function(err, twin) {
if (err) {
console.error('could not get twin');
} else {
console.log('retrieved device twin');
twin.properties.reported.favourite_colour = {
color: "green"
}
twin.on('properties.desired', function(desiredChange) {
console.log("received change: "+JSON.stringify(desiredChange));
if (desiredChange.telemetryConfig) {
// Same as sample
} else if (desiredChange.favourite_colour) {
var currentfavourite_colour = twin.properties.reported.favourite_colour;
currentfavourite_colour.pendingConfig = twin.properties.desired.favourite_colour;
currentfavourite_colour.status = "Pending Color";
var patch = {
favourite_colour: currentfavourite_colour
};
initConfigChange(twin, patch);
}
});
}
});
}
});
What I did was use a if else statement to check the reported property; as I mentioned there are other ways to do this, check the GitHub code I provided earlier. Once I have the matched reported property I can update the in the same way as the existing sample.

Merging json text into single dto

is there a mechanism in servicestack.text to merge two json strings into a single dto?
The use case is merging complex settings from multiple sources into a single settings file
i.e.
{ "blah": { "params": { "default": "bar", "misc": "0", } } }
and
{ "blah": { "params": { "value": "val", "misc": "1", } } }
becomes
{ "blah": { "params": { "default": "bar", "value": "val", "misc": "1", } } }
Thanks
Be careful of the trailing comma's as it's not valid JSON. But you can use the dynamic API of ServiceStack's JSON Serializer to do this:
var json1 = "{\"blah\":{\"params\":{\"default\":\"bar\", \"misc\": \"0\" } } }";
var json2 = "{\"blah\":{\"params\":{\"value\":\"val\", \"misc\": \"1\" } } }";
var jsonObj = JsonObject.Parse(json1);
var jsonParams =jsonObj.Object("blah").Object("params");
foreach (var entry in JsonObject.Parse(json2).Object("blah").Object("params"))
{
jsonParams[entry.Key] = entry.Value;
}
var to = new { blah = new { #params = jsonParams } };
to.ToJson().Print();
Which will output:
{"blah":{"params":{"default":"bar","misc":"1","value":"val"}}}
Well, if you don't ever going to use JsonArrays, solution above could be written in recursive way:
public static JsonObject Merge(JsonObject #this, JsonObject that) {
foreach (var entry in that) {
var exists = #this.ContainsKey (entry.Key);
if (exists) {
var otherThis = JsonObject.Parse(#this.GetUnescaped (entry.Key));
var otherThat = JsonObject.Parse(that.GetUnescaped (entry.Key));
#this [entry.Key] = Merge (otherThis, otherThat).ToJson ();
} else {
#this [entry.Key] = entry.Value;
}
}
return #this;
}

Resources