├── .gitignore ├── CSECodeSpaces101Introduction.mp4 ├── Challenges ├── Challenge1.md ├── Challenge2.md ├── Challenge3.md ├── Challenge4.md ├── Challenge5.md └── Challenge6.md ├── Images ├── PortForwardingDetection.png ├── PortForwardingPorts.png ├── PortUnknownProcess.png └── RestClientExtension.png ├── Readme.md ├── api ├── __init__.py └── math_api.py ├── dev_requirements.txt ├── math.http ├── requirements.txt └── tests ├── __init__.py └── api ├── __init__.py └── test_math_api.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ -------------------------------------------------------------------------------- /CSECodeSpaces101Introduction.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cse-labs/codespaces101/8e5e926fe9e608a38ae19ccbddca92c8e2cfccb3/CSECodeSpaces101Introduction.mp4 -------------------------------------------------------------------------------- /Challenges/Challenge1.md: -------------------------------------------------------------------------------- 1 | # Using predefined container configurations 2 | ## Background 3 | In order to learn about configuring development containers, it is helpful to start with the simplist definition. The simplist .devcontainer/devcontainer.json file has only an image entry. 4 | ```json 5 | { 6 | "image": "python:3.9" 7 | } 8 | ``` 9 | 10 | Although the above works, you will likely want a more helpful configuration. If you are getting started building a devcontainer, the easiest way is to get started is to use the [predefined container configurations](https://docs.github.com/en/codespaces/customizing-your-codespace/configuring-codespaces-for-your-project#using-a-predefined-container-configuration) that are available. They provide a good starting development environment configuration for different environments. 11 | 12 | ## Challenge 13 | In this challenge, you need to accomplish the following, using **Visual Studio Code**: 14 | - Using a predefined container configuration, create a development container configuration 15 | - Open the root folder (CodespacesTraining) in a development container (Remote-Containers) 16 | - Run the python tests. 6 should pass. In order to run the tests, you need to open a bash terminal in the development container and run the following: 17 | ```bash 18 | $ pytest 19 | ``` 20 | You should see the following (or something like it) 21 | ```bash 22 | ====================================================== test session starts ======================================================= 23 | platform linux -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 24 | rootdir: /workspaces/Codespaces 25 | plugins: mock-3.6.1 26 | collected 6 items 27 | 28 | tests/api/test_math_api.py ...... [100%] 29 | 30 | ======================================================= 6 passed in 0.32s ======================================================== 31 | ``` 32 | 33 | ## Helpful hints 34 | - You will need to clone this repo so you have the code locally 35 | - You can find information about [predefined container configurations here](https://docs.github.com/en/codespaces/customizing-your-codespace/configuring-codespaces-for-your-project#using-a-predefined-container-configuration) 36 | - You will need to have the Remote-Containers extension installed in Visual Studio Code 37 | - You can find the helpful commands in VS Code by pressing (Ctrl+Shift+P on Windows) or (Cmd+Shift+P on a Mac) and typing "Remote-Containers". You will see the remote containers options in the menu. 38 | - This is a Python app 39 | - You can use Python 3.9 40 | - The requirements.txt and dev_requirements.txt files holds all the dependencies required by the application 41 | - You can install the requirements by running the following in a bash terminal: 42 | ```bash 43 | python -m pip install --upgrade pip 44 | pip install -r requirements.txt 45 | pip install -r dev_requirements.txt 46 | ``` 47 | - **Important - You will need to choose Remote-Containers: Rebuild Container" if you want to see a change you made and you have already built the remote container. 48 | -------------------------------------------------------------------------------- /Challenges/Challenge2.md: -------------------------------------------------------------------------------- 1 | # Customizing the development container with the onCreateCommand hook 2 | ## Background 3 | You likely noticed in Challenge1 that the default Python development environment configuration did not yield a development environment that was fully configured for the application. You were forced to further configure the environment for the application dependencies in order to run the tests or access the api. This is certainly not ideal, as every developer would have to **know** both that they need to install the dependencies **and** how to install them. A better solution would be to have the dependencies installed in the development environment. 4 | [Development containers](https://code.visualstudio.com/docs/remote/devcontainerjson-reference) have hooks that allow you to run commands at different points in the development container lifecycle. These include: 5 | - onCreateCommand - Run when creating the container 6 | - postCreateCommand - Run inside the container after it is created 7 | - postStartCommand - Run every time the container starts 8 | 9 | ## Challenge 10 | In this challenge, you will use the postCreateCommand to install the application dependencies. 11 | 12 | 13 | ## Helpful Hints 14 | - Remember that in challenge 1 you needed to run the following in a bash terminal: 15 | ```bash 16 | python -m pip install --upgrade pip 17 | pip install -r requirements.txt 18 | pip install -r dev_requirements.txt 19 | ``` 20 | -------------------------------------------------------------------------------- /Challenges/Challenge3.md: -------------------------------------------------------------------------------- 1 | # Moving the customizations from postCreateCommand to the local Dockerfile 2 | Once we have validated the code in the bash script, the next step is to move the changes to the local Dockerfile. This brings us back to the tip from solution1: 3 | 4 | ## (from solution 1) From the Field 5 | We never just have an image pointer in devcontainer.json like below: 6 | ```json 7 | { 8 | "image": "python:3.9" 9 | } 10 | ``` 11 | 12 | Even if we are just using the base image, we have a Dockerfile with a FROM and point to the image. Below is a simplified equivelant to the above, using a Dockerfile: 13 | ```json 14 | { 15 | "build": { 16 | "dockerfile": "Dockerfile" 17 | } 18 | } 19 | ``` 20 | 21 | ```Dockerfile 22 | FROM python:3.9 23 | ``` 24 | 25 | This refactor illustrates why that tip is in place. We already have a Dockerfile in our solution where we can move our changes. 26 | 27 | ## Challenge 28 | Move the logic that installs the dependencies for the application from the bash script file to the local Dockerfile. 29 | -------------------------------------------------------------------------------- /Challenges/Challenge4.md: -------------------------------------------------------------------------------- 1 | ## Forwarding Ports 2 | When running applications inside of the container, they are not accessible to the host unless you forward the port that the application is running on. Ports can be forwarded either temporarily or you can configure your dev container to always forward the ports. 3 | Visual Studio Code often detects ports that are running in the dev container and automatically, temporarily forwards the ports for you. See the images below: 4 | ![Port Forwarding Detection](../Images/PortForwardingDetection.png)
5 | ![Port Forwarding Ports](../Images/PortForwardingPorts.png) 6 | Here, you will notice that when we ran our simple Flask API, Visual Studio Code detected that the API was running on port 5000 and automatically forwarded it for us. You can see the ports that are being forwarded on the ports tab. 7 | 8 | There are 2 potential challenges that we may want to address: 9 | 1. We may not want to rely on Visual Studio Code to detect the ports. This does not always happen. In more complicated development environments, we will likely want to control what parts are forwarded. 10 | 2. We may want to provide some context as to what is running on each port via a label. 11 | ![Port Unknown Process](../Images/PortUnknownProcess.png) 12 | In the above example, we ran the yeasy/simple-web docker image in a docker-in-docker development container. Looking are the forwarded ports, it is unclear what is running on port 5020. 13 | 14 | ## Challenge 15 | In this challenge, you need to accomplish 2 things: 16 | 1. Configure the dev container to forward port 5000. 17 | 2. Label port 5000 "Math API" 18 | 19 | ## Helpful Hints 20 | [devcontainer.json reference](https://code.visualstudio.com/docs/remote/devcontainerjson-reference) 21 | -------------------------------------------------------------------------------- /Challenges/Challenge5.md: -------------------------------------------------------------------------------- 1 | ## Extensions 2 | As noted in Readme.md, the goal of these technologies is to allow developers to define a fully-configured development environment. Fully-configured goes well beyond just defining a container with all of the application dependencies that the developer can develop in. Developer containers also have a Visual Studio Code Server running in them and this server can also be configured by the devcontainer.json file. 3 | 4 | One of the options is to configure the extensions that are installed in the code server running in the developer container. The "in the developer container" part of that sentence is important. [The following hold true for extensions configured in devcontainer.json](https://code.visualstudio.com/docs/remote/containers#_managing-extensions): 5 | - Some extensions have to run locally, including themes and snippets 6 | - Extensions that can be run on the server are **not** installed on your local Visual Studio Code Client. They **will be** installed in the Visual Studio Code Server running in the developer container. 7 | 8 | Beyond the devcontainer.json, it is possible to install extensions that are installed locally in Visual Studio Code in the remote container. This can be done via the Extensions tab. 9 | 10 | ### Rest Client - a helpful extension 11 | There is an extension that is very helpful when developing REST APIs. It is the [Rest Client](https://marketplace.visualstudio.com/items?itemName=humao). Among other things, it allows you to send HTTP requests from a file (*.http) and see the results in a separate panel. See the example below: 12 | ![Rest Client Extension](../Images/RestClientExtension.png) 13 | 14 | ## Challenge 15 | Install the [Rest Client extension](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) in the developer container and make a call to the add operation. 16 | 17 | ## Helpful Hints 18 | Make sure the API is running. -------------------------------------------------------------------------------- /Challenges/Challenge6.md: -------------------------------------------------------------------------------- 1 | # GitHub Codespaces 2 | As you read in the Readme.md file, devcontainers provide the ability to define fully-configured development environments. GitHub Codespaces builds upon devcontainers to allow for a one-click hosted development environment. The following are some of the key benefits of GitHub Codespaces: 3 | - The only prerequisite is a modern browser. While devcontainers have minimal prerequisites, they do require Docker and Visual Studio Code to be installed. 4 | - Codespaces allows developers to add features, fix bugs or review PRs without installing anything on their machine. 5 | 6 | This challenge will illustrate the power of GitHub Codespaces. 7 | 8 | # Challenge 9 | - Open the repository in GitHub Codespaces 10 | - Perform the following using the browser client 11 | - Run the API in debug mode 12 | - Set a breakpoint on line 10 in api/math_api.py 13 | - Click on 'Send Request' in math.http just above 14 | ```bash 15 | GET http://localhost:5000/add?x=100&y=10101 HTTP/1.1 16 | ``` 17 | - Debug through the call to add 18 | - Perform the same steps above using Visual Studio Code as the client 19 | 20 | # Helpful Hints 21 | - You have several choices with regard to the repository and branch you open in GitHub Codespaces: 22 | - You can use the Solution5 or Solution6 branch in https://github.com/RobBagby/codespaces-training 23 | - You can use fork https://github.com/RobBagby/codespaces-training and push to your own GitHub repository and 24 | - Use the Solution5 or Solution6 branch 25 | - Use master, as long as you have followed all the challenges 26 | - You have to have access to GitHub CodeSpaces in order to follow this challenge -------------------------------------------------------------------------------- /Images/PortForwardingDetection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cse-labs/codespaces101/8e5e926fe9e608a38ae19ccbddca92c8e2cfccb3/Images/PortForwardingDetection.png -------------------------------------------------------------------------------- /Images/PortForwardingPorts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cse-labs/codespaces101/8e5e926fe9e608a38ae19ccbddca92c8e2cfccb3/Images/PortForwardingPorts.png -------------------------------------------------------------------------------- /Images/PortUnknownProcess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cse-labs/codespaces101/8e5e926fe9e608a38ae19ccbddca92c8e2cfccb3/Images/PortUnknownProcess.png -------------------------------------------------------------------------------- /Images/RestClientExtension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cse-labs/codespaces101/8e5e926fe9e608a38ae19ccbddca92c8e2cfccb3/Images/RestClientExtension.png -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | I. [Overview](#overview)
3 |    A. [Why you care](#why-you-care)
4 |    B. [What you will learn](#what-you-will-learn)
5 | II. [How this training is structured](#how-this-training-is-structured)
6 | III. [Requirements](#requirements)
7 | IV. [Overview of Development containers, GitHub Codespaces And Visual Studio Code](#overview-of-development-containers-github-codespaces-and-visual-studio-code)
8 |    A. [Development Container](#development-container)
9 |    B. [Visual Studio Code](#visual-studio-code)
10 |    C. [GitHub Codespaces](#github-codespaces)
11 | V. [Challenges](#challenges)
12 |    A. [Challenge1](Challenges/Challenge1.md)
13 |    B. [Challenge2](Challenges/Challenge2.md)
14 |    C. [Challenge3](Challenges/Challenge3.md)
15 |    D. [Challenge4](Challenges/Challenge4.md)
16 |    E. [Challenge5](Challenges/Challenge5.md)
17 |    F. [Challenge6](Challenges/Challenge6.md)
18 | 19 | # Overview 20 | ## Why you care 21 | Traditionally, there is considerable friction for developers when setting up development environments. It is not uncommon for devs new to projects to spend days updating their environment before being able to start contributing to the project. 22 | 23 | The more complex the requirements, the greater the friction. Consider the following 2 examples: 24 | 1. Configuring a local Kubernetes development environment with the following: 25 | - Grafana 26 | - Prometheus 27 | - Fluentbit 28 | 2. A Python API with: 29 | - The current version of Python 30 | - Debugging configured 31 | - Pytest 32 | - Flask 33 | 34 | The above are 2 very real examples. The Retail Dev Crew team in CSE has been working with some of the largest Kubernetes deployments in the world. The dev environment includes everything listed above in example #1 plus much more. Despite the complex dev environment, the team prides itself upon new devs creating a PR on their first day. This is only possible because the Retail Dev Crew's use of development containers and GitHub Codespaces. 35 | 36 | Python environments are notoriously challenging to configure. This is especially true with regard to debugging. 37 | 38 | A large blocker to contributing to OSS projects is configuring the development environment. Imagine being able to instantiate a fully-configured development environment with the click of a button. That is the promise of development containers and GitHub Codespaces. 39 | 40 | ## What you will learn 41 | If you complete this self-directed training, you will: 42 | - Learn what development containers are 43 | - Learn what GitHub Codespaces are 44 | - Understand the relationship between Visual Studio Code, development containers and GitHub Codespaces 45 | - Learn how to build devcontainers 46 | - Using an existing docker image 47 | - Using the commands 48 | - onCreateCommand 49 | - postCreateCommand 50 | - postStartCommand 51 | - Creating a custom docker image 52 | - Updating the developer experience 53 | - Installing extensions 54 | - dotfiles 55 | - Patterns and best practices working with development containers and GitHub Codespaces 56 | 57 | # How this training is structured 58 | This GitHub repository has a master branch and a collection of solution branches. 59 | 60 | The master branch contains the following: 61 | - Readme.md - The main training file. Start here. 62 | - Challenges/* - The challenge files for this training. Each Challenge file will contain some learnings/background on the challenge, the challenge itself and, optionally, some helpful hints. 63 | - api/math_api.py - A very simple python Flask REST API 64 | - tests/api/test_math_api.py - Pytest unit tests 65 | - requirements.txt and dev_requirements.txt - Python requirements files containing the dependencies for the application and application development environment 66 | - math.http - A manual test file for use in Challenge 5 67 | 68 | Each challenge has its own solution branch. Use your git client to open each Solution branch. For example: 69 | ```bash 70 | git checkout Solution1 71 | 72 | # Or if you don't have the solution branch local 73 | git checkout --track origin/Solution1 74 | ``` 75 | 76 | Each branch contains the following: 77 | - Solution(1-N).md - File describing the solution. **This file may also contain a "From the Field" section where we list some of the learnings the CSE Retail Dev Crews team has had working with GitHub codespaces with our largest customers** 78 | - The solution configured in the .devcontainer folder 79 | - Solution(1-N).mp4 - A video outlining a solution to the challenge. **Open the videos from the file system, not Visual Studio Code** 80 | 81 | 82 | # Requirements 83 | You will need the following to complete the development container challenges in this training [(see detailed installation instructions here)](https://code.visualstudio.com/docs/remote/containers#_system-requirements): 84 | - Docker for Windows/Mac/Linux 85 | - Visual Studio Code 86 | 87 | You will need to be enabled for GitHub Codespaces in order to complete the codespaces challenges. [(see documentation here about getting access to Codespaces)](https://docs.github.com/en/codespaces/managing-codespaces-for-your-organization/enabling-codespaces-for-your-organization) 88 | 89 | 90 | 91 | # Overview of Development containers, GitHub Codespaces And Visual Studio Code 92 | The goal of these technologies is to allow developers to define a fully-configured development environment, run it in a container and develop against it with Visual Studio Code running as a client application or running in the browser. This section will provide a high-level overview of these technologies and how they interrelate. You will find links to more information throughout this section. 93 | 94 | ## Development Container 95 | As noted above, a development container is a fully-featured development environment running in a container. The development container is a Docker container running locally or remotely that, at a high-level, contains the following: 96 | - All the application dependencies - Defined in a Docker image, Dockerfile or docker-compose file and potentially updated via scripts called by well-defined hooks. 97 | - The application code - mounted, copied or cloned into the container 98 | - Visual Studio Code Server - configured with the appropriate Visual Studio Code Extensions required to develop 99 | 100 | [Default Images](https://docs.github.com/en/codespaces/customizing-your-codespace/configuring-codespaces-for-your-project#using-the-default-configuration) can be used for general development. However, for a more productive development experience, you will likely want to define your own development containers. The configuration for the development container is in a [devcontainer.json](https://code.visualstudio.com/docs/remote/devcontainerjson-reference) file which exists either at the root of the project or under a .devcontainer folder at the root. **From the field: We always put the devcontainer.json file under a .devcontainer folder. We do that because we always have additional files that accompany the devcontainer.json file. These files include bash scripts and a Dockerfile. We will get into more details about these files later.** During the challenges in this training you will explore and learn the common configuration patterns in a devcontainer.json file. For the time being, we will show you a [very simple example of a devcontainer.json file](https://code.visualstudio.com/docs/remote/containers#_create-a-devcontainerjson-file) taken from the documentation: 101 | 102 | ```json 103 | { 104 | "image": "mcr.microsoft.com/vscode/devcontainers/typescript-node:0-12", 105 | "forwardPorts": [3000], 106 | "extensions": ["dbaeumer.vscode-eslint"] 107 | } 108 | ``` 109 | Again, we will explore each of the above in more detail in the challenges. For now, it is enough to understand that the devcontainer.json points to an existing typescript-node image. This is the image that will be used when starting the developer (Docker) container. The configuration further specifies that port 3000 should be forwarded from the container to the host. Lastly, it specifies that a linting extension should be installed in the VS Code Server running in the developer container. 110 | 111 | ## Visual Studio Code 112 | Visual Studio Code has a [Remote-Containers Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) that enables the use of a Docker container as a fully configured development environment. This is enabled through a client-server architecture. As noted above, running development containers have a Visual Studio Code Server running in them. The Visual Studio Code Client can access a running container or can create an instance of a new development container and connect to it. 113 | 114 | The challenges will mainly be using Visual Studio Code to create and run development containers. 115 | 116 | ## GitHub Codespaces 117 | GitHub Codespaces enables exposing a fully configured development environment for GitHub repositories. This can be used for anthing from new feature development to code reviews. Codespaces extends the use of development containers by providing a remote hosting environment for them. Developers can simply click on a button in GitHub to open a Codespace for the repo. Behind the scenes, GitHub Codespaces is: 118 | - Spinning up a VM 119 | - Shallow cloning the repo in that VM. The shallow clone pulls the devcontainer.json onto the VM 120 | - Spins up the development container on the VM 121 | - Clones the repository in the development container 122 | - Connects you to the remotely hosted development container - via the browser or GitHub 123 | 124 | # The Challenges - Building a Devcontainer 125 | The challenges below are designed to provide a stepwise approach to building development containers. They start with the simplist approach, with each subsequent challenge teaching you a further aspect. Throughout the challeges, we will be providing real-world guidance that we have learned working with real customers in the field. 126 | - [Challenge1](Challenges/Challenge1.md) 127 | - [Challenge2](Challenges/Challenge2.md) 128 | - [Challenge3](Challenges/Challenge3.md) 129 | - [Challenge4](Challenges/Challenge4.md) 130 | - [Challenge5](Challenges/Challenge5.md) 131 | - [Challenge6](Challenges/Challenge6.md) 132 | 133 | -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cse-labs/codespaces101/8e5e926fe9e608a38ae19ccbddca92c8e2cfccb3/api/__init__.py -------------------------------------------------------------------------------- /api/math_api.py: -------------------------------------------------------------------------------- 1 | import flask 2 | 3 | app = flask.Flask(__name__) 4 | 5 | BAD_REQUEST = "Bad request. Add requires x and y in the querystring - .../add?x=1&y=2" 6 | 7 | @app.route('/add/', methods=['GET']) 8 | def add(): 9 | addend1 = flask.request.args.get('x', None, type=float) 10 | if addend1 is None or _is_number(addend1) is False: 11 | return BAD_REQUEST, 400 12 | 13 | addend2 = flask.request.args.get('y', None, type=float) 14 | if addend2 is None or _is_number(addend1) is False: 15 | return BAD_REQUEST, 400 16 | 17 | return str(addend1 + addend2) 18 | 19 | def _is_number(var): 20 | return isinstance(var, int) or isinstance(var, float) 21 | 22 | if __name__ == '__main__': 23 | app.run(debug=False, use_reloader=False) -------------------------------------------------------------------------------- /dev_requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | mock 3 | pytest-mock -------------------------------------------------------------------------------- /math.http: -------------------------------------------------------------------------------- 1 | # math_api app endpoints 2 | 3 | # add 4 | GET http://localhost:5000/add?x=5&y=3 HTTP/1.1 5 | 6 | ### 7 | 8 | # add again 9 | GET http://localhost:5000/add?x=100&y=10101 HTTP/1.1 10 | 11 | ### -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-restful -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cse-labs/codespaces101/8e5e926fe9e608a38ae19ccbddca92c8e2cfccb3/tests/__init__.py -------------------------------------------------------------------------------- /tests/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cse-labs/codespaces101/8e5e926fe9e608a38ae19ccbddca92c8e2cfccb3/tests/api/__init__.py -------------------------------------------------------------------------------- /tests/api/test_math_api.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import mock 3 | import flask 4 | import pdb; 5 | 6 | import api.math_api 7 | from api.math_api import add 8 | 9 | def test__add__x_not_passed__returns_400(mocker): 10 | app = flask.Flask(__name__) 11 | 12 | with app.test_request_context('/?y=4'): 13 | message, code = add() 14 | assert code == 400 15 | 16 | def test__add__y_not_passed__returns_400(mocker): 17 | app = flask.Flask(__name__) 18 | 19 | with app.test_request_context('/?x=4'): 20 | message, code = add() 21 | assert code == 400 22 | 23 | def test__add__x_not_numeric__returns_400(mocker): 24 | app = flask.Flask(__name__) 25 | 26 | with app.test_request_context('/?x=ggg&y=4'): 27 | message, code = add() 28 | assert code == 400 29 | 30 | def test__add__y_not_numeric__returns_400(mocker): 31 | app = flask.Flask(__name__) 32 | 33 | with app.test_request_context('/?x=4&y=ggg'): 34 | message, code = add() 35 | assert code == 400 36 | 37 | def test__add__z_and_y_integers_passed__returns_sum(mocker): 38 | app = flask.Flask(__name__) 39 | 40 | with app.test_request_context('/?x=4&y=5'): 41 | response = add() 42 | assert response == '9.0' 43 | 44 | def test__add__z_and_y_floats_passed__returns_sum(mocker): 45 | app = flask.Flask(__name__) 46 | 47 | with app.test_request_context('/?x=4.4&y=5.5'): 48 | response = add() 49 | assert response == '9.9' --------------------------------------------------------------------------------