From 69ff073431d769691203bc11c55829bcc013cc51 Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 12:37:57 +0200 Subject: [PATCH 1/9] Add README --- .github/workflows/release-on-push.yml | 30 +---- README.md | 171 ++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 29 deletions(-) create mode 100644 README.md diff --git a/.github/workflows/release-on-push.yml b/.github/workflows/release-on-push.yml index 0719340..c62dd64 100644 --- a/.github/workflows/release-on-push.yml +++ b/.github/workflows/release-on-push.yml @@ -25,32 +25,4 @@ jobs: run: echo -e "Tag name ${{ steps.release.outputs.tag_name }}\nRelease version ${{ steps.release.outputs.version }}" outputs: tag_name: ${{ steps.release.outputs.tag_name }} - version: ${{ steps.release.outputs.version }} - - update-pyproject: - needs: release-on-push - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Get the version built by the previous job and remove the v prefix - id: get-version - run: echo "${{ needs.release-on-push.outputs.version }}" | sed 's/^v//' > $GITHUB_ENV_VERSION - - - name: Verify the matching version in pyproject.toml - run: | - if [ "$(cat pyproject.toml | grep version | cut -d' ' -f3 | sed 's/"//g')" != "$GITHUB_ENV_VERSION" ]; then - echo "The version in pyproject.toml does not match the version built by the previous job" - sed -i "s/version = .*/version = \"$GITHUB_ENV_VERSION\"/" pyproject.toml - fi - - - name: Update pyproject.toml - run: | - git config --global user.name "GitHub Action" - git config --global user.email "SamuelGuillemet@users.noreply.github.com" - git add pyproject.toml - git commit -m "Update pyproject.toml version to $GITHUB_ENV_VERSION" - git push + version: ${{ steps.release.outputs.version }} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1cab31e --- /dev/null +++ b/README.md @@ -0,0 +1,171 @@ +# Microsoft Python client + +[![Python tests](https://github.com/cern-vc/MS-python-client/actions/workflows/python-tests.yml/badge.svg)](https://github.com/cern-vc/MS-python-client/actions/workflows/python-tests.yml) +[![pre-commit](https://github.com/cern-vc/MS-python-client/actions/workflows/pre-commit.yaml/badge.svg)](https://github.com/cern-vc/MS-python-client/actions/workflows/pre-commit.yaml) +[![CodeQL](https://github.com/cern-vc/MS-python-client/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/cern-vc/MS-python-client/actions/workflows/codeql-analysis.yml) +[![codecov](https://codecov.io/gh/cern-vc/MS-python-client/branch/main/graph/badge.svg?token=04EY0K0P2S)](https://codecov.io/gh/cern-vc/MS-python-client) + +Microsoft graph API Python client with support for [Server to Server Oauth tokens](https://learn.microsoft.com/en-us/graph/auth/auth-concepts?view=graph-rest-1.0) with App Only access. + +## Install + +This package is [available on Pypi](https://pypi.org/project/MS-python-client/) + +```bash +pip install MS-python-client +``` + +## Requirements + +- Python >= 3.10 + +## Usage + +### Defining your env variables + +Define the following variables in your `env` or your `.env` file: + +- ms_ACCOUNT_ID +- ms_CLIENT_ID +- ms_CLIENT_SECRET + +#### For testing purposes + +For testing purposes, you can use the following values: + +- ms_ACCESS_TOKEN + +This token could be obtained from the [Microsoft Graph Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer) by clicking on the `Sign in with Microsoft` button and then clicking on the `Access Token` tab. + +### Initialize the MSApiClient from environment variables + +```python +from ms_python_client.ms_api_client import MSApiClient + +ms_client = MSApiClient.init_from_env() +``` + +### Initialize the MSApiClient from .env + +```python +from ms_python_client.ms_api_client import MSApiClient + +ms_client = MSApiClient.init_from_dotenv() +``` + +### Initialize the MSApiClient manually + +```python +from ms_python_client.ms_api_client import MSApiClient + +ms_client = MSApiClient( + account_id="", + client_id="", + client_secret="") +``` + +### Use the file system to store the access token instead of environment + +There are some cases where you might want to store the access token in the file system in order to share its value with other elements of the application (Ex. different pods on a Kubernetes/Openshift application). + +You can define the path where the token will be stored, passing the `use_path` variable to the constructor: + +```python +from ms_python_client.ms_api_client import MSApiClient + +ms_client = MSApiClient( + account_id="", + client_id="", + client_secret="", + use_path="/path/to/token/folder") +``` + +## How to make API calls + +```python +USER_ID = "12345" +SUBJECT = "Test meeting" + +query = {"$count": "true", "$filter": f"contains(subject,'{SUBJECT}')"} +result = cern_ms_client.events.list_events(USER_ID, query) +``` + +## Optional: How to configure the logging + +```python +from ms_python_client.utils.logger import setup_logs + +setup_logs(log_level=logging.DEBUG) +``` + +## Available endpoints + +### **events**: + +1. get all events +2. get a single event +3. create an event +4. update an event +5. delete an event + +## CERN specific endpoints + +Instead of using the `MSApiClient` class, you can use the `CERNMSApiClient` class, which is a subclass of the `MSApiClient` class. +This class will provide you some more utilities but it will only work for CERN users (obviously). + +This will be used in the context of synchronizing the events between the CERN Indico system and the calendars of the Zoom Rooms. + +### How to initialize the CERNMSApiClient + +```python +from ms_python_client.cern_ms_api_client import CERNMSApiClient + +cern_ms_client = CERNMSApiClient.init_from_env() +``` + +### Available endpoints + +#### **events**: + +1. get all events +2. get a single event using indico id +3. create an event +4. update an event using indico id +5. delete an event using indico id + +You will find useful the `EventParameters` and `PartialEventParameters` classes, which will help you to create the events. + +**indico_event_id** is the id of the event in the indico system which is mandatory to create an event. + +**USER_ID** is the email of the Zoom Room. + +```python +from ms_python_client.cern_ms_api_client import ( + CERNMSApiClient, + EventParameters, + PartialEventParameters, + ) + +cern_ms_client = CERNMSApiClient.init_from_env() + +USER_ID = os.getenv("USER_ID") # Which is the email of the Zoom Room +INDICO_EVENT_ID = os.getenv("INDICO_EVENT_ID") + +event_parameters = EventParameters( + subject="Test meeting", + start_time="2021-10-01T12:00:00", + end_time="2021-10-01T13:00:00", + timezone="Europe/Zurich", + indico_event_id=INDICO_EVENT_ID, + zoom_url="https://cern.zoom.us/******", +) + +partial_event_parameters = PartialEventParameters( + indico_event_id=INDICO_EVENT_ID, + end_time="2021-10-01T14:00:00", +) # You can update only the end_time of the event for example + +cern_ms_client.events.create_event(USER_ID, event_parameters) +cern_ms_client.events.update_event_by_indico_id(USER_ID, partial_event_parameters) +cern_ms_client.events.delete_event_by_indico_id(USER_ID, INDICO_EVENT_ID) +``` From e8f54133e5c463dfcf7cb3ff6a342b5aa2f8b3b1 Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 13:47:01 +0200 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Workflow=20forPR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-labels.yaml | 82 +++++++++++----------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/.github/workflows/pull-request-labels.yaml b/.github/workflows/pull-request-labels.yaml index 3aec93f..04e5f6d 100644 --- a/.github/workflows/pull-request-labels.yaml +++ b/.github/workflows/pull-request-labels.yaml @@ -6,45 +6,47 @@ on: - main jobs: - add-labels: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 + add-labels: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 - - name: Get the current version - id: get_current_version - run: | - echo "$(cat pyproject.toml | grep version | cut -d' ' -f3 | sed 's/"//g')" > $VERSION + - name: Get the current version + id: get_current_version + run: | + echo ::set-output name=version::$(cat pyproject.toml | grep version | cut -d' ' -f3 | sed 's/"//g') - - name: Get the last release version - uses: actions/github-script@v3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const labels = ["bump:patch", "bump:minor", "bump:major"]; - const last_release = await github.repos.getLatestRelease({ - owner: context.repo.owner, - repo: context.repo.repo, - }); - const last_release_tag = last_release.data.tag_name; - const last_release_version = last_release_tag.replace("v", ""); - if (last_release_version === process.env.VERSION) { - core.setFailed("The version has not been bumped"); - } else { - const last_release_version_split = last_release_version.split("."); - const current_version_split = process.env.VERSION.split("."); - if (last_release_version_split[0] !== current_version_split[0]) { - core.setOutput("label", labels[2]); - } else if (last_release_version_split[1] !== current_version_split[1]) { - core.setOutput("label", labels[1]); - } else { - core.setOutput("label", labels[0]); - } - } - - name: Add labels - uses: actions-ecosystem/action-add-labels@v1 - with: - labels: ${{ steps.get_current_version.outputs.label }} \ No newline at end of file + - name: Get the last release version + uses: actions/github-script@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const labels = ["bump:patch", "bump:minor", "bump:major"]; + const last_release = await github.repos.getLatestRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + }); + const last_release_tag = last_release.data.tag_name; + const last_release_version = last_release_tag.replace("v", ""); + if (last_release_version === process.env.VERSION) { + core.setFailed("The version has not been bumped"); + } else { + const last_release_version_split = last_release_version.split("."); + const current_version_split = process.env.VERSION.split("."); + if (last_release_version_split[0] !== current_version_split[0]) { + core.setOutput("label", labels[2]); + } else if (last_release_version_split[1] !== current_version_split[1]) { + core.setOutput("label", labels[1]); + } else { + core.setOutput("label", labels[0]); + } + } + env: + VERSION: ${{ steps.get_current_version.outputs.version }} + - name: Add labels + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: ${{ steps.get_current_version.outputs.label }} From a68dc967b98ea391387dd84ab1d0b18fe6c9c77d Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 13:48:55 +0200 Subject: [PATCH 3/9] Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 41a3ceb..7f28a12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ms-python-client" -version = "0.0.1" +version = "0.0.2" exclude = ["tests*", "example*", ".github*", ".git*", ".vscode*"] description = "This package is used to interact with the microsoft grap API" authors = ["Samuel Guillemet "] From f822caf66e49c17f03a4221b0908860e1de18087 Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 13:55:02 +0200 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Name=20of=20labels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-labels.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request-labels.yaml b/.github/workflows/pull-request-labels.yaml index 04e5f6d..03dc73f 100644 --- a/.github/workflows/pull-request-labels.yaml +++ b/.github/workflows/pull-request-labels.yaml @@ -1,4 +1,4 @@ -name: Add labels to the pull request on main for bumping the version +name: Add labels to the pull request on main for bumping the version from pyproject.toml on: pull_request: @@ -24,7 +24,7 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const labels = ["bump:patch", "bump:minor", "bump:major"]; + const labels = ["release:patch", "release:minor", "release:major"]; const last_release = await github.repos.getLatestRelease({ owner: context.repo.owner, repo: context.repo.repo, From dd8a8581b63a85cc148ad5300b7e20585320ca68 Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 13:55:17 +0200 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Trigger=20for=20workf?= =?UTF-8?q?lows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pypi-publish.yml | 7 +++---- .github/workflows/python-tests.yml | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 1dda7cb..94f5ac3 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -1,9 +1,8 @@ name: Build and publish Python package on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+" - - "v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+" # for beta releases + release: + types: [created] + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 224411b..bb7a4e0 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -1,7 +1,6 @@ name: Python tests on: - pull_request: push: branches: [main, develop] From e0aefb799c041e6b8f76d41e574b7e9b51e674fd Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 13:59:51 +0200 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Add=20output=20of=20l?= =?UTF-8?q?abels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/codeql-analysis.yml | 1 - .github/workflows/pre-commit.yaml | 1 - .github/workflows/pull-request-labels.yaml | 6 ++++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 51b3e43..5c329ce 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,7 +12,6 @@ name: "CodeQL" on: - pull_request: push: branches: [main, develop] schedule: diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 8e48735..c072c1b 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -1,7 +1,6 @@ name: pre-commit on: - pull_request: push: branches: [main, develop] diff --git a/.github/workflows/pull-request-labels.yaml b/.github/workflows/pull-request-labels.yaml index 03dc73f..9197ab3 100644 --- a/.github/workflows/pull-request-labels.yaml +++ b/.github/workflows/pull-request-labels.yaml @@ -19,7 +19,8 @@ jobs: run: | echo ::set-output name=version::$(cat pyproject.toml | grep version | cut -d' ' -f3 | sed 's/"//g') - - name: Get the last release version + - name: Get the label to add + id: get_label uses: actions/github-script@v3 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -46,7 +47,8 @@ jobs: } env: VERSION: ${{ steps.get_current_version.outputs.version }} + - name: Add labels uses: actions-ecosystem/action-add-labels@v1 with: - labels: ${{ steps.get_current_version.outputs.label }} + labels: ${{ steps.get_label.outputs.label }} From 0c9a16cab3ec197404fa871f910cd18a6fbfa107 Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 14:02:43 +0200 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Logger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ms_python_client/utils/logger.py | 26 +++++++++++++++++++++ tests/ms_python_client/utils/test_logger.py | 9 +++++++ 2 files changed, 35 insertions(+) create mode 100644 ms_python_client/utils/logger.py create mode 100644 tests/ms_python_client/utils/test_logger.py diff --git a/ms_python_client/utils/logger.py b/ms_python_client/utils/logger.py new file mode 100644 index 0000000..2feb83c --- /dev/null +++ b/ms_python_client/utils/logger.py @@ -0,0 +1,26 @@ +import logging +import sys + + +def setup_logs(log_level=logging.WARNING): + logger = logging.getLogger("ms_python_client") + + logger.setLevel(log_level) + + formatter = logging.Formatter( + "%(asctime)s | %(levelname)s | %(name)s | %(message)s | %(pathname)s:%(lineno)d | %(funcName)s()" + ) + + configure_stdout_logging(logger=logger, formatter=formatter, log_level=log_level) + + return logger + + +def configure_stdout_logging(logger, formatter=None, log_level=logging.WARNING): + stream_handler = logging.StreamHandler(stream=sys.stdout) + + stream_handler.setFormatter(formatter) + stream_handler.setLevel(log_level) + + logger.addHandler(stream_handler) + print(f"Logging {str(logger)} to stdout -> True") diff --git a/tests/ms_python_client/utils/test_logger.py b/tests/ms_python_client/utils/test_logger.py new file mode 100644 index 0000000..5a3e542 --- /dev/null +++ b/tests/ms_python_client/utils/test_logger.py @@ -0,0 +1,9 @@ +import logging + +from ms_python_client.utils.logger import setup_logs + + +def test_setup_logs(): + logger = setup_logs(log_level=logging.DEBUG) + + assert logger is not None From 25381ce2ecdea935f9e79bbca7dec776a9baa2e1 Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 14:04:47 +0200 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Workflows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pre-commit.yaml | 2 +- .github/workflows/pull-request-labels.yaml | 2 +- .github/workflows/release-on-push.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index c072c1b..d4d418e 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -2,7 +2,7 @@ name: pre-commit on: push: - branches: [main, develop] + branches: [develop] jobs: pre-commit: diff --git a/.github/workflows/pull-request-labels.yaml b/.github/workflows/pull-request-labels.yaml index 9197ab3..e1de63c 100644 --- a/.github/workflows/pull-request-labels.yaml +++ b/.github/workflows/pull-request-labels.yaml @@ -1,4 +1,4 @@ -name: Add labels to the pull request on main for bumping the version from pyproject.toml +name: Labels pull request on: pull_request: diff --git a/.github/workflows/release-on-push.yml b/.github/workflows/release-on-push.yml index c62dd64..8e48c50 100644 --- a/.github/workflows/release-on-push.yml +++ b/.github/workflows/release-on-push.yml @@ -4,7 +4,7 @@ name: Release on push to main branch on: push: - branches: ["main"] + branches: [main] jobs: release-on-push: @@ -25,4 +25,4 @@ jobs: run: echo -e "Tag name ${{ steps.release.outputs.tag_name }}\nRelease version ${{ steps.release.outputs.version }}" outputs: tag_name: ${{ steps.release.outputs.tag_name }} - version: ${{ steps.release.outputs.version }} \ No newline at end of file + version: ${{ steps.release.outputs.version }} From 0e2e9c0e2b1f26d3331d887063e1d9c18e21b8bd Mon Sep 17 00:00:00 2001 From: Samuel Guillemet Date: Fri, 7 Jul 2023 14:09:00 +0200 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Remove=20deprecated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-labels.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-labels.yaml b/.github/workflows/pull-request-labels.yaml index e1de63c..a3a48b0 100644 --- a/.github/workflows/pull-request-labels.yaml +++ b/.github/workflows/pull-request-labels.yaml @@ -17,7 +17,7 @@ jobs: - name: Get the current version id: get_current_version run: | - echo ::set-output name=version::$(cat pyproject.toml | grep version | cut -d' ' -f3 | sed 's/"//g') + echo "version=$(cat pyproject.toml | grep version | cut -d' ' -f3 | sed 's/"//g')" >> $GITHUB_OUTPUT - name: Get the label to add id: get_label