diff --git a/.flake8 b/.flake8 index a7b56e7..870aa6c 100644 --- a/.flake8 +++ b/.flake8 @@ -1,8 +1,10 @@ [flake8] per-file-ignores = - # ignore type hints returns in test + # Ignore type hints returns in test tests/*: ANN201 **/__init__.py: F401 + # Ignore invalid syntax in file that use python3.10 + examples/pr_notifier/*: E999 extend-ignore = # See: https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html E203, diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 84ef0f6..d9c8871 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -74,4 +74,3 @@ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.ht For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq - diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 1fd05f1..a58e3ea 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -49,7 +49,7 @@ The goal of the following guidelines is to have Pull Requests (PRs) that are fai - **Pull Request title should respect [conventional commits](https://www.conventionalcommits.org/en/v1.0.0) specifications** and be clear on what is being changed. - **Keep it readable for human reviewers** and prefer a subset of functionality (code) with tests and documentation over delivering them separately -- **Don't forget commenting code** to help reviewers understand +- **Don't forget commenting code** to help reviewers understand - **Notify Work In Progress PRs** by prefixing the title with `[WIP]` - **Please, keep us updated.** We will try our best to merge your PR, but please notice that PRs may be closed after 30 days of inactivity. @@ -63,4 +63,3 @@ Keep in mind only the **pull request title** will be used as commit message as w See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Thank you for reading through all of this, if you have any question feel free to [reach us](README.md#reach-us)! - diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 543a665..a3b5538 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -29,4 +29,3 @@ labels: bug ## More info - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 1a72dcf..1ee7f8f 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -23,4 +23,3 @@ labels: enhancement ### References - diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0b84302..6b02b8c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -21,4 +21,3 @@ If the change is not user facing, just write "NONE" in the release-note block be - [ ] I have added unit test covering every changes I have made - [ ] I have updated the relevant documentation - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a00faa3..66c6a79 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,19 +1,24 @@ +ci: + # Can't use a local installation of pylint with pre-commit.ci + skip: [pylint] + # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 22.12.0 hooks: - id: black + args: [--safe, --quiet] - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.11.4 hooks: - id: isort name: isort (python) @@ -30,7 +35,7 @@ repos: "--rcfile=pyproject.toml", ] - repo: https://github.com/pycqa/flake8 - rev: 5.0.4 + rev: 6.0.0 hooks: - id: flake8 additional_dependencies: [flake8-annotations] @@ -39,3 +44,7 @@ repos: hooks: - id: python-bandit-vulnerability-check args: [--skip, "B101", --recursive, clumper] + - repo: https://github.com/regebro/pyroma + rev: "4.1" + hooks: + - id: pyroma diff --git a/README.md b/README.md index a407f26..2911f6d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ def hello_world(event, context): return "Hello World!" ``` -Deploy it with `scw_serverless`: +Deploy it with `scw-serverless`: ```console scw-serverless deploy app.py @@ -26,10 +26,10 @@ scw-serverless deploy app.py ### Install ```console -pip install scw_serverless +pip install scw-serverless ``` -This will install `scw-serverless`: +This will install the `scw-serverless` CLI: ```console scw-serverless --help @@ -53,7 +53,7 @@ def hello_world(event, context): ``` The configuration is done by passing arguments to the decorator. -To view which arguments are supported, head over to this [documentation](https://serverless-apifw-docs.s3-website.fr-par.scw.cloud/configuring.html) page. +To view which arguments are supported, head over to this [documentation](https://serverless-api-project.readthedocs.io/) page. When you are ready, you can deploy your function with the `scw-serverless` CLI tool: @@ -65,7 +65,7 @@ The tool will use your Scaleway credentials from your environment and config fil ## What’s Next? -To learn more about the framework, have a look at the [documentation](https://serverless-apifw-docs.s3-website.fr-par.scw.cloud/index.html). +To learn more about the framework, have a look at the [documentation](https://serverless-api-project.readthedocs.io/). If you want to see it in action, we provide some [examples](https://github.com/scaleway/serverless-api-project/tree/main/examples) to get you started. ## Contributing diff --git a/SECURITY.md b/SECURITY.md index d015af4..14f3a41 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,4 +11,3 @@ We will follow up with you promptly with more information and a plan for remedia We currently do not offer a paid security bounty program, but we would love to send some Scaleway swag your way along with our deepest gratitude for your assistance in making Scaleway a more secure Cloud ecosystem. - diff --git a/examples/cron/handler.py b/examples/cron/handler.py index f715972..6f0fe24 100644 --- a/examples/cron/handler.py +++ b/examples/cron/handler.py @@ -14,7 +14,7 @@ inputs={"myname": "Georges"}, privacy="public", ) -def hello_cron(event: dict[str, Any], _context: dict[str, Any]): +def hello_cron(event: dict[str, Any], _context: dict[str, Any]) -> dict[str, Any]: """A simple cron that regularly greets you during business days.""" body = json.loads(event["body"]) my_name = body["myname"] diff --git a/examples/github_actions/requirements.txt b/examples/github_actions/requirements.txt index e7937d6..2ee6dac 100644 --- a/examples/github_actions/requirements.txt +++ b/examples/github_actions/requirements.txt @@ -1 +1 @@ -scw-serverless +scw-serverless~=0.0.2 diff --git a/examples/pr_notifier/notifier.py b/examples/pr_notifier/notifier.py index cd17d8f..c763273 100644 --- a/examples/pr_notifier/notifier.py +++ b/examples/pr_notifier/notifier.py @@ -146,7 +146,10 @@ def bucket_path(self) -> str: def from_github(repository: dict[str, Any], pull_request: dict[str, Any]): """Creates from a GitHub PR. - .. seealso:: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request + .. seealso:: + + GitHub Documentation + https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request """ return PullRequest( number=pull_request["number"], @@ -191,11 +194,11 @@ def from_gitlab( deletions=None, ) - def on_draft(self): + def on_draft(self) -> None: """Saves a PR marked as a draft to notify when it's ready.""" save_pr_to_bucket(self, "") - def on_created(self): + def on_created(self) -> None: """Sends a notification for a newly created PR.""" response = client.chat_postMessage( channel=SLACK_CHANNEL, blocks=self._as_slack_notification() @@ -206,13 +209,13 @@ def on_created(self): timestamp = str(response["ts"]) save_pr_to_bucket(self, timestamp) - def on_updated(self): + def on_updated(self) -> None: """Performs the necessary changes when a PR is updated.""" _timestamp, pull = load_pr_from_bucket(self.bucket_path) if pull.is_draft and not self.is_draft: self.on_created() - def on_reviewed(self, review: Review, reviewer: Developer): + def on_reviewed(self, review: Review, reviewer: Developer) -> None: """Updates the notification when a new review is made.""" timestamp, pull = load_pr_from_bucket(self.bucket_path) self.reviews = pull.reviews.copy() @@ -233,7 +236,7 @@ def on_reviewed(self, review: Review, reviewer: Developer): if not response["ok"]: logging.warning(response["error"]) - def on_closed(self): + def on_closed(self) -> None: """Sends a message in the thread when the PR is merged.""" if self.is_merged: timestamp, _pull = load_pr_from_bucket(self.bucket_path) @@ -297,19 +300,20 @@ def get_reminder_slack_blk(self, timestamp: str) -> blks.SectionBlock: """Gets the message to be added to the reminder.""" url = self.url if SLACK_INSTANCE: - url = f"https://{ SLACK_INSTANCE }.slack.com/archives/{ SLACK_CHANNEL }/p{ timestamp }" + instance_url = f"https://{SLACK_INSTANCE}.slack.com" + url = f"{instance_url}/archives/{SLACK_CHANNEL}/p{timestamp}" reminder = f"*<{url}|{self.title}>*" return blks.SectionBlock( text=blks.MarkdownTextObject(text=reminder, verbatim=True), ) -def delete_pr_from_bucket(bucket_path: str): +def delete_pr_from_bucket(bucket_path: str) -> None: """Deletes a PR.""" s3.Object(S3_BUCKET, bucket_path).delete() -def save_pr_to_bucket(pull: PullRequest, timestamp: str): +def save_pr_to_bucket(pull: PullRequest, timestamp: str) -> None: """Saves a PR associated with a Slack timestamp.""" s3.Object(S3_BUCKET, pull.bucket_path).put( Body=json.dumps({"ts": timestamp, "pull_request": pull.to_dict()}) @@ -325,10 +329,13 @@ def load_pr_from_bucket(bucket_path: str) -> Tuple[str, PullRequest]: @app.func() -def handle_github(event, _content): +def handle_github(event: dict[str, Any], _content: dict[str, Any]) -> dict[str, Any]: """Handles GitHub webhook request. - .. seealso:: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request + .. seealso:: + + GitHub Events Documentation + https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request """ body = json.loads(event["body"]) match body: @@ -366,10 +373,13 @@ def handle_github(event, _content): @app.func(min_scale=1, memory_limit=1024) -def handle_gitlab(event, _content): +def handle_gitlab(event: dict[str, Any], _content: dict[str, Any]) -> dict[str, Any]: """Handles GitLab webhook request. - .. seealso:: https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#merge-request-events + .. seealso:: + + GitLab Events Documentation + https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#merge-request-events """ body = json.loads(event["body"]) match body: @@ -418,7 +428,9 @@ def handle_gitlab(event, _content): @app.schedule(REMINDER_SCHEDULE) -def pull_request_reminder(_event, _content): +def pull_request_reminder( + _event: dict[str, Any], _content: dict[str, Any] +) -> dict[str, Any]: """Daily reminder to review opened pull-requests.""" blocks = [blks.HeaderBlock(text="PRs awaiting for review: "), blks.DividerBlock()] for opened_pr in s3.Bucket(S3_BUCKET).objects.all(): diff --git a/examples/pr_notifier/requirements.txt b/examples/pr_notifier/requirements.txt index b23f62f..9265e74 100644 --- a/examples/pr_notifier/requirements.txt +++ b/examples/pr_notifier/requirements.txt @@ -1,4 +1,4 @@ boto3~=1.26 dataclass-wizard~=0.22 -scw_serverless==0.0.1b0 +scw-serverless~=0.0.2 slack_sdk~=3.19 diff --git a/pyproject.toml b/pyproject.toml index a510de3..730ac84 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,10 +6,13 @@ build-backend = "setuptools.build_meta" name = "scw_serverless" version = "0.0.2" description = "An API framework to make it easy to work with Scaleway Serverless functions." +authors = [ + { name = "Scaleway Serverless Team", email = "opensource@scaleway.com" }, +] readme = "README.md" requires-python = ">=3.9" license = { file = "LICENSE" } -keywords = ["serverless", "scaleway", "functions", "cloud"] +keywords = ["serverless", "scaleway", "functions", "cloud", "faas"] # Should be one of: # 'Development Status :: 3 - Alpha' @@ -23,12 +26,15 @@ classifiers = [ "Topic :: Internet", "License :: OSI Approved :: MIT License", "Programming Language :: Python", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ] dependencies = [ "click >= 8", "PyYAML >= 6", - "scaleway >= 0.2", + "scaleway >= 0.5", # Requires support for pyproject.toml "setuptools >= 61", "requests >= 2", @@ -43,9 +49,8 @@ repository = "https://github.com/scaleway/serverless-api-project" scw-serverless = "scw_serverless.cli:main" [tool.setuptools] +# Using: https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#flat-layout include-package-data = true -# See: https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#flat-layout -packages = ["scw_serverless"] [tool.setuptools.package-data] scw_serverless = ["**/*.json", "**/*.yml"] diff --git a/requirements.txt b/requirements.txt index c466c29..f183234 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ click==8.1.3 PyYAML==6.0 -scaleway==0.2.0 +scaleway==0.5.0 setuptools==66.0.0 requests==2.28.2 typing_extensions==4.4.0 diff --git a/scw_serverless/app.py b/scw_serverless/app.py index 8087e20..0172ac9 100644 --- a/scw_serverless/app.py +++ b/scw_serverless/app.py @@ -1,13 +1,11 @@ -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union +from typing import Any, Callable, List, Optional, Union -from typing_extensions import Unpack - -if TYPE_CHECKING: - try: - from typing import Unpack - except ImportError: - from typing_extensions import Unpack +try: + from typing import Unpack +except ImportError: + from typing_extensions import Unpack # pylint: disable=wrong-import-position # Conditional import considered a statement + from scw_serverless.config.function import Function, FunctionKwargs from scw_serverless.config.route import HTTPMethod from scw_serverless.triggers import CronTrigger diff --git a/scw_serverless/cli.py b/scw_serverless/cli.py index 9195bea..2600649 100644 --- a/scw_serverless/cli.py +++ b/scw_serverless/cli.py @@ -29,10 +29,14 @@ ), ) -# TODO: link to the doc + @click.group() def cli() -> None: - """Deploy your Serverless functions on Scaleway's Cloud.""" + """Deploy your Serverless functions on Scaleway's Cloud. + + Documentation: + https://serverless-api-project.readthedocs.io/en/latest/ + """ # TODO?: clean up the locals by introducing a class diff --git a/scw_serverless/config/function.py b/scw_serverless/config/function.py index 5a8ac9f..f1616fe 100644 --- a/scw_serverless/config/function.py +++ b/scw_serverless/config/function.py @@ -46,10 +46,12 @@ class FunctionKwargs(TypedDict): :param timeout: Max duration to respond to a request. :param description: Description. Defaults to the function docstring if defined. :param http_option: Either "enabled" or "redirected". - If "redirected" (default), redirects http traffic to your function. + If "redirected" (default), allow http traffic to your function. Blocked otherwise. - .. seealso:: https://developers.scaleway.com/en/products/functions/api/#create-a-function + .. seealso:: + Scaleway Developers Documentation + https://developers.scaleway.com/en/products/functions/api/#create-a-function """ env: NotRequired[dict[str, str]] diff --git a/scw_serverless/config/generators/terraform.py b/scw_serverless/config/generators/terraform.py index 1180179..4cac0e4 100644 --- a/scw_serverless/config/generators/terraform.py +++ b/scw_serverless/config/generators/terraform.py @@ -119,7 +119,10 @@ def from_serverless(serverless: Serverless): class TerraformGenerator(Generator): """Generates the Terraform configuration to deploy the functions. - .. seealso:: https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/function + .. seealso:: + + Scaleway Terraform Provider Documentation: + https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/function """ def __init__(self, instance: Serverless, deps_manager: DependenciesManager): diff --git a/scw_serverless/dependencies_manager.py b/scw_serverless/dependencies_manager.py index 55cc92e..b07a2ca 100644 --- a/scw_serverless/dependencies_manager.py +++ b/scw_serverless/dependencies_manager.py @@ -17,15 +17,13 @@ class DependenciesManager: It does not currently handles native dependencies. - .. seealso:: https://developers.scaleway.com/en/products/functions/api/#python-additional-dependencies + .. seealso:: + + Scaleway Documentation + """ def __init__(self, in_path: pathlib.Path, out_path: pathlib.Path) -> None: - if type(in_path) == str: - in_path = pathlib.Path(in_path) - if type(out_path) == str: - out_path = pathlib.Path(out_path) - self.in_path = in_path self.out_path = out_path self.logger = get_logger() diff --git a/scw_serverless/deploy/backends/scaleway_api_backend.py b/scw_serverless/deploy/backends/scaleway_api_backend.py index a693844..2e9ed97 100644 --- a/scw_serverless/deploy/backends/scaleway_api_backend.py +++ b/scw_serverless/deploy/backends/scaleway_api_backend.py @@ -32,7 +32,7 @@ def _get_or_create_function(self, function: Function, namespace_id: str) -> str: self.logger.default(f"Looking for an existing function {function.name}...") created_function = None # Checking if a function already exists - for func in self.api.list_functions_all(namespace_id): + for func in self.api.list_functions_all(namespace_id=namespace_id): if func.name == function.name: created_function = func if not created_function: @@ -93,10 +93,10 @@ def _deploy_function( self.logger.default(f"Deploying function {function.name}...") # deploy the newly uploaded function - self.api.deploy_function(function_id) + self.api.deploy_function(function_id=function_id) return self.api.wait_for_function( - function_id, + function_id=function_id, options=WaitForOptions( timeout=DEPLOY_TIMEOUT, stop=lambda f: (f.status != sdk.FunctionStatus.PENDING), @@ -106,7 +106,7 @@ def _deploy_function( def _deploy_trigger(self, function_id: str, trigger: Trigger) -> sdk.Cron: created_trigger = None # Checking if a trigger already exists - for cron in self.api.list_crons_all(function_id): + for cron in self.api.list_crons_all(function_id=function_id): if cron.name == trigger.name: created_trigger = cron if not created_trigger: @@ -118,11 +118,11 @@ def _deploy_trigger(self, function_id: str, trigger: Trigger) -> sdk.Cron: ) else: created_trigger = self.api.update_cron( - created_trigger.id, + cron_id=created_trigger.id, schedule=trigger.schedule, args=trigger.args, ) - return self.api.wait_for_cron(created_trigger.id) + return self.api.wait_for_cron(cron_id=created_trigger.id) def _create_deployment_zip(self) -> int: """Create a ZIP archive containing the entire project.""" @@ -154,21 +154,21 @@ def _upload_deployment_zip(self, upload_url: str, zip_size: int): def _remove_missing_functions(self, namespace_id: str): """Deletes functions no longer present in the code.""" function_names = [func.name for func in self.app_instance.functions] - for func in self.api.list_functions_all(namespace_id=namespace_id): - if func.name not in function_names: - self.logger.info(f"Deleting function {func.name}...") - self.api.delete_function(func.id) + for function in self.api.list_functions_all(namespace_id=namespace_id): + if function.name not in function_names: + self.logger.info(f"Deleting function {function.name}...") + self.api.delete_function(function_id=function.id) def _remove_missing_triggers(self, namespace_id: str, deployed_triggers: set[str]): """Deletes triggers no longer present in the code.""" for function in self.api.list_functions_all(namespace_id=namespace_id): unmanaged = filter( lambda c: c.id not in deployed_triggers, - self.api.list_crons_all(function.id), + self.api.list_crons_all(function_id=function.id), ) for cron in unmanaged: self.logger.info(f"Deleting Cron {cron.name}...") - self.api.delete_cron(cron.id) + self.api.delete_cron(cron_id=cron.id) def _get_or_create_namespace(self) -> str: project_id = self.api.client.default_project_id diff --git a/scw_serverless/logger.py b/scw_serverless/logger.py index 50c32e7..3f7b756 100644 --- a/scw_serverless/logger.py +++ b/scw_serverless/logger.py @@ -1,3 +1,5 @@ +from typing import NamedTuple + import click DEBUG = 0 @@ -11,66 +13,75 @@ _LOGGER_SINGLETON = None -def get_logger(): - """Get""" - global _LOGGER_SINGLETON +def get_logger() -> "Logger": + """Gets the global logger instance.""" + # pylint: disable=global-statement # Known issue FIXME + global _LOGGER_SINGLETON # noqa # This is the first time we call get_logger, init the singleton if not _LOGGER_SINGLETON: _LOGGER_SINGLETON = Logger() return _LOGGER_SINGLETON -class _LogRecord: - def __init__(self, message: str = None, level: int = 0): - self.message = message - self.level = level +_LogRecord = NamedTuple("LogRecord", [("level", int), ("message", str)]) class Logger: + """A logger built on top of click.echo.""" + def __init__(self): self.level = DEFAULT - def set_level(self, level: int): + def set_level(self, level: int) -> None: + """Sets the log level.""" self.level = level - def critical(self, message: str): + def critical(self, message: str) -> None: + """Logs a critical message.""" self.log(CRITICAL, message) - def error(self, message: str): + def error(self, message: str) -> None: + """Logs an error message.""" self.log(ERROR, message) - def warning(self, message: str): + def warning(self, message: str) -> None: + """Logs a warning message.""" self.log(WARNING, message) - def success(self, message: str): + def success(self, message: str) -> None: + """Logs a success message.""" self.log(SUCCESS, message) - def info(self, message: str): + def info(self, message: str) -> None: + """Logs an info message.""" self.log(INFO, message) - def default(self, message: str): + def default(self, message: str) -> None: + """Logs a message.""" self.log(DEFAULT, message) - def debug(self, message: str): + def debug(self, message: str) -> None: + """Logs a debug message.""" self.log(DEBUG, message) - def log(self, level: int, message: str): - self.emit(_LogRecord(message=message, level=level)) + def log(self, level: int, message: str) -> None: + """Logs a message with a specific level.""" + self._emit(_LogRecord(message=message, level=level)) - def emit(self, record: _LogRecord): + def _emit(self, record: _LogRecord): if record.level < self.level: return err = False - fg = "" - if record.level == ERROR or record.level == CRITICAL: + color = "" + if record.level in [ERROR, CRITICAL]: err = True - fg = "red" + color = "red" elif record.level == WARNING: - fg = "yellow" + color = "yellow" elif record.level == INFO: - fg = "cyan" + color = "cyan" elif record.level == SUCCESS: - fg = "green" + color = "green" - click.echo(click.style(record.message, fg=fg), err=err) + click.echo(click.style(record.message, fg=color), err=err) diff --git a/scw_serverless/triggers/cron.py b/scw_serverless/triggers/cron.py index 0d4a8bf..be2a353 100644 --- a/scw_serverless/triggers/cron.py +++ b/scw_serverless/triggers/cron.py @@ -10,7 +10,10 @@ class CronTrigger: :param args: Data to be sent in the body. :param name: Name to give to your resource. - .. seealso:: https://developers.scaleway.com/en/products/functions/api/#create-a-cron-trigger-for-your-function + .. seealso:: + + Scaleway Documentation + """ schedule: str diff --git a/tests/integrations/utils.py b/tests/integrations/utils.py index 419bc8f..4048b75 100644 --- a/tests/integrations/utils.py +++ b/tests/integrations/utils.py @@ -186,7 +186,7 @@ def _create_project(self) -> str: sha = os.getenv("GITHUB_SHA", "local") name = f"apifw-{sha[:7]}-{random.randint(0, 9999)}" project = AccountV2API(self.client).create_project( - name, + name=name, description="Created by the Serverless API Framework integration suite.", ) return project.id @@ -229,7 +229,7 @@ def _delete_project(self, max_tries: int = 5): api = AccountV2API(self.client) for _ in range(max_tries): try: - api.delete_project(self.project_id) + api.delete_project(project_id=self.project_id) return except ScalewayException as e: # This is just very finicky, the account API is somewhat unstable.