diff --git a/docker/Dockerfile b/docker/Dockerfile index 46070eb..be75a3f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -16,7 +16,6 @@ WORKDIR /home/docker_user/workspace # Install Poetry RUN curl -sSL -o install-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py \ - && echo '8bb6d1f65ed1f19fe500f8baf11328217459a30bd9d94fa5ec3bc9fc0fc345e7 install-poetry.py' | sha256sum --check \ && POETRY_HOME=/home/docker_user/poetry python install-poetry.py --preview \ && rm install-poetry.py @@ -25,8 +24,8 @@ ENV PATH="${PATH}:/home/docker_user/.poetry/bin:/home/docker_user/poetry/bin" COPY pyproject.toml ./ COPY poetry.lock ./ -RUN poetry install --no-root --no-dev +RUN poetry install --no-root --only main COPY . /home/docker_user/workspace/ -ENTRYPOINT ["poetry", "run", "github_tests_validator_app"] +CMD ["poetry", "run", "python3", "server.py"] diff --git a/docker/cloudbuild.yaml b/docker/cloudbuild.yaml new file mode 100644 index 0000000..48758f3 --- /dev/null +++ b/docker/cloudbuild.yaml @@ -0,0 +1,30 @@ +steps: +- name: 'gcr.io/cloud-builders/docker' + args: + - 'build' + - '--tag' + - 'europe-west1-docker.pkg.dev/school-of-data-github-app/github-test-validator-app/github_test_validator_app_server:lastest' + - "--file=./docker/Dockerfile" + - '.' + id: Build Artifact +- name: 'gcr.io/cloud-builders/gcloud' + args: + - 'run' + - 'deploy' + - 'github-test-validator-app-server' + - '--image' + - 'europe-west1-docker.pkg.dev/school-of-data-github-app/github-test-validator-app/github_test_validator_app_server:lastest' + - '--region' + - 'europe-west1' + - '--platform' + - 'managed' + - '--allow-unauthenticated' + - '--service-account=school-of-data-app-dev@school-of-data-github-app.iam.gserviceaccount.com' + - '--set-env-vars' + - 'GH_APP_ID=230621' + - '--set-env-vars' + - 'USER_SHARE=bruno.zheng@artefact.com' + - '--set-env-vars' + - 'ENV=PROD' +images: +- 'europe-west1-docker.pkg.dev/school-of-data-github-app/github-test-validator-app/github_test_validator_app_server:lastest' diff --git a/github_tests_validator_app/bin/github_event_process.py b/github_tests_validator_app/bin/github_event_process.py index c8f72aa..77f6f88 100644 --- a/github_tests_validator_app/bin/github_event_process.py +++ b/github_tests_validator_app/bin/github_event_process.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any, Dict, List import logging @@ -16,8 +16,8 @@ GSHEET_DETAILS_SPREADSHEET, USER_SHARE, ) -from github_tests_validator_app.lib.connectors.gddrive import GoogleDriveConnector -from github_tests_validator_app.lib.connectors.gsheet import GSheetConnector +from github_tests_validator_app.lib.connectors.google_drive import GoogleDriveConnector +from github_tests_validator_app.lib.connectors.google_sheet import GSheetConnector from github_tests_validator_app.lib.models.file import GSheetDetailFile, GSheetFile, WorkSheetFile from github_tests_validator_app.lib.models.users import GitHubUser from github_tests_validator_app.lib.utils import init_github_user_from_github_event @@ -48,11 +48,13 @@ def handle_process(payload: Dict[str, Any]) -> str: def init_gsheet_file( - google_drive: GoogleDriveConnector, info: Dict[str, Any], parent_id: str, user_share: str + google_drive: GoogleDriveConnector, + info: Dict[str, Any], + parent_id: str, + shared_user_list: List[str], ) -> GSheetFile: - gsheet = google_drive.get_gsheet(info["name"], parent_id, user_share) - + gsheet = google_drive.get_gsheet(info["name"], parent_id, shared_user_list) list_worksheets = [ WorkSheetFile(NAME=worksheet["name"], HEADERS=worksheet["headers"]) for _, worksheet in info["worksheets"].items() @@ -66,7 +68,7 @@ def init_gsheet_file( def init_gsheet_detail_file( - google_drive: GoogleDriveConnector, info: Dict[str, Any], parent_id: str, user_share: str + google_drive: GoogleDriveConnector, info: Dict[str, Any], parent_id: str, user_share: List[str] ) -> GSheetDetailFile: gsheet = google_drive.get_gsheet(info["name"], parent_id, user_share) @@ -94,19 +96,19 @@ def run(payload: Dict[str, Any]) -> Any: return # Init Google Drive connector and folders - googe_drive = GoogleDriveConnector() - folder_school_of_data = googe_drive.get_gdrive_folder(GDRIVE_MAIN_DIRECTORY_NAME, USER_SHARE) + google_drive = GoogleDriveConnector() + folder_school_of_data = google_drive.get_gdrive_folder(GDRIVE_MAIN_DIRECTORY_NAME, USER_SHARE) # Init Google sheets gsheet_summary_file = init_gsheet_file( - googe_drive, GDRIVE_SUMMARY_SPREADSHEET, folder_school_of_data["id"], USER_SHARE + google_drive, GDRIVE_SUMMARY_SPREADSHEET, folder_school_of_data["id"], USER_SHARE ) gsheet_details_file = init_gsheet_detail_file( - googe_drive, GSHEET_DETAILS_SPREADSHEET, folder_school_of_data["id"], USER_SHARE + google_drive, GSHEET_DETAILS_SPREADSHEET, folder_school_of_data["id"], USER_SHARE ) # Init Google sheet connector and worksheets - gsheet = GSheetConnector(gsheet_summary_file, gsheet_details_file) + gsheet = GSheetConnector(google_drive.credentials, gsheet_summary_file, gsheet_details_file) # Init GitHubUser student_user = init_github_user_from_github_event(payload) diff --git a/github_tests_validator_app/bin/github_repo_validation.py b/github_tests_validator_app/bin/github_repo_validation.py index 0c70950..10cd6af 100644 --- a/github_tests_validator_app/bin/github_repo_validation.py +++ b/github_tests_validator_app/bin/github_repo_validation.py @@ -4,14 +4,16 @@ from github import ContentFile from github_tests_validator_app.config.config import ( + GCP_PROJECT_ID, + GH_ACCESS_TOKEN_NAME, GH_SOLUTION_OWNER, GH_SOLUTION_REPO_NAME, - GH_SOLUTION_TESTS_ACCESS_TOKEN, GH_TESTS_FOLDER_NAME, default_message, ) from github_tests_validator_app.lib.connectors.github_connector import GitHubConnector -from github_tests_validator_app.lib.connectors.gsheet import GSheetConnector +from github_tests_validator_app.lib.connectors.google_secret_manager import GSecretManager +from github_tests_validator_app.lib.connectors.google_sheet import GSheetConnector from github_tests_validator_app.lib.models.users import GitHubUser commit_sha_path: Dict[str, List[str]] = { @@ -91,12 +93,15 @@ def github_repo_validation( student_github_connector: GitHubConnector, gsheet: GSheetConnector, payload: Dict[str, Any] ) -> None: - solution_user = GitHubUser(LOGIN=str(GH_SOLUTION_OWNER)) + solution_user = GitHubUser(LOGIN=GH_SOLUTION_OWNER) + gsecret_manager = GSecretManager(GCP_PROJECT_ID) + acces_token = gsecret_manager.get_access_secret_version(GH_ACCESS_TOKEN_NAME) + solution_github_connector = GitHubConnector( user=solution_user, repo_name=GH_SOLUTION_REPO_NAME, branch_name="main", - access_token=GH_SOLUTION_TESTS_ACCESS_TOKEN, + access_token=acces_token, ) if not solution_github_connector: gsheet.add_new_repo_valid_result( diff --git a/github_tests_validator_app/bin/student_challenge_results_validation.py b/github_tests_validator_app/bin/student_challenge_results_validation.py index 31af8da..7962e83 100644 --- a/github_tests_validator_app/bin/student_challenge_results_validation.py +++ b/github_tests_validator_app/bin/student_challenge_results_validation.py @@ -4,7 +4,7 @@ from github_tests_validator_app.config.config import CHALLENGE_DIR from github_tests_validator_app.lib.connectors.github_connector import GitHubConnector -from github_tests_validator_app.lib.connectors.gsheet import GSheetConnector +from github_tests_validator_app.lib.connectors.google_sheet import GSheetConnector from github_tests_validator_app.lib.models.pytest_result import PytestResult diff --git a/github_tests_validator_app/config/__init__.py b/github_tests_validator_app/config/__init__.py index 44c8e8e..7b49f67 100644 --- a/github_tests_validator_app/config/__init__.py +++ b/github_tests_validator_app/config/__init__.py @@ -1,12 +1,20 @@ import logging +import os -FORMAT = "%(asctime)s - %(levelname)s: %(message)s" -DATEFMT = "%H:%M:%S" -logging.basicConfig( - format=FORMAT, - level=logging.INFO, - datefmt=DATEFMT, -) +import google.cloud.logging -if logging.getLogger("uvicorn") and logging.getLogger("uvicorn").handlers: - logging.getLogger("uvicorn").removeHandler(logging.getLogger("uvicorn").handlers[0]) +if os.getenv("ENV", "") == "PROD": + logging_client = google.cloud.logging.Client() + logging_client.get_default_handler() + logging_client.setup_logging() +else: + FORMAT = "%(asctime)s - %(levelname)s: %(message)s" + DATEFMT = "%H:%M:%S" + logging.basicConfig( + format=FORMAT, + level=logging.INFO, + datefmt=DATEFMT, + ) + + if logging.getLogger("uvicorn") and logging.getLogger("uvicorn").handlers: + logging.getLogger("uvicorn").removeHandler(logging.getLogger("uvicorn").handlers[0]) diff --git a/github_tests_validator_app/config/config.py b/github_tests_validator_app/config/config.py index 7df80a4..de4d7b1 100644 --- a/github_tests_validator_app/config/config.py +++ b/github_tests_validator_app/config/config.py @@ -6,22 +6,25 @@ # GitHub GH_APP_ID = cast(str, os.getenv("GH_APP_ID", None)) -GH_APP_KEY_PATH = os.getenv("GH_APP_KEY", "") -GH_SOLUTION_TESTS_ACCESS_TOKEN = cast(str, os.getenv("SOLUTION_TESTS_ACCESS_TOKEN", None)) GH_SOLUTION_OWNER = "artefactory-fr" GH_SOLUTION_REPO_NAME = "school_of_data_tests" GH_TESTS_FOLDER_NAME = "tests" GH_API = "https://api.github.com/repos" GH_ALL_ARTIFACT_ENDPOINT = "actions/artifacts" +# GCP +GCP_PROJECT_ID = "school-of-data-github-app" + +# Google Secrets +GH_APP_KEY_NAME = "github_test_validator_app_key" +GH_ACCESS_TOKEN_NAME = "github_personal_access_token" + # Google Drive -GDRIVE_CREDENTIALS_PATH = "credentials.json" GDRIVE_MAIN_DIRECTORY_NAME = "school_of_data_results" # Google Sheet -GSHEET_SA_JSON = cast(str, os.getenv("GSHEET_SA_JSON", None)) -GDRIVE_HIERARCHY_APTH = "github_tests_validator_app/config/data/gdrive_hierarchy.yml" -with open(GDRIVE_HIERARCHY_APTH) as file: +GDRIVE_HIERARCHY_PATH = "github_tests_validator_app/config/data/gdrive_hierarchy.yml" +with open(GDRIVE_HIERARCHY_PATH) as file: data = yaml.safe_load(file) GDRIVE_SUMMARY_SPREADSHEET = data["gdrive_summary_spreadsheet"] @@ -38,4 +41,4 @@ # Common CHALLENGE_DIR = "tests/tests/" DATE_FORMAT = "%d/%m/%Y %H:%M:%S" -USER_SHARE = os.getenv("USER_SHARE", "") +USER_SHARE = os.getenv("USER_SHARE", "").split(",") diff --git a/github_tests_validator_app/lib/connectors/github_connector.py b/github_tests_validator_app/lib/connectors/github_connector.py index 87bf5cf..f7342d0 100644 --- a/github_tests_validator_app/lib/connectors/github_connector.py +++ b/github_tests_validator_app/lib/connectors/github_connector.py @@ -8,11 +8,13 @@ import requests from github import ContentFile, Github, GithubIntegration, Repository from github_tests_validator_app.config.config import ( + GCP_PROJECT_ID, GH_ALL_ARTIFACT_ENDPOINT, GH_API, GH_APP_ID, - GH_APP_KEY_PATH, + GH_APP_KEY_NAME, ) +from github_tests_validator_app.lib.connectors.google_secret_manager import GSecretManager from github_tests_validator_app.lib.models.users import GitHubUser from github_tests_validator_app.lib.utils import get_hash_files @@ -40,12 +42,13 @@ def __init__( def set_git_integration(self) -> None: - with open(GH_APP_KEY_PATH) as f: - GH_APP_KEY = f.read() - self.git_integration = GithubIntegration( - GH_APP_ID, - GH_APP_KEY, - ) + gsecret_manager = GSecretManager(GCP_PROJECT_ID) + github_app_key = gsecret_manager.get_access_secret_version(GH_APP_KEY_NAME) + + self.git_integration = GithubIntegration( + GH_APP_ID, + github_app_key, + ) def set_access_token(self, repo_name: str) -> None: self.ACCESS_TOKEN = self.git_integration.get_access_token( diff --git a/github_tests_validator_app/lib/connectors/gddrive.py b/github_tests_validator_app/lib/connectors/google_drive.py similarity index 59% rename from github_tests_validator_app/lib/connectors/gddrive.py rename to github_tests_validator_app/lib/connectors/google_drive.py index 76f5777..ccba540 100644 --- a/github_tests_validator_app/lib/connectors/gddrive.py +++ b/github_tests_validator_app/lib/connectors/google_drive.py @@ -1,42 +1,48 @@ from typing import Any, Dict, List -import json import logging +from readline import set_completion_display_matches_hook -from github_tests_validator_app.config.config import GDRIVE_CREDENTIALS_PATH -from google.oauth2 import service_account -from googleapiclient.discovery import Resource, build - -SCOPES = ["https://www.googleapis.com/auth/drive"] +from google.auth import default +from googleapiclient.discovery import build class GoogleDriveConnector: def __init__(self): - - self.creds_path = GDRIVE_CREDENTIALS_PATH - self.creds_file = json.load(open(self.creds_path)) - logging.info(f"Connecting to Google Drive API ...") - self.creds = service_account.Credentials.from_service_account_info( - self.creds_file, scopes=SCOPES - ) - self.drive_api = build("drive", "v3", credentials=self.creds) + self.credentials = self._get_credentials() + self.client = self._get_client() logging.info(f"Done.") - def get_all_folder(self) -> Any: + def _get_credentials(self): + credentials, _ = default( + scopes=[ + "https://www.googleapis.com/auth/drive", + ] + ) + return credentials + + def _get_client(self): + return build("drive", "v3", credentials=self.credentials) + + def search_folder(self, folder_name: str) -> Any: """Get all folders from a google drive. ... :return: All folders informations. :rtype: Any """ results = ( - self.drive_api.files() - .list(q="mimeType = 'application/vnd.google-apps.folder'", spaces="drive") + self.client.files() + .list( + q=f"name = '{folder_name}' and (mimeType = 'application/vnd.google-apps.folder')", + spaces="drive", + fields="files(id, name, mimeType, permissions)", + ) .execute() ) return results.get("files", []) - def get_all_file(self, parent_folder_ids: str = "") -> Any: + def get_all_file(self, file_name: str, parent_folder_ids: str = "") -> Any: """Get all files from a folder on Google Drive. :param parent_folder_ids: Folder ID. ... @@ -45,13 +51,13 @@ def get_all_file(self, parent_folder_ids: str = "") -> Any: """ query = "" if parent_folder_ids: - query = f"parents in '{parent_folder_ids}'" + query = f"name = '{file_name}'" response = ( - self.drive_api.files() + self.client.files() .list( q=query, spaces="drive", - fields="nextPageToken, files(id, name, mimeType)", + fields="files(id, name, mimeType, permissions)", pageToken=None, ) .execute() @@ -69,9 +75,8 @@ def create_folder(self, folder_name: str) -> Any: file_metadata = {"name": folder_name, "mimeType": "application/vnd.google-apps.folder"} # pylint: disable=maybe-no-member - folder = self.drive_api.files().create(body=file_metadata, fields="id").execute() - id = folder.get("id") - logging.info(f'Folder {folder_name} has created with ID: "{id}".') + folder = self.client.files().create(body=file_metadata).execute() + logging.info(f'Folder {folder["name"]} has created with ID: "{folder["id"]}".') return folder def share_file(self, real_file_id: str, user_email: str) -> List[Any]: @@ -92,16 +97,24 @@ def callback(request_id, response, exception): else: ids.append(response.get("id")) - batch = self.drive_api.new_batch_http_request(callback=callback) + batch = self.client.new_batch_http_request(callback=callback) user_permission = {"type": "user", "role": "writer", "emailAddress": user_email} batch.add( - self.drive_api.permissions().create(fileId=file_id, body=user_permission, fields="id") + self.client.permissions().create(fileId=file_id, body=user_permission, fields="id") ) batch.execute() return ids - def get_gdrive_folder(self, folder_name: str, user_share: str) -> Any: + def share_file_from_users(self, file_info: Dict[str, Any], users: List[str] = []) -> None: + if not users: + return + user_shared = [user["emailAddress"] for user in file_info.get("permissions", [])] + new_shared_users = list(set(users) - set(user_shared)) + for user in new_shared_users: + self.share_file(file_info["id"], user) + + def get_gdrive_folder(self, folder_name: str, shared_user_list: List[str] = []) -> Any: """Get the folder information in google drive. .. note :: If the folder doesn't exist, it will create a new one. @@ -111,23 +124,20 @@ def get_gdrive_folder(self, folder_name: str, user_share: str) -> Any: :return: informations of the folder :rtype: Any """ - list_dir = self.get_all_folder() - for dir in list_dir: - if dir["name"] == folder_name: - return dir + list_folder = self.search_folder(folder_name) + for folder in list_folder: + if folder.get("name", None) == folder_name: + if shared_user_list: + self.share_file_from_users(folder, shared_user_list) + return folder folder = self.create_folder(folder_name) - if "id" in folder: - self.share_file(folder["id"], user_share) - return { - "name": folder_name, - "id": folder["id"], - "kind": "drive#file", - "mimeType": "application/vnd.google-apps.folder", - } + if "id" in folder and shared_user_list: + self.share_file_from_users(folder, shared_user_list) + return folder def get_gsheet( - self, gsheet_name: str, parent_folder_ids: str = "", user_share: str = "" + self, gsheet_name: str, parent_folder_ids: str = "", shared_user_list: List[str] = [] ) -> Any: """Get the google sheet information. .. note :: @@ -139,22 +149,23 @@ def get_gsheet( :return: informations of the google sheet :rtype: Any """ - list_file = self.get_all_file(parent_folder_ids) + list_file = self.get_all_file(gsheet_name, parent_folder_ids) for file in list_file: if file["name"] == gsheet_name and "spreadsheet" in file["mimeType"]: + if shared_user_list: + self.share_file_from_users(file, shared_user_list) return file file = self.create_google_file( - self.drive_api, gsheet_name, "application/vnd.google-apps.spreadsheet", [parent_folder_ids], ) - if user_share: - self.share_file(file["id"], user_share) + if shared_user_list: + self.share_file_from_users(file, shared_user_list) return file def create_google_file( - self, drive_api: Resource, title: str, mimeType: str, parent_folder_ids: List[str] = [] + self, title: str, mimeType: str, parent_folder_ids: List[str] = [] ) -> Any: """Create a new file on Google drive. .. note :: @@ -170,12 +181,8 @@ def create_google_file( "name": title, "mimeType": mimeType, } - if parent_folder_ids: body["parents"] = parent_folder_ids - - req = drive_api.files().create(body=body) + req = self.client.files().create(body=body) new_sheet = req.execute() - - # Get id of fresh sheet return new_sheet diff --git a/github_tests_validator_app/lib/connectors/google_secret_manager.py b/github_tests_validator_app/lib/connectors/google_secret_manager.py new file mode 100644 index 0000000..cb25de7 --- /dev/null +++ b/github_tests_validator_app/lib/connectors/google_secret_manager.py @@ -0,0 +1,15 @@ +from typing import Any + +from google.cloud import secretmanager + + +class GSecretManager: + def __init__(self, project_id: str): + self.parent = project_id + self.client = secretmanager.SecretManagerServiceClient() + + def get_access_secret_version(self, secret_name: str, version: str = "1") -> Any: + name = self.client.secret_version_path(self.parent, secret_name, version) + response = self.client.access_secret_version(request={"name": name}) + payload = response.payload.data.decode("UTF-8") + return payload diff --git a/github_tests_validator_app/lib/connectors/gsheet.py b/github_tests_validator_app/lib/connectors/google_sheet.py similarity index 95% rename from github_tests_validator_app/lib/connectors/gsheet.py rename to github_tests_validator_app/lib/connectors/google_sheet.py index 01d701b..deeff4e 100644 --- a/github_tests_validator_app/lib/connectors/gsheet.py +++ b/github_tests_validator_app/lib/connectors/google_sheet.py @@ -4,19 +4,25 @@ import logging import gspread -from github_tests_validator_app.config.config import GDRIVE_SUMMARY_SPREADSHEET, GSHEET_SA_JSON +from github_tests_validator_app.config.config import GDRIVE_SUMMARY_SPREADSHEET from github_tests_validator_app.lib.models.file import GSheetDetailFile, GSheetFile from github_tests_validator_app.lib.models.pytest_result import PytestResult from github_tests_validator_app.lib.models.users import GitHubUser +from google.oauth2.service_account import Credentials class GSheetConnector: - def __init__(self, gsheet_summary_file: GSheetFile, gsheet_details_file: GSheetDetailFile): + def __init__( + self, + credentials: Credentials, + gsheet_summary_file: GSheetFile, + gsheet_details_file: GSheetDetailFile, + ): self.gsheet_summary_file = gsheet_summary_file self.gsheet_details_file = gsheet_details_file logging.info(f"Connecting to Google Sheet API ...") - self.gs_client = gspread.service_account(filename=GSHEET_SA_JSON) + self.gs_client = gspread.authorize(credentials) logging.info("Done.") logging.info(f"Init spreadsheet ...") diff --git a/github_tests_validator_app/lib/utils.py b/github_tests_validator_app/lib/utils.py index 4b738d1..8c5077b 100644 --- a/github_tests_validator_app/lib/utils.py +++ b/github_tests_validator_app/lib/utils.py @@ -6,7 +6,7 @@ from github import ContentFile from github_tests_validator_app.config.config import DATE_FORMAT -from github_tests_validator_app.lib.connectors.gsheet import GSheetConnector +from github_tests_validator_app.lib.connectors.google_sheet import GSheetConnector from github_tests_validator_app.lib.models.users import GitHubUser diff --git a/poetry.lock b/poetry.lock index 670b255..8b7b67b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -301,6 +301,8 @@ python-versions = ">=3.7" [package.dependencies] google-auth = ">=1.25.0,<3.0dev" googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} protobuf = ">=3.20.1,<5.0.0dev" requests = ">=2.18.0,<3.0.0dev" @@ -372,6 +374,80 @@ requests-oauthlib = ">=0.7.0" [package.extras] tool = ["click (>=6.0.0)"] +[[package]] +name = "google-cloud-appengine-logging" +version = "1.1.4" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.0,<5.0.0dev" + +[[package]] +name = "google-cloud-audit-log" +version = "0.2.4" +description = "Google Cloud Audit Protos" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +googleapis-common-protos = ">=1.56.2,<2.0dev" +protobuf = ">=3.6.0,<5.0.0dev" + +[[package]] +name = "google-cloud-core" +version = "2.3.2" +description = "Google Cloud API client core library" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" + +[package.extras] +grpc = ["grpcio (>=1.38.0,<2.0dev)"] + +[[package]] +name = "google-cloud-logging" +version = "3.2.2" +description = "Stackdriver Logging API client library" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-appengine-logging = ">=0.1.0,<2.0.0dev" +google-cloud-audit-log = ">=0.1.0,<1.0.0dev" +google-cloud-core = ">=2.0.0,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.0,<5.0.0dev" + +[[package]] +name = "google-cloud-secret-manager" +version = "2.12.4" +description = "Secret Manager API API client library" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.2.5)"] + [[package]] name = "googleapis-common-protos" version = "1.56.4" @@ -381,11 +457,51 @@ optional = false python-versions = ">=3.7" [package.dependencies] +grpcio = {version = ">=1.0.0,<2.0.0dev", optional = true, markers = "extra == \"grpc\""} protobuf = ">=3.15.0,<5.0.0dev" [package.extras] grpc = ["grpcio (>=1.0.0,<2.0.0dev)"] +[[package]] +name = "grpc-google-iam-v1" +version = "0.12.4" +description = "IAM API client library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.0.0,<2.0.0dev" + +[[package]] +name = "grpcio" +version = "1.49.0" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +six = ">=1.5.2" + +[package.extras] +protobuf = ["grpcio-tools (>=1.49.0)"] + +[[package]] +name = "grpcio-status" +version = "1.49.0" +description = "Status proto mapping for gRPC" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.49.0" +protobuf = ">=4.21.3" + [[package]] name = "gspread" version = "5.5.0" @@ -588,6 +704,20 @@ pyyaml = ">=5.1" toml = "*" virtualenv = ">=20.0.8" +[[package]] +name = "proto-plus" +version = "1.22.1" +description = "Beautiful, Pythonic protocol buffers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + [[package]] name = "protobuf" version = "4.21.5" @@ -800,7 +930,7 @@ tokenize-rt = ">=3.2.0" name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" @@ -1118,7 +1248,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "06aa11dbdd9e6e2654bfc500c9a86d76918763d50c02a223f644f04a9e3777e2" +content-hash = "991adfa70ce1e1ad4a6ef2f72029523cb0478278f4f4545701559c8292a2546b" [metadata.files] anyio = [ @@ -1336,10 +1466,85 @@ google-auth-oauthlib = [ {file = "google-auth-oauthlib-0.5.2.tar.gz", hash = "sha256:d5e98a71203330699f92a26bc08847a92e8c3b1b8d82a021f1af34164db143ae"}, {file = "google_auth_oauthlib-0.5.2-py2.py3-none-any.whl", hash = "sha256:6d6161d0ec0a62e2abf2207c6071c117ec5897b300823c4bb2d963ee86e20e4f"}, ] +google-cloud-appengine-logging = [ + {file = "google-cloud-appengine-logging-1.1.4.tar.gz", hash = "sha256:8a6d00d78390821c98aa0b640fbebba86fa788a417e23a5bd299b6c8d0700424"}, + {file = "google_cloud_appengine_logging-1.1.4-py2.py3-none-any.whl", hash = "sha256:06ba651b1d46fcf380ab978b554e95fd5da725829e8203c06731d31b884eef43"}, +] +google-cloud-audit-log = [ + {file = "google-cloud-audit-log-0.2.4.tar.gz", hash = "sha256:aabe5543671e4f8a9c7a4c0e3ea9a87ba2ba2b96f9ffad6faa02209900e1f308"}, + {file = "google_cloud_audit_log-0.2.4-py2.py3-none-any.whl", hash = "sha256:29ed77fe0b21eb2d5dfb998edb59ee2adc65fea5d5ac9fcf17828d60d25b58b3"}, +] +google-cloud-core = [ + {file = "google-cloud-core-2.3.2.tar.gz", hash = "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a"}, + {file = "google_cloud_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe"}, +] +google-cloud-logging = [ + {file = "google-cloud-logging-3.2.2.tar.gz", hash = "sha256:719121491cc349ee3f84600f3c2916b5bb95d6860b94d90d34f8f63d979d1220"}, + {file = "google_cloud_logging-3.2.2-py2.py3-none-any.whl", hash = "sha256:1870ac9dae719b5344f31d523005930bd8fc8031e8686c55b6764912a0108bbf"}, +] +google-cloud-secret-manager = [ + {file = "google-cloud-secret-manager-2.12.4.tar.gz", hash = "sha256:9db33eb99afb6b7457bed2c8ee7e576590fdafd655a21e62092a190270c8b904"}, + {file = "google_cloud_secret_manager-2.12.4-py2.py3-none-any.whl", hash = "sha256:ddb2b856b767ba3dca5edc2fe8d7a06ec253074e32f4990b8a675caa70c231c8"}, +] googleapis-common-protos = [ {file = "googleapis-common-protos-1.56.4.tar.gz", hash = "sha256:c25873c47279387cfdcbdafa36149887901d36202cb645a0e4f29686bf6e4417"}, {file = "googleapis_common_protos-1.56.4-py2.py3-none-any.whl", hash = "sha256:8eb2cbc91b69feaf23e32452a7ae60e791e09967d81d4fcc7fc388182d1bd394"}, ] +grpc-google-iam-v1 = [ + {file = "grpc-google-iam-v1-0.12.4.tar.gz", hash = "sha256:3f0ac2c940b9a855d7ce7e31fde28bddb0d9ac362d32d07c67148306931a0e30"}, + {file = "grpc_google_iam_v1-0.12.4-py2.py3-none-any.whl", hash = "sha256:312801ae848aeb8408c099ea372b96d253077e7851aae1a9e745df984f81f20c"}, +] +grpcio = [ + {file = "grpcio-1.49.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:79298b2b153d00f34ecbf5db8ec1be9bf37172ff9d2b63a2e0c6ef67f85daf1e"}, + {file = "grpcio-1.49.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ebb539e0cf618593dc08ea9bf6fc72c538b632663f8efc1a344f6db62545dfaa"}, + {file = "grpcio-1.49.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7ea88f3dfe2606a93193e8dc240659b5eedcee77ef90d655cbc792273a5c3eb2"}, + {file = "grpcio-1.49.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a821831518758b725ba857d791a43959e8ffdbe5935e89a50222496fe2ed0f9c"}, + {file = "grpcio-1.49.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91763e949104894b639ad47243c69988fc0bbdcbead995280f7d3fcba3824a79"}, + {file = "grpcio-1.49.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ad119d3db3a284e8f0dbf0149b904280e17a13c9eca38a250109cbee9d569a1d"}, + {file = "grpcio-1.49.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e26d4b147447cf63e7e20e92c7b938b6279c0cd463b421f0f6ecc2d991161614"}, + {file = "grpcio-1.49.0-cp310-cp310-win32.whl", hash = "sha256:172cfa96c6d4de9d84bbd00ffea7aebe820f44430d31cdfea6ec6d209007c117"}, + {file = "grpcio-1.49.0-cp310-cp310-win_amd64.whl", hash = "sha256:07bbbb2675d5bcd5ea0dd5dc39d47d8cd9f81e12d5689d79079f5eb9bad87127"}, + {file = "grpcio-1.49.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:a3eccffe9cac99883cbfdcaac0af3d346d5499a7fcb7d57fdd2ec21209471c39"}, + {file = "grpcio-1.49.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:77742b6805c2b46a59b76f924b3018de89c4e3b565df7ef2d79d4d8eecd37a20"}, + {file = "grpcio-1.49.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:875cb37144865cc4d26e91add8c9672cf2c95f3547f15ae3daba8f27064fdb30"}, + {file = "grpcio-1.49.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1532064b23130be7760c940b82961e3661cb785f686204a937e8edca007069fe"}, + {file = "grpcio-1.49.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3477cec9b89334ddbb09b44cf099e9a81f4090b48fdd63310f91b71a762896d6"}, + {file = "grpcio-1.49.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb4a3812e90d8189ff96b1779c7e7e56d13e7525ef4928b7d897c62c0d8936e3"}, + {file = "grpcio-1.49.0-cp311-cp311-win32.whl", hash = "sha256:a9e154ff1d55540db0ff9ee123687c45e7946a235d22a44c5347b2384b2a4753"}, + {file = "grpcio-1.49.0-cp311-cp311-win_amd64.whl", hash = "sha256:894fba84ef949c7f2403eaf09c9d2421eeeec447f4d61829353a371d4d1edb57"}, + {file = "grpcio-1.49.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:fcd45c259cabb739b8b0c3a539ad2796f627fef77e53e55e6c2b5389b59a00ff"}, + {file = "grpcio-1.49.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:4ae542364be7b2cb13d8372c2a601a84c77a93327c93a31cda64fd9c5c333448"}, + {file = "grpcio-1.49.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:3e7b20e78b8dcee038002fd42ae13d5e6e81efa7d21cc6682a25f4cbd18f0491"}, + {file = "grpcio-1.49.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82a40b2fea553a85d045b1934633d108643d3b870a08fa19d47137d967e8e716"}, + {file = "grpcio-1.49.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85150bce2438feb2a814e0c2dec09fb6e86a58b69a06279a1a49426b88b84176"}, + {file = "grpcio-1.49.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:db056a709ea38b2da97a6811ed6719329f6576a32240e102e897a05b681a1674"}, + {file = "grpcio-1.49.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:41d8e725b816964691650967b017a621d935ba7393a153fcf8d6dcae5e41e4b3"}, + {file = "grpcio-1.49.0-cp37-cp37m-win32.whl", hash = "sha256:6ea56034245fe04ca3c411d2bf7032782722973e4cd2964e89d55110d2142bac"}, + {file = "grpcio-1.49.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bc39e9bd568b210ba9e44819736eb5e95cada054381dec5a3d2ec0a5f7c0178f"}, + {file = "grpcio-1.49.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:8d6f4ace5b41502b11c2456d2fa3a2d7ee776bbb7d2c3c05e0309d420654c95a"}, + {file = "grpcio-1.49.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:9736f24b9a0a5b66df739fa2e82132c1c420cc470df003e53596dcec8d90a52a"}, + {file = "grpcio-1.49.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a0c39a3d81a727d36c855781886a66d868681e5dc8de76a766a7c3c4827f649b"}, + {file = "grpcio-1.49.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63c3823fc525882989c0a42191d1efeb37c4d4f4c1dc555cc21d721fe11570e3"}, + {file = "grpcio-1.49.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0522e5c678411977db57bb6a4c6dd29e1defc0961ebf2c0a29871459cce0fd9"}, + {file = "grpcio-1.49.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3cc65127b0755fc0cf4f7e095f2d0ed25fe225eb1525b76c0a32de3efaf7c22f"}, + {file = "grpcio-1.49.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:717ed04e949022e33e10c4d11b2a9567da2de5a02019535058482ea54d9f795c"}, + {file = "grpcio-1.49.0-cp38-cp38-win32.whl", hash = "sha256:dcb480c4141dd051120a3de9af555ef09fc3ce1d0cb47e95b22ac1406554af18"}, + {file = "grpcio-1.49.0-cp38-cp38-win_amd64.whl", hash = "sha256:faa91b060bf3c520d0a4f11bd550d1b4026d649f188b082690270182fb462ec4"}, + {file = "grpcio-1.49.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:5df1748ac9d679f47ee39226a7389f73e8c7c49910c7493b0f61735825a0b5ec"}, + {file = "grpcio-1.49.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:4f70df4d99945a7dc4a358cf0d2c027bba2d3125bc8ad1acb069480747fc58de"}, + {file = "grpcio-1.49.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:6cbcbbab1c8f677d6b3bb55b22bfce87bd10713a8ef439098f86ada96e69eda8"}, + {file = "grpcio-1.49.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b40ca1a353ad0410f979d3ac7fdb07e2ad65717411c0c3e2df12a4b811898637"}, + {file = "grpcio-1.49.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede5041e8a98ce8761d7b6d966da872ac6a5bf2d8de776b402ea509c8ba8a8bc"}, + {file = "grpcio-1.49.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:49a8a3aa2d61f1940983795cea78d336f86ab2e5c92029adfc7e6a8ee0fe6e3a"}, + {file = "grpcio-1.49.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b3bc29e95a578b5c5a1bf2908ef56f45ad8b669178298cf6a8357a6883baff99"}, + {file = "grpcio-1.49.0-cp39-cp39-win32.whl", hash = "sha256:60d31d1377429f0f83fe10887276e8bdef16fb297dfbccab896a145d7b171bc8"}, + {file = "grpcio-1.49.0-cp39-cp39-win_amd64.whl", hash = "sha256:4e0374c5b31fdf5cd4a2f3209e5341762a34c80acedbaca0896ccb904ecdd707"}, + {file = "grpcio-1.49.0.tar.gz", hash = "sha256:90ec337f36db26fbc70a4c032bcbabb2008f950f4194e99385118a12688fdf92"}, +] +grpcio-status = [ + {file = "grpcio-status-1.49.0.tar.gz", hash = "sha256:91c4ba698aec928c807d0163ba6981614d201b7929dd96125082f5a4576885b2"}, + {file = "grpcio_status-1.49.0-py3-none-any.whl", hash = "sha256:9daff44251a3df615dcfe2f79e5d5842099d2e86353a3cfb7d193b31552d0d88"}, +] gspread = [ {file = "gspread-5.5.0-py3-none-any.whl", hash = "sha256:787b5fab9dd61a7d6d84af73356d7ff905cd3978438e528dc66dc8a9407fb851"}, {file = "gspread-5.5.0.tar.gz", hash = "sha256:8620e987e5340315f2b8d8d26cf97e4736a84b3325a17c7d9bcff70525dc3003"}, @@ -1472,6 +1677,10 @@ pre-commit = [ {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, ] +proto-plus = [ + {file = "proto-plus-1.22.1.tar.gz", hash = "sha256:6c7dfd122dfef8019ff654746be4f5b1d9c80bba787fe9611b508dd88be3a2fa"}, + {file = "proto_plus-1.22.1-py3-none-any.whl", hash = "sha256:ea8982669a23c379f74495bc48e3dcb47c822c484ce8ee1d1d7beb339d4e34c5"}, +] protobuf = [ {file = "protobuf-4.21.5-cp310-abi3-win32.whl", hash = "sha256:5310cbe761e87f0c1decce019d23f2101521d4dfff46034f8a12a53546036ec7"}, {file = "protobuf-4.21.5-cp310-abi3-win_amd64.whl", hash = "sha256:e5c5a2886ae48d22a9d32fbb9b6636a089af3cd26b706750258ce1ca96cc0116"}, diff --git a/pyproject.toml b/pyproject.toml index 2ea7a0c..940e6a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,9 @@ types-PyYAML = "^6.0.11" google-api-python-client = "^2.60.0" google-auth-httplib2 = "^0.1.0" google-auth-oauthlib = "^0.5.2" +google-cloud-secret-manager = "^2.12.4" +PyYAML = "^6.0" +google-cloud-logging = "^3.2.2" [tool.poetry.dev-dependencies] darglint = ">=1.8.0" diff --git a/server.py b/server.py index 4e729a7..0e25515 100644 --- a/server.py +++ b/server.py @@ -1,6 +1,5 @@ -from typing import Any - import logging +import os import traceback import uvicorn @@ -22,4 +21,4 @@ async def main(request: Request): if __name__ == "__main__": - uvicorn.run("server:app", host="127.0.0.1", port=5000, log_level="info") + uvicorn.run("server:app", host="0.0.0.0", port=os.environ.get("PORT", 8080), log_level="info")