Google Calendar Integration with Google App Engine - google-app-engine

I have a Google App Engine Service set up and running on a Google account A. What I would like to be able to do is book events on my business's Google Calendar account B. I followed this as a general guide. Here are the steps I have done to do this (Note: the AppEngine is already running and functional.
Enabled the Calendar API in my Project for Google Account A
Gone to my account B's admin console and went to Security -> Advanced Settings -> Manage API Client access and authorized the scope https://www.googleapis.com/auth/calendar to the id of my service for my App Engine in account A.
Code below in Go
Note: in the getCalendarService, I'm getting the credentials using ADC following this repo's README
func getCalendarService() (*calendar.Service, error) {
ctx := context.Background()
return calendar.NewService(ctx, option.WithScopes(calendar.CalendarScope))
}
// code adapted from https://developers.google.com/calendar/create-events/
func bookEvent() {
srv, err := getCalendarService()
if err != nil {logAndPrintError(err)}
event := &calendar.Event{
Summary: "Test Title",
Description: "Lorem ipsum",
Start: &calendar.EventDateTime{
DateTime: "2020-02-12T09:00:00-07:00",
TimeZone: "America/Chicago",
},
End: &calendar.EventDateTime{
DateTime: "2020-02-12T17:00:00-07:00",
TimeZone: "America/Chicago",
},
}
calendarId := "primary"
event, err = srv.Events.Insert(calendarId, event).Do()
if err != nil {
logAndPrintError(err)
}
log.Printf("Event created: %s\n", event.HtmlLink)
}
When I look in log and error reporting there is no error, and the logs show this message:
Event created: a Calendar URL
but when I copy the link and go to my Google Account B and load it, Google Calendar says "Could not find the requested event". It is worth mentioning that if I load the url into Account A, it says the same thing.
For some reason there is no error, but the event does not work. I don't think it is a problem in my code but a problem in the credentials, but I could be wrong.
Note: I'm not downloading any keys, I'm using ADC with App Engine to get my credentials.

If I am not mistaken,
you creating a calendar event with a Service account and insert this
event into the primary calendar OF THE SERVICE ACCOUNT
This is probably not what you wanted,instead, if you want to insert the event into your primary calendar either
You need to specify the id of this calendar as calendarId instead of primary - this implies that your share your calendar with the service account beforehand
Or, you use impersonation, that is build the the service account service in such a way that it acts as you (or another user of your domain specified as the ServiceAccountUser

Related

Using Cloud Firestore with AppEngine Go Standard Environment returns rpc error when running on AppEngline

I'm trying to use Firestore in my AppEngine (standard environment) app written in Go. I've been following the "Getting Started with Cloud Firestore" guide and have been using the firestore package documentation to implement a simple example that works fine when running it on my local dev server.
However when I deploy the app and try the deployed version the call to DocumentRef.Set() fails with the error
rpc error: code = Unavailable desc = all SubConns are in TransientFailure
This is my code that reproduces the issue:
func init() {
http.HandleFunc("/test", testHandler)
}
type testData struct {
TestData string `firestore:"myKey,omitempty"`
}
func testHandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
var firestoreClient *firestore.Client
var firebaseApp *firebase.App
var err error
conf := &firebase.Config{ProjectID: "my-project"}
firebaseApp, err = firebase.NewApp(ctx, conf)
if err != nil {
fmt.Fprintf(w, "Failed to create a new firestore app: %v", err)
return
}
firestoreClient, err = firebaseApp.Firestore(ctx)
if err != nil {
fmt.Fprintf(w, "Failed to create a new firestore client: %v", err)
return
}
data := testData{"my value"}
_, err = firestoreClient.Collection("testCollection").Doc("testDoc").Set(ctx, data)
if err != nil {
fmt.Fprintf(w, "Failed to create a firestore document: %v", err)
return
}
firestoreClient.Close()
fmt.Fprint(w, "Data stored in Firestore successfully")
}
As mentioned before, on the dev server this works fine. So there the returned page contains the text Data stored in Firestore successfully.
When running the deployed code I get Failed to create a firestore document: rpc error: code = Unavailable desc = all SubConns are in TransientFailure instead. Why do I get this error and how can I avoid it?
I've raised an issue about this in the Firestore client library issue tracker and it seems like the situation is a bit complex.
When using App Engine the Firestore client library's network connections goes trough the App Engine socket library. However sockets is only available for paid App Engine apps:
Sockets are only available for paid apps, and traffic from sockets is billed as outgoing bandwidth. Sockets are also limited by daily and per minute (burst) quotas.
So this is the reason why the Firestore client library fails. For small scale projects it's possible to enable billing of your App Engine app and still stay within the free range. If billing is enabled it should work when the app is deployed as well.
However if you are living within the European Union you are not allowed to have a paid App Engine app for non commercial purposes due to Google policies:
If you are located in the European Union and the sole purpose for which you want to use Google Cloud Platform services has no potential economic benefit you should not use the service. If you have already started using Google Cloud Platform, you should discontinue using the service. See Create, modify, or close your billing account to learn how to disable billing on your projects.
So if you are in Europe or for some other reason are unable to use have a paid App Engine app you will not be able to use the Firestore client library.
One alternative in this case is to use the Firestore REST API instead and manually make HTTP requests to Firestore. It's a bit more work, but for smaller scale projects it works.
On AppEngine you need to create a client that uses Http client provided by urlfetch service.
The firestore.NewClient() function accepts ClientOptions parameters that you can create using WithHTTPCLient() function.
Here is an article on issuing HTTP requests from AppEngine Go.
That should help.

How can I give my Google Cloud App Engine access to my Firebase Storage bucket

I'm getting an error: API error 7 (images: ACCESS_DENIED)
Normally I would have the Firebase app associated with my Google App Engine service, but since Firestore is not compatible with App Engine, I have to create seperate instances of the service. However, now I need my app engine image service to be able to reference the files in the firebase cloud storage folder. How do I set up access, would I need to create some sort of IAM that's shared across both services?
Example of my app-engine go function referencing the firebase storage bucket the-other-firebase-app
func photoHandler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
v := r.URL.Query()
fileName := v.Get("file")
if (fileName == "") {
fmt.Fprint(w, "file name is required")
} else {
filename := "/gs/the-other-firebase-app.appspot.com/photos/" + fileName;
blobKey,err := blobstore.BlobKeyForFile(c, filename)
if (err == nil) {
url, urlErr := image.ServingURL(c, blobKey, nil)
if (urlErr == nil) {
fmt.Fprint(w, url)
} else {
fmt.Fprint(w, urlErr)
}
} else {
fmt.Fprint(w, "image does not exist")
}
}
}
I had a very similar issue, but the exact error message thrown was:
_"your-project-name#appspot.gserviceaccount.com does not have storage.objects.get access to storage_file_path_here>"_
I fixed it thanks to suggestions from eltonnobrega on Github: https://github.com/firebase/functions-samples/issues/299
Here's a summary:
Got to Google Cloud Console and select the project which your storage bucket is under - the bucket you want to access from your service account.
Go to the Storage tab
On the browser tab, you should see the storage buckets in this project. Each one will have an options icon at the line ending on the right (three vertical dots)
From these options choose Edit Bucket permissions
From there you can add members. Add your service account (your-project-name#appspot.gserviceaccount.com) and give it the Storage Object Admin role
If your app is a standard env one it should already have a service account, see Using service accounts:
The App Engine application runs using the identity of the App Engine
default service account. You can see this account on the IAM page in
the Cloud Platform Console.
If your app is a flex engine one see Service Account for the App Engine Flexible Environment:
When you enable the Google App Engine flexible environment API, a
specific service account is automatically created. You can view your
project's service accounts in the IAM section of the Cloud Platform
Console. The email for the App Engine flexible environment service
account is
service-[PROJECT-NUMBER]#gae-api-prod.google.com.iam.gserviceaccount.com,
where [PROJECT-NUMBER] is the project number listed in the IAM
settings.
In the Firebase app project you need to add the GAE app's service account as a team member and grant it the necessary permissions.
As mentioned in the comment:
Firebase Dasbhoard -> Project Settings Cog -> Users and permissions –
MonkeyBonkey
Potentially of interest could also be Integrate with Google Cloud Platform.

Go, Appengine, SMTP, Gmail

For some reason I cannot figure out how to send emails using a gmail account, Appengine and Golang.
Here's what I've done:
I went to Google Cloud Platform > Appengine > Settings > Select Project and I added the gmail account on Email API authorized senders.
I' tried to make this work using the code from (https://golang.org/pkg/net/smtp/#pkg-examples) (func SendMail)
package main
import (
"log"
"net/smtp"
)
func main() {
// Set up authentication information.
auth := smtp.PlainAuth("", "user#gmail.com", "password", "smtp.gmail.com")
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
to := []string{"recipient#example.net"}
msg := []byte("To: recipient#example.net\r\n" +
"Subject: discount Gophers!\r\n" +
"\r\n" +
"This is the email body.\r\n")
err := smtp.SendMail(smtp.gmail.com:587", auth, "sender#example.org", to, msg)
if err != nil {
log.Fatal(err)
}
}
On the front-end (JavaScript) I get an unsuccessful response after trying to run this code.
I've been running this on the appengine staging server
I tried different smtp server, ports, users and it still not work (support.google.com/a/answer/176600?hl=en)
I found a few examples on github and some other blog and I tried them but it didn't make different.
github.com/golang/go/wiki/SendingMail
nathanleclaire.com/blog/2013/12/17/sending-email-from-gmail-using-golang/
On all the examples it everything looks straight forward but there's something that I'm definitely missing or misunderstanding.
There're some limitations with establishing raw tcp connections on GAE:
https://cloud.google.com/appengine/docs/go/sockets/#limitations_and_restrictions
I would recommend to use GAE mail api (which is slightly diffrent from standart smtp package) to send emails:
https://cloud.google.com/appengine/docs/go/mail/sending-receiving-with-mail-api

The resource 'projects/<my project>' was not found" error when trying to get list of running instances

My goal is to test out google's orchestrator and the compute engine api by first retrieving a list of active instances. The orchestrator project including the servlet file is stored in a jar.
I'm trying to test out the java google compute engine client api. I have a cron job which calls on the orchestrator servlet. The target for the cron is a backend. From which I try to get the list of instances:
...
AppIdentityCredential credential = getCredential(computeScope);
String appName = ConfigProperties.getInstance().getGceConfigProperties().get("projectId");
try {
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
final Compute compute = new Compute.Builder(
httpTransport, JSON_FACTORY, credential).setApplicationName(appName)
.build();
logger.info("================== Listing Compute Engine Instances ==================");
Compute.Instances.List instances = compute.instances().list(projectId, zone);
InstanceList list = instances.execute();
if (list.getItems() == null) {
logger.info("No instances found. Sign in to the Google APIs Console and create "
+ "an instance at: code.google.com/apis/console");
} else {
for (Instance instance : list.getItems()) {
logger.info(instance.toPrettyString());
}
}
...
There error response I get is(I omitted my project name from the response, I confirmed that I'm using the correct project id in my code):
com.google.cloud.solutions.sampleapps.orchestration.orchestrator.server.GceClientApiUtils
getInstances: com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 OK
{
"code" : 404,
"errors" : [ {
"domain" : "global",
"message" : "The resource 'projects/<project-name-here>' was not found",
"reason" : "notFound"
} ],
"message" : "The resource 'projects/<project-name_here>' was not found"
}
I've also attempted this by retrieving an access token and making a RESTful call to get the list of instances and i received the exact same response. I confirmed the Url constructed was correct by comparing it against a successful query of the instances using the api explorer.
EDIT: I determined the solution to the issue with help of another post:
I was finally able to find the solution in the post Compute Engine API call fails with http 404
I needed to add my app engine service account as a team member with edit capabilities, which it does not have by default. Once I did this, the code worked as expected. I had to do this through cloud.google.com/console, as if done through appengine.google.com, a pending status will be given to the service account and will not have access.
For me i had to make sure i had authorization. Try this in the terminal gcloud auth login
Make sure you are in the right project, you can run this command on your vm to see if you are in the right project:
gcloud config list
Take a look at this post in Google Groups
Do you have access to the developers console https://console.developers.google.com?
It seems that the user account #appspot.gserviceaccount.com has not access to compute engine. In my case I see #developer.gserviceaccount.com.
If you don't have one, visit https://developers.google.com/console/help/new/#generatingoauth2 to create a new Client ID

Parsing incoming mail with Google App Engine?

We have our mail setup with Google Apps. We want to be able to run some regular expressions on incoming mail and process this information.
Is this possible today with Google App Engine? Does Google provide some kind of infrastructure that can do this?
from google documentation here:
Receiving Mail
Your app can receive email at addresses of the following form:
string#appid.appspotmail.com
Note that even if your app is deployed on a custom domain, your app can't receive email sent to addresses on that domain.
Email messages are sent to your app as HTTP requests. These requests are generated by App Engine and posted to your app. In your app's configuration, you specify handlers that will be called to handle these HTTP requests. In your handlers, you receive the MIME data for email messages, which you then parse into its individual fields.
Email messages are sent to your app as HTTP POST requests using the following URL:
/_ah/mail/address
where address is a full email address, including domain name.
The ability to receive mail in your app is disabled by default. To enable your app to receive mail, you must specify that you want this service enabled in your app.yaml file by including this:
inbound_services:
- mail
The Python SDK defines InboundMailHandler, a webapp class for handling incoming email. To use InboundMailHandler, you subclass it and override the receive() method. The receive() method is called with an argument of class InboundEmailMessage, another class defined by the Python SDK.
InboundMailHandler is in the google.appengine.ext.webapp.mail_handlers package. You can create an instance of InboundEmailMessage like this:
import logging, email
from google.appengine.ext import webapp
from google.appengine.ext.webapp.mail_handlers import InboundMailHandler
from google.appengine.ext.webapp.util import run_wsgi_app
class LogSenderHandler(InboundMailHandler):
def receive(self, mail_message):
logging.info("Received a message from: " + mail_message.sender)
The InboundEmailMessage object includes attributes to access other message fields:
subject contains the message subject.
sender is the sender's email address.
to is a list of the message's primary recipients.
cc contains a list of the cc recipients.
date returns the message date.
attachments is a list of file attachments, possibly empty. Each value in the list is a tuple of two elements: the filename and the file contents.
original is the complete message, including data not exposed by the other fields such as email headers, as a Python email.message.Message.
handle_incoming_email.py
app.yaml
Update: It's now supported.
Processing incoming email is not yet supported. It is however on their roadmap: http://code.google.com/appengine/docs/roadmap.html
Google don't currently support handling email in App Engine, though it is on the roadmap. In the meantime, services like smtp2web will handle it for you (disclaimer: I wrote smtp2web).
You could setup an email account and have an external server (one you create and host outside of AE) access the gmail account via IMAP. Your "mail server" then reads the messages and accesses the /email API of your app on AE.
Python has an email module, so you could post the entire message there, or if that doesn't work (due to whatever restrictions), you could preprocess it on your mail server and post the simplified version to your app.
The downside is that you'll have to resort to polling for information, but that should be ok since email is accepted to have somewhat of a delay.
As another option to the answer from Gabriel I would recommend to use go environment of App Engine on Sending and Receiving Mail with the Mail API.
From The Documentation:
Receiving Mail
Your app can receive email at addresses of the following form:
anything#appid.appspotmail.com
Compare to a configuration on handling incoming mail in python as discussed here, enabling incoming mail in your app's app.yaml file is fairly simple:
inbound_services:
- mail
Name your app file as mail.go, then register a handler to the /_ah/mail/ path and read the email's data from the *http.Requestlike using net/mail like this:
func incomingMail(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
defer r.Body.Close()
var b bytes.Buffer
if _, err := b.ReadFrom(r.Body); err != nil {
log.Errorf(ctx, "Error reading body: %v", err)
return
}
log.Infof(ctx, "Received mail: %v", b)
}
Sending Mail
Follow this guideline to register your sender emails as authorized senders
Use the mail.Message type to set the sender, recipient, subject, and body of the message.
Send the email with the mail.Send function.
func confirm(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
addr := r.FormValue("email")
url := createConfirmationURL(r)
msg := &mail.Message{
Sender: "Example.com Support <support#example.com>",
To: []string{addr},
Subject: "Confirm your registration",
Body: fmt.Sprintf(confirmMessage, url),
}
if err := mail.Send(ctx, msg); err != nil {
log.Errorf(ctx, "Couldn't send email: %v", err)
}
}
Deploy
The complete sample code of both Receiving and Sending is available here on GitHub:
GoogleCloudPlatform/golang-samples/docs/appengine/mail/mail.go
To clone the sample code, go to your Console. Click the button to open Cloud Shell:
then similar with this quickstart enter the steps below:
$ SOURCEDIR=https://github.com/GoogleCloudPlatform/golang-samples.git
$ TUTORIALDIR=~/src/your-application-id/go_gae_samples
$ git clone $SOURCEDIR $TUTORIALDIR
$ cd $TUTORIALDIR
$ git checkout master
$ cat docs/appengine/mail/app.yaml
$ cat docs/appengine/mail/mail.go
$ goapp serve docs/appengine/mail/app.yaml
From here you can access the app on port 8080 by using Web preview.
To terminate press Ctrl+C in the Cloud Shell.
Finally you may deploy your app
goapp deploy -application your-application-id -version 0
Click the URL to visit it
http://your-application-id.appspot.com/
Then send an email to anything#your-application-id.appspotmail.com see if it works.

Resources