Added spaces and LRs in JSON via Appmaker - google-apps-script

I'm working in AppMaker to create a new employee/user provisioning workflow. I'm at the point where I am creating a new G Suite user, and I have a very weird problem with spaces in my JSON. It's leading to an error: GoogleJsonResponseException: API call to directory.users.insert failed with error:
Invalid Input at provisionUser (AdminDirectory:5)
I've been troubleshooting this for a while, and it seems that somewhere between AppMaker and the AdminDirectory call, some extra spaces and hard returns are inserted into my user data. Below I'm going to show:
The client-side code for gathering user information
The server-side user creation script
The console output of the user JSON
The output of the user JSON that comes to my email for troubleshooting
The emailed output as seen in Notepad++
Client-Side Code
...a bunch of data gathering from a form, then...
var user = {
primaryEmail: email,
name: {
givenName: firstName,
familyName: lastName
},
addresses: [{
type: 'work',
formatted: address
}],
organizations: [{
title: title,
department: department,
fullTimeEquivalent: ftpt
}],
phones: [{
type: 'work',
value: phone
}],
locations: [{
buildingId: building,
type: 'desk',
area: 'desk'
}],
password: 'xxxxxxxx',
changePasswordAtNextLogin: true,
orgUnitPath: orgUnit,
relations: [{
type: 'manager',
value: supervisor
}],
customSchemas: {
sclsnj: {
startDate: effective,
mls: mls,
location: location
}
}
};
google.script.run.provisionUser(user, grouparray);
Server-Side Code
function provisionUser(user, grouparray) {
user = JSON.stringify(user);
MailApp.sendEmail('lhoffman#xxxxxxxx.org', 'New Google User', user);
console.log(user);
user = AdminDirectory.Users.insert(user);
for (var g = 0; g < grouparray.length; g++) {
var groupEmail = grouparray[g] + '#xxxxxxxx.org';
var member = {
email: user,
role: 'MEMBER'
};
Logger.log(groupEmail);
AdminDirectory.Members.insert(member, groupEmail);
}
}
Console Output
{"primaryEmail":"jsmith#xxxxxxxx.org","name":{"givenName":"John","familyName":"Smith"},"addresses":[{"type":"work","formatted":"Bound Brook branch\n402 E High Street, Bound Brook, NJ 08805"}],"organizations":[{"title":"Library Technician","department":"Adult Services","fullTimeEquivalent":100000}],"phones":[{"type":"work","value":"908-458-8410"}],"locations":[{"buildingId":"BBROOK","type":"desk","area":"desk"}],"password":"xxxxxxxx","changePasswordAtNextLogin":true,"orgUnitPath":"/Branches/Bound Brook branch","relations":[{"type":"manager","value":"msmith#xxxxxxxx.org"}],"customSchemas":{"sclsnj":{"startDate":"2019-05-20T04:00:00.000Z","mls":false,"location":"Bound Brook branch"}}}
Email Output
{"primaryEmail":"jsmith#xxxxxxxx.org","name":{"givenName":"John","familyName":"Smith"},"addresses":[{"type":"work","formatted":"Bound
Brook
branch\n402 E High Street, Bound Brook, NJ
08805"}],"organizations":[{"title":"Library Technician","department":"Adult
Services","fullTimeEquivalent":100000}],"phones":[{"type":"work","value":"908-458-8410"}],"locations":[{"buildingId":"BBROOK","type":"desk","area":"desk"}],"password":"xxxxxxxx","changePasswordAtNextLogin":true,"orgUnitPath":"/Branches/Bound
Brook
branch","relations":[{"type":"manager","value":"msmith#xxxxxxxx.org"}],"customSchemas":{"sclsnj":{"startDate":"2019-05-20T04:00:00.000Z","mls":false,"location":"Bound
Brook branch"}}}
Email Output in Notepad++ (with control characters)
NOTE: I realize the JSON is actually represented in the image twice, but you can definitely see the extra space and left feed characters there.
I've confirmed that I can provision a user with the console output by using the Google API Explorer and copying and pasting it into the request body for the Directory API Users.insert.
I've also confirmed that the emailed version of the output does not work using the same method. When I paste that version into the request body in the API Explorer, I get an error alert and it's clear from the color-coding in the body field that it is not correct. Even when I don't email the output to myself, I get the same error behavior, so I'm pretty sure that the act of emailing isn't messing things up.
Help!!

The invalid input is a result of a specific field not correclty formatted. In your case, I see that you are using custom schemas. There is a specific filed in the custom schema that is not properly formatted, that is startDate. Instead of providing the value of 2019-05-20T04:00:00.000Z, just use 2019-05-20.
The reason for that is because the field is expecting a complete date only value instead of a complete date plus hours and minutes. As you can see on the ISO-8601 date format, the complete date format is YYYY-MM-DD.
Reference: https://developers.google.com/admin-sdk/directory/v1/reference/schemas/insert

What happens if you do it this way?
function provisionUser(user, grouparray) {
var user = JSON.stringify(user);
MailApp.sendEmail('lhoffman#xxxxxxxx.org', 'New Google User', user);
console.log(user);
AdminDirectory.Users.insert(user);
for (var g = 0; g < grouparray.length; g++) {
var groupEmail = grouparray[g] + '#xxxxxxxx.org';
var member = {
email: user,
role: 'MEMBER'
};
Logger.log(groupEmail);
AdminDirectory.Members.insert(member, groupEmail);
}
}

Related

Destruct Parse.Object instance

I would like to know an easy way to destruct a Parse.Object instance.
Lets say I have an Parse.Object instance with following attributes:
const Address = new Parse.Object<Address>("Address", {
address: "St. Nowhere",
zipCode: 33111,
timezoneOffset: -2,
dayLightSavingTime: true
})
I want to destruct Address to get is't properties values easily. Like:
const {
address,
zipCode,
...OtherAttributes
} = Address
Well I found that it is easy as:
const {
address,
zipCode,
...OtherAttributes
} = Address.toJSON()
Be advised that if you are using Typescript and Address is possible undefined you will get the error:
TS2339: Property 'address' does not exist on type '(ToJSON & JSONBaseAttributes) | undefined'.
One solution would be:
const {
address,
zipCode,
...OtherAttributes
} = Address.toJSON() ?? {}
Caution with Date attributes. They are transformed to { __type: 'Date'; iso: string; } where iso is the date in ISO 8601 format.

How to change course owner using Classroom.Courses.patch()

I'm trying to use Apps Script to change the owner of a Google Classroom course.
According to the documentation I can do it using .patch(). This is the relevant script:
var body = {
ownerId: owner
}
var mask={
mask:'ownerId'
}
var optArgs = (mask, body);
var course = {
name: classname,
courseState: state,
description: desc,
descriptionHeading: descHead,
room: room,
section: section
};
Classroom.Courses.patch(course, courseId, optArgs )
When I run it to update the owner I get the following error message
Could not be updated Invalid JSON payload received. Unknown name "ownerId": Cannot bind query parameter. Field 'ownerId' could not be found in request message.
Just to demonstrate a contrast, when I run .update() instead:
Classroom.Courses.update(course, courseId)
...the rest of the course details are updated perfectly, however .update() doesn't allow you to change the owner; it has to be a .patch() to do that.
Just had the same problem, the issue is the optArgs - you just want the mask in there, not your changed data.
var optArgs = { mask: 'ownerId' };
var course = {
name: classname,
courseState: state,
description: desc,
descriptionHeading: descHead,
room: room,
section: section,
ownerId: owner
};
Classroom.Courses.patch(course, courseId, optArgs );
I just tried this a few moments ago and it worked. Take into account that, for this to work, the user must already be a teacher in the specific class, and then this will make that teacher the owner.
function changeTeacherOwner() {
var course = { 'ownerId': "EMAIL OF TEACHER" };
Classroom.Courses.patch(course, "COURSE ID", {'updateMask':'ownerId'});
}
Based on documentation, this is working as intended.
courses.update has no ownerId parameters, only id.
But if you check courses.patch it has the updateMask parameter which accepts ownerId as a valid field.
For detailed information on this service, see the reference documentation for the Classroom API.

Publish a google spreadsheet as json

The Sheetsee library uses google spreadsheets as a data backend. I'm trying to publish my google spreadsheet as json so that I can access it using the Sheetsee library. The current 'Publish to the web' function available in google docs doesn't show any option to publish the data as json. Is this something that has been removed from Google Spreadsheets or is it available somewhere else in google docs?
First, you must publish your spreadsheet to the web, using File -> Publish To Web in your Google Spreadsheet.
You can then access your readable JSON API using the /api endpoint.
http://gsx2json.com/api?id=SPREADSHEET_ID&sheet=SHEET_NUMBER&q=QUERY
This will update live with changes to the spreadsheet.
Parameters :
id (required): The ID of your document. This is the big long aplha-numeric code in the middle of your document URL.
sheet (optional): The number of the individual sheet you want to get data from. Your first sheet is 1, your second sheet is 2, etc. If no sheet is entered then 1 is the default. Example
q (optional): A simple query string. This is case insensitive and will add any row containing the string in any cell to the filtered result. Example
integers (optional - default: true): Setting 'integers' to false will return numbers as a string (useful for decimal points). Example
rows (optional - default: true): Setting 'rows' to false will return only column data. Example
columns (optional - default: true): Setting 'columns' to false will return only row data.
Example Response:-
There are two sections to the returned data - Columns (containing each column as a data array), and Rows (containing each row of data as an object.
{
columns: {
name: [
"Nick",
"Chris",
"Barry"
],
age: [
21,
27,
67;
]
},
rows: [
{
name: "Nick",
age: 21
},
{
name: "Chris",
age: 27
},
{
name: "Barry",
age: 67
}
]
}
src="http://gsx2json.com/"

GAS adding a non domain member to google group doesnt work

I'd like to create a spreadsheet to let user create his own google groups for mailing list purpose.
I can add domain's member to grups with this code:
function addGroupMember (groupEmail, userEmail,ruolo) {
var member = {
email: userEmail,
role: ruolo
};
try {
member = AdminDirectory.Members.insert(member, groupEmail);
Logger.log('User %s added as a member of group %s.', userEmail, groupEmail);
}catch(e){
Logger.log("adding in:" + groupEmail + " failed user email: "+ userEmail + " role: " + ruolo + " error:" + e);
}
But if i try to add a non domain's user (i can do it in group management) it fails with this error:
[16-08-12 14:54:13:113 CEST] adding in: mygropu#mydomain.it failed user e mail: user#otherdomanin.it role: MEMBER error: Exception: Invalid Input: memberKey
As the error log shows exception Invalid Input: memberKey, it means that you may not able to give the real memberKey/ authentic memberKey. memberKey is the user's or group priary email address, user's alias email address or the user's unique id
To get the memberKey, you can use the Members:get to retrieves a group members properties.
HTTP request:
GET https://www.googleapis.com/admin/directory/v1/groups/groupKey/members/memberKey
Response:
{
"kind": "admin#directory#member",
"etag": etag,
"id": string,
"email": string,
"role": string,
"type": string
}
id is the unique ID of the group member. A member id can be used as a member request URI's memberKEy
I made some test more, and found the clue!
The Exception: "Resource Not Found: email#otherdomain.com" rises up when the otherdomain is gmail.com and the mail address does not exist.
Using any other domain works even for non existent addresses! I think that G.A.S. checks only for his own domain addresses, but can't do it for other domains.

Node.js JSON extract certain data

I'm trying to get certain data from a json link:
bittrex.com/api/v1.1/public/getticker?market=BTC-DRS
in my node IRC bot using:
https://www.npmjs.org/package/node.bittrex.api
Part of the code:
var url = ('https://bittrex.com/api/v1.1/public/getticker?market=BTC-DRS');
bittrex.options({
'apikey' : settings.ticker.apikey,
'apisecret' : settings.ticker.secretkey,
'stream' : false,
'verbose' : false,
'cleartext' : true,
});
case 'ticker':
var user = from.toLowerCase();
bittrex.sendCustomRequest(url, function(ticker, err) {
if(err) {
winston.error('Error in !ticker command.', err);
client.say(channel, settings.messages.error.expand({name: from}));
return;
}
winston.info('Fetched Price From BitTrex', ticker);
client.say(channel, settings.messages.ticker.expand({name: user, price: ticker}));
});
break;
It works but outputs in IRC
[1:21am] <nrpatten> !ticker
[1:21am] <DRSTipbot> nrpatten The current DRS price at BitTrex {"success":true,"message":"","result":{"Bid":0.00000155,"Ask":0.00000164,"Last":0.00000155}}
I have used a couple of things to get it to show only "Last" from the reply but i keep getting errors.
Or get certain data from https://bittrex.com/api/v1.1/public/getmarketsummaries
Like any info i want from:
{"MarketName":"BTC-DRS","High":0.00000161,"Low":0.00000063,"Volume":280917.11022708,"Last":0.00000155,"BaseVolume":0.33696054,"TimeStamp":"2014-10-04T15:14:19.66","Bid":0.00000155,"Ask":0.00000164,"OpenBuyOrders":33,"OpenSellOrders":138,"PrevDay":0.00000090,"Created":"2014-06-18T04:35:38.437"}
Thanks for any help
Assuming you've parsed the JSON (e.g. via JSON.parse(str);), you just use whatever property name you want to get at. For example:
var info = JSON.parse('{"MarketName":"BTC-DRS","High":0.00000161,"Low":0.00000063,"Volume":280917.11022708,"Last":0.00000155,"BaseVolume":0.33696054,"TimeStamp":"2014-10-04T15:14:19.66","Bid":0.00000155,"Ask":0.00000164,"OpenBuyOrders":33,"OpenSellOrders":138,"PrevDay":0.00000090,"Created":"2014-06-18T04:35:38.437"}');
console.log(info.Bid);
Also, on an unrelated matter, typically callback parameters follow the error-first format (e.g. (err, result) instead of (result, err)) in order to be consistent with node core and most other modules on npm.