Given the JSON object:
errors =
{
hashed_password: {
message: 'Validator "Password cannot be blank" failed for path hashed_password',
name: 'ValidatorError',
path: 'hashed_password',
type: 'Password cannot be blank' },
username: {
message: 'Validator "Username cannot be blank" failed for path username',
name: 'ValidatorError',
path: 'username',
type: 'Username cannot be blank' },
email: {
message: 'Validator "Email cannot be blank" failed for path email',
name: 'ValidatorError',
path: 'email',
type: 'Email cannot be blank' },
name: {
message: 'Validator "Name cannot be blank" failed for path name',
name: 'ValidatorError',
path: 'name',
type: 'Name cannot be blank' }
}
How do I iterate through the properties of each "current context" object?
I would think you do something like this:
{#errors}
{#.}
{type}
{/.}
{/errors}
If you really have to put meaningful data into object keys, consider writing a contextual helper, as per:
https://github.com/akdubya/dustjs/issues/9
Dust.js output JSON key
It is not possible to iterate through members of an object in Dust. Part of the reason is that you cannot know the order of the members. Another part is that this is seen as bringing too much logic into Dust.
Instead, you can change the JSON to look more like this:
{
errors: [
{
hashed_password: {
message: 'Validator "Password cannot be blank" failed for path hashed_password',
name: 'ValidatorError',
path: 'hashed_password',
type: 'Password cannot be blank'
}
},
{
username: {
message: 'Validator "Username cannot be blank" failed for path username',
name: 'ValidatorError',
path: 'username',
type: 'Username cannot be blank'
}
},
{
email: {
message: 'Validator "Email cannot be blank" failed for path email',
name: 'ValidatorError',
path: 'email',
type: 'Email cannot be blank'
}
},
{
name: {
message: 'Validator "Name cannot be blank" failed for path name',
name: 'ValidatorError',
path: 'name',
type: 'Name cannot be blank'
}
]
}
You can iterate over an object using a helper.
For example, you could define a helper like this one:
dust.helpers.iter = function(chunk, context, bodies, params) {
var obj = dust.helpers.tap(params.obj, chunk, context);
var iterable = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var value = obj[key];
iterable.push({
'$key': key,
'$value': value,
'$type': typeof value
});
}
}
return chunk.section(iterable, context, bodies);
};
Then, in your template, you would loop through like this:
{#iter obj=errors}
{$value.type}
{/iter}
Related
I'm trying to correctly define OpenAPI spec for the purposes of generating api client from that spec. I've encoutered a problem where we have a complex query object with nested objects and arrays of objects for get a GET route.
Lets take these classes as an example.
class Person {
#ApiProperty()
name!: string
#ApiProperty()
location!: string
}
class CompanyDto {
#ApiProperty()
name!: string
#ApiProperty({
type: [Person],
})
employees!: Person[]
}
And a get request with #Query decorator.
#Get('test')
async testing(#Query() dto: CompanyDto): Promise<void> {
// ...
}
What I'm getting is.
{
get: {
operationId: 'testing',
parameters: [
{
name: 'name',
required: true,
in: 'query',
schema: {
type: 'string',
},
},
{
name: 'name',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
{
name: 'location',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
],
responses: {
'200': {
description: '',
},
},
tags: ['booking'],
},
}
I've also tries to define Query params by adding #ApiQuery decorator and it almost works.
#ApiQuery({
style: 'deepObject',
type: CompanyDto,
})
--
{
get: {
operationId: 'testing',
parameters: [
{
name: 'name',
required: true,
in: 'query',
schema: {
type: 'string',
},
},
{
name: 'name',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
{
name: 'location',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
{
name: 'name',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
{
name: 'employees',
in: 'query',
required: true,
schema: {
type: 'array',
items: {
$ref: '#/components/schemas/Person',
},
},
},
],
responses: {
'200': {
description: '',
},
},
tags: ['booking'],
},
}
However now I'm getting duplicate query definitions mashed in to one. Is there a way to prevent or overwrite #Query definition? Or just a better way to define complex #Query in general?
Ended up creating a custom decorator to extract query without generating OpenAPI Spec.
export const SilentQuery = createParamDecorator(
(data: string | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest()
if (data) {
return request.query[data]
} else {
return request.query
}
},
)
So now you can use #ApiQuery with deepObject style.
Also if your're using ValidationPipes with class-validator for example. Make sure to set validateCustomDecorators to true
#SilentQuery(new ValidationPipe({ validateCustomDecorators: true }))
So my :decimal formatted DB field is being returned as a string, and making my tests fail.
I have a request spec, using RSwag, that I'm trying to get to pass with a format check, yet it's telling me the following:-
Rswag::Specs::UnexpectedResponse:
Expected response body to match schema: The property '#/budget' of type string did not match the following type: number in schema
My test:-
path '/api/v1/jobs/{id}' do
parameter name: 'id', in: :path, type: :string, description: 'Job id'
get('show job') do
tags 'Jobs'
response(200, 'successful') do
schema type: :object,
properties: {
title: { type: :string },
description: { type: :string },
date: { type: :string, format: 'date-time', nullable: true },
budget: { type: :number, format: 'double' },
awarded: { type: :boolean },
created_at: { type: :datetime},
updated_at: { type: :datetime}
}
let(:id) { #job.id }
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
run_test!
end
end
What is the best way around this? It's a little pointless going to the effort of using Swagger for my API docs if I have to make all the fields strings.
Suppose I want to prompt the user to select among a list of database options with "type checkbox":
{
type: 'checkbox',
name: 'databaseType',
message: `Which ${chalk.yellow('*type*')} of database(s) would you like to use?`,
choices: response => {
const databaseOpts = [
{
value: 'mysql',
name: 'MySQL'
},
{
value: 'cassandra',
name: 'Cassandra'
},
{
value: 'mongodb',
name: 'MongoDB'
}
];
return databaseOpts;
},
default: 0
},
After this initial prompt, I want to get the response for whichever option(s) they selected and prompt them again for the name of the database based on their selection(s) then store them:
{
when: response => response.databaseType === 'mysql',
type: 'input',
name: 'selectedMySQL',
message: 'What is the name of your mysql database? ',
default: 'testmysql',
store: true
},
{
when: response => response.databaseType === 'cassandra',
type: 'input',
name: 'selectedCassandra',
message: 'What is the name of your cassandra database? ',
default: 'testcassandra',
store: true
},
{
when: response => response.databaseType === 'mongo',
type: 'input',
name: 'selectedMongo',
message: 'What is the name of your mongo database? ',
default: 'testmongo',
store: true
}
Sample Desired Output:
For example user's choices in databaseOpts: {mysql, mongodb}
It will then ask:
1) What is the name of your mysql database?:
2) What is the name of your mongo database?:
User enters the name(s) respectively which will be stored in selectedMySQL and selectedMongo.
I'm unfamiliar with yeoman syntax, but this is the logic I want to implement. Please help, Thanks.
I tried:
when: response => response.databaseType.databaseOpts === 'mysql'
but it's still skipping the response prompts.
I had my users page displaying users but then when I stopped working on the code for a while and came back to it I am getting an error which says
Error while processing route: users Unexpected end of JSON input SyntaxError: Unexpected end of JSON input
and also
Uncaught SyntaxError: Unexpected end of JSON input
I don't know where to start on trying to fix this issue.
Here is the front-end code in the route file users.js:
model() {
return this.get("store").findAll("user");
},
Here is the api code in the users controller:
# GET /users
def index
render jsonapi: User.all
end
When I go to http://localhost:3000/users I get the following json:
{
data: [
{
id: "1",
type: "users",
attributes: {
name: "Katie",
email: "katie#gmail.com"
}
},
{
id: "2",
type: "users",
attributes: {
name: "Katie",
email: "katie#gmail.com"
}
},
{
id: "3",
type: "users",
attributes: {
name: "Katie",
email: "katie#gmail.com"
}
},
{
id: "4",
type: "users",
attributes: {
name: "K",
email: "k"
}
},
{
id: "5",
type: "users",
attributes: {
name: "R",
email: "r"
}
}
],
jsonapi: {
version: "1.0"
}
}
I also get this warning: WARNING: The server returned an empty string for GET http://localhost:3000/users, which cannot be parsed into a valid JSON. Return either null or {}.
/users route is not providing the valid JSON output is the reason for this error. You can check /users output using POSTMAN or writing the below code,
let url ="/users";
Ember.$.getJSON(url).then((result) => {
console.log(' Result ', result);
});
I am migrating from Mandrill to SparkPost and have a Rails back-end.
The data structure I currently have is the following --
message = {
subject: "Welcome",
merge_vars: [{
rcpt: user.email,
vars: email_vars(user)
}],
to:[{
email: user.email,
name: user.name
}],
track_clicks: true,
track_opens: true,
inline_css: true,
}
This sends the response --
m = Mandrill::API.new
template_content = []
result = m.messages.send_template 'email-confirmation', template_content, message
Would I need to update the JSON data structure at all?
Once JSON is good, how do I pass values to specific template with SparkPost?
I attempted the following --
m = SparkPost::Client.new()
template_content = []
result = m.messages.send_template 'email-confirmation', template_content, message
But I have also seen this --
host = 'https://api.sparkpost.com'
SparkPost::Request.request("#{host}/api/v1/transmissions", API_KEY, {
recipients: [
{
address: { email: user.email },
substitution_data: {
first_name: user.name,
email: user.email
}
}
],
content: {
template_id: 'email-confirmation'
},
substitution_data: {
name: user.name,
email: user.email
}
})
Appreciate the help!
If you're using the official gem, it has a convenient method called send_payload which you can use to send a prepared payload.
The substitution_data inside recipients collection is a per recipient substitution.
For example, I've following templates.
To send using this template, this is my complete code
sp = SparkPost::Client.new() # pass api key or get api key from ENV
payload = {
recipients: [
{
address: { email: 'RECIPIENT1' },
substitution_data: {
name: 'User one',
username: 'userone'
}
}, {
address: { email: 'RECIPIENT2' },
substitution_data: {
name: 'User two',
username: 'user2'
}
}
],
content: {
template_id: 'test-template'
},
substitution_data: {
company: 'Awesome company'
}
}
response = sp.transmission.send_payload(payload)
p response
The email will look like
Hello User one, Your username, userone, is created. Thanks Awesome company