Logging from inside a Customized Bad Request Response - asp.net-core

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" }
};
};
});

Related

Discord.JS Announce Command Issue

I'm trying to build an announce command for my bot using a rich embed.
Here is my announce.js file:
const Discord = require('discord.js');
module.exports = {
name: 'announce',
description: 'Send an announcement.',
guildOnly: true,
execute(message, args) {
console.log("embedding")
const embed = new Discord.RichEmbed()
.setTitle("Announcement")
.setDescription("A Staff member has sent an announcement")
.setColor(0x00AE86)
.setFooter("Guardian", "https://raw.githubusercontent.com/phantomdev-github/Resources/master/Discord%20Bots/Guardian/src/avatar.png")
.setThumbnail("https://raw.githubusercontent.com/phantomdev-github/Resources/master/Discord%20Bots/Guardian/src/avatar.png")
.setTimestamp()
.setURL("https://github.com/phantomdev-github/Resources/tree/master/Discord%20Bots/Guardian")
.addBlankField(true)
.addField("Announcement", "message contents here", false))
message.channel.send({ embed });
}
};
I rebuilt it since the post it and took me a while to get back to this post. I'm trying to rebuild all of my messages from my bot into rich embeds. Thus the different code. I've also simplified my fs command and events handler.
indexjs
const fs = require('fs');
const Discord = require('discord.js');
const client = new Discord.Client();
const { token } = require('./token.json');
client.commands = new Discord.Collection();
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
console.log(file,command)
}
fs.readdir('./events/', (err, files) => {
if (err) return console.error(err);
files.forEach(file => {
if(!file.endsWith('.js')) return;
const eventFunction = require(`./events/${file}`);
console.log(eventFunction)
eventFunction.execute(client)
});
});
client.login(token);
message.js
const { prefix } = require('./prefix.json');
module.exports = {
name: 'message',
description: 'client message event.',
execute:function(client) {
client.on('message',message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (!client.commands.has(command)) return;
try {
client.commands.get(command).execute(message, args);
} catch (error) {
console.error(error);
message.reply('there was an error trying to execute that command!');
}
})
}};
Basically I need to know what to put for "message contents here" to make it post the message typed into the #announcements channel.
My question is how to I make it place the announcement message into the .addField section of the richEmbed?
Would it be something along the lines of this?
const Discord = require('discord.js');
module.exports = {
name: 'announce',
description: 'Send an announcement to the specified channel.',
guildOnly: true,
execute(message, args) {
console.log("embedding")
enter code here
if(args.length < 2) return /* error message */;
let channel = message.mentions.channels.first();
if(!channel) return ;
let announcement = args.slice(1).join(" ");
const embed = new Discord.RichEmbed()
.setTitle("Notice!")
.setDescription("Announcememnt from PhantomDEV Staff!")
.setColor(0x00AE86)
.setFooter("Guardian", "https://raw.githubusercontent.com/phantomdev-github/Resources/master/Discord%20Bots/Guardian/src/avatar.png")
.setThumbnail("https://raw.githubusercontent.com/phantomdev-github/Resources/master/Discord%20Bots/Guardian/src/avatar.png")
.setTimestamp()
.setURL("https://github.com/phantomdev-github/Resources/tree/master/Discord%20Bots/Guardian")
.addBlankField(true)
.addField("Message", "", false);
message.channel.send({ embed });
.catch(console.error);
};
At the end of your message event, use this line to call the execution of the command...
command.execute(message, args);
Define your execute function to use the args parameter that you need. Also, Collection.first() is the method you're looking for when declaring channel. Your function should look like so...
execute: function(message, args) {
if (args.length < 2) return /* error message */;
// Careful using this; if just an announcement is provided
// and it mentions a channel, that channel will be used.
let channel = message.mentions.channels.first();
if (!channel) return /* error message */;
let announcement = args.slice(1).join(" ");
channel.send(announcement)
.catch(console.error);
}
There's no need to check that the command is "announce" in the execution function, because it'll only be called if it is.

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.

IBM Worklight 6.0 - Getting "Uncaught type error cannot call method 'initCollection' of undefined"

I'm working with Worklight to build an application which uses a local storage. I declared a function createCollection() in common/js/myApp.js.
However when I run it on the browser simulator, the console JavaScript shows:
Uncaught TypeError: Cannot call method 'initCollection' of undefined.
Any suggestions?
My JavaScript:
function wlCommonInit(){
// Common initialization code goes here
}
// inizializzazione json
window.onload = createCollection;
var isOpen = true;
var menuboxw = $("#menubox").css("width");
$("#contentbox").css("left",menuboxw);
var headerh = $("#header").height();
$("#contentbox").css("top", headerh);
$("#menu_btn").click(function(){menu()});
// apertura/chiusura menu principale
function menu() {
if(isOpen){
$('#contentbox').animate({ left: -5 }, 1);
$("#menubox").css("visibility", "hidden");
isOpen = false;
}
else{
$('#contentbox').animate({ left: menuboxw }, 1);
$("#menubox").css("visibility", "visible");
isOpen = true;
}
}
// creazione collection 'canti' e 'categorie'
function createCollection(){
WL.Logger.debug("Called createCollection");
WL.SimpleDialog.show("Message", "createCollection called", [{text: "Ok"}]);
var collectionCanti = "canti";
var searchFieldsCanti = {titolo: "string", autore: "string", id_categoria: "string", testo: "string"};
var collectionCategorie = "categorie";
var searchFieldsCategorie = {titolo: "string", attiva: "number"};
var success = function(data){
logMessage("Collection created successfully " + data);
};
var failure = function(data){
logMessage("Collection doesn't created " + data);
};
var options = {onSuccess: success, onFailure: failure};
canti = WL.JSONStore.initCollection(collectionCanti, searchFieldsCanti, options);
categorie = WL.JSONStore.initCollection(collectionCategorie, searchFieldsCategorie, options);
}
Do the following:
Remove window.onload = createCollection;
Add createCollection(); inside wlCommonInit()
BTW, that logMessage produces errors. Should probably be changed to WL.Logger.debug (which you are already utilizing in the code...).
Please go over the IBM Worklight Getting Started training material. No skipping.

vline add remote stream for callee fail

I am trying to use your api in a custom app with imported users.
Everything works fine (auth_token, login, call initiation) , but when the callee should get a response and add the remotestream nothing happens. no errors get shown in the console.
I would appreciate if someone takes a look at the code and tells me what i m missing.
I tried the vline demo at https://freeofcinema.vline.com and it worked with the same browsers and conditions between the two computers. In my app it is a http , but i tried it also with https, and the same problem came up. This is some simplified code i used to test the api.
var Streams = [];
var Vsession = null;
var Vline = (function(){
var Client;
var authToken;
var service_id = 'freeofcinema';
var profile = null;
var Person;
var Calls = [];
var onMessage = function(event){
//alert('message');
var msg = event.message, sender = msg.getSender();
console.log(sender.getDisplayName() +'sais: '+ msg.getBody());
console.log(event);
}
var onMediaSession = function(event){
console.log(event);
var mediaSession = event.target;
InitSession(mediaSession);
}
function Call(mediaSession) {
mediaSession.
on('change', alert_info);
}
function alert_info(b){
console.log(b);
}
function InitSession(mediaSession){
mediaSession.on('mediaSession:addRemoteStream', function(event) {
alert('addRemoteStream');
});
mediaSession.on('mediaSession:addLocalStream', function(event) {
alert('addLocalStream');
});
mediaSession.on('mediaSession:removeLocalStream mediaSession:removeRemoteStream', function(event) {
console.log('removedStream');
});
Calls.push(new Call(mediaSession));
}
return {
init : function(){
if(profile){
return;
}
profile = {
"displayName" : //some getusrname function...
};
$.post('vtoken.php',{//get auth token
id : Comm.Voip_user().id
},function(data){
authToken = data;
Client = vline.Client.create({
"serviceId": service_id,
"ui" : true
});
Client.on('recv:im', onMessage , this);
Client.on('add:mediaSession', onMediaSession, this);
Client.on('login', function(e) {
Vsession = e.target;
//alert('loged in');
});
Client.login(service_id, profile, authToken);
});
},
getPerson : function(id){//id of user to call
if(Vsession){
Vsession.getPerson(id).
done(function(person){
Person = person;
Vsession.startMedia(id);
});
}
}
}
}());
Thank you for your response.
I tried with one user from the app, and another from the https://freeofcinema.vline.com, and the same problem occured. Also the call (in pending state) gets terminated after a short while..
When passing ui:true when creating the client, you do not have to handle the media sessions yourself. Just comment the line Client.on('add:mediaSession', onMediaSession, this); and it should just work.

Angular http testing

I have a fairly simple controller that gets a simple json list of objects ...
function ProductGroupsCtrl($scope, $http, $routeParams, sharedService, popupService) {
$scope.list = null;
$scope.selectedItem = null;
$scope.selectedItemJsonString = '';
$scope.selectItem = function (item) {
$scope.selectedItem = item;
$scope.selectedItemJsonString = JSON.stringify(item);
//alert(JSON.stringify(item));
};
$scope.closePopup = function () {
$scope.selectedItem = null;
$scope.selectedItemJsonString = '';
};
// sharedService.prepForBroadcast($routeParams.anotherVar);
$http({
method: 'GET',
url: '/ProductGroup'
}).success(function (data) {
$scope.list = data;
}).
error(function (data) {
$scope.message = 'There was an error with the data request.';
});
}
I then try to mock the request in the test class:
var scope, ctrl, $httpBackend, sharedServiceMock = {}, popupServiceMock = {};
beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httsypBackend.expectGET('/ProductGroup').
respond([{
ProductGroupID: 5,
MenuTitle: "Promotional Products",
AlternativeText: "Coming soon - a collection of environmentally friendly Promotional Products",
OrdinalPosition: 5,
Active: false
}]);
scope = $rootScope.$new();
ctrl = $controller(ProductGroupsCtrl, {
$scope: scope,
$http: $httpBackend,
sharedService: sharedServiceMock,
popupService: popupServiceMock
});}));
However I receive an error in the testacular window object undefined. What have I done wrong here?
Found the answer. If I remove the error callback function from the $http.get method then it works, i.e. remove the following ...
error(function (data) {
$scope.message = 'There was an error with the data request.';
}
I have to say Angular sure is a steep learning curve for someone who is not a day to day JavaScript programmer (although I seem to be doing more and more). Thanks for the help anyway KatieK :-)