diff --git a/.circleci/config.yml b/.circleci/config.yml index a1aef42e..4cc6f9a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,9 +1,35 @@ version: 2.1 +##################### +####### Orbs ######## +##################### orbs: + # Currently docker-publish orb is used to build the docker image and not publish it, + # Therefor the user and password environment variables does not need to be set. docker-publish: circleci/docker-publish@0.1.7 + + + +##################### +##### Executors ##### +##################### executors: + # Used for running one-time docker images. + # Currnet image (201808-01) runs docker 18.06.0-ce and docker-compose 1.22.0. + # https://circleci.com/docs/2.0/configuration-reference/#available-machine-images + machine-docker: + machine: + image: "circleci/classic:201808-01" + working_directory: ~/workspace + + # Used for executing npm modules. + node-docker: + docker: + - image: "circleci/node:lts" + working_directory: ~/workspace + + # Used for starting python programs. python-docker: docker: - image: "circleci/python:3.7.3-stretch" @@ -11,46 +37,64 @@ executors: environment: TZ: "Asia/Jerusalem" - node-docker: - docker: - - image: "circleci/node:lts" - working_directory: ~/workspace + + +##################### +##### Commands ###### +##################### commands: - install-dockerlint: - description: Download and install the dockerlint linter + ################################################################## + ### Commands for creating environments and save/restore caches ### + ################################################################## + + # Docker images are pulled here and saved to cache. + # Use with the machine-docker executer as preparation before running one-time docker containers. + prepare-machine: + description: Prepare the virtual machine steps: - restore_cache: keys: - - v1-switcher-webapi-node-{{ .Branch }}-{{ checksum "Dockerfile" }}-{{ checksum "shellscripts/dockerlint-verify.sh" }} + - v1-switcher-webapi-machine-{{ .Branch }}-{{ checksum "Dockerfile" }}-{{ checksum ".dockerignore" }} - run: - name: Install dockerlint 0.3.11 - command: bash shellscripts/dockerlint-download-prepare.sh 0.3.11 + name: Download docker images and save to cache + command: | + filename=docker-cache/saved_images.tar + if [[ -f "$filename" ]]; then docker load < "$filename"; fi + mkdir -p docker-cache + docker pull hadolint/hadolint:v1.16.3 + docker pull koalaman/shellcheck:v0.6.0 + docker save -o "$filename" hadolint/hadolint:v1.16.3 koalaman/shellcheck:v0.6.0 + - save_cache: - key: v1-switcher-webapi-node-{{ .Branch }}-{{ checksum "Dockerfile" }}-{{ checksum "shellscripts/dockerlint-verify.sh" }} + key: v1-switcher-webapi-machine-{{ .Branch }}-{{ checksum "Dockerfile" }}-{{ checksum ".dockerignore" }} paths: - - dockerlint-0.3.11 - - install-prettier: - description: Install prettier with npm + - docker-cache + + # Node packages are installed here and then saved to cache. + # Use with the node-docker executer as preparation before executing npm modules. + prepare-node: + description: Prepare node environment steps: - restore_cache: keys: - - v1-switcher-webapi-npm-{{ .Branch }}-{{ checksum ".prettierrc.yml" }}-{{ checksum ".prettierignore" }} + - v1-switcher-webapi-node-{{ .Branch }}-{{ checksum ".prettierignore" }}-{{ checksum ".prettierrc.yml" }} - run: name: Install prettier - command: npm install prettier + command: npm install prettier@1.18.2 - save_cache: - key: v1-switcher-webapi-npm-{{ .Branch }}-{{ checksum ".prettierrc.yml" }}-{{ checksum ".prettierignore" }} + key: v1-switcher-webapi-node-{{ .Branch }}-{{ checksum ".prettierignore" }}-{{ checksum ".prettierrc.yml" }} paths: - ./node_modules - prepare-venv: - description: Prepare the virtual environment + # Pypi packages are installed here into a venv that is saved to cache. + # Use with the python-docker executer as preparaton before starting python programs. + prepare-python: + description: Prepare python's virtual environment steps: - restore_cache: keys: - - v1-switcher-webapi-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements_test.txt" }}-{{ checksum "requirements_constraints.txt" }} + - v1-switcher-webapi-python-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements_test.txt" }} - run: name: Create the venv, install all requirements and save to cache command: | @@ -59,56 +103,94 @@ commands: pip install -q --progress-bar off -r requirements.txt -c requirements_constraints.txt pip install -q --progress-bar off -r requirements_test.txt -c requirements_constraints.txt - save_cache: - key: v1-switcher-webapi-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements_test.txt" }}-{{ checksum "requirements_constraints.txt" }} + key: v1-switcher-webapi-python-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements_test.txt" }} paths: - ./venv - run-dockerlint: - description: Run dockerlint linter + ###################################################################################################### + ### Commands running inside the machine-docker executer after running the prepare-machine command ### + ###################################################################################################### + hadolint-docker: + description: Run one-time container for hadolint linter against Dockerfile + steps: + - run: + name: Run hadolint docker + command: docker run --rm -i hadolint/hadolint:v1.16.3 < Dockerfile + + shellcheck-docker: + description: Run one-time container for shellcheck linter against shellscripts/*.sh steps: - run: - name: Dockerlint - command: node dockerlint-0.3.11/bin/dockerlint.js -dpf Dockerfile + name: Run shellcheck docker + command: docker run --rm -v "$PWD/shellscripts/:/mnt/" koalaman/shellcheck:v0.6.0 $(ls -A1 shellscripts) + ###################################################################################################### + ###### Commands running inside the node-docker executer after running the prepare-node command ####### + ###################################################################################################### run-prettier: - description: Run prettier linter + description: Execute npm's prettier module to linting yaml and md files steps: - run: name: Prettier command: node_modules/prettier/bin-prettier.js --config .prettierrc.yml --ignore-path .prettierignore --check *.* **/*.* + ###################################################################################################### + #### Commands running inside the python-docker executer after running the prepare-python command ##### + ###################################################################################################### + run-docs-linters: + description: Run python doc8 linter against docs/source/*.rst + steps: + - run: + name: Doc8 + command: | + . venv/bin/activate + doc8 --config doc8.ini docs/source + + # Note, currently the _static folder is empty and therefore it's being checked-out + # Becouse of the -W argument, sphinx convert the "can't find _static folder" warning to and show-stopper error. + # Therefore the _static folder is created before running sphinx-build. + sphinx-build: + description: Verify build docs with python sphinx by building into temp directory + steps: + - run: + name: Sphinx-build + command: | + . venv/bin/activate + mkdir sphinxtemp + mkdir docs/source/_static + sphinx-build -W -b html -d sphinxtemp/doctrees docs/source sphinxtemp/html + rm -r sphinxtemp + check-security: - description: Run bandit tests + description: Run python bandit against py files steps: - run: name: Code-security command: | . venv/bin/activate - bandit -rvc bandit.yml pyscripts -l -ii + bandit -rvc bandit.yml pyscripts docs/source/conf.py -l -ii - run-linters: - description: Run flake8, pydocstyle and pydocstyle linters + run-code-linters: + description: Run python flake8 and pylint againt py files steps: - run: name: Linters command: | . venv/bin/activate - flake8 --statistics --count --doctests pyscripts/ - pydocstyle -v --count pyscripts/ - pycodestyle -v --statistics --show-pep8 --count pyscripts/ - pylint --disable fixme --rcfile pylintrc pyscripts/ + flake8 --statistics --count --doctests --verbose pyscripts/ docs/source/ + pylint --disable fixme --rcfile pylintrc pyscripts/ docs/source/conf.py run-mypy: - description: Run mypy type checking + description: Run mypy type-checking againt py files steps: - run: name: Mypy command: | . venv/bin/activate - mypy --config-file mypy.ini pyscripts/ + mypy --config-file mypy.ini pyscripts/ docs/source/conf.py run-tests-with-coverage: - description: Run pytest test cases with coverage report + description: Run pytest test cases with coverage report against py files steps: - run: name: Pytest @@ -124,29 +206,67 @@ commands: - store_test_results: path: coverage_report + + + +##################### +####### Jobs ######## +##################### jobs: - dockerlint-job: + + ############################################# + ### Jobs for the docs-lint-build workflow ### + ############################################# + docs-linters-job: executor: - name: node-docker + name: python-docker + steps: + - checkout + - prepare-python + - run-docs-linters + + sphinx-build-job: + executor: + name: python-docker steps: - checkout - - install-dockerlint - - run-dockerlint + - prepare-python + - sphinx-build + ############################################# + ### Jobs for the code-lint-test workflow #### + ############################################# prettier-job: executor: name: node-docker steps: - checkout - - install-prettier + - prepare-node - run-prettier + shellcheck-docker-job: + executor: + name: machine-docker + steps: + - checkout + - prepare-machine + - shellcheck-docker + + hadolint-docker-job: + executor: + name: machine-docker + steps: + - checkout + - prepare-machine + - hadolint-docker + docker-check-and-build-job: executor: name: docker-publish/docker steps: - checkout - - setup_remote_docker + - setup_remote_docker: + docker_layer_caching: true - docker-publish/build: dockerfile: Dockerfile path: . @@ -158,31 +278,23 @@ jobs: name: python-docker steps: - checkout - - prepare-venv + - prepare-python - check-security - linters-job: + code-linters-job: executor: name: python-docker steps: - checkout - - prepare-venv - - run-linters - - pylint-job: - executor: - name: python-docker - steps: - - checkout - - prepare-venv - - run-pylint + - prepare-python + - run-code-linters mypy-job: executor: name: python-docker steps: - checkout - - prepare-venv + - prepare-python - run-mypy unittests-job: @@ -190,7 +302,7 @@ jobs: name: python-docker steps: - checkout - - prepare-venv + - prepare-python - run-tests-with-coverage coverage-reports-job: @@ -198,7 +310,7 @@ jobs: name: python-docker steps: - checkout - - prepare-venv + - prepare-python - attach_workspace: at: coverage_report/ - run: @@ -208,24 +320,45 @@ jobs: codecov --file coverage_report/coverage.xml python-codacy-coverage -r coverage_report/coverage.xml + + + +##################### +##### Workslows ##### +##################### workflows: version: "2" - build: + + # Lint and build the documantation site files + docs-lint-build: jobs: - - dockerlint-job + - docs-linters-job + - sphinx-build-job: + requires: + - docs-linters-job + + # Lint and the test the code, scripts and Dockerfile + code-lint-test: + jobs: - prettier-job - - docker-check-and-build-job: + - shellcheck-docker-job + + - hadolint-docker-job: requires: - - dockerlint-job - prettier-job + - shellcheck-docker-job + + - docker-check-and-build-job: + requires: + - hadolint-docker-job - security-job: requires: - docker-check-and-build-job - - linters-job: + - code-linters-job: requires: - docker-check-and-build-job @@ -236,7 +369,7 @@ workflows: - unittests-job: requires: - security-job - - linters-job + - code-linters-job - mypy-job - coverage-reports-job: diff --git a/.dockerignore b/.dockerignore index 69d16d78..81369d4f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,7 @@ .git .mypy_cache .tox +docs shellscripts .codecov.yml .coverage @@ -12,6 +13,7 @@ shellscripts .prettierignore .prettierrc.yml bandit.yml +doc8.ini Dockerfile Makefile mypy.ini diff --git a/.prettierignore b/.prettierignore index 8b45ed77..957a93e8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ -.git +.git* +docs* .tox* .coverage .coveragerc @@ -6,6 +7,7 @@ .env_vars .gitignore .prettierignore +.shellcheckrc Dockerfile Makefile pylintrc diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 00000000..66440593 --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,4 @@ +shellscripts/dockerlint-download-prepare.sh +shellscripts/dockerlint-verify.sh +shellscripts/push-docker-description.sh +shellscripts/shellcheck-verify.sh \ No newline at end of file diff --git a/doc8.ini b/doc8.ini new file mode 100644 index 00000000..3ba4c18d --- /dev/null +++ b/doc8.ini @@ -0,0 +1,5 @@ +[doc8] +allow-long-titles=1 +max-line-length=80 +extension=.rst +verbose=1 diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /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/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..76191810 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,16 @@ +# pylint: disable=invalid-name,redefined-builtin + +"""Configuration file for Sphinx Documentation Generator.""" + +project = 'SwitcherDockerizedWbapi' +copyright = "2019, Tomer Figenblat" +author = "Tomer Figenblat" +templates_path = ['_templates'] +html_theme = 'alabaster' +html_static_path = ['_static'] +language = 'en' + +with open('../../VERSION', 'r') as version_file: + version = version_file.readline() + +release = version diff --git a/docs/source/credits.rst b/docs/source/credits.rst new file mode 100644 index 00000000..ea3889e1 --- /dev/null +++ b/docs/source/credits.rst @@ -0,0 +1,16 @@ +Credits +******* + +This project was enabled by creating the aioswitcher_ pypi module, +initially created for use with the `home assistant component`_. + +.. _aioswitcher: https://pypi.org/project/aioswitcher/ +.. _home assistant component: https://www.home-assistant.io/components/switcher_kis + +Not this nor the aioswitcher project would have been able to happen without +the amazing work preformed by NightRang3r and AviadGolan in the +`Switcher-V2-Python`_ project. + +Thank you guys! + +.. _Switcher-V2-Python: https://github.com/NightRang3r/Switcher-V2-Python diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..163e3651 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,22 @@ +Welcome to switcher_webapi's documentation! +=========================================== + +.. toctree:: + :maxdepth: 3 + :caption: Contents: + + what + prerequisites + notes + install + usage + credits + license + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/source/install.rst b/docs/source/install.rst new file mode 100644 index 00000000..6d3361aa --- /dev/null +++ b/docs/source/install.rst @@ -0,0 +1,48 @@ +Install +******* + +.. code-block:: shell + + docker run -d -p 8000:8000 \ + -e CONF_DEVICE_IP_ADDR=192.168.100.157 \ + -e CONF_PHONE_ID=1234 \ + -e CONF_DEVICE_ID=ab1c2d \ + -e CONF_DEVICE_PASSWORD=12345678 \ + --name switcher_webapi tomerfi/switcher_webapi:latest" + + +You can also add another optional environment variable: + +.. code-block:: shell + + -e CONF_THROTTLE=5.0 + +for setting the throttle time between consecutive requests, +this is optional and the default value is **5.0**. + +Here's an example of running the container using *docker-compose* setting the +environment variables in a designated file. + +.. code-block:: yaml + + # docker-compose.yml + version: "3.7" + + services: + switcher_api: + image: tomerfi/switcher_webapi:latest + container_name: "switcher_webapi" + env_file: + - .env_vars + ports: + - 8000:8000 + restart: unless-stopped + +.. code-block:: ini + + # .env_vars + CONF_DEVICE_IP_ADDR=192.168.100.157 + CONF_PHONE_ID=1234 + CONF_DEVICE_ID=ab1c2d + CONF_DEVICE_PASSWORD=12345678 + CONF_THROTTLE=5.0 diff --git a/docs/source/license.rst b/docs/source/license.rst new file mode 100644 index 00000000..ba9f16fb --- /dev/null +++ b/docs/source/license.rst @@ -0,0 +1,24 @@ +License +******* + +MIT License + +Copyright (c) 2019 Tomer Figenblat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/source/notes.rst b/docs/source/notes.rst new file mode 100644 index 00000000..f9dc9f04 --- /dev/null +++ b/docs/source/notes.rst @@ -0,0 +1,14 @@ +Notes +***** + +.. hlist:: + :columns: 1 + + * If you don't want to be forced to restart the container if the device's ip address changes, please consider assigning the device with a static ip address. + * The Switcher-V2-Python repository is build with python 2.7. + * The aioswitcher_ was tested with the Switcher V2 device by myself and with the Switcher Touch device by the community. + * This project was intended for local usage, it's ok if you want to use it remotely, just make sure to take the proper security measures such as reverse proxy and ssl. + * The WebAPI has a throttle mechanism to prevent overfloating the device with frequent requests, it defaults to 5 seconds throttle time. + * Some users have been reporting lately about failures using the Switcher-V2-Python script after upgrading the device firmware to 3.0, please follow the relevant issues in the script repository before doing the same. + +.. _aioswitcher: https://pypi.org/project/aioswitcher/ diff --git a/docs/source/prerequisites.rst b/docs/source/prerequisites.rst new file mode 100644 index 00000000..8fd13e14 --- /dev/null +++ b/docs/source/prerequisites.rst @@ -0,0 +1,14 @@ +Prerequisites +************* +.. hlist:: + :columns: 1 + + * Install and configure your Switcher device. + * Collect the following information from the device's following NightRang3r instructions in the `Switcher-V2-Python`_ repository: + * ip_address + * phone_id + * device_id + * device_pass + * Install docker + +.. _Switcher-V2-Python: https://github.com/NightRang3r/Switcher-V2-Python diff --git a/docs/source/usage.rst b/docs/source/usage.rst new file mode 100644 index 00000000..ea34cdca --- /dev/null +++ b/docs/source/usage.rst @@ -0,0 +1,360 @@ +Usage +***** + +Once running, you can send REST requests towards the container. +With the exception of the *create_schedule* requests, +all the requests requiring input accepts it as a json body or in the form of +query parameters. + +/switcher/get_state +^^^^^^^^^^^^^^^^^^^ + +**Method:** *GET* + +**Request parameters:** *None* + +**Response body example:** + +.. code-block:: json + + { + "successful": true, + "state": "on", + "time_left": "00:47:25", + "auto_off": "01:30:00", + "power_consumption": 2669, + "electric_current": 12.3 + } + + +/switcher/turn_on +^^^^^^^^^^^^^^^^^ + +**Method:** *POST* + +**Request parameters:** + ++-------------+------------+-----------------------------------------+ +| Key | Required | Description | ++=============+============+=========================================+ +| **minutes** | *Optional* | turn on the device with an off timer of | +| | | 1-180 minutes. | ++-------------+------------+-----------------------------------------+ + + +**Request body example:** + +.. code-block:: json + + { + "minutes": "30" + } + +**Response body example:** + +.. code-block:: json + + { + "successful": true + } + +/switcher/turn_off +^^^^^^^^^^^^^^^^^^ + +**Method:** *POST* + +**Request parameters:** *None* + +**Response body example:** + +.. code-block:: json + + { + "successful": true + } + +/switcher/set_auto_shutdown +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Method:** *POST* + +**Request parameters:** + ++-------------+-------------+---------------------+ +| Key | Required | Description | ++=============+=============+=====================+ +| **hours** | *Mandatory* | hours value 1-3. | ++-------------+-------------+---------------------+ +| **minutes** | *Mandatory* | minutes value 0-59. | ++-------------+-------------+---------------------+ + +.. note:: + + The auto shutdown configuration value accept any total value of hours and minutes between 1 and 3 hours. + +**Request body example:** + +.. code-block:: json + + { + "hours": "1", + "minutes": "30" + } + +**Response body example:** + +.. code-block:: json + + { + "successful": true + } + +/switcher/set_device_name +^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Method:** *POST* + +**Request parameters:** + ++----------+-------------+-------------------------------------------------+ +| Key | Required | Description | ++==========+=============+=================================================+ +| **name** | *Mandatory* | device name, accepts length of 2-32 characters. | ++----------+-------------+-------------------------------------------------+ + +**Request body example:** + +.. code-block:: json + + { + "name": "my new device name" + } + +**Response body example:** + +.. code-block:: json + + { + "successful": true + } + +/switcher/get_schedules +^^^^^^^^^^^^^^^^^^^^^^^ + +**Method:** *GET* + +**Request parameters:** *None* + +**Response body example:** + +.. code-block:: json + + { + "successful": true, + "found_schedules": true, + "schedules": [ + { + "schedule_id": "0", + "enabled": true, + "recurring": true, + "days": [ + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + ], + "start_time": "17:30", + "end_time": "18:30", + "duration": "1:00:00", + "schedule_data": "0001fc01e871a35cf87fa35c", + "next_run": "Due next Tuesday at 17:30" + }, + { + "schedule_id": "1", + "enabled": true, + "recurring": true, + "days": ["Monday"], + "start_time": "17:00", + "end_time": "18:00", + "duration": "1:00:00", + "schedule_data": "0101020160a6c95c70b4c95c", + "next_run": "Due tommorow at 17:00" + } + ] + } + +.. note:: + + The *schedules* list can contain up to 8 schedules with the identifiers + of 0-7 representing the actual schedule slots on the device. + +/switcher/enable_schedule +^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Method:** *PATCH* + +**Request parameters:** + ++-------------------+-------------+-------------------------------------------+ +| Key | Required | Description | ++===================+=============+===========================================+ +| **schedule_data** | *Mandatory* | the *schedule_data* associated with the | +| | | chosen schedule. | +| | | | +| | | retrieved with */switcher/get_schedules*. | ++-------------------+-------------+-------------------------------------------+ + +**Request body example:** + +.. code-block:: json + + { + "schedule_data": "0101020160a6c95c70b4c95c" + } + +**Response body example:** + +.. code-block:: json + + { + "successful": true + } + +/switcher/disable_schedule +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Method:** *PATCH* + +**Request parameters:** + ++-------------------+-------------+-------------------------------------------+ +| Key | Required | Description | ++===================+=============+===========================================+ +| **schedule_data** | *Mandatory* | the *schedule_data* associated with the | +| | | chosen schedule. | +| | | | +| | | retrieved with */switcher/get_schedules*. | ++-------------------+-------------+-------------------------------------------+ + +**Request body example:** + +.. code-block:: json + + { + "schedule_data": "0101020160a6c95c70b4c95c" + } + +**Response body example:** + +.. code-block:: json + + { + "successful": true + } + +/switcher/delete_schedule +^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Method:** *DELETE* + +**Request parameters:** + ++-----------------+-------------+-------------------------------------------+ +| Key | Required | Description | ++=================+=============+===========================================+ +| **schedule_id** | *Mandatory* | the *schedule_id* associated with the | +| | | chosen schedule. | +| | | | +| | | retrieved with */switcher/get_schedules*. | ++-----------------+-------------+-------------------------------------------+ + +**Request body example:** + +.. code-block:: json + + { + "schedule_id": "2" + } + +**Response body example:** + +.. code-block:: json + + { + "successful": true + } + +/switcher/create_schedule +^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Method:** *PUT* + +**Request parameters:** + ++-------------------+-------------+------------------------------------------+ +| Key | Required | Description | ++===================+=============+==========================================+ +| **days** | *Mandatory* | list of days for the schedule to run in. | +| | | | +| | | (empty for non-recurring schedules). | ++-------------------+-------------+------------------------------------------+ +| **start_hours** | *Mandatory* | start time hours value 0-23. | ++-------------------+-------------+------------------------------------------+ +| **start_minutes** | *Mandatory* | start minutes value 0-59. | ++-------------------+-------------+------------------------------------------+ +| **stop_hours** | *Mandatory* | stop time hours value 0-23. | ++-------------------+-------------+------------------------------------------+ +| **stop_minutes** | *Mandatory* | stop minutes value 0-59. | ++-------------------+-------------+------------------------------------------+ + +**Request body example:** + +.. code-block:: json + + { + "days": ["Monday", "Wednesday", "Friday"], + "start_hours": "20", + "start_minutes": "30", + "stop_hours": "21", + "stop_minutes": "0" + } + +**Response body example:** + +.. code-block:: json + + { + "successful": true + } + +Possible values for the *days* list: + +.. hlist:: + + * Sunday + * Monday + * Tuesday + * Wednesday + * Thursday + * Friday + * Saturday + + +.. note:: + + Due to its complexity, the *create_schedule* request accepts its arguments + in the form of a json body only, query parameters will not be accepted. + +Exceptions +^^^^^^^^^^ + +Unless unhandled, all exceptions will return a json object in response body: + +.. code-block:: json + + { + "successful": false, + "message": "the error description" + } diff --git a/docs/source/what.rst b/docs/source/what.rst new file mode 100644 index 00000000..83c05976 --- /dev/null +++ b/docs/source/what.rst @@ -0,0 +1,17 @@ +What +**** + +An asynchronous sanic_ webapp running inside a `python docker image`_ +using uvloop_ as the event loop. +Used as a rest api wrapper for aioswitcher_. + +.. _sanic: https://pypi.org/project/sanic/ +.. _python docker image: https://hub.docker.com/_/python +.. _uvloop: https://pypi.org/project/uvloop/ +.. _aioswitcher: https://pypi.org/project/aioswitcher/ + +If you're using the `Switcher water heater`_ +and you want to wrap a rest api around it... +you came to the right place! + +.. _Switcher water heater: https://switcher.co.il/ diff --git a/requirements.txt b/requirements.txt index d49ca189..92e63a55 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ aioswitcher asyncio_throttle +sphinx==2.1.1 sanic uvloop diff --git a/requirements_test.txt b/requirements_test.txt index 519a1f0f..f9448a92 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -2,16 +2,18 @@ aiohttp==3.5.4 aioswitcher asynctest==0.13.0 asyncio_throttle -bandit==1.6.0 +bandit==1.6.1 beautifulsoup4==4.7.1 codecov==2.0.15 codacy-coverage==1.3.11 +doc8==0.8.0 flake8==3.7.7 flake8-docstrings==1.3.0 mypy==0.701 mypy-extensions==0.4.1 +Pygments==2.4.2 pylint==2.3.1 -pytest==4.6.2 +pytest==4.6.3 pytz==2019.1 pytest-cov==2.7.1 pytest-sanic==1.0.0 diff --git a/shellscripts/dockerlint-download-prepare.sh b/shellscripts/dockerlint-download-prepare.sh deleted file mode 100644 index 02a181a5..00000000 --- a/shellscripts/dockerlint-download-prepare.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -# dockerlint-download-prepare.sh -# -# The script downloads the source code for dockerlint from github, extracts it and prepare it for use. -# The script uses sudo, so if you're not entitled for that usage, you cannont run this script. -# The script uses wget for the download and npm and coffeescript for the build. -# To use the dockerlint js file after build, you need nodejs. - -display_usage() { - echo -e "please provide desired version as the first and only argument.\n" - echo "usage: $0 0.3.11" -} - -if [ -z "$1" ] -then - display_usage - exit 1 -fi - -if [ -d "dockerlint-$1" ] -then - echo "dockerlint-$1 found." - exit 0 -else - echo "dockerlint-$1 was not found, downloading and preparing." - sudo apt-get update - sudo apt-get install coffeescript --no-install-recommends -y - wget -v --https-only https://github.com/RedCoolBeans/dockerlint/archive/$1.tar.gz - tar xzf $1.tar.gz - cd dockerlint-$1 - make clean js - cd .. - rm $1.tar.gz - echo "dockerlint-$1 created." - exit 0 -fi diff --git a/shellscripts/dockerlint-verify.sh b/shellscripts/dockerlint-verify.sh index a2dfc164..acef0cec 100644 --- a/shellscripts/dockerlint-verify.sh +++ b/shellscripts/dockerlint-verify.sh @@ -3,8 +3,8 @@ # dockerlint-verify.sh # # The script verifies the existence of npm (https://www.npmjs.com/) and the npm package dockerlint (https://www.npmjs.com/package/dockerlint). -# If both exists the script will run the dockerlint linter against the Dockerfile passed as the first argumetn to the script. -# The script does not install anythong on your behalf, for using it, please manually install npm and dockerlint. +# If both exists the script will run the dockerlint linter against the Dockerfile passed as the first argument to the script. +# The script does not install anything on your behalf, for using it, please manually install npm and dockerlint. display_no_npm() { echo -e "### !!!SKIPPING TEST!!! ###\n" diff --git a/shellscripts/shellcheck-verify.sh b/shellscripts/shellcheck-verify.sh new file mode 100644 index 00000000..d10f4c51 --- /dev/null +++ b/shellscripts/shellcheck-verify.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# "shellcheck-verify.sh" +# +# The script verifies the existence of shellcheck (https://github.com/koalaman/shellcheck). +# If exists the script will run the shellcheck against the path passed in the first argument. +# The script does not install anything on your behalf, for using it, please manually install shellcheck. + +display_no_shellcheck() { + echo -e "### !!!SKIPPING TEST!!! ###\n" + echo -e "shellcheck is not installed.\n" + echo "please consider installing shellcheck for linting your shell scripts." +} + +display_usage() { + echo -e "please provide the exact path of the shellscripts as the first and only argument.\n" + echo "usage: $0 /absolute/or/relative/path/my-script.sh" + echo "usage: $0 /absolute/or/relative/path/*.sh" +} + +if command -v shellcheck > /dev/null +then + if [ -z "$1" ] + then + display_usage + exit 1 + else + echo "shellcheck is present, running shellcheck" + if [ -z "$2" ] + then + shellcheck "$1" + else + shellcheck "$1"/*."$2" + fi + fi +else + display_no_shellcheck + exit 0 +fi diff --git a/tox.ini b/tox.ini index cfb73e97..61cca861 100644 --- a/tox.ini +++ b/tox.ini @@ -1,24 +1,37 @@ [tox] envlist = - dockerlint + docslint + docsbuild prettier - lint - typing + shellcheck + dockerlint codesec + codelint + typing py37 [testenv] basepython = python3.7 skip_install = true +skipsdist=True deps = py37: -r{toxinidir}/requirements.txt - {lint,pylint,typing,py37}: -r{toxinidir}/requirements_test.txt - {lint,pylint,typing,py37}: -c{toxinidir}/requirements_constraints.txt + {codelint,typing,py37}: -r{toxinidir}/requirements_test.txt + {codelint,typing,py37}: -c{toxinidir}/requirements_constraints.txt -[testenv:dockerlint] -description = Run local only dockerlint linter +[testenv:docslint] +description = Pass restructuredText files through doc8 linter +deps = + doc8==0.8.0 + Pygments==2.4.2 +commands = doc8 --config {toxinidir}/doc8.ini {toxinidir}/docs/source + +[testenv:docsbuild] +changedir = docs +depends = docslint +deps = sphinx==2.1.1 commands = - /bin/bash shellscripts/dockerlint-verify.sh {toxinidir}/Dockerfile + sphinx-build -W -b html -d {envtmpdir}/doctrees source {envtmpdir}/html [testenv:prettier] description = Use nodeenv to run prettier in python venv @@ -27,32 +40,47 @@ whitelist_externals = prettier changedir = {toxinidir}/.tox/prettier commands = nodeenv --python-virtualenv - npm install --no-save prettier + npm install --no-save prettier@1.18.2 prettier --config {toxinidir}/.prettierrc.yml --ignore-path {toxinidir}/.prettierignore --check {toxinidir}/*.* {toxinidir}/**/*.* -[testenv:lint] -description = Run flake8, pydocstyle, pydocstyle and pylint linters +[testenv:shellcheck] +description = Run shellcheck (if exists) for linting shell scripts +depends = prettier +commands = /bin/bash {toxinidir}/shellscripts/shellcheck-verify.sh {toxinidir}/shellscripts/ sh + +[testenv:dockerlint] +description = Run local only dockerlint linter +depends = + prettier + shellcheck +commands = + /bin/bash {toxinidir}/shellscripts/dockerlint-verify.sh {toxinidir}/Dockerfile + +[testenv:codesec] +deps = bandit == 1.6.1 +depends = dockerlint +commands = + bandit -rvc {toxinidir}/bandit.yml {toxinidir}/pyscripts {toxinidir}/docs/source/conf.py -l -ii + +[testenv:codelint] +description = Run flake8 and pylint linters depends = dockerlint commands = - flake8 --statistics --count --doctests {toxinidir}/pyscripts/ - pydocstyle -v --count {toxinidir}/pyscripts/ - pycodestyle -v --statistics --show-pep8 --count {toxinidir}/pyscripts/ - pylint --disable fixme --rcfile {toxinidir}/pylintrc {toxinidir}/pyscripts/ + flake8 --statistics --count --doctests --verbose {toxinidir}/pyscripts/ {toxinidir}/docs/source/ + pylint --disable fixme --rcfile {toxinidir}/pylintrc {toxinidir}/pyscripts/ {toxinidir}/docs/source/conf.py [testenv:typing] description = Run mypy type checking depends = dockerlint commands = - mypy --config-file {toxinidir}/mypy.ini {toxinidir}/pyscripts/ + mypy --config-file {toxinidir}/mypy.ini {toxinidir}/pyscripts/ {toxinidir}/docs/source/conf.py -[testenv:codesec] -deps = - bandit == 1.6.0 -commands = - bandit -rvc {toxinidir}/bandit.yml {toxinidir}/pyscripts -l -ii [testenv:py37] description = Run pytest test cases with coverage report -depends = dockerlint +depends = + codesec + codelint + typing commands = pytest -vs --cov --cov-config={toxinidir}/.coveragerc {toxinidir}/pyscripts/test_server.py