Why is the equation object empty in json when returned from the google doc api? - node.js

I am trying to fetch the google doc content using the google doc api but I see that the json returned from the google doc api is missing eqaution information.
Example content :-
google doc excerpt
Example request :-
const baseUrl = `https://docs.googleapis.com/v1/documents/${doc_id}`
const response = await googleReq(baseUrl, ctx.accessToken).then(res => {
let data = res.json()
return data
}).catch(err => console.log(err))
const googleReq = (url, token) => {
if (!token) return new Error("No accessToken")
return fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`
}
})
}
example response :-
...
{
"startIndex": 321,
"endIndex": 330,
"equation": {}
}
...
As you can see the equation block is not returning anything. I am not sure why it would be the case.

Answer:
Unfortunately, at present, you can not get equation information from the Google Docs API.
More Information:
As per the developer documentation, the equation block returned by the API should contain:
suggestedInsertionIds[]
suggestedDeletionIds[]
These IDs would be alphanumeric strings which indicate the suggestions that an editor would have inserted or deleted.
This block will not contain any equation text or other related info.
The Only Way:
At present, there is only one to get the equation text programmatically, and that is using Google Apps Script methods.
An example script to do this would be:
// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0
function getEquations() {
const searchElementType = DocumentApp.ElementType.EQUATION
const documentBody = DocumentApp.getActiveDocument().getBody()
const searchResult = null
while (searchResult = documentBody.findElement(searchElementType,searchResult)) {
let par = searchResult.getElement().asEquation()
console.log("Search Result : " + par.getText())
}
}
As a workaround, you could deploy this as a web app which serves the equation text as JSON content, and then use the fetch API in JavaScript to get this data rather than using the Google Docs API directly.
Feature Request:
You can however let Google know that this is a feature that is important for access to their APIs, and that you would like to request they implement it.
Google's Issue Tracker is a place for developers to report issues and make feature requests for their development services, I'd urge you to make a feature request there. The best component to file this under would be the Google Docs component, with the Feature Request template.
References:
REST Resource: documents | Google Docs API | Google Developers
Enum ElementType | Apps Script | Google Developers
Class DocumentApp | Apps Script | Google Developers
Class ContainerElement | Apps Script | Google Developers
References for Web App-based Workround:
Web Apps | Apps Script | Google Developers
Content Service | Apps Script | Google Developers
Serving JSON from scripts
Class ContentService | Apps Script | Google Developers

Related

Why is my binary file in OneDrive, sent first from Postman then forwarded by Node.js code using Graph API, not identical to the original?

I use Postman to POST a binary (Excel) file to my Node.js (Typescript) Azure function, which PUTs the body of the request on to Microsoft Graph API as a new file upload - so it acts as a proxy for an upload to OneDrive using the Graph API. I use axios rather than the Graph client.
The file is created at the expected location in OneDrive but the file data is not identical to the original - and when opened in Excel, it sees it as corrupted.
So sending it via Postman:
this is the (simplified) Typescript code receiving it and passing it onto Graph API:
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const fileBytes = req.body;
const config:AxiosRequestConfig = {
headers: {
'Authorization': `Bearer ${bearerToken}`,
'Content-Type': 'text/plain'
}
}
const url:string = `https://graph.microsoft.com/v1.0/me/drive/root:/Testing/TDA/Test.xlsx:/content`;
const uploadResult:AxiosResponse<any, any> = await axios.put(url, fileBytes, config);
}
The file size is larger (22KB vs the original 18KB) and the encoding looks to be different as opening the original and its new copy in OneDrive in Notepad++ shows there are differences (one example highlighted in green):
What am I missing here? Is there a way I can ensure the format/encoding of the file uploaded and saved into OneDrive through Microsoft Graph API ends up being identical (in binary terms) to the original file sent through Postman?
Cheers - Nicolaas

Mongoose/NodeJS - Sort button in .EJS template

In my Mongoose controller I am sorting according to creation date of the product:
let products = await Product.find().sort({ 'createdAt': -1 });
I want to create a button in EJS template which allows the user to select different criteria to sort by (e.g. price). Creating the button itself is no problem, but I don't know how to access the sort function in the controller when an option is selected (e.g. if price selected, sort by price), and where I should be putting the code, in the controller or in the EJS template?
I assume I should be using req.query, but I don't know how. Thanks.
Part One - Abstraction Mongoose API to JS
Mongoose allows you to build your query in sections. Let say you have a function the query Product with additional options object.
findOptions - contains the filter options (the where part of the query)
pagingOptions - contains sorting+ordering,, page and pageSize.
This is all the context you need to build a query.
async function getProducts(findOptions, pagingOptions) {
const query = Product.find(findOptions);
if(pagingOptions.sort) {
// Note: This example assumes that paging.sort has the same structure as Monguse sort object.
query.sort(paging.sort);
}
let products = await query.exec();
...
return products;
}
Usage:
let products = await getProducts({}, { 'createdAt': -1 });
let products = await getProducts({ 'createdAt': { $gt: Date.now() }}, { '_id': 1 });
You can add or restrict many many things using the options object to manage the query context
See: mongoose query doc
Part Two - Exposing an API
Now that we have a method at the server-side that can query the Products using only JS object (JSON) you will need to expose an HTTP API so a client will be able to call the server with his query criteria.
Let defined this is the HTTP API that we will send to the client to integrate with:
POST /products
// request body
{
sort: { FIELD_NAME: ASC|DESC }, // ASC=1, DESC=-1
}
Usage:
curl -X POST -H "Content-Type: application/json" --data '{ sort: { "createdAt": "1" } }' http:localhost:3000
Note: you can extend both the HTTP API and the Backend method options to extend the functionality further.
See the following to check how to implement HTTP endpoint via express
https://expressjs.com/en/guide/routing.html
http://expressjs.com/en/resources/middleware/body-parser.html#bodyparserjsonoptions
Part Three - FrontEnd Integration
Now all is left is to implement an HTTP client and expose the HTTP API so the rest of the FrontEnd modules will be able to call the backend.
The following is a simple fetch example but you can use any HTTP client lib that you like.
const productsEndpoint = baseUrl + '/products';
async function getProducts(options) {
const response = await fetch(productsEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(options)
});
return response.json();
}
These three steps will you to have a well-defined API across components and the layers within them.

How to query the gitlab API from the browser?

Just to give some context, I'd like to implement a blog with gitlab pages, so I want to use snippets to store articles and comments. The issue is that querying the API from the browser triggers a CORS error. Here is the infamous code:
const postJson = function(url, body) {
const client = new XMLHttpRequest();
client.open('POST', url);
client.setRequestHeader('Content-Type', 'application/json');
return new Promise((resolve, reject) => {
client.onreadystatechange = () => {
if (client.readyState === 4) {
client.status === 200
? resolve(client.responseText)
: reject({status: client.status, message: client.statusText, response: client.responseText})
}
}
client.send(body)
})
};
postJson('https://gitlab.com/api/graphql', `query {
project(fullPath: "Boiethios/test") {
snippets {
nodes {
title
blob {
rawPath
}
}
}
}
}`).then(console.log, console.error);
That makes perfect sense, because it would allow to fraudulently use the user's session.
There are several options:
Ideally, I would like to have an option to disable all form of authentication (particularly the session), so I could only access the information that is public for everybody.
I could use a personal access token, but I'm not comfortable with this, because the scopes are not fine-grained at all, and leaking such a PAT would allow everybody to see everything in my account. (doesn't work)
I could use OAuth2 to ask for every reader the authorization to access their gitlab account, but nobody wants to authenticate to read something.
I could create a dummy account, and then create a PAT. That's the best IMO, but that adds some unnecessary complexity. (doesn't work)
What is to correct way to query the gitlab API from the browser?
After some research, I have found this way to get the articles and the comments. The CORS policy was triggered because of the POST request with a JSON content. A mere GET request does not have this restriction.
I could recover the information in 2 times:
I created a dummy account, so that I could have a token to query the API for my public information only,
Then I used the API V4 instead of the GraphQL one:
// Gets the snippets information:
fetch('https://gitlab.com/api/v4/projects/7835068/snippets?private_token=AmPeG6zykNxh1etM-hN3')
.then(response => response.json())
.then(console.log);
// Gets the comments of a snippet:
fetch('https://gitlab.com/api/v4/projects/7835068/snippets/1742788/discussions?private_token=AmPeG6zykNxh1etM-hN3')
.then(response => response.json())
.then(console.log);

Unable to upload file using googleapis on Google Drive

I have a problem with the file upload on google drive using the package googleapis (v.40):
In my web application (written in Vue js) i need to upload an image file of user 'A' into the google drive space of user 'B' using googleapis.
For this, i have created a "service-account" from the google console platform (of user B) and generated the credentials.json for the API access. (JWToken,service-to-service scenario)
In my web application, after getting the AccessToken by means the json credentials of the service account, i'm ready to upload the file. But, when i call the drive.files.create(...) api i get the following error:
Invalid multipart request with 0 mime parts.
Here some code:
...
// get google api
const {google} = require("googleapis");
const drive = google.drive("v3");
// get authorization token
let authToken = await getAuthToken() // it perfectly works
console.log(authToken)
let metadata = {
name: name,
parents: [idOfTheParentFolder]
}
let media = {
mimeType: 'image/jpeg',
body: file
}
let objImage = {
auth: authToken,
resource:metadata,
media: media,
fields: 'id',
}
drive.files.create(objImage, function (error, success) {
if (!error) {
...
}
else{
// here i got the error in question
}
})
I've tried exactly this code in a single node js file and it works fine, but it doesn't works in the web browser Vue application.
Let me say first that the API for showing the files (drive.files.list()) and the API for create a folder (drive.files.create()) they work just fine in my web application.
Any suggestion?

Google analytics steps to get number of views per page from an API

I want to start somewhere.
This is my use case.
I have a restAPI written in node.js express framework.
I have a mobile application written in the react native.
Also I have a web application for the administration purpose of the mobile app, written in Vue.js
Mobile app is food app. Where you can search restaurants or dishes of a restaurant.
I want to pass the restaurant id and get the restaurant views of the app with in the last 7 days.
Pretty straight forward requirement. But hard to implement. Since I don't know where to start from.
I went through the docs and have found there are 7 APIs.
Core reporting API
Management API
Embed API
User deletion API
Multi channel reporting API
Real time API
Meta data API
End of the day, I would love to do something similar to this with the API.
showMeTheView(restaurant_id)
Also If I want to pass additional parameter to say get only last month views count.
showMeTheViews(restaurant_id, last_month)
I can't figure out what are the essential steps to achieve my requirement?
What need to be done in the react-native app?
What need to be done in the vue.js web app?
What need to be done in between these two?
First off you should be using the Core reporting api this is the api that can be used to extract data from Google analytics. The Json object used to extract data form Google analytics is quite extensive batch get
It will make your life a lot easier if you use the Google apis node.js client libray
code ripped from sample found here
'use strict';
const {google} = require('googleapis');
const sampleClient = require('../sampleclient');
const analyticsreporting = google.analyticsreporting({
version: 'v4',
auth: sampleClient.oAuth2Client,
});
async function runSample() {
const res = await analyticsreporting.reports.batchGet({
requestBody: {
reportRequests: [
{
viewId: '65704806',
dateRanges: [
{
startDate: '2018-03-17',
endDate: '2018-03-24',
},
{
startDate: '14daysAgo',
endDate: '7daysAgo',
},
],
metrics: [
{
expression: 'ga:users',
},
],
},
],
},
});
console.log(res.data);
return res.data;
}
// if invoked directly (not tests), authenticate and run the samples
if (module === require.main) {
const scopes = ['https://www.googleapis.com/auth/analytics'];
sampleClient
.authenticate(scopes)
.then(runSample)
.catch(console.error);
}
// export functions for testing purposes
module.exports = {
runSample,
client: sampleClient.oAuth2Client,
};

Resources