diff --git a/docker-compose.yaml b/docker-compose.yaml index 157fe01..c6061f9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -26,6 +26,14 @@ services: volumes: - ./data/:/app/data + swagger: + build: ./swagger/ + labels: + - "traefik.enable=true" + - "traefik.http.routers.swagger.rule=PathPrefix(`/ui`) || PathPrefix(`/openapi`)" + - "traefik.http.routers.swagger.entrypoints=web" + + reverse: image: traefik:v3.0 command: diff --git a/swagger/.dockerignore b/swagger/.dockerignore new file mode 100644 index 0000000..cdd823e --- /dev/null +++ b/swagger/.dockerignore @@ -0,0 +1,72 @@ +.travis.yaml +.swagger-codegen-ignore +README.md +tox.ini +git_push.sh +test-requirements.txt +setup.py + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +venv/ +.python-version + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +#Ipython Notebook +.ipynb_checkpoints diff --git a/swagger/.swagger-codegen-ignore b/swagger/.swagger-codegen-ignore new file mode 100644 index 0000000..c5fa491 --- /dev/null +++ b/swagger/.swagger-codegen-ignore @@ -0,0 +1,23 @@ +# Swagger Codegen Ignore +# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/swagger/.swagger-codegen/VERSION b/swagger/.swagger-codegen/VERSION new file mode 100644 index 0000000..248908e --- /dev/null +++ b/swagger/.swagger-codegen/VERSION @@ -0,0 +1 @@ +3.0.54 \ No newline at end of file diff --git a/swagger/.travis.yml b/swagger/.travis.yml new file mode 100644 index 0000000..dd6c445 --- /dev/null +++ b/swagger/.travis.yml @@ -0,0 +1,13 @@ +# ref: https://docs.travis-ci.com/user/languages/python +language: python +python: + - "3.2" + - "3.3" + - "3.4" + - "3.5" + #- "3.5-dev" # 3.5 development branch + #- "nightly" # points to the latest development branch e.g. 3.6-dev +# command to install dependencies +install: "pip install -r requirements.txt" +# command to run tests +script: nosetests diff --git a/swagger/Dockerfile b/swagger/Dockerfile new file mode 100644 index 0000000..f9243a5 --- /dev/null +++ b/swagger/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.6-alpine + +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +COPY requirements.txt /usr/src/app/ + +RUN pip3 install --no-cache-dir -r requirements.txt + +COPY . /usr/src/app + +EXPOSE 8080 + +ENTRYPOINT ["python3"] + +CMD ["-m", "swagger_server"] \ No newline at end of file diff --git a/swagger/README.md b/swagger/README.md new file mode 100644 index 0000000..d4ba614 --- /dev/null +++ b/swagger/README.md @@ -0,0 +1,49 @@ +# Swagger generated server + +## Overview +This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the +[OpenAPI-Spec](https://github.com/swagger-api/swagger-core/wiki) from a remote server, you can easily generate a server stub. This +is an example of building a swagger-enabled Flask server. + +This example uses the [Connexion](https://github.com/zalando/connexion) library on top of Flask. + +## Requirements +Python 3.5.2+ + +## Usage +To run the server, please execute the following from the root directory: + +``` +pip3 install -r requirements.txt +python3 -m swagger_server +``` + +and open your browser to here: + +``` +http://localhost:8080//ui/ +``` + +Your Swagger definition lives here: + +``` +http://localhost:8080//swagger.json +``` + +To launch the integration tests, use tox: +``` +sudo pip install tox +tox +``` + +## Running with Docker + +To run the server on a Docker container, please execute the following from the root directory: + +```bash +# building the image +docker build -t swagger_server . + +# starting up a container +docker run -p 8080:8080 swagger_server +``` \ No newline at end of file diff --git a/swagger/git_push.sh b/swagger/git_push.sh new file mode 100644 index 0000000..160f6f2 --- /dev/null +++ b/swagger/git_push.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/swagger/requirements.txt b/swagger/requirements.txt new file mode 100644 index 0000000..62c7fff --- /dev/null +++ b/swagger/requirements.txt @@ -0,0 +1,5 @@ +connexion >= 2.6.0 +connexion[swagger-ui] >= 2.6.0 +python_dateutil == 2.6.0 +setuptools >= 21.0.0 +swagger-ui-bundle >= 0.0.2 diff --git a/swagger/setup.py b/swagger/setup.py new file mode 100644 index 0000000..00cd052 --- /dev/null +++ b/swagger/setup.py @@ -0,0 +1,36 @@ +# coding: utf-8 + +import sys +from setuptools import setup, find_packages + +NAME = "swagger_server" +VERSION = "1.0.0" +# To install the library, run the following +# +# python setup.py install +# +# prerequisite: setuptools +# http://pypi.python.org/pypi/setuptools + +REQUIRES = [ + "connexion", + "swagger-ui-bundle>=0.0.2" +] + +setup( + name=NAME, + version=VERSION, + description="API Jo", + author_email="", + url="", + keywords=["Swagger", "API Jo"], + install_requires=REQUIRES, + packages=find_packages(), + package_data={'': ['swagger/swagger.yaml']}, + include_package_data=True, + entry_points={ + 'console_scripts': ['swagger_server=swagger_server.__main__:main']}, + long_description="""\ + No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + """ +) diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml new file mode 100644 index 0000000..0f24d7a --- /dev/null +++ b/swagger/swagger.yaml @@ -0,0 +1,252 @@ +openapi: '3.0.2' +info: + title: API Jo + version: '1.0' +servers: + - url: http://localhost/ + +components: + schemas: + Athlete: + type: object + properties: + name: + type: string + surname: + type: string + email: + type: string + country: + type: string + isDisabled: + type: boolean + default: false + Sport: + type: object + properties: + name: + type: string + place: + type: string + category: + type: string + Medal: + type: object + properties: + rank: + type: string + enum: + - gold + - silver + - bronze + sportID: + type: integer + athleteID: + type: integer + + + + +paths: + /athlete: + get: + tags: + - Athletes + summary: Display athletes list + responses: + '200': + description: OK + post: + tags: + - Athletes + summary: Add a new athlete + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Athlete' + + /athlete/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: integer + example: 1 + get: + tags: + - Athletes + summary: Display one athlete using the ID + responses: + '200': + description: 'OK' + content: + application/json: + schema: + $ref: '#/components/schemas/Athlete' + '404': + description: 'Athlete not found' + patch: + tags: + - Athletes + summary: Modify a athlete using the ID + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/Athlete' + responses: + '200': + description: 'OK' + '404': + description: 'Athlete not found' + delete: + tags: + - Athletes + summary: Delete an athlete using the ID + responses: + '200': + description: 'OK' + '404': + description: 'Athete not found' + /sport: + get: + tags: + - Sports + summary: List all available sports + responses: + '200': + description: OK + post: + tags: + - Sports + summary: Add a sport + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Sport' + /sport/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: integer + example: 1 + get: + tags: + - Sports + summary: Display a specific sport using the ID + responses: + '200': + description: 'OK' + content: + application/json: + schema: + $ref: '#/components/schemas/Sport' + '404': + description: 'Sport not found' + + patch: + tags: + - Sports + summary: Edit a specific sport using the ID + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/Sport' + responses: + '200': + description: 'OK' + '404': + description: 'Sport not found' + delete: + tags: + - Sports + summary: Delete a specific sport using the ID + responses: + '200': + description: 'OK' + '404': + description: 'Sport not found' + /medal: + get: + tags: + - Medal + summary: Display medals list + responses: + '200': + description: OK + post: + tags: + - Medal + summary: Add a new medal + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Medal' + /medal/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: integer + example: 1 + get: + tags: + - Medal + summary: Display a medal + responses: + '200': + description: 'OK' + content: + application/json: + schema: + $ref: '#/components/schemas/Medal' + '404': + description: 'Not found' + patch: + tags: + - Medal + summary: Modify element from medal + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/Medal' + responses: + '200': + description: 'OK' + '404': + description: 'Medal not found' + delete: + tags: + - Medal + summary: Delete medal from Id + responses: + '200': + description: 'OK' + '404': + description: 'Medal not found' + + diff --git a/swagger/swagger_server/__init__.py b/swagger/swagger_server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/swagger/swagger_server/__main__.py b/swagger/swagger_server/__main__.py new file mode 100644 index 0000000..770114c --- /dev/null +++ b/swagger/swagger_server/__main__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +import connexion + +from swagger_server import encoder + + +def main(): + app = connexion.App(__name__, specification_dir='./swagger/') + app.app.json_encoder = encoder.JSONEncoder + app.add_api('swagger.yaml', arguments={'title': 'API Jo'}, pythonic_params=True) + app.run(port=8080) + + +if __name__ == '__main__': + main() diff --git a/swagger/swagger_server/controllers/__init__.py b/swagger/swagger_server/controllers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/swagger/swagger_server/controllers/athletes_controller.py b/swagger/swagger_server/controllers/athletes_controller.py new file mode 100644 index 0000000..f03237d --- /dev/null +++ b/swagger/swagger_server/controllers/athletes_controller.py @@ -0,0 +1,74 @@ +import connexion +import six + +from swagger_server.models.athlete import Athlete # noqa: E501 +from swagger_server import util + + +def athlete_get(): # noqa: E501 + """Display athletes list + + # noqa: E501 + + + :rtype: None + """ + return 'do some magic!' + + +def athlete_id_delete(id): # noqa: E501 + """Delete an athlete using the ID + + # noqa: E501 + + :param id: + :type id: int + + :rtype: None + """ + return 'do some magic!' + + +def athlete_id_get(id): # noqa: E501 + """Display one athlete using the ID + + # noqa: E501 + + :param id: + :type id: int + + :rtype: Athlete + """ + return 'do some magic!' + + +def athlete_id_patch(id, body=None): # noqa: E501 + """Modify a athlete using the ID + + # noqa: E501 + + :param id: + :type id: int + :param body: + :type body: dict | bytes + + :rtype: None + """ + if connexion.request.is_json: + body = Athlete.from_dict(connexion.request.get_json()) # noqa: E501 + return 'do some magic!' + + +def athlete_post(body): # noqa: E501 + """Add a new athlete + + # noqa: E501 + + :param body: + :type body: dict | bytes + + :rtype: None + """ + if connexion.request.is_json: + body = Athlete.from_dict(connexion.request.get_json()) # noqa: E501 + return 'do some magic!' diff --git a/swagger/swagger_server/controllers/authorization_controller.py b/swagger/swagger_server/controllers/authorization_controller.py new file mode 100644 index 0000000..2f7b0bb --- /dev/null +++ b/swagger/swagger_server/controllers/authorization_controller.py @@ -0,0 +1,6 @@ +from typing import List +""" +controller generated to handled auth operation described at: +https://connexion.readthedocs.io/en/latest/security.html +""" + diff --git a/swagger/swagger_server/controllers/medal_controller.py b/swagger/swagger_server/controllers/medal_controller.py new file mode 100644 index 0000000..04b2d4a --- /dev/null +++ b/swagger/swagger_server/controllers/medal_controller.py @@ -0,0 +1,74 @@ +import connexion +import six + +from swagger_server.models.medal import Medal # noqa: E501 +from swagger_server import util + + +def medal_get(): # noqa: E501 + """Display medals list + + # noqa: E501 + + + :rtype: None + """ + return 'do some magic!' + + +def medal_id_delete(id): # noqa: E501 + """Delete medal from Id + + # noqa: E501 + + :param id: + :type id: int + + :rtype: None + """ + return 'do some magic!' + + +def medal_id_get(id): # noqa: E501 + """Display a medal + + # noqa: E501 + + :param id: + :type id: int + + :rtype: Medal + """ + return 'do some magic!' + + +def medal_id_patch(id, body=None): # noqa: E501 + """Modify element from medal + + # noqa: E501 + + :param id: + :type id: int + :param body: + :type body: dict | bytes + + :rtype: None + """ + if connexion.request.is_json: + body = Medal.from_dict(connexion.request.get_json()) # noqa: E501 + return 'do some magic!' + + +def medal_post(body): # noqa: E501 + """Add a new medal + + # noqa: E501 + + :param body: + :type body: dict | bytes + + :rtype: None + """ + if connexion.request.is_json: + body = Medal.from_dict(connexion.request.get_json()) # noqa: E501 + return 'do some magic!' diff --git a/swagger/swagger_server/controllers/sports_controller.py b/swagger/swagger_server/controllers/sports_controller.py new file mode 100644 index 0000000..e0de71f --- /dev/null +++ b/swagger/swagger_server/controllers/sports_controller.py @@ -0,0 +1,74 @@ +import connexion +import six + +from swagger_server.models.sport import Sport # noqa: E501 +from swagger_server import util + + +def sport_get(): # noqa: E501 + """List all available sports + + # noqa: E501 + + + :rtype: None + """ + return 'do some magic!' + + +def sport_id_delete(id): # noqa: E501 + """Delete a specific sport using the ID + + # noqa: E501 + + :param id: + :type id: int + + :rtype: None + """ + return 'do some magic!' + + +def sport_id_get(id): # noqa: E501 + """Display a specific sport using the ID + + # noqa: E501 + + :param id: + :type id: int + + :rtype: Sport + """ + return 'do some magic!' + + +def sport_id_patch(id, body=None): # noqa: E501 + """Edit a specific sport using the ID + + # noqa: E501 + + :param id: + :type id: int + :param body: + :type body: dict | bytes + + :rtype: None + """ + if connexion.request.is_json: + body = Sport.from_dict(connexion.request.get_json()) # noqa: E501 + return 'do some magic!' + + +def sport_post(body): # noqa: E501 + """Add a sport + + # noqa: E501 + + :param body: + :type body: dict | bytes + + :rtype: None + """ + if connexion.request.is_json: + body = Sport.from_dict(connexion.request.get_json()) # noqa: E501 + return 'do some magic!' diff --git a/swagger/swagger_server/encoder.py b/swagger/swagger_server/encoder.py new file mode 100644 index 0000000..61ba472 --- /dev/null +++ b/swagger/swagger_server/encoder.py @@ -0,0 +1,20 @@ +from connexion.apps.flask_app import FlaskJSONEncoder +import six + +from swagger_server.models.base_model_ import Model + + +class JSONEncoder(FlaskJSONEncoder): + include_nulls = False + + def default(self, o): + if isinstance(o, Model): + dikt = {} + for attr, _ in six.iteritems(o.swagger_types): + value = getattr(o, attr) + if value is None and not self.include_nulls: + continue + attr = o.attribute_map[attr] + dikt[attr] = value + return dikt + return FlaskJSONEncoder.default(self, o) diff --git a/swagger/swagger_server/models/__init__.py b/swagger/swagger_server/models/__init__.py new file mode 100644 index 0000000..8fc3725 --- /dev/null +++ b/swagger/swagger_server/models/__init__.py @@ -0,0 +1,8 @@ +# coding: utf-8 + +# flake8: noqa +from __future__ import absolute_import +# import models into model package +from swagger_server.models.athlete import Athlete +from swagger_server.models.medal import Medal +from swagger_server.models.sport import Sport diff --git a/swagger/swagger_server/models/athlete.py b/swagger/swagger_server/models/athlete.py new file mode 100644 index 0000000..49a41ff --- /dev/null +++ b/swagger/swagger_server/models/athlete.py @@ -0,0 +1,166 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from swagger_server.models.base_model_ import Model +from swagger_server import util + + +class Athlete(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + def __init__(self, name: str=None, surname: str=None, email: str=None, country: str=None, is_disabled: bool=False): # noqa: E501 + """Athlete - a model defined in Swagger + + :param name: The name of this Athlete. # noqa: E501 + :type name: str + :param surname: The surname of this Athlete. # noqa: E501 + :type surname: str + :param email: The email of this Athlete. # noqa: E501 + :type email: str + :param country: The country of this Athlete. # noqa: E501 + :type country: str + :param is_disabled: The is_disabled of this Athlete. # noqa: E501 + :type is_disabled: bool + """ + self.swagger_types = { + 'name': str, + 'surname': str, + 'email': str, + 'country': str, + 'is_disabled': bool + } + + self.attribute_map = { + 'name': 'name', + 'surname': 'surname', + 'email': 'email', + 'country': 'country', + 'is_disabled': 'isDisabled' + } + self._name = name + self._surname = surname + self._email = email + self._country = country + self._is_disabled = is_disabled + + @classmethod + def from_dict(cls, dikt) -> 'Athlete': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Athlete of this Athlete. # noqa: E501 + :rtype: Athlete + """ + return util.deserialize_model(dikt, cls) + + @property + def name(self) -> str: + """Gets the name of this Athlete. + + + :return: The name of this Athlete. + :rtype: str + """ + return self._name + + @name.setter + def name(self, name: str): + """Sets the name of this Athlete. + + + :param name: The name of this Athlete. + :type name: str + """ + + self._name = name + + @property + def surname(self) -> str: + """Gets the surname of this Athlete. + + + :return: The surname of this Athlete. + :rtype: str + """ + return self._surname + + @surname.setter + def surname(self, surname: str): + """Sets the surname of this Athlete. + + + :param surname: The surname of this Athlete. + :type surname: str + """ + + self._surname = surname + + @property + def email(self) -> str: + """Gets the email of this Athlete. + + + :return: The email of this Athlete. + :rtype: str + """ + return self._email + + @email.setter + def email(self, email: str): + """Sets the email of this Athlete. + + + :param email: The email of this Athlete. + :type email: str + """ + + self._email = email + + @property + def country(self) -> str: + """Gets the country of this Athlete. + + + :return: The country of this Athlete. + :rtype: str + """ + return self._country + + @country.setter + def country(self, country: str): + """Sets the country of this Athlete. + + + :param country: The country of this Athlete. + :type country: str + """ + + self._country = country + + @property + def is_disabled(self) -> bool: + """Gets the is_disabled of this Athlete. + + + :return: The is_disabled of this Athlete. + :rtype: bool + """ + return self._is_disabled + + @is_disabled.setter + def is_disabled(self, is_disabled: bool): + """Sets the is_disabled of this Athlete. + + + :param is_disabled: The is_disabled of this Athlete. + :type is_disabled: bool + """ + + self._is_disabled = is_disabled diff --git a/swagger/swagger_server/models/base_model_.py b/swagger/swagger_server/models/base_model_.py new file mode 100644 index 0000000..97999c3 --- /dev/null +++ b/swagger/swagger_server/models/base_model_.py @@ -0,0 +1,69 @@ +import pprint + +import six +import typing + +from swagger_server import util + +T = typing.TypeVar('T') + + +class Model(object): + # swaggerTypes: The key is attribute name and the + # value is attribute type. + swagger_types = {} + + # attributeMap: The key is attribute name and the + # value is json key in definition. + attribute_map = {} + + @classmethod + def from_dict(cls: typing.Type[T], dikt) -> T: + """Returns the dict as a model""" + return util.deserialize_model(dikt, cls) + + def to_dict(self): + """Returns the model properties as a dict + + :rtype: dict + """ + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model + + :rtype: str + """ + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/swagger/swagger_server/models/medal.py b/swagger/swagger_server/models/medal.py new file mode 100644 index 0000000..d655d5f --- /dev/null +++ b/swagger/swagger_server/models/medal.py @@ -0,0 +1,120 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from swagger_server.models.base_model_ import Model +from swagger_server import util + + +class Medal(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + def __init__(self, rank: str=None, sport_id: int=None, athlete_id: int=None): # noqa: E501 + """Medal - a model defined in Swagger + + :param rank: The rank of this Medal. # noqa: E501 + :type rank: str + :param sport_id: The sport_id of this Medal. # noqa: E501 + :type sport_id: int + :param athlete_id: The athlete_id of this Medal. # noqa: E501 + :type athlete_id: int + """ + self.swagger_types = { + 'rank': str, + 'sport_id': int, + 'athlete_id': int + } + + self.attribute_map = { + 'rank': 'rank', + 'sport_id': 'sportID', + 'athlete_id': 'athleteID' + } + self._rank = rank + self._sport_id = sport_id + self._athlete_id = athlete_id + + @classmethod + def from_dict(cls, dikt) -> 'Medal': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Medal of this Medal. # noqa: E501 + :rtype: Medal + """ + return util.deserialize_model(dikt, cls) + + @property + def rank(self) -> str: + """Gets the rank of this Medal. + + + :return: The rank of this Medal. + :rtype: str + """ + return self._rank + + @rank.setter + def rank(self, rank: str): + """Sets the rank of this Medal. + + + :param rank: The rank of this Medal. + :type rank: str + """ + allowed_values = ["gold", "silver", "bronze"] # noqa: E501 + if rank not in allowed_values: + raise ValueError( + "Invalid value for `rank` ({0}), must be one of {1}" + .format(rank, allowed_values) + ) + + self._rank = rank + + @property + def sport_id(self) -> int: + """Gets the sport_id of this Medal. + + + :return: The sport_id of this Medal. + :rtype: int + """ + return self._sport_id + + @sport_id.setter + def sport_id(self, sport_id: int): + """Sets the sport_id of this Medal. + + + :param sport_id: The sport_id of this Medal. + :type sport_id: int + """ + + self._sport_id = sport_id + + @property + def athlete_id(self) -> int: + """Gets the athlete_id of this Medal. + + + :return: The athlete_id of this Medal. + :rtype: int + """ + return self._athlete_id + + @athlete_id.setter + def athlete_id(self, athlete_id: int): + """Sets the athlete_id of this Medal. + + + :param athlete_id: The athlete_id of this Medal. + :type athlete_id: int + """ + + self._athlete_id = athlete_id diff --git a/swagger/swagger_server/models/sport.py b/swagger/swagger_server/models/sport.py new file mode 100644 index 0000000..43a1ae1 --- /dev/null +++ b/swagger/swagger_server/models/sport.py @@ -0,0 +1,114 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from swagger_server.models.base_model_ import Model +from swagger_server import util + + +class Sport(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + def __init__(self, name: str=None, place: str=None, category: str=None): # noqa: E501 + """Sport - a model defined in Swagger + + :param name: The name of this Sport. # noqa: E501 + :type name: str + :param place: The place of this Sport. # noqa: E501 + :type place: str + :param category: The category of this Sport. # noqa: E501 + :type category: str + """ + self.swagger_types = { + 'name': str, + 'place': str, + 'category': str + } + + self.attribute_map = { + 'name': 'name', + 'place': 'place', + 'category': 'category' + } + self._name = name + self._place = place + self._category = category + + @classmethod + def from_dict(cls, dikt) -> 'Sport': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Sport of this Sport. # noqa: E501 + :rtype: Sport + """ + return util.deserialize_model(dikt, cls) + + @property + def name(self) -> str: + """Gets the name of this Sport. + + + :return: The name of this Sport. + :rtype: str + """ + return self._name + + @name.setter + def name(self, name: str): + """Sets the name of this Sport. + + + :param name: The name of this Sport. + :type name: str + """ + + self._name = name + + @property + def place(self) -> str: + """Gets the place of this Sport. + + + :return: The place of this Sport. + :rtype: str + """ + return self._place + + @place.setter + def place(self, place: str): + """Sets the place of this Sport. + + + :param place: The place of this Sport. + :type place: str + """ + + self._place = place + + @property + def category(self) -> str: + """Gets the category of this Sport. + + + :return: The category of this Sport. + :rtype: str + """ + return self._category + + @category.setter + def category(self, category: str): + """Sets the category of this Sport. + + + :param category: The category of this Sport. + :type category: str + """ + + self._category = category diff --git a/swagger/swagger_server/swagger/swagger.yaml b/swagger/swagger_server/swagger/swagger.yaml new file mode 100644 index 0000000..45701f7 --- /dev/null +++ b/swagger/swagger_server/swagger/swagger.yaml @@ -0,0 +1,348 @@ +openapi: 3.0.2 +info: + title: API Jo + version: "1.0" +servers: +- url: http://localhost/ +paths: + /athlete: + get: + tags: + - Athletes + summary: Display athletes list + operationId: athlete_get + responses: + "200": + description: OK + x-openapi-router-controller: swagger_server.controllers.athletes_controller + post: + tags: + - Athletes + summary: Add a new athlete + operationId: athlete_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Athlete' + required: true + responses: + "200": + description: OK + x-openapi-router-controller: swagger_server.controllers.athletes_controller + /athlete/{id}: + get: + tags: + - Athletes + summary: Display one athlete using the ID + operationId: athlete_id_get + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Athlete' + "404": + description: Athlete not found + x-openapi-router-controller: swagger_server.controllers.athletes_controller + delete: + tags: + - Athletes + summary: Delete an athlete using the ID + operationId: athlete_id_delete + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + responses: + "200": + description: OK + "404": + description: Athete not found + x-openapi-router-controller: swagger_server.controllers.athletes_controller + patch: + tags: + - Athletes + summary: Modify a athlete using the ID + operationId: athlete_id_patch + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Athlete' + required: false + responses: + "200": + description: OK + "404": + description: Athlete not found + x-openapi-router-controller: swagger_server.controllers.athletes_controller + /sport: + get: + tags: + - Sports + summary: List all available sports + operationId: sport_get + responses: + "200": + description: OK + x-openapi-router-controller: swagger_server.controllers.sports_controller + post: + tags: + - Sports + summary: Add a sport + operationId: sport_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Sport' + required: true + responses: + "200": + description: OK + x-openapi-router-controller: swagger_server.controllers.sports_controller + /sport/{id}: + get: + tags: + - Sports + summary: Display a specific sport using the ID + operationId: sport_id_get + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Sport' + "404": + description: Sport not found + x-openapi-router-controller: swagger_server.controllers.sports_controller + delete: + tags: + - Sports + summary: Delete a specific sport using the ID + operationId: sport_id_delete + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + responses: + "200": + description: OK + "404": + description: Sport not found + x-openapi-router-controller: swagger_server.controllers.sports_controller + patch: + tags: + - Sports + summary: Edit a specific sport using the ID + operationId: sport_id_patch + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Sport' + required: false + responses: + "200": + description: OK + "404": + description: Sport not found + x-openapi-router-controller: swagger_server.controllers.sports_controller + /medal: + get: + tags: + - Medal + summary: Display medals list + operationId: medal_get + responses: + "200": + description: OK + x-openapi-router-controller: swagger_server.controllers.medal_controller + post: + tags: + - Medal + summary: Add a new medal + operationId: medal_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Medal' + required: true + responses: + "200": + description: OK + x-openapi-router-controller: swagger_server.controllers.medal_controller + /medal/{id}: + get: + tags: + - Medal + summary: Display a medal + operationId: medal_id_get + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Medal' + "404": + description: Not found + x-openapi-router-controller: swagger_server.controllers.medal_controller + delete: + tags: + - Medal + summary: Delete medal from Id + operationId: medal_id_delete + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + responses: + "200": + description: OK + "404": + description: Medal not found + x-openapi-router-controller: swagger_server.controllers.medal_controller + patch: + tags: + - Medal + summary: Modify element from medal + operationId: medal_id_patch + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: integer + example: 1 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Medal' + required: false + responses: + "200": + description: OK + "404": + description: Medal not found + x-openapi-router-controller: swagger_server.controllers.medal_controller +components: + schemas: + Athlete: + type: object + properties: + name: + type: string + surname: + type: string + email: + type: string + country: + type: string + isDisabled: + type: boolean + default: false + example: + country: country + surname: surname + name: name + isDisabled: false + email: email + Sport: + type: object + properties: + name: + type: string + place: + type: string + category: + type: string + example: + name: name + place: place + category: category + Medal: + type: object + properties: + rank: + type: string + enum: + - gold + - silver + - bronze + sportID: + type: integer + athleteID: + type: integer + example: + sportID: 0 + athleteID: 6 + rank: gold + diff --git a/swagger/swagger_server/test/__init__.py b/swagger/swagger_server/test/__init__.py new file mode 100644 index 0000000..6445063 --- /dev/null +++ b/swagger/swagger_server/test/__init__.py @@ -0,0 +1,16 @@ +import logging + +import connexion +from flask_testing import TestCase + +from swagger_server.encoder import JSONEncoder + + +class BaseTestCase(TestCase): + + def create_app(self): + logging.getLogger('connexion.operation').setLevel('ERROR') + app = connexion.App(__name__, specification_dir='../swagger/') + app.app.json_encoder = JSONEncoder + app.add_api('swagger.yaml') + return app.app diff --git a/swagger/swagger_server/test/test_athletes_controller.py b/swagger/swagger_server/test/test_athletes_controller.py new file mode 100644 index 0000000..ac2e7b8 --- /dev/null +++ b/swagger/swagger_server/test/test_athletes_controller.py @@ -0,0 +1,79 @@ +# coding: utf-8 + +from __future__ import absolute_import + +from flask import json +from six import BytesIO + +from swagger_server.models.athlete import Athlete # noqa: E501 +from swagger_server.test import BaseTestCase + + +class TestAthletesController(BaseTestCase): + """AthletesController integration test stubs""" + + def test_athlete_get(self): + """Test case for athlete_get + + Display athletes list + """ + response = self.client.open( + '//athlete', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_athlete_id_delete(self): + """Test case for athlete_id_delete + + Delete an athlete using the ID + """ + response = self.client.open( + '//athlete/{id}'.format(id=56), + method='DELETE') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_athlete_id_get(self): + """Test case for athlete_id_get + + Display one athlete using the ID + """ + response = self.client.open( + '//athlete/{id}'.format(id=56), + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_athlete_id_patch(self): + """Test case for athlete_id_patch + + Modify a athlete using the ID + """ + body = Athlete() + response = self.client.open( + '//athlete/{id}'.format(id=56), + method='PATCH', + data=json.dumps(body), + content_type='application/json') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_athlete_post(self): + """Test case for athlete_post + + Add a new athlete + """ + body = Athlete() + response = self.client.open( + '//athlete', + method='POST', + data=json.dumps(body), + content_type='application/json') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/swagger/swagger_server/test/test_medal_controller.py b/swagger/swagger_server/test/test_medal_controller.py new file mode 100644 index 0000000..ba75709 --- /dev/null +++ b/swagger/swagger_server/test/test_medal_controller.py @@ -0,0 +1,79 @@ +# coding: utf-8 + +from __future__ import absolute_import + +from flask import json +from six import BytesIO + +from swagger_server.models.medal import Medal # noqa: E501 +from swagger_server.test import BaseTestCase + + +class TestMedalController(BaseTestCase): + """MedalController integration test stubs""" + + def test_medal_get(self): + """Test case for medal_get + + Display medals list + """ + response = self.client.open( + '//medal', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_medal_id_delete(self): + """Test case for medal_id_delete + + Delete medal from Id + """ + response = self.client.open( + '//medal/{id}'.format(id=56), + method='DELETE') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_medal_id_get(self): + """Test case for medal_id_get + + Display a medal + """ + response = self.client.open( + '//medal/{id}'.format(id=56), + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_medal_id_patch(self): + """Test case for medal_id_patch + + Modify element from medal + """ + body = Medal() + response = self.client.open( + '//medal/{id}'.format(id=56), + method='PATCH', + data=json.dumps(body), + content_type='application/json') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_medal_post(self): + """Test case for medal_post + + Add a new medal + """ + body = Medal() + response = self.client.open( + '//medal', + method='POST', + data=json.dumps(body), + content_type='application/json') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/swagger/swagger_server/test/test_sports_controller.py b/swagger/swagger_server/test/test_sports_controller.py new file mode 100644 index 0000000..7b54906 --- /dev/null +++ b/swagger/swagger_server/test/test_sports_controller.py @@ -0,0 +1,79 @@ +# coding: utf-8 + +from __future__ import absolute_import + +from flask import json +from six import BytesIO + +from swagger_server.models.sport import Sport # noqa: E501 +from swagger_server.test import BaseTestCase + + +class TestSportsController(BaseTestCase): + """SportsController integration test stubs""" + + def test_sport_get(self): + """Test case for sport_get + + List all available sports + """ + response = self.client.open( + '//sport', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_sport_id_delete(self): + """Test case for sport_id_delete + + Delete a specific sport using the ID + """ + response = self.client.open( + '//sport/{id}'.format(id=56), + method='DELETE') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_sport_id_get(self): + """Test case for sport_id_get + + Display a specific sport using the ID + """ + response = self.client.open( + '//sport/{id}'.format(id=56), + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_sport_id_patch(self): + """Test case for sport_id_patch + + Edit a specific sport using the ID + """ + body = Sport() + response = self.client.open( + '//sport/{id}'.format(id=56), + method='PATCH', + data=json.dumps(body), + content_type='application/json') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_sport_post(self): + """Test case for sport_post + + Add a sport + """ + body = Sport() + response = self.client.open( + '//sport', + method='POST', + data=json.dumps(body), + content_type='application/json') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/swagger/swagger_server/type_util.py b/swagger/swagger_server/type_util.py new file mode 100644 index 0000000..0563f81 --- /dev/null +++ b/swagger/swagger_server/type_util.py @@ -0,0 +1,32 @@ +# coding: utf-8 + +import sys + +if sys.version_info < (3, 7): + import typing + + def is_generic(klass): + """ Determine whether klass is a generic class """ + return type(klass) == typing.GenericMeta + + def is_dict(klass): + """ Determine whether klass is a Dict """ + return klass.__extra__ == dict + + def is_list(klass): + """ Determine whether klass is a List """ + return klass.__extra__ == list + +else: + + def is_generic(klass): + """ Determine whether klass is a generic class """ + return hasattr(klass, '__origin__') + + def is_dict(klass): + """ Determine whether klass is a Dict """ + return klass.__origin__ == dict + + def is_list(klass): + """ Determine whether klass is a List """ + return klass.__origin__ == list diff --git a/swagger/swagger_server/util.py b/swagger/swagger_server/util.py new file mode 100644 index 0000000..812b2ad --- /dev/null +++ b/swagger/swagger_server/util.py @@ -0,0 +1,142 @@ +import datetime + +import six +import typing +from swagger_server import type_util + + +def _deserialize(data, klass): + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if klass in six.integer_types or klass in (float, str, bool, bytearray): + return _deserialize_primitive(data, klass) + elif klass == object: + return _deserialize_object(data) + elif klass == datetime.date: + return deserialize_date(data) + elif klass == datetime.datetime: + return deserialize_datetime(data) + elif type_util.is_generic(klass): + if type_util.is_list(klass): + return _deserialize_list(data, klass.__args__[0]) + if type_util.is_dict(klass): + return _deserialize_dict(data, klass.__args__[1]) + else: + return deserialize_model(data, klass) + + +def _deserialize_primitive(data, klass): + """Deserializes to primitive type. + + :param data: data to deserialize. + :param klass: class literal. + + :return: int, long, float, str, bool. + :rtype: int | long | float | str | bool + """ + try: + value = klass(data) + except UnicodeEncodeError: + value = six.u(data) + except TypeError: + value = data + return value + + +def _deserialize_object(value): + """Return an original value. + + :return: object. + """ + return value + + +def deserialize_date(string): + """Deserializes string to date. + + :param string: str. + :type string: str + :return: date. + :rtype: date + """ + try: + from dateutil.parser import parse + return parse(string).date() + except ImportError: + return string + + +def deserialize_datetime(string): + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :type string: str + :return: datetime. + :rtype: datetime + """ + try: + from dateutil.parser import parse + return parse(string) + except ImportError: + return string + + +def deserialize_model(data, klass): + """Deserializes list or dict to model. + + :param data: dict, list. + :type data: dict | list + :param klass: class literal. + :return: model object. + """ + instance = klass() + + if not instance.swagger_types: + return data + + for attr, attr_type in six.iteritems(instance.swagger_types): + if data is not None \ + and instance.attribute_map[attr] in data \ + and isinstance(data, (list, dict)): + value = data[instance.attribute_map[attr]] + setattr(instance, attr, _deserialize(value, attr_type)) + + return instance + + +def _deserialize_list(data, boxed_type): + """Deserializes a list and its elements. + + :param data: list to deserialize. + :type data: list + :param boxed_type: class literal. + + :return: deserialized list. + :rtype: list + """ + return [_deserialize(sub_data, boxed_type) + for sub_data in data] + + +def _deserialize_dict(data, boxed_type): + """Deserializes a dict and its elements. + + :param data: dict to deserialize. + :type data: dict + :param boxed_type: class literal. + + :return: deserialized dict. + :rtype: dict + """ + return {k: _deserialize(v, boxed_type) + for k, v in six.iteritems(data)} diff --git a/swagger/test-requirements.txt b/swagger/test-requirements.txt new file mode 100644 index 0000000..2640639 --- /dev/null +++ b/swagger/test-requirements.txt @@ -0,0 +1,7 @@ +flask_testing==0.8.0 +coverage>=4.0.3 +nose>=1.3.7 +pluggy>=0.3.1 +py>=1.4.31 +randomize>=0.13 +tox==3.20.1 diff --git a/swagger/tox.ini b/swagger/tox.ini new file mode 100644 index 0000000..2751b21 --- /dev/null +++ b/swagger/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py38 + +[testenv] +deps=-r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +commands= + nosetests \ + [] \ No newline at end of file