Here is the scenario of what I want to achieve:
User input data is validated in frontend using Joi.
Data is validated in backend, also using Joi.
Potential validation errors are returned to frontend, which can handle those since the same validation framework is being used.
In the frontend, we have translated messages for some errors, and fallbacks to Joi default messages for the rest. In the backend, we have no translations.
My problems come when I'm trying to recreate the error message. How can I:
1) Access a "template string"
In the Joi library each value type has it's own set of template messages:
const messages = {
// ...
'string.min': '{{#label}} length must be at least {{#limit}} characters long',
// ...
};
I can't find out how to access these in an easy way. And I also want to add translated messages, and use these before falling back to the default ones. Is there anything like Joi.getTemplate('string.min')?
2) Recreate the error message
When I get the array with Joi errors from the backend, I want to recreate the error messages, localized if available. The errors look like this:
"details": [
{
"message": "\"name\" length must be at least 10 characters long",
"path": [
"name"
],
"type": "string.min",
"context": {
"limit": 10,
"value": "Joe",
"label": "name",
"key": "name"
}
}
]
Is there some way of doing something like this:
Joi.generateMessageFromTemplate('string.any', details[0].context) or Joi.generateMessage('{{#label}} length must be at least {{#limit}} characters long', details[0].context)
I also need to load the translated message before I do that, in some way.
My feeling is that there should be ways to achieve what I want, but I can't find in the documentation (or the source code) exactly how I should proceed. Please help me :)
const Joi = require('joi').defaults(schema => schema.options({ messages: {
ar:{
'string.empty': 'لا يمكن ان يكون فارغ {{#label}}',
'number.base': 'يجب ان يكون رقم {{#label}}',
'any.invalid': 'قمية غير صالحة {{#label}}',
'phoneNumber.invalid': 'رقم غير صالح {{#label}}',
'array.unique': 'قيمة متكررة {{#label}}',
},
en:{
'string.empty': '{{#error}} Cannot be empty',
'number.base': '{{#label}} Must be a number',
'any.invalid': '{{#label}} Value not valid',
'phoneNumber.invalid': 'Phone Number not valid',
'array.unique': '{{#label}} contains a duplicate value',
}
} }));
and on validate you can select your prefered language like so:
const result = yourSchema.validate({mydatafield:"val"}, {
errors:{language: 'ar'} //en or ar
});
Related
I am a newbie to Amazon Lex, I have created a bot that offers several services:
open a case
check status
status via email
when a user checks the status, he is forced to provide a "case number." The issue I am facing is that it only gets value from digits such as digit representation.
When the user uses "nine" instead of 9, it doesn't work. You can see it here.
Guide me on that issue, I would be very thankful to you.
It seems an AWS Lambda will be best suited to assist you with what you're trying to achieve.
When testing via the AWS Lex console, click the "Inspect" button and view the JSON-formatted requests and responses.
What you will find is that when the user enters a numeric value in words - as in your example "nine" instead of "9" - Lex will provide you with the original input as well as the numeric form of the input.
Take a look at the JSON payload below that I've extracted from my own example:
{
"messages":[
{
"content":"Well done!",
"contentType":"PlainText"
}
],
"sessionState":{
"dialogAction":{
"type":"Close"
},
"intent":{
"name":"MainIntent",
"slots":{
"quantity":{
"value":{
"originalValue":"two",
"interpretedValue":"2",
"resolvedValues":[
"2"
]
}
}
},
"state":"ReadyForFulfillment",
"confirmationState":"None"
},
"originatingRequestId":"e309a92b-124b-4250-bbd3-2fb8891aa90f"
},
"interpretations":[
{
"nluConfidence":{
"score":1
},
"intent":{
"name":"MainIntent",
"slots":{
"quantity":{
"value":{
"originalValue":"two",
"interpretedValue":"2",
"resolvedValues":[
"2"
]
}
}
},
"state":"ReadyForFulfillment",
"confirmationState":"None"
}
},
{
"intent":{
"name":"FallbackIntent",
"slots":{
}
}
}
],
"sessionId":"858917309331613"
}
Have a look at the interpretedValue field in the payload. This is the numeric value that you're looking for.
Using a Lambda function, you can extract this value and perform any validation that you may need before continuing with the rest of the flow or user journey.
You can find out more about integrating Lambda functions into Lex from the developer guide:
https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html
I am using Indicative in my project to validate my controller, but, Indicative don't have a "Unique" rule in "Validation Rules", but the framework Adonis have a rule call "unique" that does exactly what i need.
My project is made in Adonis, but i prefer to use "Indicative" and not "Validator" in Adonis, because i think is more easy and beautiful write the code direct in the Controller
code: 'required|string|max:255',
description: 'required|string|max:255|unique:tabela',
authors: 'string|max:255',
status: 'boolean',
user_id: 'integer',
created_at: [
importValidate.validations.dateFormat(['YYYY-MM-DD HH:mm:ss'])
],
updated_at: [
importValidate.validations.dateFormat(['YYYY-MM-DD HH:mm:ss'])
]
}
In the example above, I need the "code" to be "Unique" and return an error message and a response status. How can I do this?
The unique method of Validator will automatically search in the database. I don't think it's possible to do it with Indicative
I propose this solution (in your controller):
const { validate } = use('Validator')
...
const rules = {
code: 'unique:<table_name>,<field_name>'
}
const messages = {
'code.unique': '...'
}
const validation = await validate({ code: ... }, rules, messages)
if (validation.fails()) {
...
}
To use this command it is necessary to use Validator. I don't think there's an equivalent with Indicative
I was thinking about ways of implementing graphql response that would contain both an error and data.
Is it possible to do so without creating a type that would contain error?
e.g.
Mutation addMembersToTeam(membersIds: [ID!]! teamId: ID!): [Member] adds members to some team. Suppose this mutation is called with the following membersIds: [1, 2, 3].
Members with ids 1 and 2 are already in the team, so an error must be thrown that these members cannot be added, but member with an id 3 should be added as he is not in the team.
I was thinking about using formatResponse but seems that I can't get an error there.
Is it possible to solve this problem without adding error field to the return type?
Is it possible to solve this problem without adding error field to the return type?
Unfortunately, no.
A resolver can either return data, or return null and throw an error. It cannot do both. To clarify, it is possible to get a partial response and some errors. A simple example:
const typeDefs = `
type Query {
foo: Foo
}
type Foo {
a: String
b: String
}
`
const resolvers = {
Query: {
foo: () => {},
}
Foo: {
a: () => 'A',
b: () => new Error('Oops!'),
}
}
In this example, querying both fields on foo will result in the following response:
{
"data": {
"foo": {
"a": "A",
"b": null
}
},
"errors": [
{
"message": "Oops",
"locations": [
{
"line": 6,
"column": 5
}
],
"path": [
"foo",
"b"
]
}
]
}
In this way, it's possible to send back both data and errors. But you cannot do so for the same field, like in your question. There's a couple of ways around this. As you point out, you could return the errors as part of the response, which is usually how this is done. You could then use formatResponse, walk the resulting data, extract any errors and combine them with them with any other GraphQL errors. Not optimal, but it may get you the behavior you're looking for.
Another alternative is to modify the mutation so it takes a single memberId. You can then request a separate mutation for each id you're adding:
add1: addMemberToTeam(memberId: $memberId1 teamId: $teamId): {
id
}
add2: addMemberToTeam(memberId: $memberId2 teamId: $teamId): {
id
}
add3: addMemberToTeam(memberId: $memberId3 teamId: $teamId): {
id
}
This can be trickier to handle client-side, and is of course less efficient, but again might get you the expected behavior.
If you think about combining the GraphQL error - there is a way to do it in Apollo.
You need to set errorPolicy to all. That will help you notify users about the error and at the same time have as much data as possible.
none: This is the default policy to match how Apollo Client 1.0
worked. Any GraphQL Errors are treated the same as network errors and
any data is ignored from the response.
ignore: Ignore allows you to
read any data that is returned alongside GraphQL Errors, but doesn’t
save the errors or report them to your UI.
all: Using the all policy
is the best way to notify your users of potential issues while still
showing as much data as possible from your server. It saves both data
and errors into the Apollo Cache so your UI can use them.
But according to best practices, you shouldn't manipulate it in this way.
This is a great article about handling errors in GraphQL.
So, preferable way is to add "errors" field as part of your response and handle it in JS code.
We can achieve this by using a union. I would recommend visiting the great article Handling GraphQL errors like a champ
Example:
Mutation part: We can return the union type for the response & capture the result according to types.
type MemberType {
id: ID!
name: String!
}
enum ErrorType {
BAD_REQUEST_ERROR
FORBIDDEN_ERROR
INTERNAL_SERVER_ERROR
NOT_FOUND_ERROR
UNAUTHORIZED_ERROR
}
type GraphqlError {
type: ErrorType!
code: String!
message: String!
helpLink: URL
}
union UserRegisterResult = MemberType | GraphqlError;
addMembersToTeam(membersIds: [ID!]! teamId: ID!): UserRegisterResult!
Response:
addMembersToTeam(membersIds: [ID!]! teamId: ID!): {
...on MemberType{
id,
name,
}
...on GraphqlError{
id,
message,
statusCode,
}
}
In telegram API documentation I see: "You can either pass a file_id as String to resend a photo that is already on the Telegram servers", but I can't find ways to get file_id of uploaded file. How can I get it?
Its depended to your content_types ,for example:
Video:
message.video.file_id
Audio:
message.audio.file_id
Photo:
message.photo[2].file_id
For more see this link.
This is the easiest way I've found to do it.
Upload your file to any chat and forward the message to #RawDataBot. It will return something like this:
{
"update_id": 754677603,
"message": {
"message_id": 403656,
"from": {
"id": xxx,
"is_bot": false,
"first_name": "xxx",
"username": "xxx",
"language_code": "en"
},
"chat": {
"id": xxx,
"first_name": "xxx",
"username": "xxx",
"type": "private"
},
"date": 1589342513,
"forward_from": {
"id": xxx,
"is_bot": false,
"first_name": "xxx",
"username": "xxx",
"language_code": "en"
},
"forward_date": 1589342184,
"document": {
"file_name": "filename.pdf",
"mime_type": "application/pdf",
"file_id": "This_Is_The_Thing_You_Need",
"file_unique_id": "notthis",
"file_size": 123605
}
}
}
What you need is the string under file_id. Once you have copied that, you can simply the following code to send the message.
context.bot.sendDocument(chat_id=update.effective_chat.id,
document = "Your_FILE_ID_HERE")
Depending on the method (File type) which you chose to send a file, after sending a file to Telegram a response is returned. For example if you send a MP3 file to Telegram using sendAudio method, Telegram returns an Audio object which contains the file ID.
Source: https://core.telegram.org/bots/api#audio
In addition to the answers above, you can log Updates that comes to your bot, Either from https://api.telegram.org/bot'.BOT_TOKEN.'/getUpdates or throw updates that come in your application. there you will find a Json property like below:
{
"update_id" = 1111111,
"message" =
{
"message_id" = 1111111,
"from" =
{
"id" = 111111,
...
}
"chat" =
{
"id" = 111111,
...
}
"date" = 111111,
"photo" =
{
{
"file_id" = HERE IS YOU FILE ID 1,
"file_size" => XXXX,
"width" => XX,
"height" => XX,
}
}
}
}
Say you receive a Message with an array of PhotoSize
https://core.telegram.org/bots/api#photosize
As you can see, there's a file_id, you can use this to send a photo through sendPhoto.
If we assume Update is an object, with in it a Message object, which in turn provides a Chat object with in it a id of the chat where the initial message came from and an array of PhotoSize (excuse me for using PHP here, but that's my main language...)
$update->message->photo is how you can access the array.
Use some kind of For loop to iterate over the items, or just access the first one if the array isn't bigger than 1.
After that, you can use the result(s) to extract the file_id and send it as a string via sendPhoto's photo parameter and the Chat ID via the chat_id parameter.
I hope this helped!
P.S. Here is a diagram of my current implementation of the API, i hope it brings some clarity to you!
if you use PHP:
you can write this line for full size:
$file_id = $updates['message']['photo'][1]['file_id'];
and this line for thumb:
$file_id = $updates['message']['photo'][0]['file_id'];
According to the latest docs (v20.0a6) plenty of classes have been changed. I have found that the easiest way to get started with files is using the effective_attachment property.
async def handle_file(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
media_item = await context.bot.get_file(update.message.effective_attachment[0].file_id)
media_url = media_item.file_path
For declaring the handler there have also been changes to filters, here is a simple way to declare it:
application.add_handler(MessageHandler(filters.ATTACHMENT, handle_file))
I'm working on a project that involves making multiple HTTP GET requests to different APIs, each requiring information from the last. I'm trying to avoid nested-callaback-and-counter-hell, and have been trying to get it working with the async module.
This is what I need to do: I have an array of 1..n course identifiers (['2014/summer/iat/100/d100', '2014/spring/bisc/372/d100']). For each course in the array, I need to fetch its course outline via a HTTP GET.
The resulting outline looks something like this:
{
"info": {
"nodePath": "2014/spring/bisc/372/d100",
"number": "372",
"section": "D100",
"title": "Special Topics in Biology",
"term": "Spring 2014",
"description": "Selected topics in areas not currently offered...",
"name": "BISC 372 D100",
"dept": "BISC",
},
"instructor": [
{
"lastName": "Smith",
"commonName": "Frank",
"phone": "1 555 555-1234",
"email": "franksmith#school.edu",
"name": "Frank Smith",
"roleCode": "PI"
},
{
"lastName": "Doe",
"commonName": "John",
"phone": "1 555 555-9876",
"email": "johndoe#school.edu",
"name": "John Doe",
"roleCode": "PI"
}
]
}
(a bunch of non-relevant fields omitted)
Each outline object may contain an instructor property which is an array of 0..n instructor objects for the course. For each member of the instructor array, I need to then call another API to get additional data. When that call returns, I need to insert it into the right instructor object.
Finally, when everything is done, the data gets passed to a template for express to render and return to the client.
I've tried getting this working using async and had some success with async.waterfall when doing a proof-of-concept with only getting one of the instructor profiles (e.g. not looping over the array, just getting instructor[0]). The async module's docs are comprehensive, but pretty dense and I'm having a hard time determining what I actually need to do. I had a Frankenstein combination of various nested async calls which still didn't work.
I don't really care how I accomplish the task - flow-control, promises, magic pixie dust, whatever. Any hints greatly appreciated.
Using Q for promises, you can probably do something like this:
return Q
.all(course_ids.map(function(course) {
return HTTP.GET(course); // Assuming this returns a promise
}))
.then(function(course_data) {
var instructors = [];
course_data.forEach(function(course) {
var p = Q
.all(course.instructor.map(function(instructor) {
return HTTP.GET(instructor.id);
}))
.then(function(instructors) {
course.instructors_data = instructors;
return course;
});
promises.push(p);
});
return Q.all(promises);
});
Will resolve with an array containing the courses, each of which contains
an array of instructor data in its instructors_data value.
You could use async.each(), which would do the API requests in parallel (assuming there is no concurrent API request limits on the server side, if that is the case, use async.eachLimit() instead):
async.each(instructors, function(instructor, callback) {
// call API here, store result on `instructor`,
// and call `callback` when done
}, function(err){
if (err)
console.log('An error occurred while processing instructors');
else
console.log('All instructors have been processed successfully');
});