Google calendar api: recurring events exceptions + master event update - google-calendar-api

I have an app with the following scenario:
user creates a recurring event in google calendar in my app (via google calendar API v3)
they change time of one of the instances (which creates exception instance - 'exception' in a good way)
now they want to extend that recurring event and they change either count or end date of the master event
The problem is that all the exceptions disappears after that last step (calendar UI seem to handle that, but if done via the API the exceptions are gone). I've noticed however that outlook manages that case just fine via their API.
Is there any simple way of preserving the exceptions in this scenario in google calendar via API?
I've also noticed that if I try to further update one of the original exceptions after the master is updated, that gives error (an 'exception' in a bad way) and points that the instance event is marked as deleted hence can't be updated ('get' still returns it). In this case it created a new instance event with a new id in place of the original one.
UPDATE
A pseudo code to update the master event would look something like this:
let originalMastEvent = {
id:"google_generated_master_event_id",
...
recurrence: [ 'RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=WE,SA;COUNT=6' ],
...
}
//change the count from 6 to 10, the rest stays the same:
let newMastEvent = {
id:"google_generated_master_event_id",
summary: 'Summary',
start: { dateTime: '2020-06-24T01:00:00+02:00', timeZone: 'UTC' },
end: { dateTime: '2020-06-24T02:00:00+02:00', timeZone: 'UTC' },
recurrence: [ 'RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=WE,SA;COUNT=10' ],
sendUpdates: 'all',
attendees:[ { email: 'attendee1#gmail.com'} ]
}
const {google} = require('googleapis/build/src/index'); //I'm on node.js
let auth = {...} //setup google api oauth obj
let calendar = await google.calendar({version: 'v3', auth});
let res = await calendar.events.update({
calendarId: 'primary',
eventId: newMastEvent.id,
resource: newMastEvent,
sendUpdates: newMastEvent.sendUpdates
})

Your issue is related to how are you adding the exceptions and also in the way you are updating the main event.
According to this page here you need to use a POST request when inserting an exception.
Therefore, you will have to use the Events:insert request:
POST https://www.googleapis.com/calendar/v3/calendars/calendarId/events
With the following body:
{
"start": {
"dateTime": "MODIFIED_START_DATE",
"timeZone": "YOUR_TIMEZONE"
},
"end": {
"dateTime": "MODIFIED_END_DATE",
"timeZone": "YOUR_TIMEZONE"
},
"recurringEventId": "ID_OF_THE_RECURRENT_EVENT",
"originalStartTime": {
"dateTime": "ORIGINAL_DATE_TIME",
"timeZone": "YOUR_TIMEZONE"
},
"iCalUID": "ID",
}
Afterwards, if you want to update the main event, for example extending the number of occurrences, you can simply to the request below:
PUT https://www.googleapis.com/calendar/v3/calendars/calendarId/events/eventId
With the following body:
{
"start": {
"dateTime": "START_DATE_OF_THE_ORIGINAL_EVENT",
"timeZone": "YOUR_TIMEZONE"
},
"end": {
"dateTime": "END_DATE_OF_THE_ORIGINAL_EVENT",
"timeZone": "YOUR_TIMEZONE"
},
"recurrence": [
"RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=FR"
]
}
After the above requests, you should be able to still have the exception in place while also making the extension of the event.
Reference
Calendar API Events: list;
Calendar API Events: insert;
Calendar API Events: instances;
How do I add an exception to a recurring event?.

Related

RTK Query invalidatesTags doesn't seem to remove cached data every time

I have an RTK Query mutation endpoint rejectApplication, that invalidates the getApplication query. These are in the same API.
rejectApplication: builder.mutation<RejectResponse, string>({
query: (applicationId) => ({
url: `/applications/${applicationId}`,
method: "DELETE",
}),
invalidatesTags: (_result, _error, applicationId) => [
"Status",
{ type: "Application", id: "LIST" },
{ type: "Application", id: applicationId },
],
}),
getApplication: builder.query<ApplicationResponse, string>({
query: (applicationId: string) => ({
method: "GET",
url: `/applications/${applicationId}`,
}),
providesTags: (_result, _error, id) => [{ type: "Application", id: id }],
}),
Problem is that I have two components that use the useRejectApplicationMutation hook, but for some reason only one of them seems to correctly remove the query result from cache after it has been invalidated. I can observe this through the Redux devtools, where I can see the removeQueryResult actions being dispatched after the reject mutation has fulfilled in one component, but not firing in the other component. This leads to the getApplication data in the component not changing, which breaks the flow of the application.
const {
data,
isLoading: getApplicationIsLoading,
isError: getApplicationIsError,
} = useGetApplicationQuery(props.application.applicationId as string);
useEffect(() => {
if (data) {
dispatch(setIncompleteApplication(data));
}
}, [data]);
So in this case, the useEffect with data is not called because data doesn't seem to be refetched, although it should be invalidated after reject mutation is fulfilled. Weirdly, in the console it does look like it should be correctly refetching the application and status which are invalidated, as the MSW endpoints are hit after the delete request.
[MSW] 12:37:38 DELETE /v1/applications/XA1234567 (200 OK)
[MSW] 12:37:38 GET /v1/status (200 OK)
[MSW] 12:37:39 GET /v1/applications/XA1234567 (200 OK)
To me, the problem seems to be that the cache isn't properly cleared for some reason, so although the tags are invalidated and refetches made the data isn't properly reset. Any ideas to what might be causing this inconsistancy?
invalidatesTags does not always remove things from cache. It only removes things from cache for cache entries that are not being used in a component at the moment - for everything else it triggers a refetch, so the request is fired off again and if there is new data in the response, the response is updated accordingly.
So in your component, isFetching will switch to true, but the data will not disappear - in most cases that is the preferred behaviour, as you don't want everything to jump to a loading indicator, but just update displayed data after a mutation.
Now, if your endpoint returns data that is structurally equal to the data it returned before, it will also not be a new data object, but just the old one. On a refetch, RTK-Query compares the old and new result and tries to keep as much of it referentially equal as possible, so useEffects are not fired off when the underlying data did actually not change at all.
Is your applicationId representing only an id or it represent an object with properties in it? Because if represents an object you should refactor your code from
invalidatesTags: (_result, _error, applicationId) => [
"Status",
{ type: "Application", id: "LIST" },
{ type: "Application", id: applicationId },
],
to
invalidatesTags: (_result, _error, { applicationId }) => [
"Status",
{ type: "Application", id: "LIST" },
{ type: "Application", id: applicationId },
],
Notice the curly braces around applicationId? This way you are destructuring your object from the result of the http request and you're passing it to the id property for invalidating.

How to delete a scheduled event using API?

Disclaimer: I am new to Hasura. I think I am missing some key understanding of how Hasura works.
Here is the list of steps I did so far:
Initiazed a new Hasura project using Heroku Postgresql database
using /v1/query and the following post body, I managed to create a scheduled event (I see it in the Hasura Web Console):
{
type: "create_scheduled_event",
args: {
webhook: "some API endpoint",
schedule_at: "somedate",
headers: [
{ name: "method", value: "POST" },
{ name: "Content-Type", value: "application/json" },
],
payload: "somepayload",
comment: "I SUPPLY A UNIQUI ID TO USE IN THE FOLLOWING DELETE QUERY",
retry_conf: {
num_retries: 3,
timeout_seconds: 120,
tolerance_seconds: 21675,
retry_interval_seconds: 12,
}
}
}
Now, I am trying to delete this event using this post body:
{
type: "delete",
args: {
table: {
schema: "hdb_catalog",
name: "hdb_scheduled_events",
},
where: {
comment: {
$eq: `HERE I PROVIDE THE UNIQUE ID I SET ON THE EVENT CREATION ABOVE`,
}
}
}
}
and getting back this response:
data: {
path: '$.args',
error: 'table "hdb_catalog.hdb_scheduled_events" does not exist',
code: 'not-exists'
}
as I understand hdb_catalog is the schema that I should work against but it does not appear anywhere in my Heroku database. I actually managed to create a scheduled event even without any database connected to the project. So, it seems that Hasura uses something else to store my scheduled events, but what??? How to access that database/table? Would you please help me?
You should use the delete_scheduled_event API instead of trying to delete the row itself from the hdb_catalog

Is there a way to dynamically assign a country to defaultCountry in firebase phone authentication for web?

I am trying to use firebase phone authentication for web and vuejs. I want to detect the country of the user and assign the detected country as the defaultCountry in the firebaseui config.
signInOptions: [
firebase.auth.EmailAuthProvider.PROVIDER_ID,
{
provider: firebase.auth.PhoneAuthProvider.PROVIDER_ID,
recaptchaParameters: {
type: 'image',
size: 'invisible',
badge: 'bottomleft'
},
defaultCountry: `'${this.countryCode}'`
}
]
Below is the method I used to successfully get the country and assign to a variable in data ()
created() {
this.getDefaultCountry()
},
I even tried
defaultCountry: this.countryCode
If I hardcode a countryCode ('US', 'NZ', ... ), it works.
Thank you
If this.getDefaultCountry() is synchronous (i.e. doesn't require any database lookups, promises, etc), you should be able to use the following code, where defaultCountry is swapped out for a getter instead of a static value:
signInOptions: [
firebase.auth.EmailAuthProvider.PROVIDER_ID,
{
provider: firebase.auth.PhoneAuthProvider.PROVIDER_ID,
recaptchaParameters: {
type: 'image',
size: 'invisible',
badge: 'bottomleft'
},
get defaultCountry() {
// contents of getDefaultCountry here
}
}
]
If your this.getDefaultCountry() is asynchronous, you will instead have to show some form of loading screen while you get the value, build signInOptions, give it to your FirebaseUI config and then finally render it.

Google Analytics Reporting API - Get Activity with User ID

I can't get User Activity from the Google Analytics Reporting API (v4) using a User ID.
Client ID (in a different, non-User ID view) works, however.
Am I missing something?
--
For Client ID
I go here in my Google Analytics web interface:
https://analytics.google.com/analytics/web/#/report/visitors-user-activity/a45743608w76484324p79062844/_u.date00=20190327&_u.date01=20190402&_r.userId=1142688158.1525244974
I get user activity details as expected:
--
I call the following:
POST https://analyticsreporting.googleapis.com/v4/userActivity:search?key={YOUR_API_KEY}
{
"viewId": "79062844",
"dateRange": {
"startDate": "2019-03-27",
"endDate": "2019-04-02"
},
"user": {
"type": "CLIENT_ID",
"userId": "1142688158.1525244974"
}
}
I get the expected response:
{
"sessions": [
…
],
"totalRows": 14,
"sampleRate": 1
}
--
For User ID
I go here in my Google Analytics web interface:
https://analytics.google.com/analytics/web/#/report/visitors-user-activity/a45743608w76484324p185098721/_u.date00=20190327&_u.date01=20190402&_r.userId=Not%20Logged%20In-
I get user activity details as expected:
--
I call the following:
POST https://analyticsreporting.googleapis.com/v4/userActivity:search?key={YOUR_API_KEY}
{
"viewId": "185098721",
"dateRange": {
"startDate": "2019-03-27",
"endDate": "2019-04-02"
},
"user": {
"type": "USER_ID",
"userId": "Not Logged In-"
}
}
I get an error instead:
{
"error": {
"code": 400,
"message": "USER_ID: Not Logged In- not found.",
"status": "INVALID_ARGUMENT"
}
}
--
I have tried different IDs, different dates, changing the type to CLIENT_ID, URL encoding the User ID, and User IDs with no spaces - no joy.
It seems this is indeed a bug which several others are also experiencing.
This bug can be +1'd and followed here:
https://issuetracker.google.com/issues/130161158
Edit: This has now been fixed.
From your URL, you are looking for an ID called Not%20Logged%20In- which is not a valid id. Its probably changing the id when you copy paste the URL.

How can I use googles "Interesting Calendars" with their v3 FreeBusy API?

When I attempt to use a calendar such as en.usa#holiday#group.v.calendar.google.com (Or anything with the ID ending in group.v.calendar.google.com) The FreeBusy API errors with:
"calendars": {
"en.usa#holiday#group.v.calendar.google.com": {
"errors": [
{
"domain": "global",
"reason": "notFound"
}
],
"busy": []
}
}
Making the same request but asking for another calendar succeeds.
The request I am making is reproducible in the Google API Docs at https://developers.google.com/calendar/v3/reference/freebusy/query
Where the request body is:
{
"timeMin": "2018-07-01T11:21:53.682-04:00",
"timeMax": "2018-08-25T11:21:53.682-04:00",
"items": [
{
"id": "en.usa#holiday#group.v.calendar.google.com"
}
],
"timeZone": "UTC"
}
I ruled out credentials being an issue; the same credentials work with this calendarId when it is used on the events API. This should also mean that the calendarId is valid.
The google account being used has this interesting calendar added to it. (Those events show up in the combined view)

Resources