diff --git a/.flake8 b/.flake8
index c430446..e21fafd 100644
--- a/.flake8
+++ b/.flake8
@@ -4,7 +4,3 @@
# E501 line too long (83 > 79 characters)
exclude = .git,.pycache,build,.eggs
-
-per-file-ignores =
- ./src/actinia_example_plugin/wsgi.py: F401
- ./tests/test_resource_base.py: F401
diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml
deleted file mode 100644
index fd65518..0000000
--- a/.github/workflows/black.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: Python code style check with black
-
-on: [push]
-
-# only one run per PR/branch happens at a time, cancelling the old run when a new one starts
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- black:
-
- runs-on: ubuntu-22.04
-
- steps:
- - uses: actions/checkout@v4
- - name: Install apt dependencies
- run: |
- sudo apt-get update && sudo apt-get install python3 python3-pip -y
- - name: Install pip dependencies
- run: |
- pip3 install black==23.1.0
- - name: Check code style with Black
- run: |
- black --check --diff --line-length 79 .
diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml
deleted file mode 100644
index 03141ae..0000000
--- a/.github/workflows/flake8.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-name: Python Flake8 Code Quality
-
-on: [push]
-
-# only one run per PR/branch happens at a time, cancelling the old run when a new one starts
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
-
- flake8-actinia:
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v4
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: 3.8
- - name: Install
- run: |
- python -m pip install --upgrade pip
- pip install flake8==3.8.0
- - name: Run Flake8
- run: |
- flake8 --config=.flake8 --count --statistics --show-source --jobs=$(nproc) .
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
index 5e37657..d51831c 100644
--- a/.github/workflows/linting.yml
+++ b/.github/workflows/linting.yml
@@ -1,17 +1,13 @@
---
name: Linting and code quality check
-on: [push, pull_request]
+on:
+ push:
+ branches:
+ - main
+ - develop
+ pull_request:
jobs:
lint:
uses: mundialis/github-workflows/.github/workflows/linting.yml@main
- # with:
- # # set pylint-version to empty string to skip the pylint workflow
- # pylint-version: ''
- # BASH_SEVERITY: 'warning'
- # VALIDATE_DOCKERFILE_HADOLINT: false
- # VALIDATE_JSON: false
- # VALIDATE_HTML: false
- # VALIDATE_CSS: false
- # VALIDATE_BASH_EXEC: false
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 74f7ddf..3dce8ca 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -1,42 +1,13 @@
-# This workflow will upload a Python Package using Twine when a release is created
-# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
-
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
-name: Upload Python Package
+name: Upload Python Package to test PyPI
on:
release:
types: [published]
jobs:
- deploy:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: '3.x'
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- pip install build
- - name: Build package
- run: python3 -m build --outdir build .
- - name: Release
- uses: softprops/action-gh-release@v2
- if: startsWith(github.ref, 'refs/tags/')
- with:
- files: build/*.whl
-
- # - name: Publish package
- # uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
- # with:
- # user: __token__
- # password: ${{ secrets.PYPI_API_TOKEN }}
+ publish-python:
+ uses: mundialis/github-workflows/.github/workflows/python-publish.yml@main
+ with:
+ test_pypi: true
+ secrets:
+ PYPI_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml
deleted file mode 100644
index c85e40c..0000000
--- a/.github/workflows/super-linter.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: General linting
-
-on: [push]
-
-# only one run per PR/branch happens at a time, cancelling the old run when a new one starts
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- super-linter:
- name: GitHub Super Linter
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
- - name: Lint code base
- uses: github/super-linter@v5
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- # Listed but commented out linters would be nice to have.
- # (see https://github.com/github/super-linter#environment-variables)
- #
- # Python (supported using Pylint) and C/C++ (not supported) are
- # handled separately due to the complexity of the settings.
- # VALIDATE_BASH: true
- # VALIDATE_CSS: true
- # VALIDATE_DOCKER: true
- VALIDATE_JAVASCRIPT_ES: true
- # VALIDATE_JAVASCRIPT_STANDARD: true
- VALIDATE_JSON: true
- VALIDATE_MARKDOWN: true
- VALIDATE_POWERSHELL: true
- # VALIDATE_XML: true
- VALIDATE_YAML: true
- FILTER_REGEX_EXCLUDE: ./config/templates/
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 32e7c9e..cbe6bd4 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,3 +1,5 @@
+---
+
name: actinia tests
on:
@@ -14,8 +16,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- # with:
- # path: "."
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Replace run only unittest command
diff --git a/.gitignore b/.gitignore
index e1798f6..7bfa463 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,9 @@ sdist/*
# docker
docker/redis_data/dump.rdb
!docker/actinia-example-plugin-test/actinia-example-plugin-test.cfg
+
+# linting with shared config file
+.pylintrc
+.pylintrc_allowed_to_fail
+ruff-github-workflows.toml
+ruff-merged.toml
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..7d6a505
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,27 @@
+---
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v5.0.0
+ hooks:
+ - id: trailing-whitespace
+ exclude: |
+ (?x)^(
+ .*\.ref$|
+ .*\.svg$|
+ build/|
+ dist/|
+ src/actinia_core.egg-info/
+ )
+ - id: end-of-file-fixer
+ exclude: |
+ (?x)^(
+ .*\.ref$|
+ .*\.svg$|
+ build/|
+ dist/|
+ src/actinia_core.egg-info/
+ )
+ - repo: https://github.com/mundialis/github-workflows
+ rev: 1.4.0
+ hooks:
+ - id: linting
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 5b868f7..f678e63 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,7 +1,7 @@
-FROM mundialis/actinia-core:latest
+FROM mundialis/actinia-core:5.0.0
# pwgen is needed for the tests
-RUN pip3 install pwgen
+RUN pip3 install --no-cache-dir pwgen==0.8.2.post0
COPY docker/actinia.cfg /etc/default/actinia
COPY src /src/actinia-example-plugin/src/
@@ -9,9 +9,13 @@ COPY setup.cfg /src/actinia-example-plugin/
COPY setup.py /src/actinia-example-plugin/
COPY requirements.txt /src/actinia-example-plugin/
-RUN pip3 install -r /src/actinia-example-plugin/requirements.txt
-RUN pip3 uninstall actinia-example-plugin.wsgi -y
+RUN pip3 install --no-cache-dir -r /src/actinia-example-plugin/requirements.txt && \
+ pip3 uninstall actinia-example-plugin.wsgi -y
# SETUPTOOLS_SCM_PRETEND_VERSION is only needed if in the plugin folder is no
# .git folder
ENV SETUPTOOLS_SCM_PRETEND_VERSION=0.0
-RUN (cd /src/actinia-example-plugin && python3 setup.py install)
+
+WORKDIR /src/actinia-example-plugin
+RUN python3 setup.py install
+
+WORKDIR /src/actinia_core
\ No newline at end of file
diff --git a/docker/actinia-example-plugin-test/Dockerfile b/docker/actinia-example-plugin-test/Dockerfile
index c24d4ac..5d5ea4d 100644
--- a/docker/actinia-example-plugin-test/Dockerfile
+++ b/docker/actinia-example-plugin-test/Dockerfile
@@ -1,16 +1,16 @@
-FROM mundialis/actinia-core:latest as actinia_test
+FROM mundialis/actinia-core:5.0.0 as actinia_test
LABEL authors="Carmen Tawalika,Anika Weinmann"
LABEL maintainer="tawalika@mundialis.de,weinmann@mundialis.de"
-ENV ACTINIA_CUSTOM_TEST_CFG /etc/default/actinia-example-plugin-test
+ENV ACTINIA_CUSTOM_TEST_CFG=/etc/default/actinia-example-plugin-test
# TODO do not set DEFAULT_CONFIG_PATH if this is fixed
-ENV DEFAULT_CONFIG_PATH /etc/default/actinia-example-plugin-test
+ENV DEFAULT_CONFIG_PATH=/etc/default/actinia-example-plugin-test
# install things only for tests
-RUN apk add redis
-RUN pip3 install iniconfig colorlog pwgen
+RUN apk add --no-cache redis==7.0.15-r1 && \
+ pip3 install --no-cache-dir iniconfig==2.0.0 colorlog==6.8.2 pwgen==0.8.2.post0
# COPY docker/actinia-example-plugin-test/start.sh /src/start.sh
@@ -31,7 +31,6 @@ COPY . /src/actinia-example-plugin/
WORKDIR /src/actinia-example-plugin/
-RUN chmod a+x tests_with_redis.sh
-RUN make install
+RUN chmod a+x tests_with_redis.sh && make install
# RUN make test
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..610ffe2
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,58 @@
+[build-system]
+requires = ["setuptools>=61.0.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "actinia-example-plugin"
+version = "1.0.0"
+description = "An actinia-core plugin which adds example endpoints to actinia-core"
+readme = "README.md"
+authors = [
+ { name = "Carmen Tawalika"},
+ { name = "Anika Weinmann"},
+]
+classifiers = [
+ "License :: OSI Approved :: Apache Software License",
+ "Programming Language :: Python :: 3",
+]
+requires-python = ">=3.8"
+keywords = [
+ "processing",
+ "earth observation",
+ "cloud-based processing",
+ "rest api",
+ "gis",
+ "grass gis",
+ "osgeo",
+ "example",
+]
+dependencies = [
+ "colorlog>=4.2.1",
+ "xmltodict",
+]
+
+[project.optional-dependencies]
+test = [
+ "pytest",
+ "pytest-cov",
+]
+
+[project.urls]
+Homepage = "https://github.com/mundialis/actinia-example-plugin"
+Tutorial = "https://mundialis.github.io/actinia_core"
+API_Docs = "https://redocly.github.io/redoc/?url=https://actinia.mundialis.de/latest/swagger.json"
+
+[tool.flake8]
+max-line-length = 79
+
+[tool.pytest.ini_options]
+minversion = "6.0"
+addopts = "--cov actinia_module_plugin --cov-report term-missing --verbose --tb=line -x -s"
+testpaths = [
+ "tests",
+]
+markers = [
+ "dev: test current in development",
+ "unittest: completely independent test",
+ "integrationtest: integration test",
+]
diff --git a/renovate.json b/renovate.json
index 39a2b6e..dfe6ee4 100644
--- a/renovate.json
+++ b/renovate.json
@@ -1,6 +1,9 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
- "config:base"
- ]
+ "config:recommended"
+ ],
+ "pre-commit": {
+ "enabled": true
+ }
}
diff --git a/ruff.toml b/ruff.toml
new file mode 100644
index 0000000..8ed9d87
--- /dev/null
+++ b/ruff.toml
@@ -0,0 +1,4 @@
+lint.ignore = ["PLR0913", "PLR0917", "PLW0603", "S107", "S606"]
+
+[lint.per-file-ignores]
+"tests/testsuite.py" = [ "PLC0415",]
\ No newline at end of file
diff --git a/scripts/create_own_plugin.sh b/scripts/create_own_plugin.sh
old mode 100644
new mode 100755
index 9a5b8a4..224f8ba
--- a/scripts/create_own_plugin.sh
+++ b/scripts/create_own_plugin.sh
@@ -40,28 +40,28 @@ PLUGIN_NAME=$1
PLUGIN_NAME2=$(tr -s '-' '_' <<< "${PLUGIN_NAME}")
-if [ ! -v $2 ] && [ $2 == "git" ]
-then
- GIT_NAME=""
- ORG_MSG=""
- # if [[ -v $3 ]]
- # then
- # GIT_ORGANIZATION=$3
- # ORG_MSG=" in the organization ${GIT_ORGANIZATION}"
- # GIT_NAME="${GIT_ORGANIZATION}/${PLUGIN_NAME}"
- # else
- # GIT_NAME=${PLUGIN_NAME}
- # fi
- echo "GIT repository will be created${ORG_MSG}"
-else
- echo "No GIT repository will be created"
-fi
+# if [ ! -v "$2" ] && [ "$2" == "git" ]
+# then
+# GIT_NAME=""
+# ORG_MSG=""
+# # if [[ -v $3 ]]
+# # then
+# # GIT_ORGANIZATION=$3
+# # ORG_MSG=" in the organization ${GIT_ORGANIZATION}"
+# # GIT_NAME="${GIT_ORGANIZATION}/${PLUGIN_NAME}"
+# # else
+# # GIT_NAME=${PLUGIN_NAME}
+# # fi
+# echo "GIT repository ${GIT_NAME} will be created${ORG_MSG}"
+# else
+# echo "No GIT repository will be created"
+# fi
# git clone git@github.com:mundialis/actinia-example-plugin.git
-git clone https://github.com/mundialis/actinia-example-plugin.git ${PLUGIN_NAME}
+git clone https://github.com/mundialis/actinia-example-plugin.git "${PLUGIN_NAME}"
-cd ${PLUGIN_NAME}
+cd "${PLUGIN_NAME}" || exit
rm -rf .git
rm -f scripts/create_own_plugin.sh
@@ -75,21 +75,19 @@ for file in $(find . -name '*actinia-example-plugin*' | sort --reverse)
do
DIR=$(dirname "${file}")
BASENAME=$(basename "${file}")
- new_name=$(echo ${BASENAME} | sed "s+actinia-example-plugin+${PLUGIN_NAME}+g")
- # echo "${file} ${DIR}/${new_name}"
- mv ${file} ${DIR}/${new_name}
+ new_name=${BASENAME//actinia-example-plugin/$PLUGIN_NAME}
+ mv "${file}" "${DIR}"/"${new_name}"
done
for file in $(find . -name '*actinia_example_plugin*' | sort --reverse)
do
DIR=$(dirname "${file}")
BASENAME=$(basename "${file}")
- new_name=$(echo ${BASENAME} | sed "s+actinia_example_plugin+${PLUGIN_NAME2}+g")
- # echo "${file} ${DIR}/${new_name}"
- mv ${file} ${DIR}/${new_name}
+ new_name=${BASENAME//actinia_example_plugin/$PLUGIN_NAME2}
+ mv "${file}" "${DIR}"/"${new_name}"
done
# create git repo
-if [ ! -v $2 ] && [ $2 == "git" ]
+if [ ! -v "$2" ] && [ "$2" == "git" ]
then
git init
git add . && git commit -m "actinia plugin created from https://github.com/mundialis/actinia-example-plugin"
diff --git a/setup.cfg b/setup.cfg
index e0a90a3..77ccd84 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,116 +1,11 @@
-# This file is used to configure your project.
-# Read more about the various options under:
-# http://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files
-
-[metadata]
-name = actinia_example_plugin.wsgi
-description = actinia example plugin
-author = Anika Weinmann
-author-email = aweinmann@mundialis.de
-license = mit
-long-description = file: README.md
-long-description-content-type = text/x-rst; charset=UTF-8
-url = https://github.com/pyscaffold/pyscaffold/
-project-urls =
- Documentation = https://pyscaffold.org/
-# Change if running only on Windows, Mac or Linux (comma-separated)
-platforms = any
-# Add here all kinds of additional classifiers as defined under
-# https://pypi.python.org/pypi?%3Aaction=list_classifiers
-classifiers =
- Development Status :: 4 - Beta
- Programming Language :: Python
-
[options]
zip_safe = False
-packages = find_namespace:
+packages = find:
include_package_data = True
package_dir =
=src
-# DON'T CHANGE THE FOLLOWING LINE! IT WILL BE UPDATED BY PYSCAFFOLD!
-setup_requires = pyscaffold>=3.2a0,<3.3a0
-# Add here dependencies of your project (semicolon/line-separated), e.g.
-# install_requires = numpy; scipy
-# The usage of test_requires is discouraged, see `Dependency Management` docs
-# tests_require = pytest; pytest-cov
-# Require a specific Python version, e.g. Python 2.7 or >= 3.4
-# python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*
[options.packages.find]
where = src
exclude =
tests
-
-[options.extras_require]
-# Add here additional requirements for extra features, to install with:
-# `pip install actinia-example-plugin[PDF]` like:
-# PDF = ReportLab; RXP
-# Add here test requirements (semicolon/line-separated)
-testing =
- pytest
- pytest-cov
-
-[options.entry_points]
-# Add here console scripts like:
-# console_scripts =
-# script_name = actinia_example_plugin.module:function
-# For example:
-# console_scripts =
-# fibonacci = actinia_example_plugin.skeleton:run
-# And any other entry points, for example:
-# pyscaffold.cli =
-# awesome = pyscaffoldext.awesome.extension:AwesomeExtension
-
-[test]
-# py.test options when running `python setup.py test`
-# addopts = --verbose
-extras = True
-
-[tool:pytest]
-# Options for py.test:
-# Specify command line options as you would do when invoking py.test directly.
-# e.g. --cov-report html (or xml) for html/xml output or --junitxml junit.xml
-# in order to write a coverage file that can be read by Jenkins.
-addopts =
- --cov actinia_example_plugin --cov-report term-missing
- --verbose --tb=line -x -s
-norecursedirs =
- dist
- build
- .tox
-markers =
- dev: test current in development
- unittest: completely independent test
- integrationtest: integration test
-
-[aliases]
-dists = bdist_wheel
-
-[bdist_wheel]
-# Use this option if your package is pure-python
-universal = 1
-
-[build_sphinx]
-source_dir = docs
-build_dir = build/sphinx
-
-[devpi:upload]
-# Options for the devpi: PyPI server and packaging tool
-# VCS export must be deactivated since we are using setuptools-scm
-no-vcs = 1
-formats = bdist_wheel
-
-[flake8]
-# Some sane defaults for the code style checker flake8
-exclude =
- .tox
- build
- dist
- .eggs
- docs/conf.py
-
-[pyscaffold]
-# PyScaffold's parameters when the project was created.
-# This will be used when updating. Do not change!
-version = 3.2.3
-package = actinia_example_plugin
diff --git a/setup.py b/setup.py
index c94a9e4..ea13c3a 100644
--- a/setup.py
+++ b/setup.py
@@ -1,23 +1,23 @@
-# -*- coding: utf-8 -*-
-"""
- Setup file for actinia_example_plugin.
- Use setup.cfg to configure your project.
+#!/usr/bin/env python
+"""Copyright (c) 2024 mundialis GmbH & Co. KG.
- This file was generated with PyScaffold 3.2.3.
- PyScaffold helps you to put up the scaffold of your new Python project.
- Learn more under: https://pyscaffold.org/
-"""
-import sys
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
-from pkg_resources import VersionConflict, require
-from setuptools import setup
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
-try:
- require("setuptools>=38.3")
-except VersionConflict:
- print("Error: version of setuptools is too old (<38.3)!")
- sys.exit(1)
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+Plugin setup file
+"""
+
+from setuptools import setup
if __name__ == "__main__":
- setup(use_pyscaffold=True)
+ setup()
diff --git a/src/actinia_example_plugin/__init__.py b/src/actinia_example_plugin/__init__.py
index bb92c58..16ff8b7 100644
--- a/src/actinia_example_plugin/__init__.py
+++ b/src/actinia_example_plugin/__init__.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,10 +20,10 @@
__license__ = "GPLv3"
__author__ = "Carmen Tawalika, Anika Weinmann"
__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
-from pkg_resources import get_distribution, DistributionNotFound
+from pkg_resources import DistributionNotFound, get_distribution
try:
# Change here if project is renamed and does not equal the package name
diff --git a/src/actinia_example_plugin/api/__init__.py b/src/actinia_example_plugin/api/__init__.py
index e69de29..ea6c7b2 100644
--- a/src/actinia_example_plugin/api/__init__.py
+++ b/src/actinia_example_plugin/api/__init__.py
@@ -0,0 +1,4 @@
+"""actinia-example-plguin API part of package.
+
+This part provides the API part of the actinia-example-plugin.
+"""
diff --git a/src/actinia_example_plugin/api/helloworld.py b/src/actinia_example_plugin/api/helloworld.py
index 2b5cebb..9fde509 100644
--- a/src/actinia_example_plugin/api/helloworld.py
+++ b/src/actinia_example_plugin/api/helloworld.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,39 +20,38 @@
__license__ = "GPLv3"
__author__ = "Anika Weinmann"
__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
-from flask import request, make_response
-from flask_restful_swagger_2 import swagger
-from flask_restful_swagger_2 import Resource
+from flask import make_response, request
+from flask_restful_swagger_2 import Resource, swagger
from actinia_example_plugin.apidocs import helloworld
+from actinia_example_plugin.core.example import transform_input
from actinia_example_plugin.model.response_models import (
SimpleStatusCodeResponseModel,
)
-from actinia_example_plugin.core.example import transform_input
class HelloWorld(Resource):
- """Returns 'Hello world!'"""
+ """Returns 'Hello world!'."""
- def __init__(self):
+ def __init__(self) -> None:
+ """Hello world class initialisation."""
self.msg = "Hello world!"
- @swagger.doc(helloworld.describeHelloWorld_get_docs)
- def get(self):
+ @swagger.doc(helloworld.describe_hello_world_get_docs)
+ def get(self) -> SimpleStatusCodeResponseModel:
"""Get 'Hello world!' as answer string."""
return SimpleStatusCodeResponseModel(status=200, message=self.msg)
- @swagger.doc(helloworld.describeHelloWorld_post_docs)
- def post(self):
+ @swagger.doc(helloworld.describe_hello_world_post_docs)
+ def post(self) -> SimpleStatusCodeResponseModel:
"""Hello World post method with name from postbody."""
-
req_data = request.get_json(force=True)
if isinstance(req_data, dict) is False or "name" not in req_data:
return make_response("Missing name in JSON content", 400)
name = req_data["name"]
- msg = transform_input(name)
+ msg = f"{self.msg} {transform_input(name)}"
return SimpleStatusCodeResponseModel(status=200, message=msg)
diff --git a/src/actinia_example_plugin/api/project_helloworld.py b/src/actinia_example_plugin/api/project_helloworld.py
new file mode 100644
index 0000000..247d05d
--- /dev/null
+++ b/src/actinia_example_plugin/api/project_helloworld.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+Hello World class
+"""
+
+__license__ = "GPLv3"
+__author__ = "Anika Weinmann"
+__copyright__ = "Copyright 2024 mundialis GmbH & Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
+
+
+from typing import ClassVar
+
+from actinia_core.models.response_models import SimpleResponseModel
+from actinia_core.rest.base.deprecated_locations import (
+ location_deprecated_decorator,
+)
+from flask import jsonify, make_response, request
+from flask.wrappers import Response
+from flask_restful_swagger_2 import Resource, swagger
+
+from actinia_example_plugin.apidocs import project_helloworld
+from actinia_example_plugin.core.example import transform_input
+
+
+class ProjectHelloWorld(Resource):
+ """Returns 'Hello world with project/location!'."""
+
+ decorators: ClassVar[list] = []
+
+ # Add decorators for deprecated GRASS GIS locations
+ decorators.append(location_deprecated_decorator)
+
+ def __init__(self) -> None:
+ """Project hello world class initialisation."""
+ self.msg = "Project: Hello world!"
+
+ @swagger.doc(project_helloworld.describe_project_hello_world_get_docs)
+ def get(self, project_name: str) -> Response:
+ """Get 'Hello world!' as answer string."""
+ msg = f"{self.msg} {project_name}"
+ return make_response(
+ jsonify(
+ SimpleResponseModel(
+ status="200",
+ message=msg,
+ ),
+ ),
+ 200,
+ )
+
+ @swagger.doc(project_helloworld.describe_project_hello_world_post_docs)
+ def post(self, project_name: str) -> Response:
+ """Hello World post method with name from postbody."""
+ req_data = request.get_json(force=True)
+ if isinstance(req_data, dict) is False or "name" not in req_data:
+ return make_response("Missing name in JSON content", 400)
+ name = req_data["name"]
+ msg = f"{self.msg} {transform_input(name)} {project_name}"
+
+ return make_response(
+ jsonify(
+ SimpleResponseModel(
+ status="200",
+ message=msg,
+ ),
+ ),
+ 200,
+ )
diff --git a/src/actinia_example_plugin/apidocs/__init__.py b/src/actinia_example_plugin/apidocs/__init__.py
index e69de29..fec4e58 100644
--- a/src/actinia_example_plugin/apidocs/__init__.py
+++ b/src/actinia_example_plugin/apidocs/__init__.py
@@ -0,0 +1,4 @@
+"""actinia-example-plguin API DOCs part of package.
+
+This part provides the API DOCs part of the actinia-example-plugin.
+"""
diff --git a/src/actinia_example_plugin/apidocs/helloworld.py b/src/actinia_example_plugin/apidocs/helloworld.py
index ece3424..b016bfb 100644
--- a/src/actinia_example_plugin/apidocs/helloworld.py
+++ b/src/actinia_example_plugin/apidocs/helloworld.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,15 +20,14 @@
__license__ = "GPLv3"
__author__ = "Anika Weinmann"
__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
from actinia_example_plugin.model.response_models import (
SimpleStatusCodeResponseModel,
)
-
-describeHelloWorld_get_docs = {
+describe_hello_world_get_docs = {
# "summary" is taken from the description of the get method
"tags": ["example"],
"description": "Hello World example",
@@ -38,11 +35,11 @@
"200": {
"description": "This response returns the string 'Hello World!'",
"schema": SimpleStatusCodeResponseModel,
- }
+ },
},
}
-describeHelloWorld_post_docs = {
+describe_hello_world_post_docs = {
# "summary" is taken from the description of the get method
"tags": ["example"],
"description": "Hello World example with name",
@@ -61,7 +58,7 @@
"type": "string",
"description": "detailed message",
"example": "Missing name in JSON content",
- }
+ },
},
},
},
diff --git a/src/actinia_example_plugin/apidocs/project_helloworld.py b/src/actinia_example_plugin/apidocs/project_helloworld.py
new file mode 100644
index 0000000..699d930
--- /dev/null
+++ b/src/actinia_example_plugin/apidocs/project_helloworld.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+Hello World class
+"""
+
+__license__ = "GPLv3"
+__author__ = "Anika Weinmann"
+__copyright__ = "Copyright 2024 mundialis GmbH & Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
+
+
+from actinia_example_plugin.model.response_models import (
+ SimpleStatusCodeResponseModel,
+)
+
+describe_project_hello_world_get_docs = {
+ # "summary" is taken from the description of the get method
+ "tags": ["example"],
+ "description": "Project Hello World example",
+ "parameters": [
+ {
+ "name": "project_name",
+ "description": "The project name that contains the data that "
+ "should be processed",
+ "required": True,
+ "in": "path",
+ "type": "string",
+ "default": "nc_spm_08",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "This response returns the string 'Hello World!'",
+ "schema": SimpleStatusCodeResponseModel,
+ },
+ },
+}
+
+describe_project_hello_world_post_docs = {
+ # "summary" is taken from the description of the get method
+ "tags": ["example"],
+ "description": "Project Hello World example with name",
+ "parameters": [
+ {
+ "name": "project_name",
+ "description": "The project name that contains the data that "
+ "should be processed",
+ "required": True,
+ "in": "path",
+ "type": "string",
+ "default": "nc_spm_08",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "This response returns the string 'Hello World "
+ "NAME!'",
+ "schema": SimpleStatusCodeResponseModel,
+ },
+ "400": {
+ "description": "This response returns a detail error message",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string",
+ "description": "detailed message",
+ "example": "Missing name in JSON content",
+ },
+ },
+ },
+ },
+ },
+}
diff --git a/src/actinia_example_plugin/core/__init__.py b/src/actinia_example_plugin/core/__init__.py
index e69de29..d7f8813 100644
--- a/src/actinia_example_plugin/core/__init__.py
+++ b/src/actinia_example_plugin/core/__init__.py
@@ -0,0 +1,4 @@
+"""actinia-example-plguin core part of package.
+
+This part provides the core part of the actinia-example-plugin.
+"""
diff --git a/src/actinia_example_plugin/core/example.py b/src/actinia_example_plugin/core/example.py
index 55d82e7..eb9f38c 100644
--- a/src/actinia_example_plugin/core/example.py
+++ b/src/actinia_example_plugin/core/example.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,10 +20,17 @@
__license__ = "GPLv3"
__author__ = "Anika Weinmann"
__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
+
+
+def transform_input(inp: str) -> str:
+ """Return a transformed string as example core function.
+
+ Args:
+ inp (str): Input string to transform
+ Returns:
+ (str) transformed string
-def transform_input(inp):
- """Example core function"""
- out = f"Hello world {inp.upper()}!"
- return out
+ """
+ return f"Hello world {inp.upper()}!"
diff --git a/src/actinia_example_plugin/endpoints.py b/src/actinia_example_plugin/endpoints.py
index 2bdbfdc..6fee3d9 100644
--- a/src/actinia_example_plugin/endpoints.py
+++ b/src/actinia_example_plugin/endpoints.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,15 +19,45 @@
__license__ = "GPLv3"
__author__ = "Carmen Tawalika, Anika Weinmann"
-__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__copyright__ = "Copyright 2022-2024 mundialis GmbH & Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
+from actinia_core.endpoints import get_endpoint_class_name
+from flask_restful_swagger_2 import Api
from actinia_example_plugin.api.helloworld import HelloWorld
+from actinia_example_plugin.api.project_helloworld import ProjectHelloWorld
+
+
+def create_project_endpoints(
+ apidoc: Api,
+ projects_url_part: str = "projects",
+) -> None:
+ """Add resources with "project" inside the endpoint url to the api.
+
+ Args:
+ apidoc (Api): Flask api
+ projects_url_part (str): The name of the projects inside the endpoint
+ URL; to add deprecated location endpoints set
+ it to "locations"
+
+ """
+ apidoc.add_resource(
+ ProjectHelloWorld,
+ f"/{projects_url_part}/",
+ endpoint=get_endpoint_class_name(ProjectHelloWorld, projects_url_part),
+ )
# endpoints loaded if run as actinia-core plugin as well as standalone app
-def create_endpoints(flask_api):
+def create_endpoints(flask_api: Api) -> None:
+ """Create plugin endpoints."""
apidoc = flask_api
apidoc.add_resource(HelloWorld, "/helloworld")
+
+ # add deprecated location endpoints
+ create_project_endpoints(apidoc, projects_url_part="locations")
+
+ # add project endpoints
+ create_project_endpoints(apidoc, projects_url_part="projects")
diff --git a/src/actinia_example_plugin/model/__init__.py b/src/actinia_example_plugin/model/__init__.py
index e69de29..41b92c6 100644
--- a/src/actinia_example_plugin/model/__init__.py
+++ b/src/actinia_example_plugin/model/__init__.py
@@ -0,0 +1,4 @@
+"""actinia-example-plguin model part of package.
+
+This part provides the model part of the actinia-example-plugin.
+"""
diff --git a/src/actinia_example_plugin/model/response_models.py b/src/actinia_example_plugin/model/response_models.py
index 92c9621..dd2ab76 100644
--- a/src/actinia_example_plugin/model/response_models.py
+++ b/src/actinia_example_plugin/model/response_models.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,8 +20,10 @@
__license__ = "GPLv3"
__author__ = "Anika Weinmann"
__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
+
+from typing import ClassVar
from flask_restful_swagger_2 import Schema
@@ -31,8 +31,8 @@
class SimpleStatusCodeResponseModel(Schema):
"""Simple response schema to inform about status."""
- type = "object"
- properties = {
+ type: str = "object"
+ properties: ClassVar[dict] = {
"status": {
"type": "number",
"description": "The status code of the request.",
@@ -42,10 +42,11 @@ class SimpleStatusCodeResponseModel(Schema):
"description": "A short message to describes the status",
},
}
- required = ["status", "message"]
+ required: ClassVar[list[str]] = ["status", "message"]
-simpleResponseExample = SimpleStatusCodeResponseModel(
- status=200, message="success"
+simple_response_example = SimpleStatusCodeResponseModel(
+ status=200,
+ message="success",
)
-SimpleStatusCodeResponseModel.example = simpleResponseExample
+SimpleStatusCodeResponseModel.example = simple_response_example
diff --git a/src/actinia_example_plugin/wsgi.py b/src/actinia_example_plugin/wsgi.py
index 64ae9ed..173f4ed 100644
--- a/src/actinia_example_plugin/wsgi.py
+++ b/src/actinia_example_plugin/wsgi.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +18,4 @@
__license__ = "GPLv3"
__author__ = "Carmen Tawalika, Anika Weinmann"
__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
-
-from actinia_example_plugin.main import app as application
+__maintainer__ = "mundialis GmbH & Co. KG"
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29..95e6a85 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1,4 @@
+"""Tests of the actinia-example-plguin.
+
+This package part provides the tests of the actinia-example-plugin.
+"""
diff --git a/tests/conftest.py b/tests/conftest.py
index 94c17d1..dc384d9 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,6 +20,6 @@
__license__ = "GPLv3"
__author__ = "Carmen Tawalika"
__copyright__ = "Copyright 2018-2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
# import pytest
diff --git a/tests/integrationtests/__init__.py b/tests/integrationtests/__init__.py
index e69de29..1f026a7 100644
--- a/tests/integrationtests/__init__.py
+++ b/tests/integrationtests/__init__.py
@@ -0,0 +1,4 @@
+"""Integration tests of the actinia-example-plguin.
+
+This package part provides the integration tests of the actinia-example-plugin.
+"""
diff --git a/tests/integrationtests/test_helloworld.py b/tests/integrationtests/test_helloworld.py
index 25eee51..9369c94 100644
--- a/tests/integrationtests/test_helloworld.py
+++ b/tests/integrationtests/test_helloworld.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,70 +20,84 @@
__license__ = "GPLv3"
__author__ = "Anika Weinmann"
__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
import json
+
import pytest
+from actinia_api import URL_PREFIX
from flask import Response
-from actinia_api import URL_PREFIX
+from tests.testsuite import ActiniaTestCase
-from ..testsuite import ActiniaTestCase
+STATUS_CODE_200 = 200
+STATUS_CODE_400 = 400
class ActiniaHelloWorldTest(ActiniaTestCase):
+ """Actinia hello world test class for hello world endpoint."""
+
@pytest.mark.integrationtest
- def test_get_helloworld(self):
- """Test the get method of the /helloworld endpoint"""
- resp = self.app.get(URL_PREFIX + "/helloworld")
+ def test_get_helloworld(self) -> None:
+ """Test the get method of the /helloworld endpoint."""
+ resp = self.app.get(f"{URL_PREFIX}/helloworld")
assert isinstance(
- resp, Response
+ resp,
+ Response,
), "The response is not of type Response"
- assert resp.status_code == 200, "The status code is not 200"
+ assert (
+ resp.status_code == STATUS_CODE_200
+ ), f"The status code is not {STATUS_CODE_200}"
assert hasattr(resp, "json"), "The response has no attribute 'json'"
- assert "message" in resp.json, (
- "There is no 'message' inside the " "response"
- )
- assert resp.json["message"] == "Hello world!", (
- "The response message" " is wrong"
- )
+ assert (
+ "message" in resp.json
+ ), "There is no 'message' inside the response"
+ assert (
+ resp.json["message"] == "Hello world!"
+ ), "The response message is wrong"
@pytest.mark.integrationtest
- def test_post_helloworld(self):
- """Test the post method of the /helloworld endpoint"""
+ def test_post_helloworld(self) -> None:
+ """Test the post method of the /helloworld endpoint."""
postbody = {"name": "test"}
resp = self.app.post(
- URL_PREFIX + "/helloworld",
+ f"{URL_PREFIX}/helloworld",
headers=self.user_auth_header,
data=json.dumps(postbody),
content_type="application/json",
)
assert isinstance(
- resp, Response
+ resp,
+ Response,
), "The response is not of type Response"
- assert resp.status_code == 200, "The status code is not 200"
+ assert (
+ resp.status_code == STATUS_CODE_200
+ ), f"The status code is not {STATUS_CODE_200}"
assert hasattr(resp, "json"), "The response has no attribute 'json'"
- assert "message" in resp.json, (
- "There is no 'message' inside the " "response"
- )
- assert resp.json["message"] == "Hello world TEST!", (
- "The response " "message is wrong"
- )
+ assert (
+ "message" in resp.json
+ ), "There is no 'message' inside the response"
+ assert (
+ resp.json["message"] == "Hello world TEST!"
+ ), "The response message is wrong"
@pytest.mark.integrationtest
- def test_post_helloworld_error(self):
- """Test the post method of the /helloworld endpoint"""
+ def test_post_helloworld_error(self) -> None:
+ """Test the post method of the /helloworld endpoint."""
postbody = {"namee": "test"}
resp = self.app.post(
- URL_PREFIX + "/helloworld",
+ f"{URL_PREFIX}/helloworld",
headers=self.user_auth_header,
data=json.dumps(postbody),
content_type="application/json",
)
assert isinstance(
- resp, Response
+ resp,
+ Response,
), "The response is not of type Response"
- assert resp.status_code == 400, "The status code is not 400"
+ assert (
+ resp.status_code == STATUS_CODE_400
+ ), f"The status code is not {STATUS_CODE_400}"
assert resp.data == b"Missing name in JSON content"
diff --git a/tests/integrationtests/test_projecthelloworld.py b/tests/integrationtests/test_projecthelloworld.py
new file mode 100644
index 0000000..7ed08a2
--- /dev/null
+++ b/tests/integrationtests/test_projecthelloworld.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+Hello World test
+"""
+
+__license__ = "GPLv3"
+__author__ = "Anika Weinmann"
+__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
+
+
+import json
+
+import pytest
+from actinia_api import URL_PREFIX
+from flask import Response
+
+from tests.testsuite import ActiniaTestCase
+
+STATUS_CODE_200 = 200
+STATUS_CODE_400 = 400
+
+
+class ActiniaHelloWorldTest(ActiniaTestCase):
+ """Actinia hello world test class for hello world endpoint."""
+
+ @pytest.mark.integrationtest
+ def test_get_helloworld(self) -> None:
+ """Test the get method of the /projects/ endpoint."""
+ resp = self.app.get(f"{URL_PREFIX}/{self.project_url_part}/project1")
+
+ assert isinstance(
+ resp,
+ Response,
+ ), "The response is not of type Response"
+ assert (
+ resp.status_code == STATUS_CODE_200
+ ), f"The status code is not {STATUS_CODE_200}"
+ assert hasattr(resp, "json"), "The response has no attribute 'json'"
+ assert (
+ "message" in resp.json
+ ), "There is no 'message' inside the response"
+ assert (
+ resp.json["message"] == "Project: Hello world! project1"
+ ), "The response message is wrong"
+
+ @pytest.mark.integrationtest
+ def test_post_helloworld(self) -> None:
+ """Test the post method of the /projects/ endpoint."""
+ postbody = {"name": "test"}
+ resp = self.app.post(
+ f"{URL_PREFIX}/{self.project_url_part}/project1",
+ headers=self.user_auth_header,
+ data=json.dumps(postbody),
+ content_type="application/json",
+ )
+ assert isinstance(
+ resp,
+ Response,
+ ), "The response is not of type Response"
+ assert (
+ resp.status_code == STATUS_CODE_200
+ ), f"The status code is not {STATUS_CODE_200}"
+ assert hasattr(resp, "json"), "The response has no attribute 'json'"
+ assert (
+ "message" in resp.json
+ ), "There is no 'message' inside the response"
+ assert (
+ resp.json["message"] == "Hello world TEST! project1"
+ ), "The response message is wrong"
+
+ @pytest.mark.integrationtest
+ def test_post_helloworld_error(self) -> None:
+ """Test the post method of the /projects/ endpoint."""
+ postbody = {"namee": "test"}
+ resp = self.app.post(
+ f"{URL_PREFIX}/{self.project_url_part}/project1",
+ headers=self.user_auth_header,
+ data=json.dumps(postbody),
+ content_type="application/json",
+ )
+ assert isinstance(
+ resp,
+ Response,
+ ), "The response is not of type Response"
+ assert (
+ resp.status_code == STATUS_CODE_400
+ ), f"The status code is not {STATUS_CODE_400}"
+ assert resp.data == b"Missing name in JSON content"
diff --git a/tests/test_resource_base.py b/tests/test_resource_base.py
index a73496a..f8f0e0b 100644
--- a/tests/test_resource_base.py
+++ b/tests/test_resource_base.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2016-2022 mundialis GmbH & Co. KG
+"""Copyright (c) 2016-2022 mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,19 +17,21 @@
Tests: Actinia resource test case base
"""
+from __future__ import annotations
+
import atexit
import base64
import os
import signal
+import tempfile
import time
+from pathlib import Path
-from werkzeug.datastructures import Headers
-
-from actinia_core.testsuite import ActiniaTestCaseBase, URL_PREFIX
-from actinia_core.core.common.user import ActiniaUser
from actinia_core.core.common.config import global_config
+from actinia_core.core.common.user import ActiniaUser
from actinia_core.endpoints import create_endpoints
-
+from actinia_core.testsuite import ActiniaTestCaseBase
+from werkzeug.datastructures import Headers
__license__ = "GPLv3"
__author__ = "Sören Gebbert, Anika Weinmann"
@@ -43,21 +43,22 @@
# Create endpoints
create_endpoints()
-redis_pid = None
-server_test = False
-custom_actinia_cfg = False
+REDIS_PID = None
+SERVER_TEST = False
+CUSTOM_ACTINIA_CFG = False
# If this environmental variable is set, then a real http request will be send
# instead of using the flask test_client.
if "ACTINIA_SERVER_TEST" in os.environ:
- server_test = bool(os.environ["ACTINIA_SERVER_TEST"])
+ SERVER_TEST = bool(os.environ["ACTINIA_SERVER_TEST"])
# Set this variable to use a actinia config file in a docker container
if "ACTINIA_CUSTOM_TEST_CFG" in os.environ:
- custom_actinia_cfg = str(os.environ["ACTINIA_CUSTOM_TEST_CFG"])
+ CUSTOM_ACTINIA_CFG = str(os.environ["ACTINIA_CUSTOM_TEST_CFG"])
-def setup_environment():
- global redis_pid
+def setup_environment() -> None:
+ """Setuo test environment."""
+ global REDIS_PID
# Set the port to the test redis server
global_config.REDIS_SERVER_SERVER = "localhost"
global_config.REDIS_SERVER_PORT = 7000
@@ -72,28 +73,28 @@ def setup_environment():
global_config.GRASS_GIS_START_SCRIPT = "/usr/local/bin/grass"
# global_config.GRASS_DATABASE= "/usr/local/grass_test_db"
# global_config.GRASS_DATABASE = "%s/actinia/grass_test_db" % home
- global_config.GRASS_TMP_DATABASE = "/tmp"
+ global_config.GRASS_TMP_DATABASE = tempfile.TemporaryDirectory().name
+ Path(global_config.GRASS_TMP_DATABASE).mkdir(parents=True)
- if server_test is False and custom_actinia_cfg is False:
+ if SERVER_TEST is False and CUSTOM_ACTINIA_CFG is False:
# Start the redis server for user and logging management
- redis_pid = os.spawnl(
+ REDIS_PID = os.spawnl(
os.P_NOWAIT,
"/usr/bin/redis-server",
"common/redis.conf",
- "--port %i" % global_config.REDIS_SERVER_PORT,
+ f"--port {global_config.REDIS_SERVER_PORT}",
)
time.sleep(1)
- if server_test is False and custom_actinia_cfg is not False:
- global_config.read(custom_actinia_cfg)
+ if SERVER_TEST is False and CUSTOM_ACTINIA_CFG is not False:
+ global_config.read(CUSTOM_ACTINIA_CFG)
-def stop_redis():
- if server_test is False:
- global redis_pid
- # Kill th redis server
- if redis_pid is not None:
- os.kill(redis_pid, signal.SIGTERM)
+def stop_redis() -> None:
+ """Stop redis server."""
+ # Kill th redis server
+ if SERVER_TEST is False and REDIS_PID is not None:
+ os.kill(REDIS_PID, signal.SIGTERM)
# Register the redis stop function
@@ -103,24 +104,28 @@ def stop_redis():
class ActiniaResourceTestCaseBase(ActiniaTestCaseBase):
+ """Actinia resource test case base class."""
+
@classmethod
def create_user(
cls,
- name="guest",
- role="guest",
- group="group",
- password="abcdefgh",
- accessible_datasets=None,
- process_num_limit=1000,
- process_time_limit=6000,
- accessible_modules=None,
- ):
- auth = bytes("%s:%s" % (name, password), "utf-8")
+ name: str = "guest",
+ role: str = "guest",
+ group: str = "group",
+ password: str = "abcdefgh",
+ accessible_datasets: dict[str, list | None] | None = None,
+ process_num_limit: int = 1000,
+ process_time_limit: int = 6000,
+ accessible_modules: list[str] | None = None,
+ ) -> (str, str, Headers()):
+ """Create actinia user."""
+ auth = bytes(f"{name}:{password}", "utf-8")
# We need to create an HTML basic authorization header
cls.auth_header[role] = Headers()
cls.auth_header[role].add(
- "Authorization", "Basic " + base64.b64encode(auth).decode()
+ "Authorization",
+ f"Basic {base64.b64encode(auth).decode()}",
)
# Make sure the user database is empty
diff --git a/tests/testsuite.py b/tests/testsuite.py
index 3d4b576..d59fad7 100644
--- a/tests/testsuite.py
+++ b/tests/testsuite.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,36 +17,50 @@
Base class for GRASS GIS REST API tests
"""
+from __future__ import annotations
+
__license__ = "GPLv3"
__author__ = "Carmen Tawalika, Sören Gebbert"
__copyright__ = "Copyright 2018-2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
-
+__maintainer__ = "mundialis GmbH & Co. KG"
import base64
import unittest
+from typing import ClassVar
import pwgen
-from werkzeug.datastructures import Headers
-
from actinia_core.core.common import redis_interface
from actinia_core.core.common.app import flask_app
from actinia_core.core.common.config import global_config
from actinia_core.core.common.user import ActiniaUser
-from actinia_core.models.response_models import ProcessingResponseModel
+from actinia_core.version import init_versions
+from werkzeug.datastructures import Headers
class ActiniaTestCase(unittest.TestCase):
+ """Actinia test case class."""
+
# guest = None
# admin = None
# superadmin = None
- user = None
- auth_header = {}
- users_list = []
-
- def setUp(self):
- """Overwrites method setUp from unittest.TestCase class"""
-
+ user: str = None
+ auth_header: ClassVar[dict] = {}
+ users_list: ClassVar[list[str]] = []
+ project_url_part: str = "projects"
+
+ # set project_url_part to "locations" if GRASS GIS version < 8.4
+ init_versions()
+ from actinia_core.version import G_VERSION
+
+ grass_version_s: str = G_VERSION["version"]
+ grass_version: ClassVar[list[int]] = [
+ int(item) for item in grass_version_s.split(".")[:2]
+ ]
+ if grass_version < [8, 4]:
+ project_url_part = "locations"
+
+ def setUp(self) -> None:
+ """Overwrite method setUp from unittest.TestCase class."""
self.app_context = flask_app.app_context()
self.app_context.push()
# from http://flask.pocoo.org/docs/0.12/api/#flask.Flask.test_client:
@@ -75,14 +87,14 @@ def setUp(self):
# create test user for roles user (more to come)
accessible_datasets = {
- "nc_spm_08": ["PERMANENT", "user1", "modis_lst"]
+ "nc_spm_08": ["PERMANENT", "user1", "modis_lst"],
}
password = pwgen.pwgen()
(
self.user_id,
self.user_group,
self.user_auth_header,
- ) = self.createUser(
+ ) = self.create_user(
name="user",
role="user",
password=password,
@@ -94,7 +106,7 @@ def setUp(self):
self.restricted_user_id,
self.restricuted_user_group,
self.restricted_user_auth_header,
- ) = self.createUser(
+ ) = self.create_user(
name="user2",
role="user",
password=password,
@@ -107,7 +119,7 @@ def setUp(self):
self.admin_id,
self.admin_group,
self.admin_auth_header,
- ) = self.createUser(
+ ) = self.create_user(
name="admin",
role="admin",
password=password,
@@ -121,9 +133,8 @@ def setUp(self):
# create_process_queue
# create_process_queue(config=global_config)
- def tearDown(self):
- """Overwrites method tearDown from unittest.TestCase class"""
-
+ def tearDown(self) -> None:
+ """Overwrite method tearDown from unittest.TestCase class."""
self.app_context.pop()
# remove test user; disconnect redis
@@ -131,22 +142,24 @@ def tearDown(self):
user.delete()
redis_interface.disconnect()
- def createUser(
+ def create_user(
self,
- name="guest",
- role="guest",
- group="group",
- password="abcdefgh",
- accessible_datasets=None,
- accessible_modules=global_config.MODULE_ALLOW_LIST,
- process_num_limit=1000,
- process_time_limit=6000,
- ):
- auth = bytes("%s:%s" % (name, password), "utf-8")
+ name: str = "guest",
+ role: str = "guest",
+ group: str = "group",
+ password: str = "abcdefgh",
+ accessible_datasets: dict[str, list | None] | None = None,
+ process_num_limit: int = 1000,
+ process_time_limit: int = 6000,
+ accessible_modules: list[str] = global_config.MODULE_ALLOW_LIST,
+ ) -> (str, str, Headers()):
+ """Create actinia user."""
+ auth = bytes(f"{name}:{password}", "utf-8")
# We need to create an HTML basic authorization header
self.auth_header[role] = Headers()
self.auth_header[role].add(
- "Authorization", "Basic " + base64.b64encode(auth).decode()
+ "Authorization",
+ f"Basic {base64.b64encode(auth).decode()}",
)
# Make sure the user database is empty
@@ -170,17 +183,18 @@ def createUser(
return name, group, self.auth_header[role]
-def check_started_process(testCase, resp):
- """Checks response of started process - TODO: can be enhanced"""
- if type(resp.json["process_results"]) == dict:
- resp.json["process_results"] = str(resp.json["process_results"])
- resp_class = ProcessingResponseModel(**resp.json)
- assert resp_class["status"] == "accepted"
- status_url = resp_class["urls"]["status"]
-
- # poll status_url
- # TODO: status stays in accepted
- status_resp = testCase.app.get(
- status_url, headers=testCase.user_auth_header
- )
- assert status_resp.json["urls"]["status"] == status_url
+# def check_started_process(test_case: , resp: ) -> None:
+# """Checks response of started process - TODO: can be enhanced."""
+# if isinstance(resp.json["process_results"], dict):
+# resp.json["process_results"] = str(resp.json["process_results"])
+# resp_class = ProcessingResponseModel(**resp.json)
+# assert resp_class["status"] == "accepted"
+# status_url = resp_class["urls"]["status"]
+
+# # poll status_url
+# # TODO: status stays in accepted
+# status_resp = test_case.app.get(
+# status_url,
+# headers=test_case.user_auth_header,
+# )
+# assert status_resp.json["urls"]["status"] == status_url
diff --git a/tests/unittests/__init__.py b/tests/unittests/__init__.py
index e69de29..07c2383 100644
--- a/tests/unittests/__init__.py
+++ b/tests/unittests/__init__.py
@@ -0,0 +1,4 @@
+"""Unittests of the actinia-example-plguin.
+
+This package part provides the unittests of the actinia-example-plugin.
+"""
diff --git a/tests/unittests/test_transformation.py b/tests/unittests/test_transformation.py
index f7f703a..df89b40 100644
--- a/tests/unittests/test_transformation.py
+++ b/tests/unittests/test_transformation.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Copyright (c) 2018-present mundialis GmbH & Co. KG
+"""Copyright (c) 2018-present mundialis GmbH & Co. KG.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,18 +20,19 @@
__license__ = "GPLv3"
__author__ = "Anika Weinmann"
__copyright__ = "Copyright 2022 mundialis GmbH & Co. KG"
-__maintainer__ = "mundialis GmbH % Co. KG"
+__maintainer__ = "mundialis GmbH & Co. KG"
import pytest
+
from actinia_example_plugin.core.example import transform_input
@pytest.mark.unittest
@pytest.mark.parametrize(
- "inp,ref_out",
+ ("inp", "ref_out"),
[("test", "Hello world TEST!"), ("bla23", "Hello world BLA23!")],
)
-def test_transform_input(inp, ref_out):
+def test_transform_input(inp: str, ref_out: str) -> None:
"""Test for tranform_input function."""
out = transform_input(inp)
assert out == ref_out, f"Wrong result from transform_input for {inp}"
diff --git a/tests_with_redis.sh b/tests_with_redis.sh
old mode 100644
new mode 100755
index 57405bf..26b66a6
--- a/tests_with_redis.sh
+++ b/tests_with_redis.sh
@@ -10,14 +10,14 @@ webhook-server --host "0.0.0.0" --port "5005" &
sleep 10
# run tests
-echo $ACTINIA_CUSTOM_TEST_CFG
-echo $DEFAULT_CONFIG_PATH
+echo "${ACTINIA_CUSTOM_TEST_CFG}"
+echo "${DEFAULT_CONFIG_PATH}"
-if [ "$1" == "dev" ]
+if [ "$1" = "dev" ]
then
echo "Executing only 'dev' tests ..."
python3 setup.py test --addopts "-m dev"
-elif [ "$1" == "integrationtest" ]
+elif [ "$1" = "integrationtest" ]
then
python3 setup.py test --addopts "-m 'integrationtest'"
else