-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Cloud Functions Examples (User Provisioner) (#849)
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
Showing
4 changed files
with
112 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"]) | ||
|
4 changes: 4 additions & 0 deletions
4
examples/python/cloud-function-user-provision/requirements.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Function dependencies, for example: | ||
# package>=version | ||
looker_sdk | ||
requests |