Drive Api v3: Changes watch request TeamDriveId being ignored? - google-api

I'm trying to specify a Team Drive to watch according to the docs here: https://developers.google.com/drive/api/v3/reference/changes/watch
teamDriveId | string | The Team Drive from which changes will be returned. If specified the change IDs will be reflective of the Team Drive; use the combined Team Drive ID and change ID as an identifier.
(Side note teamDriveId appears to be deprecated in favour of driveId, but the docs don't reflect that)
I read that as saying if you supply an ID only changes related to that folder (File) and its children will cause a push notification, but I still get notifications for any changes in that user's Drive. Am I reading the docs incorrectly?
I originally posted the question to the DotNet client library repo as I'm doing this in C#. Jon Skeet suggested that IncludeItemsFromAllDrives should be false, which seems perfectly logical and I had tried that first because of that. However, that results in the following error being returned from the API endpoint:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "includeTeamDriveItemsRequired",
"message": "The includeItemsFromAllDrives parameter must be set to true when driveId is specified or corpora contains drive or allDrives."
}
],
"code": 403,
"message": "The includeItemsFromAllDrives parameter must be set to true when driveId is specified or corpora contains drive or allDrives."
}
}
I've tried several variations of the following properties to see if I can restrict it down to a Team Drive, but have been unsuccessful:
SupportsTeamDrives
SupportsAllDrives
IncludeItemsFromAllDrives
RestrictToMyDrive
Clarification would be very welcome.
Edit:
Code as requested, it's in C#, but the issue is likely to be the main API according to Jon Skeet:
Channel channel = new Channel()
{
Id = Guid.NewGuid().ToString(),
Type = "web_hook",
Address = [address],
Token = [token], //max 256 chars
Expiration = new DateTimeOffset(DateTime.Now.AddMinutes(10).ToUnixTimeMilliseconds(),
};
var watchRequest = _driveService.Changes.Watch(channel, startPageToken);
//watchRequest.SupportsTeamDrives = true;
watchRequest.SupportsAllDrives = true;
//watchRequest.TeamDriveId = rootFolderId;
//watchRequest.IncludeTeamDriveItems = true;
watchRequest.IncludeItemsFromAllDrives = true;
watchRequest.DriveId = rootFolderId;
watchRequest.RestrictToMyDrive = true;
channel = await watchRequest.ExecuteAsync();
I've tried several variations as the commented out code shows.

Related

Google API not always returning profile data

Using django-allauth, I am allowing users to log in through their Google profiles using the all of the following settings:
SOCIALACCOUNT_PROVIDERS = {
'google': {
'SCOPE': [
'profile',
'email',
'https://www.googleapis.com/auth/gmail.labels',
'https://www.googleapis.com/auth/gmail.modify'
],
'AUTH_PARAMS': {
'access_type': 'offline',
}
}
}
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
'profile',
'email',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
]
SOCIALACCOUNT_QUERY_EMAIL = True
SOCIALACCOUNT_EMAIL_VERIFICATION = "none"
SOCIALACCOUNT_EMAIL_REQUIRED = False
SOCIALACCOUNT_STORE_TOKENS = True
SOCIALACCOUNT_AUTO_SIGNUP = True
For some reason, the extra data I am receiving is inconsistent. Sometimes, I receive all of the profile data. Sometimes, I don't get the user's name. Sometimes I don't even get the user's email. The only thing I seem to consistently get is an id and a link to a avatar photo.
The accounts I am testing are all my own and I am able to verify that they all have their publicly available names saved correctly. Google seems to only sometimes return the full set of permissible data from the profile scope. I should note that the data it returns is consistent per each account. I never get different data for an account. Has anyone had an experience like this?

/* in transcoding from HTTP to gRPC

rpc CreateBook(CreateBookRequest) returns (Book) {
option (google.api.http) = {
post: "/v1/{parent=publishers/*}/books"
body: "book"
};
}
message CreateBookRequest {
// The publisher who will publish this book.
// When using HTTP/JSON, this field is automatically populated based
// on the URI, because of the `{parent=publishers/*}` syntax.
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
child_type: "library.googleapis.com/Book"
}];
Book book = 2 [(google.api.field_behavior) = REQUIRED];
string book_id = 3;
}
I don't understand post: "/v1/{parent=publishers/*}/books"
I thought publishers was a field in CreateBookRequest, then it populates to http, so it is something like this
post: "/v1/parent=publishers_field_value/books"
But publishers is not a field in CreateBookRequest
No, publishers is part of the expected value of the parent field. So suppose you have a protobuf request like this:
{
"parent": "publishers/pub1",
"book_id": "xyz"
"book": {
"author": "Stacy"
}
}
That can be transcoded by a client into an HTTP request with:
Method: POST
URI: /v1/publishers/pub1/books?bookId=xyz (with the appropriate host name)
Body:
{
"author": "Stacy"
}
If you try to specify a request with a parent that doesn't match publishers/*, I'd expect transcoding to fail.
That's in terms of transcoding from protobuf to HTTP, in the request. (That's the direction I'm most familiar with, having been coding it in C# just this week...)
In the server, it should just be the opposite - so given the HTTP request above, the server should come up with the original protobuf request including parent="publishers/pub1".
For a lot more information on all of this, see the proto defining HttpRule.

Mailchimp API Node - create campaign for list based on tags

I'm making an async api request with a firebase cloud function to create a campaign within mailchimp for a specific set of users from a list. I read in the documentation that this can be done with tags this way I can build my own structure. I'm building a donation system for a nonprofit and would like the tag to represent the name of a client who is currently being donated to.
Below is my firebase function. I'm stuck at the segment_opts object. I want to define a segment based on whether the list member has a tag equivalent my clients name.
The doc says segment_opts is "An object representing all segmentation options. This object should contain a saved_segment_id to use an existing segment, or you can create a new segment by including both match and conditions options.". I don't have any other segments set up so I figured I'd create a new one that specifies the tags to contain the client's name.
This post helped me get to this point. Stackoverflow post
I now see that condition is supposed to be a Segment Type but in the dropdown I don't see an option for Tags. Here is a link to the documentation reference. Reference
const response = await mailchimp.post('/campaigns', {
type: 'regular',
recipients: {
list_id: functions.config().mailchimp.test,
segment_opts: {
"match": "any",
"conditions": match: 'any',
conditions: [
{
condition_type: 'StaticSegment',
field: 'static_segment',
op: 'static_is',
value: ??? (Int),
},
],
}
},
});
For now I removed segment_opts and will settle on sending campaign to entire list until I figure out how to segment by tags. This version works and creates a campaign within my mailchimp account and from the UI I can see the segment options offered in the documentation but don't see an option to filter by tags
const response = await mailchimp.post('/campaigns', {
type: 'regular',
recipients: {
list_id: functions.config().mailchimp.test,
},
settings: {
subject_line: `${firstName} has been funded!`,
preview_text: `$${goal} has been raised for ${firstName}.`,
title: `${firstName} has been funded`,
from_name: 'Organization name',
reply_to: 'org_email#gmail.com',
},
});
Here is a screenshot of the dropdown options in Mailchimp dashboard.
This is what I have for my campaign segment options. Here I'm checking for two conditions. Is the SITE merge tag = the site variable I pass in, and also does the member belong to the tag/segment called tagName. However, I can't pass a tagName, only a tagId which I lookup beforehand.
'segment_opts':
{
'match': 'all',
'conditions': [
{
'condition_type': 'TextMerge',
'field': 'SITE',
'op': 'is',
'value': site
},
{
'condition_type': 'StaticSegment',
'field': 'static_segment',
'op': 'static_is',
'value': tagId
}
]
}
To get the tagId I use this Python function:
tagId, segments = self.getSegmentIdFromTagName(tagName)
This is the Python code to get the tagId from the tagName, which gets all the Segments/Tags from the system and then looks for the name you pass in:
def getSegmentIdFromTagName(self,reqTagName,segments=None):
audienceId = self.audienceId
reqId = None
if not segments:
segments = self.mcClient.lists.segments.all(list_id=audienceId,get_all=True)
for segment in segments['segments']:
segName = segment['name']
segId = segment['id']
if segName == reqTagName:
reqId = segId
break
return reqId,segments

Web Api Query for Actions msdyn_BookingResource and msdyn_BookingResourceRequirement

From the web api reference here
I tried querying the api with no luck of success specially with the parameter Schedules being stated as type string.
1.) For msdyn_BookingResource
POST: https://bhaud365dev.crm6.dynamics.com/api/data/v9.0/msdyn_BookingResource
BODY:
{"ResourceId":[GUID],"BookingStatusId":[GUID],"BookingMethod":690970003,"BookingType":1,"Schedules":"[{\"StartDateTime\":\"2019-07-15T00:00:00Z\",\"EndDateTime\":\"2019-07-19T00:00:00Z\"}]","Timeframe":5}
RESPONSE: {
"error": {
"code": "0x80040224",
"message": "The added or subtracted value results in an un-representable DateTime.\r\nParameter name: value",
2.) For msdyn_BookingResourceRequirement
POST: https://bhaud365dev.crm6.dynamics.com/api/data/v9.1/msdyn_resourcerequirements([GUID])/Microsoft.Dynamics.CRM.msdyn_BookingResourceRequirement
BODY: {
"BookingMethod": 690970003,
"BookingStatusId": [GUID],
"BookingType": 1,
"EndDateTime": "2019-07-19T07:29:00Z",
"ResourceId": [GUID],
"StartDateTime": "2019-07-15T22:00:00Z"
}
RESPONSE: {
"error": {
"code": "0x80040224",
"message": "Object reference not set to an instance of an object.",
I was able to api query for functions but for the actions I am stuck and I am not sure on what am I doing wrong. Any tips or example is greatly appreciated.
BTW. tried the above queries also in CRM REST BUILDER v2.6.0.0 Same error responses.
I spent some time, getting same weird error and then I realized they are Internal Use only Actions. It's not intended for our usage & it's highly unsupported as they tend to break in future versions when Microsoft planned to change.
I was able to successfully create the Bookable Resource Bookings with the help of below web api request.
var entity = {};
entity["Resource#odata.bind"] = "/bookableresources(7B203E2F-F2FB-E911-A813-000D3A5A1BF8)";
entity["BookingStatus#odata.bind"] = "/bookingstatuses(026BDCEF-9257-4C10-9E49-C92539B883D6)";
entity["endtime"] = "2019-11-07T21:00:00Z";
entity["starttime"] = "2019-11-07T20:00:00Z"
entity.bookingtype = 1;
entity.msdyn_bookingmethod = 690970003;
Xrm.WebApi.online.createRecord("bookableresourcebooking", entity).then(
function success(result) {
var newEntityId = result.id;
},
function(error) {
Xrm.Utility.alertDialog(error.message);
}
);

Can't write acl rules to primary calendar in google service account

So I have set up a google service account for one of my apps. My intention is to keep a google calendar associated with the admin portal that all of the admins can post events to. I have got the JWT auth working I can post events to the calendar and perform other API actions. However, for some reason I cannot change the access control rules on the primary calendar. It is initialized with a single acl rule (role: owner, scope: {type: user, value: service_account_id}), and when I try to add public read access (role: reader, scope: {type: default}) like so:
POST https://www.googleapis.com/calendar/v3/calendars/primary/acl
Authorization: Bearer my_jwt_here
{
"role":"reader",
"scope":{
"type":"default"
}
}
I get the following error:
{
"error": {
"errors": [
{
"domain": "calendar",
"reason": "cannotRemoveLastCalendarOwnerFromAcl",
"message": "Cannot remove the last owner of a calendar from the access control list."
}
],
"code": 403,
"message": "Cannot remove the last owner of a calendar from the access control list."
}
}
This doesn't make any sense to me because this request shouldn't be trying to remove any access control rules. When I create a secondary calendar and do this I have no issues. When I do this with the primary calendar of my personal google account I have no issues. Is this some behavior specific to service accounts that I am not familiar with or what? I could settle for using a non-primary calendar but it bothers me that this isn't working. Any advice is appreciated.
so I found a weird work around for this issue and im posting here because I could not find SQUAT to help resolve this so hopefully this saves others some hassle.
I will also post some common problems I found when creating a organization-wide calendar (whether this is your use case or not I believe these tips will be helpful) - Jump to the bottom of the solution to this particular error.
First I needed to set up authentication with google calendar:
const { google } = require("googleapis");
const calendar = google.calendar("v3");
const scopes = [
"https://www.googleapis.com/auth/admin.directory.resource.calendar",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/admin.directory.user",
];
const path = require("path");
const key = require(path.join(__dirname, "google-cal-api.json"));
I created a service account and then allowed it domain wide delegation with the above listed scopes; then downloaded the key. Now if you want to do actions like create calendar events FOR users within this domain what you have to do is generate a JWT token that 'impersonates' the user whos calendar you wish to interact with; like so
const generateInpersonationKey = (email) => {
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
scopes,
email
);
return jwtClient;
};
To set up a JWT client for the service account itself (and so you can create a calendar people can subscribe to; in our case it was a google calendar to show whos on leave within the workplace; so a calendar that has ALL that people can subscribe and toggle on/off was ideal) you just replace the email with 'null' and it defaults to itself, instead of 'impersonating' someone within the domain wide org.
Creating events are simple, follow the google cal api docs, depending on the auth token will depend on where the calendar is generated
JUMP HERE FOR THE IMMEDIATE SOLUTION TO THE ABOVE
For resolving the issue you pointed out; What I did was set my personal accounts email as an owner of this service accounts calendar with the following NodeJS code:
var request = await calendar.acl.insert({
auth,
calendarId: "primary",
resource: {
role: "owner",
scope: {
type: "user",
value: "callum#orgdomain.com",
},
},
});
I set myself as an owner, then I went to Google Calendar API > Patch (Try Me) filled in the calendarId as the service account with the calendar im trying to restrict; and then rule ID would be the gsuite domain domain:orgdomain.com The body should be
{
"role": "reader",
"scope": {
"type": "domain",
"value": "orgdomain.com"
}
}
And thats how I was able to restrict people within our gsuite domain from deleting or editing custom calendar events. This solution is coming from the perspective of someone who originally inserted the domain ACL as
var request = await calendar.acl.insert({
auth,
calendarId: "primary",
resource: {
role: "owner",
scope: { type: "domain", value: "orgdomain.com" },
},
});
Because adding it as a 'reader' like this messes with the service account ownership and wont allow anything but owner
Hope this has been helpful
Callum

Resources