diff --git a/.github/workflows/codegen-ci.yml b/.github/workflows/codegen-ci.yml index 0b2bad8e6..0f31d8a1f 100644 --- a/.github/workflows/codegen-ci.yml +++ b/.github/workflows/codegen-ci.yml @@ -20,8 +20,8 @@ env: LOOKERSDK_BASE_URL: https://localhost:20000 LOOKERSDK_VERIFY_SSL: false TS_JUNIT_OUTPUT_DIR: results/sdk-codegen - LOOKERSDK_CLIENT_ID: ${{ secrets.LOOKERSDK_CLIENT_ID__21_6 }} - LOOKERSDK_CLIENT_SECRET: ${{ secrets.LOOKERSDK_CLIENT_SECRET__21_6 }} + LOOKERSDK_CLIENT_ID: ${{ secrets.LOOKERSDK_CLIENT_ID__21_8 }} + LOOKERSDK_CLIENT_SECRET: ${{ secrets.LOOKERSDK_CLIENT_SECRET__21_8 }} jobs: unit: @@ -78,9 +78,9 @@ jobs: # TODO: can we cache some layers of the image for faster download? # we probably don't want to cache the final image for IP security... run: | - docker pull --quiet us-west1-docker.pkg.dev/cloud-looker-sdk-codegen-cicd/looker/21_6 + docker pull --quiet us-west1-docker.pkg.dev/cloud-looker-sdk-codegen-cicd/looker/21_8 # set $LOOKER_OPTS to --no-ssl if we want to turn off ssl - docker run --name looker-sdk-codegen-ci -d -p 10000:9999 -p 20000:19999 us-west1-docker.pkg.dev/cloud-looker-sdk-codegen-cicd/looker/21_6 + docker run --name looker-sdk-codegen-ci -d -p 10000:9999 -p 20000:19999 us-west1-docker.pkg.dev/cloud-looker-sdk-codegen-cicd/looker/21_8 docker logs -f looker-sdk-codegen-ci --until=30s & python ${{ github.workspace }}/.github/scripts/wait_for_looker.py diff --git a/.github/workflows/lerna-publish.yml b/.github/workflows/lerna-publish.yml index 50d1919ef..fb529a8f7 100644 --- a/.github/workflows/lerna-publish.yml +++ b/.github/workflows/lerna-publish.yml @@ -21,8 +21,8 @@ jobs: env: LOOKERSDK_BASE_URL: https://localhost:20000 LOOKERSDK_VERIFY_SSL: false - LOOKERSDK_CLIENT_ID: ${{ secrets.LOOKERSDK_CLIENT_ID__21_4 }} - LOOKERSDK_CLIENT_SECRET: ${{ secrets.LOOKERSDK_CLIENT_SECRET__21_4 }} + LOOKERSDK_CLIENT_ID: ${{ secrets.LOOKERSDK_CLIENT_ID__21_8 }} + LOOKERSDK_CLIENT_SECRET: ${{ secrets.LOOKERSDK_CLIENT_SECRET__21_8 }} steps: - name: Clone repository uses: actions/checkout@v2 @@ -67,8 +67,8 @@ jobs: - name: Pull and run Looker docker image run: | - docker pull --quiet us-west1-docker.pkg.dev/cloud-looker-sdk-codegen-cicd/looker/21_4 - docker run --name looker-sdk-codegen-ci -d -p 10000:9999 -p 20000:19999 us-west1-docker.pkg.dev/cloud-looker-sdk-codegen-cicd/looker/21_4 + docker pull --quiet us-west1-docker.pkg.dev/cloud-looker-sdk-codegen-cicd/looker/21_8 + docker run --name looker-sdk-codegen-ci -d -p 10000:9999 -p 20000:19999 us-west1-docker.pkg.dev/cloud-looker-sdk-codegen-cicd/looker/21_8 docker logs -f looker-sdk-codegen-ci --until=30s & python ${{ github.workspace }}/.github/scripts/wait_for_looker.py diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 1c9375e5a..92766a8f2 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -36,7 +36,7 @@ jobs: with: python-version: 3.x - run: pip install -e . - - run: pip install mypy + - run: pip install mypy types-requests - run: mypy looker_sdk/ unit: @@ -99,10 +99,10 @@ jobs: os: - ubuntu looker: - - '7_20' - '21_0' - '21_4' - '21_6' + - '21_8' # TODO uncomment `include:` when either macos or windows works to satisfaction. #include: # TODO: macos matrix leg is functional but it takes ~20 minutes (compared diff --git a/.github/workflows/tssdk-ci.yml b/.github/workflows/tssdk-ci.yml index 641635892..5eb36b996 100644 --- a/.github/workflows/tssdk-ci.yml +++ b/.github/workflows/tssdk-ci.yml @@ -98,10 +98,10 @@ jobs: os: - ubuntu looker: - - '7_20' - '21_0' - '21_4' - '21_6' + - '21_8' steps: - name: Repo Checkout diff --git a/examples/python/README.md b/examples/python/README.md index 22f596b90..2d07cdbfa 100644 --- a/examples/python/README.md +++ b/examples/python/README.md @@ -2,20 +2,33 @@ You can find Python language examples in this folder. -## Connection management +## Connection Management - [Test a specified connection](test_connection.py) -## Manage Dashboards +## Content Access Management + +- [Add a board or dashboard to Favorites for a list of users](add_contents_to_favorite.py) + +## Dashboards Management - [Soft delete dashboard](soft_delete_dashboard.py) -## Manage Render Tasks +## Render Tasks Management - [Download dashboard tile in specified format](download_tile.py) - [Download look in specified format](download_look.py) - [Generate and download dashboard PDFs](download_dashboard_pdf.py) +## Schedules Management + +- [Transfer all schedules of a user to another user](transfer_all_schedules.py) + ## User Management - [Disable all active user sessions](logout_all_users.py) + + + + + diff --git a/examples/python/add_contents_to_favorite.py b/examples/python/add_contents_to_favorite.py new file mode 100644 index 000000000..fb558ac2d --- /dev/null +++ b/examples/python/add_contents_to_favorite.py @@ -0,0 +1,80 @@ +""" +The purpose of this script is to add a specific Dashboard or Board to "Favorites" +for a list of users, which may help new users discover useful Looker contents quicker and easier. + +The script contains two functions (add_boards_to_users and add_dashboards_to_users) that are similar +in logic and execution. Example function calls are placed at the end of the script. + +Author: Lan +Last modified: June 16, 2021 +""" + +import looker_sdk + +sdk = looker_sdk.init40() + +def add_boards_to_users(board_id: int, users_id: list): + + """ Add a specific board to the "Favorite" contents for a list of user + + Args: + board_id (int): id of a Looker board (https://company.looker.com/boards/id) + users_id (list): a list of users id (int) in the form of a native Python list + + Returns: "Successfully added!" (str) + + Raises: N/A (does not explicitly raise an exception); Looker SDK will raise an error. + """ + + content_metadata_id = sdk.board(board_id=board_id)['content_metadata_id'] + + """An admin can not update the list of favorite contents for users, + so sdk.auth.login_user() and sdk.auth.logout() are called to sudo as each user to call `create_content_favorite()""" + for i in users_id: + sdk.auth.login_user(i) + params = {} + params["user_id"] = i + params["content_metadata_id"] = content_metadata_id + sdk.create_content_favorite(params) + sdk.auth.logout() + + print("Successfully added!") + + + +""" The logic for `add_dashboards_to_users` is the same, except that `dashboard_id` is a string (because LookML dashboard id is a string). +Also, we are using `sdk.dashboard()` to retrieve `content_metadata_id`""" + + +def add_dashboards_to_users(dashboard_id: str, users_id: list): + + """ Add a specific dashboard to the list of favorite contents for a list of user + + Args: + dashboard_id (str): id of a Looker dashboard (https://company.looker.com/dashboards/id) + users_id (list): a list of users id in the form of a native Python list + + Returns: "Successfully added!" (str) + + Raises: N/A (does not explicitly raise an exception); Looker SDK will raise an error. + """ + + content_metadata_id = sdk.dashboard(dashboard_id=dashboard_id)['content_metadata_id'] + + """An admin can not update the list of favorite contents for users, + sdk.auth.login_user() and `sdk.auth.logout()` are called to sudo as each user to call `create_content_favorite()""" + for i in users_id: + sdk.auth.login_user(i) + params = {} + params["user_id"] = i + params["content_metadata_id"] = content_metadata_id + sdk.create_content_favorite(params) + sdk.auth.logout() + + print("Successfully added!") + + + +# Call the functions +add_boards_to_users(board_id = 1, users_id = [1]) +add_dashboards_to_users(dashboard_id = "string", users_id = [1]) \ No newline at end of file diff --git a/examples/python/transfer_all_schedules.py b/examples/python/transfer_all_schedules.py new file mode 100644 index 000000000..836365e6e --- /dev/null +++ b/examples/python/transfer_all_schedules.py @@ -0,0 +1,76 @@ +""" +This script transfers all schedules of a user to a different user using their email addreseses as parameter. +The script may come in handy when a user leaves an organization, and Looker admins have to re-assign all of +their existing schedules to a new user. + +The script is using customized if/else conditions to check for edge cases (i.e. if email addresses are associated +with existing Looker users, if a user has any schedules, etc.) before reading data and calling the functions. + +Author: Lan +Last modified: June 16, 2021 +""" +import looker_sdk + +sdk = looker_sdk.init40() + +def find_user_id(email: str): + + """ Given an email address, find the corresponding Looker user id + Args: email (str) + Returns: the Looker user id associated with the email addresses (int) + Raises: N/A (does not explicitly raise an exception) + """ + user_id = sdk.search_users(email=email) + + """ Customized logic block to check if an email address is associated with a Looker user""" + if len(user_id) == 0: + return 'There is no user associated with this email' + else: + return user_id[0]['id'] + + +def find_schedules(user_id: int): + + """ Return all schedules of a particular user id + Args: user_id (int) + Returns: all schedules of a particular user: result = {'name_of_schedule_plan': id_of_schedule_plan} + Raises: N/A (does not explicitly raise an exception) + """ + result = {} + schedule_plans = sdk.all_scheduled_plans(user_id=user_id) + for i in schedule_plans: + result[i['name']] = i['id'] + return result + + +def update_owner(current_owner_email: str, new_owner_email: str): + + """ Transfer all schedules of `foo@looker.com` to `bar@looker.com` + Args: current_owner_email (str), new_owner_email (str) + Returns: None (a warning message or a success message will be printed to console) + Raises: customized warning messages in if/else block + """ + current_owner_id = find_user_id(current_owner_email) + new_owner_id = find_user_id(new_owner_email) + + """ This block is executed to check if email addresses provided are associated with two Looker users """ + + if type(new_owner_id) != int and type(new_owner_id) != int: + print("The email addresses for both the current owner and the new owner are not associated with any Looker user id") + + elif type(current_owner_id) != int: + print("The email address for the current owner is not associated with any Looker user id") + + elif type(new_owner_id) != int: + print("The email address for the new owner is not associated with any Looker user id") + + else: + body = {} + body['user_id'] = new_owner_id + find = find_schedules(current_owner_id) + for i in find.values(): + sdk.update_scheduled_plan(i,body) + print("Successfully transfer all schedules of " + current_owner_email + " to " + new_owner_email) + +# Call the function +update_owner('foo@looker.com', 'bar@looker.com') diff --git a/release-please-config.json b/release-please-config.json index 360e2d78a..4b4b9d6ae 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -4,36 +4,21 @@ "bump-patch-for-minor-pre-major": true, "release-as": "", "packages": { - "packages/extension-api-explorer": {}, - "packages/extension-sdk-react": {}, - "packages/extension-sdk": {}, - "packages/hackathon": {}, - "packages/sdk-node": {}, - "packages/sdk": {}, - ".": { - "release-as": "" - }, - "packages/api-explorer": { - "release-as": "" - }, - "packages/run-it": { - "release-as": "" - }, - "packages/code-editor": {}, - "packages/sdk-codegen-scripts": { - }, - "packages/sdk-codegen-utils": { - }, - "packages/sdk-codegen": { - }, - "packages/sdk-rtl": { - }, - "packages/wholly-sheet": { - "release-as": "" - }, - "python": { - "release-type": "python", - "package-name": "looker_sdk" - } + ".": { "release-as": "" }, + "packages/api-explorer": { "release-as": "" }, + "packages/code-editor": { "release-as": "" }, + "packages/extension-api-explorer": { "release-as": "21.6.1" }, + "packages/extension-sdk": { }, + "packages/extension-sdk-react": { "release-as": "21.6.1" }, + "packages/hackathon": { "release-as": "21.6.1" }, + "packages/run-it": { "release-as": "" }, + "packages/sdk": { }, + "packages/sdk-codegen": { "release-as": "21.0.17" }, + "packages/sdk-codegen-scripts": { "release-as": "21.0.17" }, + "packages/sdk-codegen-utils": { }, + "packages/sdk-node": { }, + "packages/sdk-rtl": { }, + "packages/wholly-sheet": { "release-as": "" }, + "python": { "release-type": "python", "package-name": "looker_sdk" } } }