How to customize user feedback in Sentry? - sentry

How does one customize the user feedback form in Sentry?
By default is says something along the lines of "It looks like we're having some internal issues."
Is it possible to change the message, data collected, etc.?
Thanks.

There is an API here, but you have to get an event_id which is basically generated by an error or an exception in you app... I'm still trying to figure out how use this functionality without the default integrated form.

Sentry has a built-in front-end user feedback form which you can trigger and customize to some extent. A basic implementation in Angular4+ might look like this:
import { ErrorHandler } from '#angular/core';
import { Config } from '../config/config';
import * as Sentry from '#sentry/browser';
Sentry.init({
dsn: 'https://0123456789abcdef.sentry.io/000001',
release: Config.SENTRY_RELEASE
});
export class SentryErrorHandler implements ErrorHandler {
handleError(error: any): void {
const eventId = Sentry.captureException(error.originalError || error);
Sentry.showReportDialog({
eventId,
title: 'Aw, Snap! You broke the internet!',
subtitle: 'Send us an email and we will fix it.',
subtitle2: 'Please refresh your cache to continue (cmd+R / ctrl+R)'
});
}
}
Here's a link to the showReportDialog API

It seems the current way is this:
make your own UI
generate and enqueue a Sentry event (it's OK if the even has not been sent yet)
POST to the special endpoint that Sentry's report collector uses
The details are described in https://github.com/getsentry/sentry-javascript/issues/3111
const dsn = SENTRY_DSN;
const eventId = captureMessage("User report");
const query = Object.entries({dsn, eventId})
.map(([k, v]) => `${ k }=${ encodeURIComponent(v) }`).join("&");
const body = new FormData();
Object.entries(
{name: "Alex Foo", email: "a.foo#example.com", comments: "Long text goes here"})
.map(([k, v]) => body.append(k, v));
fetch(`https://sentry.io/api/embed/error-page/?${ query }`,
{method: "POST", body});

Related

Iron session is not updating req.session object

I have a page where users can give themselves a "role" like member or admin. They can go to another route to create messages. I am trying to update user's role from "user" to "admin". It updates req.session to admin role in the admin.js file, but when I go to messages/create.js and try to log req.session, it shows that user still has the "user" role. I am saving the changes I make by calling req.session.save(), but it is not working.
admin.js
import { withIronSessionApiRoute } from "iron-session/next";
import nc from "next-connect";
import { session_config } from "../../lib/config";
import Users from "../../models/user";
import { connectToDatabase } from "../../util/mongodb";
const handler = nc()
handler.post(async (req) => {
if (req.body.password === process.env.ADMIN_PASSWORD) {
await connectToDatabase()
await Users.findOneAndUpdate({ name: req.session.user.name }, { role: "admin" })
const updated_user = { name: req.session.user.name, role: "admin" }
req.session.user = updated_user
await req.session.save()
}
})
export default withIronSessionApiRoute(handler, session_config);
messages/create.js
import { withIronSessionApiRoute } from "iron-session/next";
import nc from "next-connect";
import { session_config } from "../../../lib/config";
const handler = nc()
handler.post(async (req) => {
console.log(req.session.user)
console.log(req.body)
})
export default withIronSessionApiRoute(handler, session_config)
Please let me know what the issue is and how I can fix it. Thank you
The first thing I noticed from looking at the code is that you're not sending a response back to the client. Iron session uses cookies to manage stateless authentication and the way it manages is by setting the response header. Because you're not sending a response, it can't update the session.
Looking further into the API documentation, session.save() - "Saves the session and sets the cookie header to be sent once the response is sent."
Not knowing your full implementation or having a working code example from something like codesandbox.io, I suggest the following code to see if this solves your problem.
// please make sure that `res` is a parameter on the `.post()` function
// on your original code. I've already set it as shown below.
handler.post(async (req, res) => {
if (req.body.password === process.env.ADMIN_PASSWORD) {
await connectToDatabase()
await Users.findOneAndUpdate({ name: req.session.user.name }, { role: "admin" })
const updated_user = { name: req.session.user.name, role: "admin" }
req.session.user = updated_user
await req.session.save()
// response below
res.send({ ok: true })
// or if you don't want to send custom data back, comment the line above,
// and then uncomment the line below
// res.status(200).end()
}
})
Attempt 2
I made an iron session demo on Codesandbox using some of the demo code from the iron session repo NextJs example.
The code example shows:
login
log out
setting a user as an admin
fetching user data from server-side
fetching user data from client-side
fetching using SWR
Some side notes to be aware of: if you are doing something like
const sessionData = req.session.user, then trying to mutate the req.session.user, and then sending the data back, it won't work because the session object will be recreated per request and node cannot store req.session as a reference.
If my demo doesn't help you, then you're going to have to share more info and code, and maybe create a Codesandbox to reproduce what is happening to you.

Convenient format for validation errors

I'm developing application with next tech stack:
Backend (NestJs, GraphQL)
Frontend (React, Apollo^3.5.0)
I have REST and Gql apis. I'm stacked in defining errors format for both apis types: REST and Gql. How to make it universal.
I will explain my problem on real example. It's registration form.
Example
In frontend app I have simple react form with 2 fields: email and password. Of course, I do validation on both sides.
On client side:
Email field should pass regexp
Password field should have >6 characters
On backend side (class-validator decorators):
Email ( #IsEmail, #UniqEmail - custom decorator, that is searching user with provided email in db )
Password ( #MinLength(6) )
Problem
I want to find the easiest and flexible way (or format) of validation errors to provide them to the client side. To make a simple map and show them in ui.
I found solution but it seems to me weird (because of low typing level). On backend side I make a special errors formatting:
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: (errors) => {
// property - property that caused error
// constraints - type of error(minLength, uniq, ...)
const formatted = errors.map((error) => ({ property: error.property, constraints: error.constraints }));
return new BadRequestException(formatted);
},
}),
);
And format provided errors for gql:
formatError: (error) => {
if (GraphqlErrorFormatter.getErrorCode(error) === 'INTERNAL_SERVER_ERROR') {
return GraphqlErrorFormatter.formatInternalError(error);
}
return GraphqlErrorFormatter.formatError(error);
},
And map errors on client side in such way:
// Dictionary to map errors in current language
const map = {
email: {
uniq: 'Email is already registered',
},
password: {
minLength: 'Bla-bla-bla',
},
};
// MUTATION PROPS
onError: (err) => {
err.graphQLErrors.forEach((graphQLError) => {
graphQLError.extensions.response.message.forEach((message) => {
const { property, constraints } = message;
const [constraint] = Object.keys(constraints);
const errorMessage = map[property][constraint];
if (errorMessage) setFieldError(property, { message: errorMessage });
});
});
}
Other solutions
Gql union type for response payload approach (e.g. put errors in data key in resp object).
I know about this but I decided to use it for bussiness errors, not validation errors.
Question
What is your experience? What approach do you use in your applications? Thanks in advance for any answer.

I want to implement auto complete search for bot from Mongo Database

I want to implement auto complete search for bot..for example bot should get responses as auto complete options from Mongo Database. Can any one suggest how to do this without Azure Search?
I have tried implementing like this but in my case i want to get the tags from Database.
May i know what are the available options to do this?
When you send a message from the bot, you can add the autocomplete options to the activity's channel data. Then in Web Chat, you can use a custom store middleware to retrieve the options and update the JQuery Auto complete widget.
Bot Framework SDK v4 (C#)
var reply = turnContext.Activity.CreateReply();
reply.Text = "Hello, World!";
reply.ChannelData = JObject.FromObject( new {
autocompleteOptions = new List<string>() { "Option 1", "Option 2", "Option 3" }
});
await turnContext.SendActivityAsync(reply);
Web Chat v4
const store = createStore(
{},
({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { payload: { activity: { channelData: { autcompleteOptions } = {}}}} = action;
if (autcompleteOptions) {
// Update JQuery Autcomplete Widget with `autocompleteOptions`
}
}
return next(action);
}
);
For more details take a look at the Incoming Event Web Chat Sample and this Stack Overflow answer.
Hope this helps!

Log Query/Mutation actions to database for Auditing

My goal is to run some kind of webhook, cloud function or say I want to perform some kind of action after each query success or mutation success in graphql.
Means I want to log each and every action performed by users (kind of history of when what was created and updated).
How can this be implemented using some kind of middleware between graphql and DB (say mongo for now)?
Means that middleware should be responsible to run the logging action each time a query or mutation is called from front-end.
Tech stack being used is- Node, express, graphQl, Redis etc.
Any suggestions would really be appreciated.
Thanks
The solution I came up with was calling a function manually each time a query or mutate.
If you're using Apollo, you can utilize the formatResponse and formatError options for logging, as outlined in the docs.
const server = new ApolloServer({
typeDefs,
resolvers,
formatError: error => {
console.log(error);
return error;
},
formatResponse: response => {
console.log(response);
return response;
},
});
Using an extension can allow you to hook into different phases of the GraphQL request and allow more granular logging. A simple example:
const _ = require('lodash')
const { GraphQLExtension } = require('graphql-extensions')
module.exports = class LoggingExtension extends GraphQLExtension {
requestDidStart(options) {
logger.info('Operation: ' + options.operationName)
}
willSendResponse(o) {
const errors = _.get(o, 'graphqlResponse.errors', [])
for (const error of errors) {
logger.error(error)
}
}
}
There's a more involved example here. You can then add your extension like this:
const server = new ApolloServer({
typeDefs,
resolvers,
extensions: [() => new YourExtension()]
});
If you're using express-graphql to serve your endpoint, your options are a bit more limited. There's still a formatError option, but no formatResponse. There is a way to pass in an extensions array as well, but the API is different from Apollo's. You can take a look at the repo for more info.

Cypress w/graphql - having issues getting AUTH with testing via UI. Better way to stub mutation?

So, if I am testing pages in a vacuum without much interaction with the backend, it works great. I am having issues with actually interacting with my UI if it hits any type of service. Basically, nothing is Auth'd. I try programmatically setCookie, no dice. I try to read the cookie, nope. Btw, my whole site requires a login.
cy.setCookie('sess', ';askjdfa;skdjfa;skdjfa;skdjfa;skfjd');<-- does not work
cy.getCookie('sess').should('exist') <-- does not work
I am having an issue on really the best way to "test" this. For example, I have an account section that a user can "update" their personals. I try, fill out the form (via UI testing), but the submission is rejected, no Auth. EVEN THOUGH I just logged in (via UI testing). - I know I need to remove that since it is bad practice to UI-Login for every section of my site.
So, I don't know how to stub graphql calls with cy.request(). Here is my mutation.
mutation Login($email: Email!, $password: String!) {
login(email: $email, password: $password) {
userID
firstName
lastName
}
}
Right now, I am importing the login spec for each section of the site i am testing. I know this is an anti-pattern. Like to solve this problem.
My AUTH (cookie) is not being set. Even when I try to set it, programmatically, doesn't work.
Maybe I should just stub out my graphql mutations? How?
Lastly, IF I am stubbing out my graphql mututations, how do I update the session ( via my main session query ). If I can get these mutations to work, then refreshing the page will get my my updated data, so I'm not completely needing this part.
Any ideas?
I didn't do the stub and all those, as you were asking how the mutation would work with cy.request in my other post. I did it this way and it just basically works. Hopefully this would help
I created a const first though
export const join_graphQL = (query, extra={}) => {
return `mutation {
${query}(join: { email: "${extra.email}", id: "${extra.id}" }) {
id, name, email
}
}`
};
request config const
export const graphqlReqConfig = (body={}, api=graphQlapi, method='POST') => {
return {
method,
body,
url: api,
failOnStatusCode: false
}
};
mutation query with cy.request
const mutationQuery = join_graphQL('mutationName', {
email: "email",
id: 38293
});
cy.request(graphqlReqConfig({
query: mutationQuery
})).then((res) => {
const data = res.body.data['mutationName']; // your result
});
hopefully it's not too messy to see.
basically the fields need to be string such as "${extra.email}" else it will give you error. Not sure how the graphql works deeply but if I just do ${extra.email} I would get an error which I forgot what error it was.
Here's a simpler way of handling a mutation with cy.request
const mutation = `
mutation {
updateUser(id: 1, firstName: "test") {
firstName
lastName
id
role
}
}`
cy.request({
url: url,
method: 'POST',
body: { query: mutation },
headers: {
Authorization: `Bearer ${token}`,
},
})

Resources