Farmer-Eds-Shed / Node-Red-Google-API-Flows

Node red flows for interacting with Google API using standard HTTP request nodes.
https://farmer-eds-shed.com/interacting-with-google-calendar-using-node-red/
GNU General Public License v3.0
3 stars 0 forks source link

Thank you!! #1

Open elgerg opened 2 years ago

elgerg commented 2 years ago

Hi Ed(?),

I was using the project from here: https://github.com/mdhom/node-red-contrib-google-oauth-calendar

But after google deprecated the OOB flow it stopped working. Ive been waiting for them to update their project for nearly 3 months but after very little contact and no resolution I looked around and found your example project.

See: https://github.com/mdhom/node-red-contrib-google-oauth-calendar/issues/8

I just wanted to say well done. It was a fantastically simple project to incorporate and works like a charm!!

There is no issue here so feel free to close this as soon as you see it. I just wanted to publicly contact you to thank you.

Thank you for your effort and publishing your findings for the rest of us to use.

Regards Alex

Farmer-Eds-Shed commented 2 years ago

Hi Alex thanks for the credit. I'm sure there are a few bugs to be worked out as it was thrown together in an couple of evenings for a couple of other projects that I had which were also dependant on https://github.com/mdhom/node-red-contrib-google-oauth-calendar so feel free to share any issues or suggestions.

elgerg commented 2 years ago

So far I havent seen any issues. The only thing I really need to find out is how long the token will last for before it needs refreshing.

One thing I did add was to write the tokens out to a .json file and read it back in on startup: image

[
    {
        "id": "da621f01f4377518",
        "type": "file",
        "z": "c516bb43d26b1e4d",
        "name": "",
        "filename": "Google_Tokens.json",
        "appendNewline": true,
        "createDir": false,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1220,
        "y": 220,
        "wires": [
            []
        ]
    },
    {
        "id": "e48bfa4f8cfa46e8",
        "type": "function",
        "z": "c516bb43d26b1e4d",
        "name": "",
        "func": "var payload = {\n    \"access_token\":  msg.payload.access_token,\n    \"refresh_token\": msg.payload.refresh_token\n};\n\nmsg.payload = payload;\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1000,
        "y": 160,
        "wires": [
            [
                "da621f01f4377518"
            ]
        ]
    }
]

Slight mod for the refresh: image

and

image

[
    {
        "id": "8b58eb08055b8983",
        "type": "inject",
        "z": "b708b115.33d18",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 170,
        "y": 3640,
        "wires": [
            [
                "4f613c611dbff7e6"
            ]
        ]
    },
    {
        "id": "852535efcf928128",
        "type": "function",
        "z": "b708b115.33d18",
        "name": "Set Global Tokens",
        "func": "global.set('Google_token', msg.payload.access_token);\nglobal.set('Google_refresh', msg.payload.refresh_token);\n    \nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 810,
        "y": 3640,
        "wires": [
            [
                "d31729e26310df03"
            ]
        ]
    },
    {
        "id": "4f613c611dbff7e6",
        "type": "file in",
        "z": "b708b115.33d18",
        "name": "",
        "filename": "Google_Tokens.json",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 380,
        "y": 3640,
        "wires": [
            [
                "33a4f78277bd9569"
            ]
        ]
    },
    {
        "id": "d31729e26310df03",
        "type": "debug",
        "z": "b708b115.33d18",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1030,
        "y": 3640,
        "wires": []
    },
    {
        "id": "33a4f78277bd9569",
        "type": "json",
        "z": "b708b115.33d18",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 610,
        "y": 3640,
        "wires": [
            [
                "852535efcf928128"
            ]
        ]
    }
]

Other than that I think it's brilliant. Gets my calendar working after being out of action for near 3 months..

Thanks again!!

elgerg commented 2 years ago

Sorry, one other thing I noticed was the dates. Might be because I'm in the UK but I needed to change the date constructor to: const date = new Date(Number(parts[2]), Number(parts[0]) - 1, Number(parts[1]));

Swapping 1 and 0 around as I ended up with some date in Sept 2023 as my min date..

Ta!

Farmer-Eds-Shed commented 2 years ago

Cool I'll have a look at your modifications when I get a chance.

Token expiry wasn't a huge priority in my use case, which is to post events from farm management applications to a calendar, these events are so infrequent that simply requesting a new token with each request is the simplest option, but I'm aware taking account of the expiry would make the flow more useful for more universal usage. The expected token expiry is supplied with each token so it shouldn't be too difficult to add an expiry check before each request and call a refresh only when needed.

Yes, dates are fun aren't they. I'm actually in Ireland myself so should be using UK dates but I've been working a lot with software with US dates. I will check tomorrow as I'm not at my computer at the moment that may be an oversight that I may have corrected on my local version.

Farmer-Eds-Shed commented 2 years ago

On your issue with authorization code expiring weekly, that is because your project is in test mode in Google Console, in production mode you no longer need to sign in weekly, but I believe you need to have a redirect URL with domain name and HTTPS

elgerg commented 2 years ago

Hi Ed,

As of yet my token hasnt expired, I only started using it today but yes, I think that it will expire after 14 days if it's in test mode.

I have mine in production mode, you dont need external HTTPS for that though..

image

image

Hope that helps someone else..

Ta! Alex

Farmer-Eds-Shed commented 2 years ago

Ah Ok Great so, I was working from memory (which is often a dangerous thing to do) I was sure there was a HTTPS requirement in production mode perhaps it depends on the application type selected or just an exemption for localhost as URL.

Anyway I'm more than happy to take your confirmation of loopback address working in production mode.

Edit: ah I am an idiot, I don't use localhost on my production machine as it is running on a remote server, I needed a domain name with HTTPS in this case. Don't believe it needs to be publicly accessible just resolve to a hostname with HTTPS.

elgerg commented 2 years ago

I'll let you know in ~14 days or so if the grant gets invalidated..

On another note, the secret and clientID, they seem to be stored in environment variables. Will that persist on a reboot?

Thanks

elgerg commented 2 years ago

Ah Ok Great so, I was working from memory (which is often a dangerous thing to do) I was sure there was a HTTPS requirement in production mode perhaps it depends on the application type selected or just an exemption for localhost as URL.

Anyway I'm more than happy to take your confirmation of loopback address working in production mode.

Edit: ah I am an idiot, I don't use localhost on my production machine as it is running on a remote server, I needed a domain name with HTTPS in this case. Don't believe it needs to be publicly accessible just resolve to a hostname with HTTPS.

You are right, if it's not localhost (127.0.0.1) it does need to be https (publicly resolvable or not...).

Farmer-Eds-Shed commented 2 years ago

Client ID and secret should persist through a reboot. I believe this was a feature added to subflow properties in later versions of node-red.

On my server global and flow variables also persist through reboots as I've changed the configuration from default options which is why my flow didn't write the tokens to file like you have done. But your solution is probably more elegant for anyone wanting to run on Node-red default config.

elgerg commented 2 years ago

Client ID and secret should persist through a reboot. I believe this was a feature added to subflow properties in later versions of node-red.

On my server global and flow variables also persist through reboots as I've changed the configuration from default options which is why my flow didn't write the tokens to file like you have done. But your solution is probably more elegant for anyone wanting to run on Node-red default config.

Oh interesting, I do have defaults set. I didn't want things writing to my drive (its on a Pi and although it's running on an SSD I didn't want it writing anything that wasn't necessary)..

If I wrote global and flow variables to disk it'd be writing to disk every few seconds thus destroying the drive over time (especially if it was running on an SD card)..

Maybe you want to change that so it only writes to disk when it needs to?

Appreciate the help and audience.

Thanks Alex

Farmer-Eds-Shed commented 2 years ago

This is probably true depending on your usage. The Node-Red instance in this case is dedicated to a few infrequent tasks so not much changing in reality also I believe it doesn't actually write back every second but every few min. You can also select which flow and global variables are written to disk and which are not.

That was probably an unesseccary aside. But the environmental variables are written to disk by default, but won't be changing as frequently anyway.

Anyway good chat and thanks for the input.

Hostiss commented 1 year ago

Hello,

i want also to thank you for this projects, works like a charm! just recently i tried 2 different projects , like these linked in the first post, and nothing worked.

One thing which i want to mention is that, while creating Credentials at Google Developer Console, you have to pick "Web application" instead of "TVs and Limited Input Devices" option. This second one is for example mentioned at one of the links posted in the first post, but this option doesn't have any of Authorized JavaScript or Authorized redirect URLs boxes available.

Private addresses were not working for me, i was also using only http and not https, at the end i had to use node-red.example.com domain name and change my hosts file to point to my node-red instance.

I found also somewhere some information about loopback not working anymore soon (which i believe is related): Aug 31, 2022 - the Loopback IP address flow is blocked for existing clients

Thank you again.

BTSSnir2023 commented 1 year ago

Good morning, sir, How are you? I wanted to thank you for the project "Interacting with Google Calendar using Node-Red". I am a student in France and I have to do a project almost similar. Your help is very helpful, especially for interacting with google calendar using node-red. Thank you and good continuation to you

Farmer-Eds-Shed commented 1 year ago

Good afternoon @BTSSnir2023, I'm very well thanks. Glad you found this flow useful