Has anyone been able to use SendGrid templates with Parse Cloud Code?
This works, but only sends text, any ideas on how to use a template_id?
I tried with the filter but was not successful.
Parse.Cloud.define("sendEmail", function(request, response) {
// Import SendGrid module and call with your SendGrid API Key
var sg = require('sendgrid')('your SendGrid API key here');
// Create the SendGrid Request
var reqSG = sg.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: {
personalizations: [
{
to: [
{
// This field is the "to" in the email
email: request.params.toEmail,
},
],
// This field is the "subject" in the email
subject: request.params.subject,
},
],
// This field contains the "from" information
from: {
email: 'info#youremail.com',
name: 'Your Name',
},
// This contains info about the "reply-to"
// Note that the "reply-to" may be different than the "from"
reply_to: {
email: 'info#youremail.com',
name: 'Your Name',
},
content: [
{
type: 'text/plain',
value: request.params.body,
},
],
},
});
sg.API(reqSG, function(SGerror, SGresponse) {
// Testing if some error occurred
if (SGerror) {
// Ops, something went wrong
console.error('Error response received');
console.error('StatusCode=' + SGresponse.statusCode);
console.error(JSON.stringify(SGresponse.body));
response.error('Error = ' + JSON.stringify(SGresponse.body));
}
else {
// Everything went fine
console.log('Email sent!');
response.success('Email sent!');
}
});
});
This is from the documentation on Back4App https://docs.back4app.com/docs/integrations/using-sendgrid-to-send-email/
If anyone is looking for the answer it turned out to be simple,
add the template_id as a personalization and change the message type the text/html,
template_id:"your template_id",
content: [
{
type: 'text/html',
// This field is the body of the email
value: "",
},
],
Related
I am trying to create a custom controller to update the user profile.
I created the routing file and the corresponding controller.
Routing file: server/src/api/profile/routes/profile.js
module.exports = {
routes: [
{
method: 'GET',
path: '/profile',
handler: 'profile.getProfile',
},
{
method: 'PUT',
path: '/profile',
handler: 'profile.updateProfile',
},
]
}
Controller: src/api/profile/controllers/profile.js
async updateProfile(ctx) {
try {
const { id } = ctx.state?.user;
const user = strapi.query('admin::user').update({
where: { id },
data: {
username: "testUsername"
}
})
ctx.body = "User updated"
} catch(error) {
ctx.badRequest("Something went wrong", { error })
}
},
The above code returns "User updated", but the username does not update. I am executing the PUT call with a correct Bearer authorisation token and the user permissions for that user are set to enable "updateProfile".
Oddly enough, the same code, when changed to update a different API item, works perfectly fine:
async updateArticle(ctx) {
try {
const { id } = ctx.state?.user;
const article = strapi.query('api::article.article').update({
where: { author: id },
data: {
title: "New title"
}
})
ctx.body = article
} catch(error) {
ctx.badRequest("Something went wrong", { error })
}
},
I am also confused by different syntaxes appearing in the official Strapi documentation, for example some docs mention:
strapi.query('admin::user').update({ id }, data)
But in other places in the documentation its:
strapi.plugins['users-permissions'].services.user.update({ id });
And then elsewhere:
strapi.query('user', 'users-permissions').update(params, values);
Another question is: do I need to sanitise the input / output in any way? If yes, how? Importing sanitizeEntity from "Strapi-utils" doesn't work, but it's mentioned in several places on the internet.
Additionally, I cannot find a list of all ctx properties. Where can I read what is the difference between ctx.body and ctx.send?
The lack of good documentation is really hindering my development. Any help with this will be greatly appreciated.
In my Laravel 5/vuejs 2.6 I upload an image with the vue-upload-component and am sending a requested image blob
I try to save it with the controller code like :
if ( !empty($requestData['avatar_filename']) and !empty($requestData['avatar_blob']) ) {
$dest_image = 'public/' . Customer::getUserAvatarPath($newCustomer->id, $requestData['avatar_filename']);
$requestData['avatar_blob']= str_replace('blob:','',$requestData['avatar_blob']);
Storage::disk('local')->put($dest_image, file_get_contents($requestData['avatar_blob']));
ImageOptimizer::optimize( storage_path().'/app/'.$dest_image, null );
} // if ( !empty($page_content_image) ) {
As result, I have an image uploaded, but it is not readable.
The source file has 5 Kib, the resulting file has 5.8 Kib and in the browser's console I see the blobs path as
avatar_blob: "blob:http://local-hostels2.com/91a18493-36a7-4023-8ced-f5ea4a3c58af"
Have do I convert my blob to save it correctly?
MODIFIED :
a bit more detailed :
In vue file I send request using axios :
let customerRegisterArray =
{
username: this.previewCustomerRegister.username,
email: this.previewCustomerRegister.email,
first_name: this.previewCustomerRegister.first_name,
last_name: this.previewCustomerRegister.last_name,
account_type: this.previewCustomerRegister.account_type,
phone: this.previewCustomerRegister.phone,
website: this.previewCustomerRegister.website,
notes: this.previewCustomerRegister.notes,
avatar_filename: this.previewCustomerRegister.avatarFile.name,
avatar_blob: this.previewCustomerRegister.avatarFile.blob,
};
console.log("customerRegisterArray::")
console.log(customerRegisterArray)
axios({
method: ('post'),
url: window.API_VERSION_LINK + '/customer_register_store',
data: customerRegisterArray,
}).then((response) => {
this.showPopupMessage("Customer Register", 'Customer added successfully ! Check entered email for activation link !', 'success');
alert( "SAVED!!::"+var_dump() )
}).catch((error) => {
});
and this.previewCustomerRegister.avatarFile.blob has value: "blob:http://local-hostels2.com/91a18493-36a7-4023-8ced-f5ea4a3c58af"
where http://local-hostels2.com is my hosting...
I set this value to preview image defined as :
<img
class="img-preview-wrapper"
:src="previewCustomerRegister.avatarFile.blob"
alt="Your avatar"
v-show="previewCustomerRegister.avatarFile.blob"
width="256"
height="auto"
id="preview_avatar_file"
>
and when previewCustomerRegister.avatarFile.blob is assigned with uploaded file I see it in preview image.
I show control with saving function in first topic but when I tried to opened my generated file with kate, I found that it
has content of my container file resources/views/index.blade.php...
What I did wrong and which is the valid way ?
MODIFIED BLOCK #2 :
I added 'Content-Type' in request
axios({
method: ('post'),
url: window.API_VERSION_LINK + '/customer_register_store',
data: customerRegisterArray,
headers: {
'Content-Type': 'multipart/form-data'
}
but with it I got validation errors in my control, as I define control action with request:
public function store(CustomerRegisterRequest $request)
{
and in app/Http/Requests/CustomerRegisterRequest.php :
<?php
namespace App\Http\Requests;
use App\Http\Traits\funcsTrait;
use Illuminate\Foundation\Http\FormRequest;
use App\Customer;
class CustomerRegisterRequest extends FormRequest
{
use funcsTrait;
public function authorize()
{
return true;
}
public function rules()
{
$request= Request();
$requestData= $request->all();
$this->debToFile(print_r( $requestData,true),' getCustomerValidationRulesArray $requestData::');
/* My debugging method to write data to text file
and with Content-Type defined above I see that $requestData is always empty
and I got validations errors
*/
// Validations rules
$customerValidationRulesArray= Customer::getCustomerValidationRulesArray( $request->get('id'), ['status'] );
return $customerValidationRulesArray;
}
}
In routes/api.php defined :
Route::post('customer_register_store', 'CustomerRegisterController#store');
In the console of my bhrowser I see : https://imgur.com/a/0vsPIsa, https://imgur.com/a/wJEbBnP
I suppose that something is wrong in axios header ? without 'Content-Type' defined my validation rules work ok...
MODIFIED BLOCK #3
I managed to make fetch of blob with metod like :
var self = this;
fetch(this.previewCustomerRegister.avatarFile.blob) .then(function(response) {
console.log("fetch response::")
console.log( response )
if (response.ok) {
return response.blob().then(function(myBlob) {
var objectURL = URL.createObjectURL(myBlob);
// myImage.src = objectURL;
console.log("objectURL::")
console.log( objectURL )
console.log("self::")
console.log( self )
let customerRegisterArray =
{
username: self.previewCustomerRegister.username,
email: self.previewCustomerRegister.email,
first_name: self.previewCustomerRegister.first_name,
last_name: self.previewCustomerRegister.last_name,
account_type: self.previewCustomerRegister.account_type,
phone: self.previewCustomerRegister.phone,
website: self.previewCustomerRegister.website,
notes: self.previewCustomerRegister.notes,
avatar_filename: self.previewCustomerRegister.avatarFile.name,
avatar: objectURL,
};
console.log("customerRegisterArray::")
console.log(customerRegisterArray)
axios({
method: 'POST',
url: window.API_VERSION_LINK + '/customer_register_store',
data: customerRegisterArray,
// headers: {
// 'Content-Type': 'multipart/form-data' // multipart/form-data - as we need to upload with image
// }
}).then((response) => {
self.is_page_updating = false
self.message = ''
self.showPopupMessage("Customer Register", 'Customer added successfully ! Check entered email for activation link !', 'success');
alert( "SAVED!!::")
}).catch((error) => {
self.$setLaravelValidationErrorsFromResponse(error.response.data);
self.is_page_updating = false
self.showRunTimeError(error, this);
self.showPopupMessage("Customer Register", 'Error adding customer ! Check Details fields !', 'warn');
// window.grecaptcha.reset()
self.is_recaptcha_verified = false;
self.$refs.customer_register_wizard.changeTab(3,0)
});
});
} else {
return response.json().then(function(jsonError) {
// ...
});
}
}).catch(function(error) {
console.log('There has been a problem with your fetch operation: ', error.message);
});
In objectURL and self I see proper values : https://imgur.com/a/4YvhbFz
1) But checking data on server in laravel's control I see the same values I had at start of my attemps to upload image:
[avatar_filename] => patlongred.jpg
[avatar] => blob:http://local-hostels2.com/d9bf4b66-42b9-4990-9325-a72dc8c3a392
Have To manipulate with fetched bnlob in some other way ?
2) If I set :
headers: {
'Content-Type': 'multipart/form-data'
}
I got validation errors that my data were not correctly requested...
?
You're using request type as application/json hence you won't be able to save the image this way, for a file upload a request type should be multipart/form-data in this case you'll need to send request as
let customerRegisterArray = new FormData();
customerRegisterArray.put('username', this.previewCustomerRegister.username);
customerRegisterArray.put('email', this.previewCustomerRegister.email);
....
customerRegisterArray.put('avatar', this.previewCustomerRegister.avatarFile);
console.log("customerRegisterArray::")
console.log(customerRegisterArray)
axios({
method: ('post'),
url: window.API_VERSION_LINK + '/customer_register_store',
data: customerRegisterArray,
headers: {
'Content-Type': 'multipart/form-data'
}
}).then((response) => {
this.showPopupMessage("Customer Register", 'Customer added successfully !Check entered email for activation link !', 'success');
alert( "SAVED!!::"+var_dump() )
}).catch((error) => {});
Thank you for your help!
Valid decision was :
var self = this;
fetch(this.previewCustomerRegister.avatarFile.blob) .then(function(response) {
if (response.ok) {
return response.blob().then(function(myBlob) {
var objectURL = URL.createObjectURL(myBlob);
let data = new FormData()
data.append('username', self.previewCustomerRegister.username)
data.append('email', self.previewCustomerRegister.email)
data.append('first_name', self.previewCustomerRegister.first_name)
data.append('last_name', self.previewCustomerRegister.last_name)
data.append('account_type', self.previewCustomerRegister.account_type)
data.append('phone', self.previewCustomerRegister.phone)
data.append('website', self.previewCustomerRegister.website)
data.append('notes', self.previewCustomerRegister.notes)
data.append('avatar_filename', self.previewCustomerRegister.avatarFile.name)
data.append('avatar', myBlob)
axios({
method: 'POST',
url: window.API_VERSION_LINK + '/customer_register_store',
data: data,
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data' // multipart/form-data - as we need to upload with image
}
}).then((response) => {
self.is_page_updating = false
self.message = ''
self.showPopupMessage("Customer Register", 'Customer added successfully ! Check entered email for activation link !', 'success');
alert( "SAVED!!::123")
// self.$router.push({path: '/'});
}).catch((error) => {
self.$setLaravelValidationErrorsFromResponse(error.response.data);
self.is_page_updating = false
self.showRunTimeError(error, this);
self.showPopupMessage("Customer Register", 'Error adding customer ! Check Details fields !', 'warn');
window.grecaptcha.reset()
self.is_recaptcha_verified = false;
self.$refs.customer_register_wizard.changeTab(3,0)
});
});
} else {
return response.json().then(function(jsonError) {
// ...
});
}
}).catch(function(error) {
console.log('There has been a problem with your fetch operation: ', error.message);
});
and common laravel's file uploading functionality :
$customerAvatarUploadedFile = $request->file('avatar');
...
I'm using a directline inside my website and I was wondering if there is anyway to get the URL of the website inside my bot code. Previously, in v3, I was initializing the chat with:
BotChat.App({
directLine: { secret: "{directline_secret}" },
user: { id: 'You', referrer: window.location.href},
bot: { id: '{bot_id}' },
resize: 'detect'
}, document.getElementById("bot"));
and I was able to get the referrer with this line of code activity.From.Properties["referrer"].ToString(), but in v4 I can't find a way to get the referrer inside the bot.
Can someone help me?
Thanks in advance.
In v4 the value is part of the turnContext.activity (in Node) or turnContext.Activity (in C#) object. Passing the url value as you have done in your question (i.e., as part of the user object) you would access it like so (Node example):
async onTurn(turnContext) {
if (
turnContext.activity.type === "event" && turnContext.activity.name === "eventName"
) {
this.userProfile.location = turnContext.activity.from.referrer;
await console.log(this.userProfile.location);
}
I included a name as well as specified a type in my BotChat.App post to match this event to in the turnContext.activity:
function testMethod(someValue) {
botConnection
.postActivity({
from: { id: 'me', referrer: window.location.href },
name: 'eventName',
type: 'event',
value: someValue
})
.subscribe(function (id) {
console.log('"eventName" sent');
});
};
In this example, the method is tied to a button being pressed on the page.
Hope of help!
I am looking to write what I am calling structural expectations with Jest and I am not sure how this could be accomplished.
To start I have a graphql server and a database with a number of todo items. I currently have the following test that just returns true if the content within the database is the same as the response that I have written. I want to check instead that the response looks like an object with data that could be anything.
Here is the code that I have:
describe('To Do:', () => {
it('add todo items', async () => {
const response = await axios.post('http://localhost:5000/graphql', {
query: `
query {
getTodoItems {
message
id
dateCreated
dateDue
}
}
`
});
const { data } = response;
expect(data).toMatchObject({
data: {
getTodoItems: [
{
message: "message",
id: "5bd9aec8406e0a2170e04494",
dateCreated: "1540992712052",
dateDue: "1111111111"
},
{
message: "message",
id: "5bd9aeec60a9b2579882a308",
dateCreated: "1540992748028",
dateDue: "1111111111"
},
{
message: "new message",
id: "5bd9af15922b27236c91837c",
dateCreated: "1540992789836",
dateDue: "1111111111"
}
]
}
})
});
});
Now I want to write something like this, where there can be any number of returned items and they follow similar structuring:
describe('To Do:', () => {
it('add todo items', async () => {
const response = await axios.post('http://localhost:5000/graphql', {
query: `
query {
getTodoItems {
message
id
dateCreated
dateDue
}
}
`
});
const { data } = response;
expect(data).toMatchObject({
data: {
getTodoItems: [
{
message: expect.any(String),
id: expect.any(String),
dateCreated: expect.any(String),
dateDue: expect.any(String)
} // There needs to be unlimited additional items here
]
}
})
});
});
I have been looking throught the docs and I even tried nesting the expectations but I can't seem to get the desired response. Let me know what yo think or if I can clarify in any way.
I figured out the best way for me to do it. I would love to hear better answers. I wrote a function within the scope of the test as a jest.fn and then I called it. In that function, I made custom checks to parse the data that was received in the response. From there I added an expect function with the 'toHaveReturnedWith' method to see what the response of my custom function was and finishing out the test.
const addTodoResponse = jest.fn(() => {
// Custom parsing and check here
// Returns true or false
});
addTodoResponse();
expect(addTodoResponse).toHaveReturnedWith(true);
Are there better ways to do this out there?
I have divided the data entry in a REST call in 4 parts. Data can be sent to REST call via:-
headers
query params
path params
request body
So in order to validate the presence of any key in any of the above 4 parts I have created a schema in this format. So if in case I have to validate anything in query params I will add the key 'query' and then add the fields inside that, that needs to be validated
const schema = {
id: 'Users_login_post',
type: 'object',
additionalProperties: false,
properties: {
headers: {
type: 'object',
additionalProperties: false,
properties: {
Authorization: {
type: 'string',
minLength: 10,
description: 'Bearer token of the user.',
errorMessages: {
type: 'should be a string',
minLength: 'should be atleast of 23 length',
required: 'should have Authorization'
}
}
},
required: ['Authorization']
},
path: {
type: 'object',
additionalProperties: false,
properties: {
orgId: {
type: 'string',
minLength: 23,
maxLength: 36,
description: 'OrgId Id of the Organization.',
errorMessages: {
type: 'should be a string',
minLength: 'should be atleast of 23 length', // ---> B
maxLength: 'should not be more than 36 length',
required: 'should have OrgId'
}
}
},
required: ['orgId']
}
}
};
Now, in my express code, I created a request object so that I can test the validity of the JSON in this format.
router.get("/org/:orgId/abc", function(req, res){
var request = { //---> A
path: {
orgId : req.params.orgId
},
headers: {
Authorization : req.headers.Authorization
}
}
const Ajv = require('ajv');
const ajv = new Ajv({
allErrors: true,
});
let result = ajv.validate(schema, request);
console.log(ajv.errorsText());
});
And I validate the above request object (at A) against my schema using AjV.
The output what I get looks something like this:
data/headers should have required property 'Authorization', data/params/orgId should NOT be shorter than 23 characters
Now I have a list of concerns:
why the message is showing data word in the data/headers and data/params/orgId even when my variable name is request(at A)
Also why not my errormessages are used, like in case of orgId I mentioned: should be atleast of 23 length (at B) as a message, even then the message came should NOT be shorter than 23 characters.
How can I show request/headers instead of data/headers.
Also, the way I used to validate my path params, query params, header params, body param, is this the correct way, if it is not, then what can be the better way of doing the same?
Please shed some light.
Thanks in advance.
Use ajv-keywords
import Ajv from 'ajv';
import AjvKeywords from 'ajv-keywords';
// ajv-errors needed for errorMessage
import AjvErrors from 'ajv-errors';
const ajv = new Ajv.default({ allErrors: true });
AjvKeywords(ajv, "regexp");
AjvErrors(ajv);
// modification of regex by requiring Z https://www.regextester.com/97766
const ISO8601UTCRegex = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?Z$/;
const typeISO8601UTC = {
"type": "string",
"regexp": ISO8601UTCRegex.toString(),
"errorMessage": "must be string of format 1970-01-01T00:00:00Z. Got ${0}",
};
const schema = {
type: "object",
properties: {
foo: { type: "number", minimum: 0 },
timestamp: typeISO8601UTC,
},
required: ["foo", "timestamp"],
additionalProperties: false,
};
const validate = ajv.compile(schema);
const data = { foo: 1, timestamp: "2020-01-11T20:28:00" }
if (validate(data)) {
console.log(JSON.stringify(data, null, 2));
} else {
console.log(JSON.stringify(validate.errors, null, 2));
}
https://github.com/rofrol/ajv-regexp-errormessage-example
AJV cannot know the name of the variable you passed to the validate function.
However you should be able to work out from the errors array which paths have failed (and why) and construct your messages from there.
See https://ajv.js.org/#validation-errors
To use custom error messages in your schema, you need an AJV plugin: ajv-errors.
See https://github.com/epoberezkin/ajv-errors