diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000..7b7997a --- /dev/null +++ b/.cruft.json @@ -0,0 +1,21 @@ +{ + "template": "D:\\open-source\\gsoc\\final\\cookiecutter-python", + "commit": "88967f43af9799dfa0ad04b2908d7f40259c02d2", + "checkout": null, + "context": { + "cookiecutter": { + "author_name": "Elixir Cloud AAI", + "author_email": "cloud-service@elixir-europe.org", + "development_status": "Beta", + "short_description": "File Handler Utilizing TUS and Minio (S3 Storage) with DRS Filer Integration", + "project_name": "tus-storagehandler", + "project_slug": "tus_storagehandler", + "github_username": "elixir-cloud-aai", + "python_version": "3.11", + "add_script": "y", + "year": "2024", + "_template": "D:\\open-source\\gsoc\\final\\cookiecutter-python" + } + }, + "directory": null +} diff --git a/.github/ISSUE_TEMPLATE/general-purpose.md b/.github/ISSUE_TEMPLATE/general-purpose.md new file mode 100644 index 0000000..8a11bde --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general-purpose.md @@ -0,0 +1,47 @@ +Your issue may already be reported! Please search on the +[issue tracker](https://github.com/elixir-cloud-aai/tus-storagehandler/issues) before creating +one. + +## Expected Behavior + +<!--- If you're describing a bug, tell us what should happen --> + +<!--- If you're suggesting a change/improvement, tell us how it should work --> + +## Current Behavior + +<!--- If describing a bug, tell us what happens instead of the expected behavior --> + +<!--- If suggesting a change/improvement, explain the difference from current behavior --> + +## Possible Solution + +<!--- Not obligatory, but suggest a fix/reason for the bug, --> + +<!--- or ideas how to implement the addition or change --> + +## Steps to Reproduce (for bugs) + +<!--- Provide a link to a live example, or an unambiguous set of steps to --> + +<!--- reproduce this bug. Include code to reproduce, if relevant --> + +1. +1. +1. +1. + +## Context + +<!--- How has this issue affected you? What are you trying to accomplish? --> + +<!--- Providing context helps us come up with a solution that is most useful in the real world --> + +## Your Environment + +<!--- Include as many relevant details about the environment you experienced the bug in --> + +- Version used: +- Browser Name and version: +- Operating System and version (desktop or mobile): +- Link to your project: diff --git a/.github/actions/setup/poetry/action.yaml b/.github/actions/setup/poetry/action.yaml new file mode 100644 index 0000000..92154b2 --- /dev/null +++ b/.github/actions/setup/poetry/action.yaml @@ -0,0 +1,92 @@ +--- +name: Setup Python and Poetry Action +description: Configure system, Python, Poetry and deps and cache management. + +inputs: + os: + default: ubuntu-latest + description: The operating system to use + python-version: + default: '3.11' + description: The version of Python to use + poetry-version: + default: '1.8.2' + description: The version of Poetry to install + poetry-install-options: + default: '' + description: Additional options to pass to poetry install + poetry-export-options: + default: '' + description: Options to pass to poetry export for hash generation for cache + invalidation + +runs: + using: composite + steps: + - uses: 'actions/setup-python@v5' + id: setup-python + with: + python-version: '${{ inputs.python-version }}' + + - name: Setup pipx environment Variables + id: pipx-env-setup + # pipx default home and bin dir are not writable by the cache action + # so override them here and add the bin dir to PATH for later steps. + # This also ensures the pipx cache only contains poetry + run: | + SEP="${{ !startsWith(runner.os, 'windows') && '/' || '\\' }}" + PIPX_CACHE="${{ github.workspace }}${SEP}pipx_cache" + echo "pipx-cache-path=${PIPX_CACHE}" >> $GITHUB_OUTPUT + echo "pipx-version=$(pipx --version)" >> $GITHUB_OUTPUT + echo "PIPX_HOME=${PIPX_CACHE}${SEP}home" >> $GITHUB_ENV + echo "PIPX_BIN_DIR=${PIPX_CACHE}${SEP}bin" >> $GITHUB_ENV + echo "PIPX_MAN_DIR=${PIPX_CACHE}${SEP}man" >> $GITHUB_ENV + echo "${PIPX_CACHE}${SEP}bin" >> $GITHUB_PATH + shell: bash + + - name: Pipx cache + id: pipx-cache + uses: actions/cache@v4 + with: + path: ${{ steps.pipx-env-setup.outputs.pipx-cache-path }} + key: ${{ runner.os }}-python- + ${{ steps.setup-python.outputs.python-version }}- + pipx-${{ steps.pipx-env-setup.outputs.pipx-version }}- + poetry-${{ inputs.poetry-version }} + + - name: Install poetry + if: steps.pipx-cache.outputs.cache-hit != 'true' + id: install-poetry + shell: bash + run: | + pipx install poetry \ + --python "${{ steps.setup-python.outputs.python-path }}" + + - name: Read poetry cache location + id: poetry-cache-location + shell: bash + run: | + echo "poetry-venv-location=$(poetry config virtualenvs.path)" \ + >> $GITHUB_OUTPUT + + - name: Generate hash only for required deps + run: | + poetry export ${{ inputs.poetry-export-options }} \ + --format=requirements.txt --output=requirements.txt + echo "DEP_HASH=$(sha256sum requirements.txt | cut -d ' ' -f 1)" \ + >> $GITHUB_ENV + shell: bash + + - uses: actions/cache@v4 + name: Poetry cache + with: + path: ${{ steps.poetry-cache-location.outputs.poetry-venv-location }} + key: ${{ runner.os }}-[python- + ${{ steps.setup-python.outputs.python-version }}]-[ + ${{ env.DEP_HASH }}]-[${{ inputs.poetry-install-options }}] + + - name: 'Poetry install' + if: steps.poetry-cache.outputs.cache-hit != 'true' + shell: bash + run: poetry install ${{ inputs.poetry-install-options }} --no-interaction +... diff --git a/.github/workflows/code_quality.yaml b/.github/workflows/code_quality.yaml new file mode 100644 index 0000000..0c252da --- /dev/null +++ b/.github/workflows/code_quality.yaml @@ -0,0 +1,84 @@ +--- +name: Code Quality + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + format: + name: Format + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--only=lint --no-root" + poetry-export-options: "--only=lint" + + - name: Check code style + run: poetry run ruff format --check + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--only=lint --no-root" + poetry-export-options: "--only=lint" + + - name: Check code quality + run: poetry run ruff check . + + spell-check: + name: Spell Check + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--only=lint --no-root" + poetry-export-options: "--only=lint" + + - name: Check spellings + run: poetry run typos . + + type-check: + name: Type Check + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--only=types --no-root" + poetry-export-options: "--only=types" + + - name: Check types + run: poetry run mypy tus_storagehandler/ +... diff --git a/.github/workflows/code_test.yaml b/.github/workflows/code_test.yaml new file mode 100644 index 0000000..0266d92 --- /dev/null +++ b/.github/workflows/code_test.yaml @@ -0,0 +1,74 @@ +--- +name: Code Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + integration-test: + name: Unit Test + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--with=test" + poetry-export-options: "--with=test" + + - name: Run tests and generate coverage as test_integration.xml + run: | + poetry run pytest \ + --cov-report term \ + --cov-report xml:test_integration.xml \ + --cov=tests/test_integration + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: test_integration + files: ./test_integration.xml + fail_ci_if_error: true + verbose: true + + unit-test: + name: Unit Test + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--with=test" + poetry-export-options: "--with=test" + + - name: Run tests and generate coverage as test_unit.xml + run: | + poetry run pytest \ + --cov-report term \ + --cov-report xml:test_unit.xml \ + --cov=tests/test_unit + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: test_unit + files: ./test_unit.xml + fail_ci_if_error: true + verbose: true +... diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 0000000..1b417b5 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,44 @@ +--- +name: Documentation Check + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + check-api-docs: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--with=docs" + poetry-export-options: "--with=docs" + + - name: Generate API docs + run: | + sphinx-apidoc -o /tmp/docs tus_storagehandler/ + + - name: Compare docs with main branch + id: check_docs_diff + run: | + shasum /tmp/docs/* > /tmp/docs.sha + shasum /docs/pages/* > /docs/project_doc.sha + diff=$(diff /tmp/docs.sha /docs/project_doc.sha) || true + if [[ -n "$diff" ]]; then + echo "::error::API documentation is out of date." + exit 1 + else + echo "API documentation is up to date." + fi +... diff --git a/.github/workflows/pr_validation.yaml b/.github/workflows/pr_validation.yaml new file mode 100644 index 0000000..4e43caa --- /dev/null +++ b/.github/workflows/pr_validation.yaml @@ -0,0 +1,67 @@ +--- +name: PR Evaluation + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review, edited] + branches: ['main'] + +jobs: + detect-unresolved-conflicts: + name: Detect unresolved merge conflicts + runs-on: ubuntu-latest + needs: semantic_pr + steps: + - uses: actions/checkout@v3 + - name: List files with merge conflict markers + run: git --no-pager grep "<<<<<<<" ":(exclude).github/" || true + - name: Fail or succeed job if any files with merge conflict markers + run: exit $(git grep "<<<<<<<" ":(exclude).github/" | wc --lines) + + pre-commit: + name: Pre-commit checks + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--only=misc --no-root" + poetry-export-options: "--only=misc" + + - name: Check all the pre-commit hooks passed + run: pre-commit run --all-files + + semantic-pr: + name: Semantic PR title + runs-on: ubuntu-latest + if: ${{ github.event.action != 'edited' || + github.event.changes.title != null }} + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + fix + feat + docs + style + refactor + perf + test + build + ci + chore + revert + subjectPattern: ^(?![A-Z])(?=.{1,50}$).+$ + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + didn't match the configured pattern. Please ensure that the subject + doesn't start with an uppercase character & not have more than 50 + characters. +... diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..1e214ed --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,26 @@ +--- +name: Release package + +on: + release: + types: [released] + +jobs: + pypi: + name: Publish to PyPI + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--only=main" + poetry-export-options: "--only=main" + + - name: Publish package to PyPI + run: poetry publish --build -u __token__ -p ${{ secrets.PYPI_PASSWORD }} +... diff --git a/.github/workflows/update.yaml b/.github/workflows/update.yaml new file mode 100644 index 0000000..4edade9 --- /dev/null +++ b/.github/workflows/update.yaml @@ -0,0 +1,55 @@ +--- +name: Update project structure + +on: + schedule: + - cron: "0 2 * * 1" # Every Monday at 2am + +jobs: + update-template: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--with=misc --no-root" + poetry-export-options: "--with=misc" + + - name: Update project structure + run: | + cruft update -y + + - name: Check if there are changes + id: changes + uses: UnicornGlobal/has-changes-action@v1.0.11 + + - name: Apply additional changes and fixes + if: steps.changes.outputs.changed == 1 + run: | + poetry lock --no-update + poetry install + pre-commit run --all-files + + - name: Get new template version + if: steps.changes.outputs.changed == 1 + run: | + COMMIT_HASH=$(cat .cruft.json | jello -r "_['commit'][:8]") + echo "TEMPLATE_COMMIT=${COMMIT_HASH}" >> $GITHUB_ENV + + - name: Create Pull Request + if: steps.changes.outputs.changed == 1 + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.AUTO_UPDATE_GITHUB_TOKEN }} + commit-message: >- + chore: update project structure to ${{ env.TEMPLATE_COMMIT }} + title: "chore: auto-sync cookiecutter template" + body: "" + branch: chore/cookiecutter-pypackage + delete-branch: true +... diff --git a/.github/workflows/vulnerability.yaml b/.github/workflows/vulnerability.yaml new file mode 100644 index 0000000..5bb93b5 --- /dev/null +++ b/.github/workflows/vulnerability.yaml @@ -0,0 +1,48 @@ +--- +name: Vulnerability Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + code-vulnerabilities: + name: Code + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--only=security --no-root" + poetry-export-options: "--only=security" + + - name: Check code vulnerabilities with bandit + run: poetry run bandit -c pyproject.toml -r tus_storagehandler/ + + dependency-vulnerabilities: + name: Dependencies + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up environment + uses: ./.github/actions/setup/poetry + with: + os: ${{ job.os }} + python-version: '3.11' + poetry-install-options: "--only=security --no-root" + poetry-export-options: "--only=security" + + - name: Check dependency vulnerabilities with safety + run: poetry run safety check --full-report +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a558c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,182 @@ +######## This section contains auto-generated .gitignore patterns for ######### +######## Python projects. Write project level gitignore below this ######### +######## section. ######### + +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +Pipfile.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python +SquareX Safe File Viewer +✕ + +######### End of auto generated .gitignore file for Python projects. ######### +######### Project level .gitignore. ######### + +# Keep pre-commit hooks and .yamllint file +!.pre-commit-config.yaml +!.yamllint.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..8a34822 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +--- +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +# Common commands: +# pre-commit install +# pre-commit autoupdate +# pre-commit run --all-files --hook-stage commit +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-added-large-files + - id: destroyed-symlinks + - id: detect-private-key + - id: end-of-file-fixer + - id: mixed-line-ending + args: [--fix=auto] + - id: trailing-whitespace + - repo: https://github.com/executablebooks/mdformat + rev: 0.7.17 + hooks: + - id: mdformat + additional_dependencies: + - mdformat-config + - mdformat-black + - mdformat-frontmatter + args: [--wrap=80] + exclude: _.+\.md|CHANGELOG\.md + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.35.1 + hooks: + - id: yamllint + exclude: ^deployment/charts + - repo: https://github.com/pappasam/toml-sort + rev: v0.23.1 + hooks: + - id: toml-sort-fix + args: [--in-place, --all, --trailing-comma-inline-array] + exclude: poetry\.lock +... diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..ec7defb --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,16 @@ +--- +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: '3.11' + jobs: + post_create_environment: + - python -m pip install poetry + post_install: + - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs + +sphinx: + configuration: docs/source/conf.py +... diff --git a/.safety-policy.yml b/.safety-policy.yml new file mode 100644 index 0000000..7089764 --- /dev/null +++ b/.safety-policy.yml @@ -0,0 +1,43 @@ +--- +version: '3.0' + +scanning-settings: + max-depth: 6 + exclude: [] + include-files: [] + system: + targets: [] + +security: + ignore-vulnerabilities: + 70612: + reason: "Won't use untrusted templates without sandboxing." + expires: '2025-5-27' + +report: + dependency-vulnerabilities: + enabled: true + auto-ignore-in-report: + python: + environment-results: true + unpinned-requirements: true + cvss-severity: [] + +fail-scan-with-exit-code: + dependency-vulnerabilities: + enabled: true + fail-on-any-of: + cvss-severity: + - critical + - high + - medium + exploitability: + - critical + - high + - medium + +security-updates: + dependency-vulnerabilities: + auto-security-updates-limit: + - patch +... diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..ad3171a --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,13 @@ +--- +# https://yamllint.readthedocs.io/en/stable/configuration.html#default-configuration +extends: default + +rules: + truthy: + ignore: + .github/**/*.yaml + document-start: + level: error + document-end: + level: error +... diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..bc09eb3 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +# ELIXIR Cloud & AAI Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting [cloud-service@elixir-europe.org][elixir-cloud-aai-email]. All complaints +will be reviewed and investigated and will result in a response that is deemed +necessary and appropriate to the circumstances. The project team is obligated +to maintain confidentiality with regard to the reporter of an incident. Further +details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor +Covenant][contributor-covenant], version 1.4, available at +<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html> + +[contributor-covenant]: https://contributor-covenant.org/ +[elixir-cloud-aai-email]: mailto:cloud-service@elixir-europe.org diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..975b5b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Elixir Cloud AAI + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..27caedf --- /dev/null +++ b/Makefile @@ -0,0 +1,125 @@ +# NOTE: This Makefile assumes that dependencies are installed and, if a virtual +# environment is used, it is activated. + +## Variables ################################################################## +# NOTE: Define any variables here if needed in the future + +## Documentation ############################################################## +# NOTE: Keep all the targets in alphabetical order for better readability. + +default: help + +.PHONY: help +help: + @echo "\nUsage: make [target] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" + @echo "Available targets:\n" + + @echo "Environment Management --------------------------------------------------------" + @echo " \033[1m\033[35mclean-venv\033[0m \033[37m(cv)\033[0m: \033[36mRemove virtual environment.\033[0m" + @echo " \033[1m\033[35minstall\033[0m \033[37m(i)\033[0m: \033[36mInstall dependencies and tus_storagehandler.\033[0m" + @echo " \033[1m\033[35mvenv\033[0m \033[37m(v)\033[0m: \033[36mCreate virtual environment.\033[0m\n" + + @echo "Code Quality ------------------------------------------------------------------" + @echo " \033[1m\033[35mformat-lint\033[0m \033[37m(fl)\033[0m: \033[36mRun linter, formatter, spellcheck.\033[0m" + @echo " \033[1m\033[35mprecommit-check\033[0m \033[37m(pc)\033[0m: \033[36mRun all pre-commit checks.\033[0m" + @echo " \033[1m\033[35msecurity\033[0m \033[37m(s)\033[0m: \033[36mRun security scans.\033[0m" + @echo " \033[1m\033[35mtype-check\033[0m \033[37m(tc)\033[0m: \033[36mPerform type checking.\033[0m\n" + + @echo "Testing -----------------------------------------------------------------------" + @echo " \033[1m\033[35mtest\033[0m \033[37m(t)\033[0m: \033[36mRun all tests.\033[0m\n" + + @echo "Documentation -----------------------------------------------------------------" + @echo " \033[1m\033[35mdocs\033[0m \033[37m(d)\033[0m: \033[36mGenerate project documentation.\033[0m\n" + +## Autogenerated Targets ###################################################### +# NOTE: Keep all the targets in alphabetical order for better readability. +# NOTE: Do not modify the autogenerated targets, unless necessary, write custom +# targets in the custom section below.. + +.PHONY: clean-venv +clean-venv: + @echo "\nRemoving the virtual environment ++++++++++++++++++++++++++++++++++++++++++++++\n" + @rm -rf .venv + +.PHONY: cv +cv: clean-venv + +.PHONY: docs +docs: + @echo "\nGenerating project documentation ++++++++++++++++++++++++++++++++++++++++++++++\n" + @sphinx-apidoc -f -o docs/source/pages tus_storagehandler + @cd docs && make html + @echo "\nSummary ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" + @echo "Documentation generated successfully." + @echo "Open docs/build/html/index.html in your browser." + @echo "Or serve it locally using:" + @echo "python -m http.server -d docs/build/html/" + +.PHONY: d +d: docs + +.PHONY: format-lint +format-lint: + @echo "\nRunning linter and formatter using ruff and typos +++++++++++++++++++++++++++++\n" + @ruff format && ruff check --fix + @typos . + +.PHONY: fl +fl: format-lint + +.PHONY: install +install: + @echo "\nInstalling dependencies and with this package +++++++++++++++++++++++++++++++++\n" + @poetry install + +.PHONY: i +i: install + +.PHONY: precommit-check +precommit-check: + @echo "\nRunning pre-commit checks +++++++++++++++++++++++++++++++++++++++++++++++++++++\n" + @pre-commit run --all-files + +.PHONY: pc +pc: precommit-check + +.PHONY: security +security: + @echo "\nRunning security scans using bandit and safety ++++++++++++++++++++++++++++++++\n" + @safety check --full-report + @bandit -c pyproject.toml -r tus_storagehandler + +.PHONY: s +s: security + +.PHONY: test +test: + @echo "\nRunning tests using pytest ++++++++++++++++++++++++++++++++++++++++++++++++++++\n" + @pytest tests/ + +.PHONY: t +t: test + +.PHONY: type-check +type-check: + @echo "\nPerforming type checking with mypy ++++++++++++++++++++++++++++++++++++++++++++\n" + @mypy tus_storagehandler + +.PHONY: tc +tc: type-check + +.PHONY: venv +venv: + @echo "\nCreating a virtual environment ++++++++++++++++++++++++++++++++++++++++++++++++\n" + @python -m venv .venv + @echo "\nSummary +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" + @echo "Virtual environment created successfully." + @echo "To activate the environment for this shell session, run:" + @echo "source .venv/bin/activate" + +.PHONY: v +v: venv + +## Custom Targets ############################################################# +# NOTE: Keep all the targets in alphabetical order for better readability. +# NOTE: Add any custom targets here if needed in the future. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..b5dd732 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,64 @@ +#### Description + +<!-- Please include a summary of the change and the relevant issue(s) it +resolves, if any (otherwise delete that line), e.g., `Fixes #123`. If the PR +addresses more than one issue, please add multiple lines, each starting with +'Fixes #'. Please stick to that syntax precisely, including whitespaces, +otherwise the issue(s) may not be linked to the PR. + +In the summary, list any dependencies that are required for this change. +Please use bullet points for the description. Please also briefly describe +the relevant motivation and context briefly. For very trivial changes that are +duly explained by the PR title, a description can be omitted. --> + +- Fixes #(issue number) + +#### Checklist + +<!-- Please go through the following checklist to ensure that your change is +ready for review. Please do not forget to double check the list after you have +modified your PR, e.g., if you have added commits to address reviewer +comments or to fix failing automated checks. **Please check items also if they +do not apply to your change**, e.g., if your change does not require an update +of the user-facing documentation, still check the box. + +Generally, **PRs are only reviewed when all boxes are ticked off and all +automated checks pass** (use the comment section below if you believe that +your PR is ready to be merged even though not all boxes were ticked off). --> + +- [ ] My code follows the [contributing guidelines][contributing-guidelines] of this + project, including, in particular, with regard to any style guidelines +- [ ] The title of my PR complies with the [Conventional Commits + specification][conv-commits]; in particular, it clearly indicates + that a change is a breaking change +- [ ] I acknowledge that all my commits will be squashed into a single commit, + using the PR title as the commit message +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code in hard-to-understand areas +- [ ] I have updated the user-facing documentation to describe any new or + changed behavior +- [ ] I have added type annotations for all function/class/method interfaces + or updated existing ones (only for Python, TypeScript, etc.) +- [ ] I have provided appropriate documentation ([Google-style Python + docstrings][py-doc-google]) for all packages/modules/functions/classes/ + methods or updated existing ones +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature + works +- [ ] New and existing unit tests pass locally with my changes +- [ ] I have not reduced the existing code coverage + + +#### Comments + +<!-- If there are unchecked boxes in the list above, but you would still like +your PR to be reviewed or considered for merging, please describe here why +boxes were not checked. For example, if you are positive that your commits +should _not_ be squashed when merging, please explain why you think the PR +warrants or requires multiple commits to be added to the history (but note that +in that case, it is a prerequisite that all commits follow the Conventional +Commits specification). --> + +[contributing-guidelines]: https://elixir-cloud-aai.github.io/guides/guide-contributor/workflow/ +[conv-commits]: https://www.conventionalcommits.org/en +[py-doc-google]: https://google.github.io/styleguide/pyguide.html diff --git a/README.md b/README.md deleted file mode 100644 index 9ce6277..0000000 --- a/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# TUS Storage Handler - -## Synopsis -This Flask application provides endpoints for uploading, downloading, and listing files in a MinIO bucket, with TUS protocol support for uploads, and CORS enabled for cross-origin requests. - -## Installation - -### Prerequisites -This flask application requires a running instance of [minio](https://min.io/download) - -Run the minio instance by executing the following command in the location where minio is installed - -`minio server /data --console-address ":9001"` - -Download the required dependencies - -1. Navigate to the folder `TusStorageHandler` -2. Create a virtual environment and activate it (optional) -3. `pip install -r requirements.txt` - -### Running the application - -`flask run` -The application will be running on `http://127.0.0.1:5000` by default. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..747ffb7 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..cfe671f --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,102 @@ +"""Configuration file for the Sphinx documentation builder.""" + +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import datetime +import os +import sys +from pathlib import Path + +import tomli + +sys.path.insert(0, os.path.abspath("../..")) + + +# -- Project information ----------------------------------------------------- +def _get_project_meta(): + _pyproject_path = Path(__file__).parents[2] / "pyproject.toml" + with open(_pyproject_path, mode="rb") as pyproject: + return tomli.load(pyproject)["tool"]["poetry"] + + +pkg_meta = _get_project_meta() +current_year = datetime.datetime.now().year +project = str(pkg_meta["name"]) +project_copyright = f"{current_year}, {str(pkg_meta['authors'][0])}" +author = str(pkg_meta["authors"][0]) + +version = str(pkg_meta["version"]) +release = version + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", + "sphinx.ext.autosummary", + # Used to write beautiful docstrings: + "sphinx.ext.napoleon", + # Used to include .md files: + "m2r2", + # Used to insert typehints into the final docs: + "sphinx_autodoc_typehints", + # Used to embed values from the source code into the docs: + "added_value", +] +# Set `typing.TYPE_CHECKING` to `True`: +# https://pypi.org/project/sphinx-autodoc-typehints/ +set_type_checking_flag = False +always_document_param_types = False + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: + +source_suffix = [".rst", ".md"] + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = "en" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "furo" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# -- Extension configuration ------------------------------------------------- +napoleon_numpy_docstring = False + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..f51987b --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,15 @@ +Welcome to tus_storagehandler's documentation! +=========================================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + pages/modules + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/images/logo-elixir-cloud-aai.svg b/images/logo-elixir-cloud-aai.svg new file mode 100644 index 0000000..18ecf21 --- /dev/null +++ b/images/logo-elixir-cloud-aai.svg @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="121.508" + height="128" + viewBox="0 0 32.148991 33.866664" + version="1.1" + id="svg8" + inkscape:version="1.2 (56b05e47e7, 2022-06-09, custom)" + sodipodi:docname="logo-elixir-cloud-aai.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs + id="defs2" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.979899" + inkscape:cx="-32.324881" + inkscape:cy="6.8185296" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + units="px" + inkscape:window-width="1848" + inkscape:window-height="1016" + inkscape:window-x="72" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:document-rotation="0" + inkscape:showpageshadow="2" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" /> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-75.684046,-39.803727)"> + <g + id="g984" + transform="matrix(1.5094201,0,0,1.509249,-51.566629,-16.028093)" + style="stroke-width:0.49464"> + <g + style="stroke-width:1.37493" + id="g1227" + transform="matrix(0.07369114,0,0,0.07390181,66.854225,-101.99645)"> + <path + sodipodi:nodetypes="ccccc" + inkscape:connector-curvature="0" + id="rect828-9-5-0" + d="m 422.307,1979.569 28.684,17.791 h -48.234 l -28.684,-17.791 z" + style="fill:#d8e0e3;fill-opacity:1;stroke:none;stroke-width:2.0624;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccccc" + inkscape:connector-curvature="0" + id="rect911-0-3" + d="m 381.11912,1953.2738 h 94.98398 l -7.05727,26.295 H 374.0734 Z" + style="fill:#f95b45;fill-opacity:1;stroke:#e2483e;stroke-width:2.0624;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="rect911-6-8-1-4" + d="m 302.71148,1919.8889 -26.28711,7.0586 55.83594,96.709 h 111.66992 l 7.06055,-26.2969 H 347.44195 Z" + style="fill:#5aba62;fill-opacity:1;stroke:#469f4e;stroke-width:2.0624;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccccc" + inkscape:connector-curvature="0" + id="rect828-9-5-0-3" + d="m 359.87419,2067.7443 28.684,17.791 h -48.234 l -28.684,-17.791 z" + style="fill:#d8e0e3;fill-opacity:1;stroke:none;stroke-width:2.0624;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccccc" + inkscape:connector-curvature="0" + id="rect911-0-6-0" + d="m 293.57421,2085.5353 h 94.98398 l -7.05727,26.2951 h -94.97243 z" + style="fill:#23a9f6;fill-opacity:1;stroke:#1b92e1;stroke-width:2.0624;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect911-6-8-1-5-4" + d="m 459.91949,2145.2151 26.28711,-7.0586 -55.83594,-96.709 H 318.70074 l -7.06055,26.2968 h 103.54883 z" + style="fill:#ffcb00;fill-opacity:1;stroke:#fcb20f;stroke-width:2.0624;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + inkscape:connector-curvature="0" /> + </g> + </g> + </g> + <style + id="style2" + type="text/css"> + .st0{fill:#FBBC05;} +</style> +</svg> diff --git a/images/logo-elixir.svg b/images/logo-elixir.svg new file mode 100644 index 0000000..af9d4b9 --- /dev/null +++ b/images/logo-elixir.svg @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + version="1.1" + id="LOGOS" + x="0px" + y="0px" + viewBox="0 0 159.43299 128" + xml:space="preserve" + sodipodi:docname="logo-elixir.svg" + width="159.433" + height="128" + inkscape:version="1.2 (56b05e47e7, 2022-06-09, custom)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"><defs + id="defs261" /><sodipodi:namedview + id="namedview259" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + showgrid="false" + inkscape:zoom="0.77122964" + inkscape:cx="-164.67209" + inkscape:cy="296.28011" + inkscape:window-width="1848" + inkscape:window-height="1016" + inkscape:window-x="72" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="LOGOS" /> +<style + type="text/css" + id="style242"> + .st0{fill:#F47D20;} + .st1{fill:#4D4848;} +</style> + + + + + + +<g + id="g270" + transform="matrix(0.16776762,0,0,0.16776762,9.3730807,10.446892)" + style="stroke-width:5.96063"><path + class="st0" + d="m 756.4,33.1 c -23.7,0 -43,19.2 -43,43 0,7.2 1.8,14.3 5.2,20.6 -4.3,2.1 -14.2,7.1 -28.2,14.4 -7.6,-16 -26.6,-22.8 -42.6,-15.3 -16,7.5 -22.8,26.6 -15.3,42.6 0.5,1 1,1.9 1.5,2.8 -7.2,4 -14.6,8.1 -22.3,12.4 -7.6,-11.6 -23.2,-14.9 -34.8,-7.3 -10.8,7 -14.5,21.1 -8.6,32.5 -7.9,4.7 -15.9,9.6 -23.9,14.5 -7.7,-6.6 -19.3,-5.6 -25.9,2.1 -3.9,4.6 -5.3,10.8 -3.7,16.7 C 482.6,233 451.9,254.9 427,276.2 397.9,211.2 355.1,139.9 295.4,81.8 l -0.2,-0.2 c -7,-6.9 -12.8,-3.8 -4.7,4.7 l 0.3,0.3 c 50.6,54.5 90.4,133.3 117.5,205.2 -102.8,87.8 -216.1,257.9 -91.9,311.3 18.1,7.8 26.3,-7.1 18.5,-11.7 -1.4,-0.8 -56.7,-30.7 -21.5,-122.6 h 88.5 c 4.7,0.2 8.7,-3.6 8.9,-8.3 0.2,-4.7 -3.6,-8.7 -8.3,-8.9 -0.2,0 -0.4,0 -0.6,0 h -81.2 c 5.3,-11.1 11.1,-22 17.5,-32.5 h 63.7 c 4.7,-0.2 8.5,-4.1 8.3,-8.9 -0.2,-4.5 -3.8,-8.1 -8.3,-8.3 h -52.7 c 6.6,-9.8 13.9,-20.1 22.2,-30.9 h 30.5 c 4.7,-0.2 8.5,-4.1 8.3,-8.9 -0.2,-4.5 -3.8,-8.1 -8.3,-8.3 H 385 c 9.6,-11.7 20.3,-24 32,-36.9 l 0.3,-0.3 c 23.3,67.6 34.6,125.2 32.1,144.5 -1.4,10.5 -8.1,14.8 -10.8,16.1 -4.3,2.1 -6.1,8.1 0.7,10.1 21.7,6.4 35.1,-15.2 37.5,-32.5 2.4,-17.5 -9.5,-81.5 -40.9,-157.7 26.2,-26.1 56.5,-51 87.4,-73.9 8.8,5.1 20,2.1 25.1,-6.7 1.6,-2.8 2.5,-6 2.5,-9.2 0,-1.2 -0.1,-2.4 -0.4,-3.6 8.2,-5.7 16.3,-11.3 24.4,-16.6 10.9,8.6 26.6,6.8 35.3,-4 4.9,-6.1 6.6,-14.1 4.7,-21.7 8.1,-5 15.9,-9.8 23.4,-14.3 12.2,12.8 32.5,13.2 45.2,1 8.6,-8.3 12,-20.6 8.6,-32.1 13.7,-7.6 23.6,-13 28.5,-15.8 13.4,19.6 40.1,24.7 59.7,11.3 19.6,-13.4 24.7,-40.1 11.3,-59.7 -7.8,-11.6 -21,-18.6 -35.2,-18.7 z" + id="path244" + style="stroke-width:5.96063" /><path + class="st1" + d="m 267,377.4 c 1.6,-3.5 2.9,-7.2 4.5,-11.8 1.9,-5.2 3.4,-10.4 4.5,-15.8 l 16.3,-70.7 c 1.3,-7.2 2.4,-12.6 2.7,-15.8 0.5,-2.8 0.7,-5.7 0.5,-8.6 v -3.2 h 37 l -22.2,98.3 c -1.6,7 -2.7,12.3 -3.2,16.1 -0.7,3.8 -0.9,7.7 -0.8,11.5 z" + id="path246" + style="stroke-width:5.96063" /><path + class="st1" + d="M 174.8,377.4 H 65.2 c -11,0 -18.5,-2.4 -22.5,-7.5 -2.1,-2.9 -3.2,-6.9 -3.2,-12 0.1,-4.6 0.7,-9.2 1.9,-13.7 L 55,284.7 c 2.9,-12 7.2,-20.6 13.1,-25.7 6.2,-5.1 14.5,-7.5 25.7,-7.5 h 80.9 c 11.5,0 19,2.4 22.8,7.5 2.4,3 3.5,7 3.5,12.3 0,4.5 -0.7,9 -1.9,13.4 l -8.8,39.4 H 80.2 l -5.9,25.7 c -0.4,2 -0.7,4.1 -0.8,6.2 0,1.3 0,2.4 0.5,2.9 0.8,1.6 3.2,2.4 7,2.4 h 80.3 c 7,0 14,-0.7 20.9,-2.1 z m -8.6,-98.3 c 0.7,-2.1 1.1,-4.2 1.1,-6.4 0,-0.9 -0.2,-1.8 -0.5,-2.7 -1.1,-1.6 -3.7,-2.4 -7.5,-2.4 h -56.8 c -3.7,0 -6.4,0.8 -8,2.4 -1.9,1.6 -3.2,4.6 -3.7,9.1 L 83.9,308 h 75.5 z" + id="path248" + style="stroke-width:5.96063" /><path + class="st1" + d="m 202.1,377.4 c 1.7,-3.9 3.1,-8.1 4.8,-13.2 2,-5.8 3.6,-11.7 4.8,-17.7 l 28.1,-130.3 c 1.7,-8.1 2.8,-14.1 3.4,-17.7 0.3,-3.3 0.6,-7.8 0.6,-13.2 H 283 l -35,161.3 c -1.7,7.8 -2.8,13.8 -3.4,18 -0.7,4.3 -1,8.6 -0.8,12.9 z" + id="path250" + style="stroke-width:5.96063" /><path + class="st1" + d="m 480.7,377.4 c 1.6,-3.5 2.9,-7.2 4.5,-11.8 1.9,-5.1 3.4,-10.4 4.6,-15.8 l 16.3,-70.7 c 1.3,-7.2 2.4,-12.6 2.7,-15.8 0.5,-2.8 0.7,-5.7 0.5,-8.6 v -3.2 h 37 L 524,349.8 c -1.6,7 -2.7,12.3 -3.2,16.1 -0.7,3.8 -0.9,7.7 -0.8,11.5 z" + id="path252" + style="stroke-width:5.96063" /><path + class="st1" + d="m 654.1,272.7 c -4,-0.3 -8,-0.5 -12.6,-0.5 h -18 c -3.5,0 -6.4,0.8 -8,2.1 -2.1,1.6 -3.5,4.3 -4.6,8.3 l -15,67.2 c -2.7,10.4 -3.8,18.5 -3.8,24.6 v 2.9 H 553 c 1.6,-4 3.2,-7.8 4.3,-11.5 1.1,-3.5 2.4,-8.8 4.3,-16.1 L 577.7,279 c 1.9,-7 2.7,-12.3 3.5,-16.3 0.4,-3.7 0.6,-7.5 0.5,-11.2 h 32.9 l -1.3,12.8 c 1.8,-3.9 4.7,-7.3 8.3,-9.6 3.5,-2.1 8,-3.2 14.2,-3.2 H 668 l 7.8,13.9 c -1.7,1.5 -3.2,3.2 -4.5,5.1 -1.9,2.4 -3.2,3.5 -4,3.5 -4.9,-0.5 -9.2,-1 -13.2,-1.3 z" + id="path254" + style="stroke-width:5.96063" /><path + class="st1" + d="m 338.8,207.5 c 0,10 -8.1,18.1 -18.1,18.1 -10,0 -18.1,-8.1 -18.1,-18.1 0,-10 8.1,-18.1 18.1,-18.1 0,0 0,0 0,0 10,-0.1 18.1,8.1 18.1,18.1 0,0 0,0 0,0 z" + id="path256" + style="stroke-width:5.96063" /></g> +</svg> diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6dcc85e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,82 @@ +[build-system] +build-backend = "poetry.core.masonry.api" +requires = ["poetry-core"] + +[tool.mypy] +warn_return_any = true +warn_unused_configs = true + +[tool.poetry] +authors = ["Elixir Cloud AAI <cloud-service@elixir-europe.org>"] +classifiers = [ + "Development Status :: 4 - Beta", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", +] +description = "File Handler Utilizing TUS and Minio (S3 Storage) with DRS Filer Integration" +license = "Apache-2.0" +maintainers = ["Elixir Cloud AAI <cloud-service@elixir-europe.org>"] +name = "tus_storagehandler" +readme = "README.md" +repository = "https://github.com/elixir-cloud-aai/tus-storagehandler" +version = "0.1.0" + +[tool.poetry.dependencies] +python = "^3.11" + +[tool.poetry.group.docs.dependencies] +added-value = ">=0.24.0,<1.0.0" +docutils = "0.20.1" +furo = ">=2024.5.6,<2025.0.0" +m2r2 = "^0.3.3.post2" +sphinx = "^7.3.7" +sphinx-autodoc-typehints = "^2.1.0" +tomli = ">=2.0.1,<3.0.0" + +[tool.poetry.group.lint.dependencies] +ruff = ">=0.4.10,<1.0.0" +typos = ">=1.22.8,<2.0.0" + +[tool.poetry.group.misc.dependencies] +cruft = ">=2.15.0,<3.0.0" +jello = ">=1.6.0,<2.0.0" +pre-commit = ">=3.7.1,<4.0.0" + +[tool.poetry.group.security.dependencies] +bandit = ">=1.7.8,<2.0.0" +safety = ">=3.2.0,<4.0.0" + +[tool.poetry.group.test.dependencies] +pytest = ">=8.2.2,<9.0.0" +pytest-cov = ">=5.0.0,<=6.0.0" + +[tool.poetry.group.types.dependencies] +mypy = ">=1.10.0,<2.0.0" + +[tool.poetry.scripts] +tus_storagehandler = "tus_storagehandler.main:main" + +[tool.ruff] +indent-width = 2 + +[tool.ruff.format] +docstring-code-format = true +indent-style = "space" +line-ending = "lf" +quote-style = "double" + +[tool.ruff.lint] +select = [ + "B", # flake8-bugbear + "D", # pydocstyle + "E", # pycodestyle + "F", # Pyflakes + "I", # isort + "PL", # pylint + "SIM", # flake8-simplify + "UP", # pyupgrade +] + +[tool.ruff.lint.pydocstyle] +convention = "google" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..f1365f8 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""This package contains tests.""" diff --git a/tests/test_integration/__init__.py b/tests/test_integration/__init__.py new file mode 100644 index 0000000..9a74cfd --- /dev/null +++ b/tests/test_integration/__init__.py @@ -0,0 +1 @@ +"""This package contains integration tests.""" diff --git a/tests/test_integration/test_dummy.py b/tests/test_integration/test_dummy.py new file mode 100644 index 0000000..1491684 --- /dev/null +++ b/tests/test_integration/test_dummy.py @@ -0,0 +1,10 @@ +"""Dummy test file to avoid the error.""" + + +# TODO: Remove this file when adding real tests. +# This test is a placeholder for the integration tests. It is a workaround +# to avoid the error "Module has no tests" when running the tests with pytest. +# Cf. https://github.com/pytest-dev/pytest/issues/2393 +def test_placeholder(): + """Dummy test to avoid the error.""" + pass diff --git a/tests/test_unit/__init__.py b/tests/test_unit/__init__.py new file mode 100644 index 0000000..2f312a6 --- /dev/null +++ b/tests/test_unit/__init__.py @@ -0,0 +1 @@ +"""This package contains unit tests.""" diff --git a/tests/test_unit/test_dummy.py b/tests/test_unit/test_dummy.py new file mode 100644 index 0000000..4443c40 --- /dev/null +++ b/tests/test_unit/test_dummy.py @@ -0,0 +1,10 @@ +"""Dummy test file to avoid the error.""" + + +# TODO: Remove this file when adding real tests. +# This test is a placeholder for the unit tests. It is a workaround +# to avoid the error "Module has no tests" when running the tests with pytest. +# Cf. https://github.com/pytest-dev/pytest/issues/2393 +def test_placeholder(): + """Dummy test to avoid the error.""" + pass diff --git a/tus_storagehandler/__init__.py b/tus_storagehandler/__init__.py new file mode 100644 index 0000000..9f8346a --- /dev/null +++ b/tus_storagehandler/__init__.py @@ -0,0 +1 @@ +"""This package contains module for the tus_storagehandler.""" diff --git a/tus_storagehandler/main.py b/tus_storagehandler/main.py new file mode 100644 index 0000000..666c8b1 --- /dev/null +++ b/tus_storagehandler/main.py @@ -0,0 +1,10 @@ +"""Entry point for python_cookiecutter.""" + + +def main(): + """Main entry point for tus_storagehandler.""" + print("Hello from tus_storagehandler!") + + +if __name__ == "__main__": + main()