Skip to content

Commit

Permalink
feat: Cloud Functions Examples (User Provisioner) (#849)
Browse files Browse the repository at this point in the history
Cloud Function Example 

Rewrite README.me, include demo and environmental variable setup in Cloud Functions UI as gifs
Refactor main.py and test with 4 cases:
1. An email of a new user: {"email": "me@gmail.com"} passed
2. An email of an existing user: {"email": "me@gmail.com"} passed
3. Wrong input: Typo in key "email" --> {"emails": "me@gmail.com"} passed (throwing an exception) 
4. Wrong input: Value of key "email" being an empty string passed (throwing an exception) 

Fixing b/199953683, previous PR: docs(python): Google Cloud Function python SDK example #543
Internal screencast: https://screencast.googleplex.com/cast/NDg1NDU2NjczNjYyNTY2NHw2MWRiNDk1Yi00YQ

Co-authored-by: Izzy Miller <izzy@looker.com>
Reviewed by: Joseph Axisa <josephaxisa@google.com>
  • Loading branch information
lanlooker authored Oct 24, 2021
1 parent a143225 commit ec2e4d0
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 2 deletions.
9 changes: 7 additions & 2 deletions examples/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ You can find Python language examples in this folder.

The full details of all Looker API endpoints are listed in Looker Docs: [Version 3.1](https://docs.looker.com/reference/api-and-integration/api-reference/v3.1), [Version 4.0](https://docs.looker.com/reference/api-and-integration/api-reference/v4.0)

## Full Applications

- [Flask full app demo](lookersdk-flask)
- [Google Cloud Function: User Creation](cloud-function-user-provision)

## Connection : Manage Database Connections

- [Test a specified connection](test_connection.py)
Expand Down Expand Up @@ -32,7 +37,7 @@ The full details of all Looker API endpoints are listed in Looker Docs: [Version
- [Pause/Resume or Copy Schedules](manage_schedules.py)
- [Create a Simple Schedule Plan](simple_schedule_plan.py)


## User : Manage Users

- [Disable all active user sessions](logout_all_users.py)
- [Disable all active user sessions](logout_all_users.py)
- [Google Cloud Function: User Creation](cloud-function-user-provision)
38 changes: 38 additions & 0 deletions examples/python/cloud-function-user-provision/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Cloud Function Example: User Provisioner

This repository contains a [Google Cloud Function](https://cloud.google.com/functions) that leverages Looker Python SDK. The repository can be used as a starter template to build serverless microservices that interact with Looker through the following workflow:

1. Send a POST request to trigger an HTTP-based Cloud Function
2. Initialize the Looker Python SDK
3. Call Looker SDK methods and build custom logic to manage users, content, queries, etc.

In this repository, the `main.py` file takes an email address as an input and checks if this email has been registered with an existing Looker user. If an exisiting user is found, an email to reset the password will be sent to the user. Otherwise, a new user will be created, and a setup email will be sent.

For more use cases and Python examples, check out [Looker's Python SDK examples](https://github.com/looker-open-source/sdk-codegen/tree/main/examples/python).

## Demo

<p align="center">
<img src="https://storage.googleapis.com/tutorials-img/Cloud%20Function%20Demo%20-%20SD%20480p.gif" alt="Demo">
</p>


## Setup

The following steps assume deployment using Google Cloud UI Console. Check out ["Your First Function: Python"](https://cloud.google.com/functions/docs/first-python) for steps to deploy using the `gcloud` command line tool

1. Obtain a [Looker API3 Key](https://docs.looker.com/admin-options/settings/users#api3_keys)

2. Follow the steps [provided here](https://cloud.google.com/functions/docs/quickstart-python) to create a new Google Cloud Function

3. Configure runtime environment variables using the Cloud Function UI: Edit > Configuration > Runtime, build, connections and security settings > Runtime environment variables. Alternatively, environtment variables can be configured through the `os` module or a `.ini`. Check out [Configuring Looker Python SDK](https://github.com/looker-open-source/sdk-codegen/tree/main/python#configuring-the-sdk) to learn more

<p align="center">
<img src="https://storage.googleapis.com/tutorials-img/Cloud%20Function_env%20-%20SD%20480p.gif" alt="Setting environmental variables in Cloud Function UI">
</p>

4. Copy and paste the contents of `main.py` in this repository into `main.py` file once inside Cloud Function's inline editor. Change the "Entry point" in the top right to `main`. `main.py` is executed once the function is triggered

5. Copy and paste the contents of `requirements.txt` in this repository to the `requirements.txt` file once inside Cloud Function's inline editor. This file is used to install neccessary libraries to execute the function

6. Deploy and test the function. Check out [this article](https://cloud.google.com/functions/docs/quickstart-python#test_the_function) for instruction
63 changes: 63 additions & 0 deletions examples/python/cloud-function-user-provision/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""This Cloud Function leverages Looker Python SDK to manage user provision. The
`main` function is used as the entry point to the code. It takes an email address
as an input through a POST request, then checks if this email has been associated
with an existing Looker user. If an exisiting user is found, then an email to
reset password will be sent. Otherwise, a new user will be created, and a setup email
will be sent.
HTTP Cloud Functions: https://cloud.google.com/functions/docs/writing/http#sample_usage"""

import looker_sdk
sdk = looker_sdk.init40()

def main(request):
try:
request_json = request.get_json()
email = request_json["email"]
result = looker_user_provision(email=email)
return result
except:
return 'Please provide JSON in the format of {"email":"test@test.com"}'

def looker_user_provision(email):
user_id = search_users_by_email(email=email)
if user_id is not None:
sdk.send_user_credentials_email_password_reset(user_id=user_id)
return f'A user with this email: {email} already existed; Password reset sent.'
else:
create_users(email=email)
return f'New user created; Setup/Welcome email sent to {email}.'

def search_users_by_email(email):
"""An email can only be assigned to one user in a Looker instance.
Therefore, search_user(email=test@test.com) will result in either
an empty dictionary, or a dictionary containing one user at index 0"""
users = sdk.search_users(email=email)
if len(users) == 0:
return None
else:
return users[0]["id"]

def create_users(email):
new_user = sdk.create_user(
body=looker_sdk.models40.WriteUser(
credentials_email=looker_sdk.models40.WriteCredentialsEmail(
email=email,
forced_password_reset_at_next_login=False
),
is_disabled=False,
models_dir_validated=False
)
)

# Create email credentials for the new user
sdk.create_user_credentials_email(
user_id=new_user.id,
body=looker_sdk.models40.WriteCredentialsEmail(
email=email,
forced_password_reset_at_next_login=False
))

# Send a welcome/setup email
sdk.send_user_credentials_email_password_reset(user_id=new_user["id"])

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Function dependencies, for example:
# package>=version
looker_sdk
requests

0 comments on commit ec2e4d0

Please sign in to comment.