How to create a physical file with docpad? - node.js

There's too little information in the docpad API page on creating a file.
This is what I've tried:
docpad.action("render", {
text: content,
filename: "random.html.md",
path: "./src/documents/posts",
attributes: {
id: "some-random-id,
title: "some-random-title",
layout: "default"
}
}, function(error, outContent, doc) {
res.json({
id: doc.get("id")
});
});
It gives me the document instance, but there's no physical file being created.

This is the approach I use to create virtual documents and write them to a file
outDirPath = docpad.config.outPath
docAttr = {
fullPath: null
body: 'some content html to be rendered'
outPath: outDirPath + '/index.html' # this where it will write the file
extension: 'md'
extensions: ['html', 'md']
relativePath: 'index.html'
filename: 'index.html'
write: true
render: true
layout: 'somelayout'
}
# create the document
virtualDocument = docpad.createDocument(docAttr)
docpad.renderDocument virtualDocument, { templateData: this, someCustomData: extraData}, (err, content, file) ->
# renderDocument complete callback
file.write ()->
# file.write complete callback
return
return

Related

formData Joi validation is failed when we use nested object

Issue
I am working on file upload operation on new hapiJS framework with the latest hapi-swagger version, The older one is supported correctly but now I facing some problem while I am using hapi-swagger payload type form.
Is there something I missed?
Used libraries
hapi-hapi 19.1.1
hapi-Joi: 17.1.1
Hapi-swagger: 13.0.2
Node: 12.18.1
const Joi = require("#hapi/joi");
// Route file
app.route({
method: 'POST',
path: '/fileUpload/{format}',
options: Controller.fileUpload()
});
// Controller file
public fileUpload(): Hapi.RouteOptions{
return {
handler: async (request: any, h: Hapi.ResponseToolkit) => {
try {
let file = request.payload.file
let data = request.payload.data
let fileFormat = request.params.format
//some logic
} catch (err) {
throw error;
}
},
tags: ['api', 'fileUpload', 'POST'],
description: 'file uploading',
plugins: {
'hapi-swagger': {
payloadType: 'form'
}
},
validate: {
params: Joi.object({
format: Joi.string().required().description('Format of file').example('docx, xml or html').allow('docx', 'xml', 'html')
}),
payload: Joi.object({
data: Joi.object({
objectData: Joi.object({
objectName: Joi.string().required().description('Name of object').example('xyz').disallow('null', 'undefined'),
objectRID: Joi.string().optional().example('#24:6').disallow('null', 'undefined')
}).required(),
objectTravelRelations: Joi.array().optional().items(
Joi.object({
objectName: Joi.string().required().description('Class of Object').disallow('null', 'undefined'),
objectUID: Joi.string().required().example('873cc6cd-d51d-44d0-8377-2061641a11a3').disallow('null', 'undefined'),
relation: Joi.string().required().description('Name of the relation').example('HasChild').disallow('null', 'undefined'),
direction: Joi.string().required().description('Direction of relation').example('in').allow('in', 'out').disallow('null', 'undefined')
}).required()
)
}),
file: Joi.any()
.meta({ swaggerType: 'file' }).required()
.description('file.')
}),
options: {
allowUnknown: true
},
failAction: async (request, h, error) => {
throw error;
},
},
payload: {
output: 'stream',
parse: true,
multipart: true,
maxBytes: 10485760
}
};
}
Expected Behavior
The form data is validated and the file upload successfully.
Actual Behavior
data has treated a string but actually its an object
message: ' "data" must be of type object '

What Is it the correct syntax to set permission 'sendNotificationEmails': 'false' to avoid "message" : "Rate limit exceeded. User message:

function createPermissionOffic(auth){
const drive = google.drive({version: 'v3', auth});
var fileId = rootFolderId;
var permissions = [
{
'type': 'user',
'role': 'writer',
'emailAddress': 'someemailaddress#gmail.com'
}
];
// Using the NPM module 'async'
async.eachSeries(permissions, (permission, permissionCallback)=> {
drive.permissions.create({
resource: permission,
fileId: fileId,
fields: 'id',
}, (err, res)=> {
if (err) {
// Handle error...
console.error(err);
permissionCallback(err);
} else {
console.log('Permission ID: '+ res)
permissionCallback();
}
});
}, (err)=> {
if (err) {
// Handle error
console.error(err);
} else {
// All permissions inserted
drive.permissions.insert(
{
'sendNotificationEmails': 'false'
});
}
});
I am getting below error : google drive API documentation does not elaborate much.here is the error I am getting.
TypeError: drive.permissions.insert is not a function at async.eachSeries
I believe your goal as follows.
You want to use sendNotificationEmail: false for Permissions: create method.
You want to achieve this using googleapis with Node.js.
You have already been able to create the permission using Drive API v3.
Modification points:
drive.permissions.insert is for Drive API v2. And in this case, when you run the script, an error occurs. In order to use sendNotificationEmail: false, please use it to drive.permissions.create.
About Rate limit exceeded, when the array length of permissions is large, such error might occur. So at first, as a test, how about checking whether the request works using a simple situation. In your sample script in your question, the array length of 1 is used. I think that how about checking the request using it?
In your case, the permission ID can be retrieved by res.data.id.
In your script, rootFolderId is used at var fileId = rootFolderId;. When rootFolderId is the root folder ID, an error like This file is never writable. occurs. So please be careful this.
When above points are reflected to your script, it becomes as follows.
Modified script:
const drive = google.drive({version: "v3", auth});
var fileId = "###"; // Please set the file ID or folder ID.
var permissions = [
{
type: "user",
role: "writer",
emailAddress: 'someemailaddress#gmail.com',
},
];
// Using the NPM module 'async'
async.eachSeries(
permissions,
(permission, permissionCallback) => {
drive.permissions.create(
{
resource: permission,
fileId: fileId,
fields: "id",
sendNotificationEmail: false, // <--- Added
},
(err, res) => {
if (err) {
// Handle error...
console.error(err);
permissionCallback(err);
} else {
console.log("Permission ID: " + res.data.id); // <--- Modified
permissionCallback();
}
}
);
},
(err) => {
if (err) {
// Handle error
console.error(err);
} else {
console.log("Done"); // <--- Modified
}
}
);
Reference:
Permissions: create

How to create a file in a team drive using Google Drive API

I would like to figure how to create a file within a Team drive using Google's Drive API.
Here is a reference to for files and teamdrives in google drive api's documentation.
https://developers.google.com/drive/api/v3/reference/files
https://developers.google.com/drive/api/v3/reference/files/create
https://developers.google.com/drive/api/v3/enable-shareddrives
const resource = {
name: fileName,
supportsAllDrives: true,
driveId: TEAMDRIVE_ID,
mimeType: 'application/vnd.google-apps.spreadsheet'
};
drive.files.create(
{
resource,
fields: 'id, name, webViewLink'
},
(err, file) => {
if (err) {
return({ msg: 'Failed Creating the file', err });
} else {
return file.data;
}
}
);
The code is able to create the file, but instead of it appearing in the team drive. It appears inside my personal drive. I am able to read files within my team drive. Creating files has been an issue for me though...
I ended up finding an answer to my question, here is what worked for me.
const resource = {
name: fileName,
driveId: TEAMDRIVE_ID,
mimeType: 'application/vnd.google-apps.spreadsheet',
parents: [parent_id]
};
drive.files.create(
{
resource,
fields: 'id, name, webViewLink'
supportsAllDrives: true,
},
(err, file) => {
if (err) {
return({ msg: 'Failed Creating the file', err });
} else {
return file.data;
}
}
);
I had to move the supportsAllDrives: true out of the resource object and move it as an option in the drive.files.create paramter.
Also needed to add parents to the resource object. The parent_id can be a Team Drive Id or a folder Id within the Team Drive.

How to read an image and text at the same time in Loopback/NodeJs

I am doing a remote method with Loopback to show some text and display an image. I am getting the path of the image and the fields by an sql query and data is showed correctly. What I want to do is to transform the path showed in the result to display the image itself.
This is my remote method so far :
cm_comediens.getprofile1 = function (options, cb) {
const token = options && options.accessToken;
const userId = token && token.userId;
var ds = app.datasources.mydb;
var sql = "SELECT comedien_perso_nom,comedien_perso_prenom,nationalite,photoscomedien.path FROM cm_comediens INNER JOIN photoscomedien ON cm_comediens.id_comedien=photoscomedien.id_comedien WHERE cm_comediens.id_comedien IN ( SELECT id_comedien FROM (SELECT id_comedien FROM cm_comediens WHERE id_utilisateur= '" + userId + "') as MakeitWork) AND photoscomedien.photo_profile=1 ";
ds.connector.execute(sql, [], function (err, cm_comedienss) {
if(err) {console.error(err);}
cb(err, cm_comedienss);
});
}
cm_comediens.remoteMethod(
'getprofile1', {
http: {verb: 'GET'},
description: 'Get Comediens infos',
accepts: [{arg: "options","type": "object","http": "optionsFromRequest"},],
returns: {arg: 'data',type: ['cm_comediens'],root: true,}
}
);
This is what I am getting so far and what I want to do exactly is to change the path to an image
Result
I tried to add the fs.readfile but weird result showed up. I changed the remote method as follows :
ds.connector.execute(sql, [], function (err, cm_comedienss) {
fs.readFile(cm_comedienss[0].path, function(err, cm_comedienss) {
if(err) {console.error(err);}
cb(err, cm_comedienss);
});
});
}
This is the result I got after adding the readfile :
after adding fs.readfile
The remoting metadata for your new method are describing the return value as a JSON data. The actual value is Buffer, which is converted by LoopBack to the value you shown on your screenshot.
{
"$type": "base64"
"$data": "(base64-encoded data of your image)"
}
If you want your API to return image, you need to make two changes:
tell LoopBack to set a different Content-Type header, e.g. image/png
tell LoopBack to treat the Buffer value as the raw response body
cm_comediens.getprofile1 = function (options, cb) {
ds.connector.execute(sql, [], function (err, found) {
if (err) return cb(err);
fs.readFile(found[0].path, function(err, image) {
if(err) return cb(err);
cb(null, image, 'image/png');
});
});
};
cm_comediens('getprofile1', {
http: {verb: 'GET'},
description: 'Get Comediens infos',
accepts: [
{arg: "options","type": "object","http": "optionsFromRequest"},
],
returns: [
{ arg: 'body', type: 'file', root: true },
{ arg: 'Content-Type', type: 'string', http: { target: 'header' } },
],
});

Why is the apostrophe-pages _children array is always empty?

I am trying to get the children of apostrophe pages to appear in my navigation object - however the _children array is always empty. My page does have child pages set up via the front end Pages UI.
My index.js for the lib/modules/apostrophe-pages module contains the following:
construct: function(self,options) {
// store the superclass method and call at the end
var superPageBeforeSend = self.pageBeforeSend;
self.pageBeforeSend = function(req, callback) {
// Query all pages with top_menu setting = true and add to menu collection
self.apos.pages.find(req, { top_menu: true }, {slug: 1, type: 1, _id: 1, title: 1})
.children(true)
.toArray(
function (err, docs) {
if (err) {
return callback(err);
}
req.data.navpages = docs;
return superPageBeforeSend(req, callback);
});
};
},
...
My top_menu attribute is set via apostrophe-custom-pages:
module.exports = {
beforeConstruct: function(self, options) {
options.addFields = [
{
name: 'subtitle',
label: 'Subtitle',
type: 'string'
},
{
name: 'css_class',
label: 'CSS Class',
type: 'string'
},
{
name: 'top_menu',
label: 'Include In Top Menu',
type: 'boolean'
}
].concat(options.addFields || []);
}
};
This gives me the pages I need with the top_menu setting.. but I want to get child pages too..
When debugging the code I can see that the docs._children array is present but is always empty, even though a page has child pages...
I have tried adding the following both to my app.js and to my index.js but it doesn't change the result:
filters: {
// Grab our ancestor pages, with two levels of subpages
ancestors: {
children: {
depth: 2
}
},
// We usually want children of the current page, too
children: true
}
How can I get my find() query to actually include the child pages?
Solved it..
I needed to add 'rank: 1, path: 1, level: 1' to the projection as per this page in the documentation: https://apostrophecms.org/docs/tutorials/howtos/children-and-joins.html#projections-and-children

Resources