├── noncloud └── python │ ├── sheets │ ├── Procfile │ ├── requirements.txt │ ├── app.yaml │ ├── templates │ │ └── index.html │ ├── .gcloudignore │ ├── noxfile.py │ ├── main.py │ ├── test_sheets.py │ └── README.md │ ├── maps │ ├── requirements.txt │ ├── Procfile │ ├── app.yaml │ ├── .dockerignore │ ├── settings-tmpl.py │ ├── app2.yaml │ ├── templates │ │ ├── index.html-orig │ │ └── index.html │ ├── .gcloudignore │ ├── noxfile.py │ ├── main.py │ ├── test_mapsgeo.py │ └── README.md │ └── README.md ├── cloud ├── python │ ├── Procfile │ ├── requirements.txt │ ├── Dockerfile │ ├── .dockerignore │ ├── templates │ │ └── index.html │ ├── app.yaml │ ├── appengine_config.py │ ├── .gcloudignore │ ├── noxfile.py │ ├── test_translate.py │ ├── main.py │ └── README.md ├── nodejs │ ├── package.json │ ├── app.yaml │ ├── templates │ │ └── index.html │ ├── test │ │ └── test_neb.js │ ├── .gcloudignore │ ├── index.js │ └── README.md └── README.md ├── .github └── CODEOWNERS ├── .gitignore ├── CONTRIBUTING.md ├── README.md └── LICENSE /noncloud/python/sheets/Procfile: -------------------------------------------------------------------------------- 1 | web: python main.py 2 | -------------------------------------------------------------------------------- /cloud/python/Procfile: -------------------------------------------------------------------------------- 1 | web: python main.py 2 | #web: gunicorn -b :$PORT -w 2 main:app 3 | -------------------------------------------------------------------------------- /noncloud/python/maps/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | googlemaps 3 | #requests-toolbelt 4 | -------------------------------------------------------------------------------- /noncloud/python/maps/Procfile: -------------------------------------------------------------------------------- 1 | web: python main.py 2 | #web: gunicorn -b :$PORT -w 2 main:app 3 | -------------------------------------------------------------------------------- /noncloud/python/sheets/requirements.txt: -------------------------------------------------------------------------------- 1 | #gunicorn 2 | flask 3 | #google-auth<2.0dev 4 | google-api-python-client #<1.12.0 5 | -------------------------------------------------------------------------------- /cloud/python/requirements.txt: -------------------------------------------------------------------------------- 1 | #gunicorn>=19.10.0 2 | grpcio<1.40.0; python_version < '3.0' 3 | flask 4 | google-cloud-translate 5 | -------------------------------------------------------------------------------- /cloud/python/Dockerfile: -------------------------------------------------------------------------------- 1 | #FROM python:3-slim 2 | FROM python:2-slim 3 | WORKDIR /app 4 | COPY requirements.txt . 5 | RUN pip install -r requirements.txt 6 | COPY . . 7 | ENTRYPOINT ["python", "main.py"] 8 | #ENTRYPOINT exec gunicorn -b :$PORT -w 2 main:app 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owners file. 2 | # This file controls who is tagged for review for any given pull request. 3 | # 4 | # For syntax help see: 5 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax 6 | 7 | # The python-samples-owners team is the default owner for anything not 8 | # explicitly taken by someone else. 9 | * @wescpy @GoogleCloudPlatform/python-samples-owners 10 | -------------------------------------------------------------------------------- /cloud/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloud-nebulous-serverless-nodejs", 3 | "version": "0.0.1", 4 | "description": "Nebulous Serverless sample app", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "mocha test/test_neb.js" 9 | }, 10 | "author": "Google LLC", 11 | "license": "Apache-2.0", 12 | "dependencies": { 13 | "@google-cloud/translate": "^6.3.1", 14 | "express": "^4.17.1", 15 | "nunjucks": "^3.2.3" 16 | }, 17 | "devDependencies": { 18 | "mocha": "^9.1.3", 19 | "supertest": "^6.1.6" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /cloud/nodejs/app.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | runtime: nodejs16 16 | -------------------------------------------------------------------------------- /noncloud/python/maps/app.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | runtime: python39 16 | -------------------------------------------------------------------------------- /noncloud/python/sheets/app.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | runtime: python39 16 | -------------------------------------------------------------------------------- /cloud/python/.dockerignore: -------------------------------------------------------------------------------- 1 | .gcloudignore 2 | 3 | # Source code control files 4 | .git/ 5 | .gitignore 6 | .hgignore 7 | .hg/ 8 | 9 | # Python 10 | *.py[cod] 11 | __pycache__/ 12 | /setup.cfg 13 | 14 | # README/text files 15 | LICENSE 16 | *.md 17 | 18 | # Installer logs 19 | pip-log.txt 20 | 21 | # Tests/results 22 | .nox/ 23 | .pytest_cache/ 24 | noxfile.py 25 | test_translate.py 26 | .coverage 27 | coverage.xml 28 | *sponge_log.xml 29 | system_tests/local_test_setup 30 | pylintrc 31 | pylintrc.test 32 | 33 | # Mac 34 | .DS_Store 35 | 36 | # IDEs/editors 37 | *.sw[op] 38 | *~ 39 | .vscode 40 | .idea 41 | 42 | # Built documentation 43 | docs/_build 44 | docs.metadata 45 | 46 | # Virtual environment 47 | env/ 48 | -------------------------------------------------------------------------------- /noncloud/python/README.md: -------------------------------------------------------------------------------- 1 | # Nebulous serverless apps demonstrating use of non-Cloud Google APIs 2 | ### Python (2 and 3) versions 3 | 4 | These apps are Python 2 and 3 compatible, deployable to all Cloud serverless platforms like the other sample apps in this repo. The featured APIs/apps and code are linked below. Also see the [Cloud API Python sample in the other folder](../cloud/python) featuring more complete coverage. 5 | 6 | 7 | ## Sample apps 8 | 9 | Featured API | Codelab link | Description 10 | --- | --- | --- 11 | [Google Sheets API](sheets) | _TBD_ | read display student information a public spreadsheet 12 | [Google Maps Geocoding API](maps) | _TBD_ | use of Maps Geocoding API to get location information 13 | -------------------------------------------------------------------------------- /noncloud/python/maps/.dockerignore: -------------------------------------------------------------------------------- 1 | .gcloudignore 2 | 3 | # Source code control files 4 | .git/ 5 | .gitignore 6 | .hgignore 7 | .hg/ 8 | 9 | # Python 10 | *.py[cod] 11 | __pycache__/ 12 | /setup.cfg 13 | settings.py 14 | 15 | # README/text files 16 | LICENSE 17 | *.md 18 | 19 | # Installer logs 20 | pip-log.txt 21 | 22 | # Tests/results 23 | .nox/ 24 | .pytest_cache/ 25 | noxfile.py 26 | test_*.py 27 | .coverage 28 | coverage.xml 29 | *sponge_log.xml 30 | system_tests/local_test_setup 31 | pylintrc 32 | pylintrc.test 33 | 34 | # Mac 35 | .DS_Store 36 | 37 | # IDEs/editors 38 | *.sw[op] 39 | *~ 40 | .vscode 41 | .idea 42 | 43 | # Built documentation 44 | docs/_build 45 | docs.metadata 46 | 47 | # Virtual environment 48 | env/ 49 | -------------------------------------------------------------------------------- /cloud/nodejs/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | My Google Translate 1990s 3 | 9 |

My Google Translate (1990s edition)

10 | 11 | {% if trans['text'] %} 12 |

Previous translation

13 |
  • Original: {{ orig['text'] }} ({{ orig['lc'][0] }})
  • 14 |
  • Translated: {{ trans['text'] }} ({{ trans['lc'][0] }})
  • 15 | {% endif %} 16 | 17 |

    Enter {{ orig['lc'][1] }} text to translate to {{ trans['lc'][1] }}:

    18 |
    19 | 20 | -------------------------------------------------------------------------------- /cloud/python/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | My Google Translate 1990s 3 | 9 |

    My Google Translate (1990s edition)

    10 | 11 | {% if trans['text'] %} 12 |

    Previous translation

    13 |
  • Original: {{ orig['text'] }} ({{ orig['lc'][0] }})
  • 14 |
  • Translated: {{ trans['text'] }} ({{ trans['lc'][0] }})
  • 15 | {% endif %} 16 | 17 |

    Enter {{ orig['lc'][1] }} text to translate to {{ trans['lc'][1] }}:

    18 |
    19 | 20 | -------------------------------------------------------------------------------- /noncloud/python/maps/settings-tmpl.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the 'License'); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an 'AS IS' BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | API_KEY = 'YOUR_API_KEY' # see developers.google.com/maps/documentation/javascript/get-api-key 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | *.py[cod] 3 | __pycache__/ 4 | /setup.cfg 5 | settings.py 6 | 7 | # JavaScript/Node.js 8 | node_modules* 9 | package-lock.json 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Packages 15 | *.egg 16 | *.egg-info 17 | dist 18 | build 19 | eggs 20 | .eggs 21 | parts 22 | bin 23 | var 24 | sdist 25 | develop-eggs 26 | .installed.cfg 27 | lib 28 | lib64 29 | 30 | # Installer logs 31 | pip-log.txt 32 | 33 | # Tests/results 34 | .nox/ 35 | .pytest_cache/ 36 | .cache 37 | .pytype 38 | .coverage 39 | coverage.xml 40 | *sponge_log.xml 41 | system_tests/local_test_setup 42 | credentials.json 43 | 44 | # Mac 45 | .DS_Store 46 | 47 | # IDEs/editors 48 | *.sw[op] 49 | *~ 50 | .vscode 51 | .idea 52 | 53 | # Built documentation 54 | docs/_build 55 | docs.metadata 56 | 57 | # Virtual environment 58 | env/ 59 | -------------------------------------------------------------------------------- /noncloud/python/sheets/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | Student Information 3 | 14 |

    Student Information

    15 | 16 | 17 | 18 | 21 | {% for col in headers %} 22 | 25 | {% endfor %} 26 | 27 | 28 | {% for student in students %} 29 | 30 | 33 | {% for col in student %} 34 | 37 | {% endfor %} 38 | 39 | {% endfor %} 40 |
    19 | # 20 | 23 | {{ col }} 24 |
    31 | {{ loop.index }} 32 | 35 | {{ col }} 36 |
    41 | 42 | 43 | -------------------------------------------------------------------------------- /noncloud/python/maps/app2.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | runtime: python27 16 | threadsafe: yes 17 | api_version: 1 18 | 19 | handlers: 20 | - url: /.* 21 | script: main.app 22 | 23 | libraries: 24 | - name: setuptools 25 | version: latest 26 | 27 | - name: ssl 28 | version: latest 29 | -------------------------------------------------------------------------------- /cloud/python/app.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #runtime: python310 16 | runtime: python27 17 | threadsafe: yes 18 | api_version: 1 19 | 20 | handlers: 21 | - url: /.* 22 | script: main.app 23 | 24 | libraries: 25 | - name: grpcio 26 | version: latest 27 | - name: setuptools 28 | version: latest 29 | -------------------------------------------------------------------------------- /cloud/python/appengine_config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pkg_resources 16 | from google.appengine.ext import vendor 17 | 18 | # Set PATH to your libraries folder. 19 | PATH = 'lib' 20 | # Add libraries installed in the PATH folder. 21 | vendor.add(PATH) 22 | # Add libraries to pkg_resources working set to find the distribution. 23 | pkg_resources.working_set.add_entry(PATH) 24 | -------------------------------------------------------------------------------- /noncloud/python/maps/templates/index.html-orig: -------------------------------------------------------------------------------- 1 | 2 | Street address geocoder 3 | 14 |

    Street address geocoder

    15 | 16 | {% if address %} 17 |

    Previous address information:

    18 |
  • Address: {{ address }}
  • 19 | {% if latlong %} 20 |
  • Complete address: {{ full_addr }} 21 |
  • Geocode location: {{ latlong }} (view in Google Maps)
  • 23 | {% endif %} 24 | {% endif %} 25 | 26 |

    Enter street address for its geocode:

    27 |
    28 | 29 | 30 |
    31 | 32 | -------------------------------------------------------------------------------- /noncloud/python/maps/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | Street address geocoder 3 | 14 |

    Street address geocoder

    15 | 16 | {% if results %} 17 |

    Location information for "{{ address }}":

    18 | {% for loc in results %} 19 | {% if loc['latlong'] %} 20 |
  • Complete address: {{ loc['full_addr'] }} 21 |
  • Geocode location: {{ loc['latlong'] }} (view in Google Maps)
  • 23 |
    24 | {% endif %} 25 | {% endfor %} 26 | {% else %} 27 |

    No location found for "{{ address }}"

    28 | {% endif %} 29 | 30 |

    Enter another street address for its geocode:

    31 |
    32 | 33 | 34 |
    35 | 36 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /cloud/nodejs/test/test_neb.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | const request = require('supertest'); 16 | const app = require('../index'); 17 | const request_app = request(app); 18 | 19 | describe('Unit Tests', () => { 20 | 21 | it('GET / should result in HTML w/"translate" in the body', done => { 22 | request_app 23 | .get('/') 24 | .expect(/translate/) 25 | .expect(200, done) 26 | }); 27 | 28 | it('POST / should have translated "hello world" correctly', done => { 29 | const SOURCE = 'hello world'; 30 | request_app 31 | .post('/') 32 | .send(`text=${SOURCE}`) 33 | .expect(/Hola Mundo/) 34 | .expect(200, done) 35 | }); 36 | 37 | after(() => 38 | console.log('DONE') 39 | ); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /cloud/nodejs/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | 11 | # JavaScript/Node.js 12 | node_modules/ 13 | test/ 14 | #package-lock.json 15 | 16 | # Source code control files 17 | .git/ 18 | .gitignore 19 | .hgignore 20 | .hg/ 21 | 22 | # README/text files 23 | *.md 24 | 25 | # Python 26 | *.py[cod] 27 | __pycache__/ 28 | /setup.cfg 29 | 30 | # C extensions 31 | *.so 32 | 33 | # Packages 34 | *.egg 35 | *.egg-info 36 | dist 37 | build 38 | eggs 39 | .eggs 40 | parts 41 | bin 42 | var 43 | sdist 44 | develop-eggs 45 | .installed.cfg 46 | lib64 47 | 48 | # Installer logs 49 | pip-log.txt 50 | 51 | # Tests/results 52 | .nox/ 53 | .pytest_cache/ 54 | noxfile.py 55 | test_translate.py 56 | .cache 57 | .pytype 58 | .coverage 59 | coverage.xml 60 | *sponge_log.xml 61 | system_tests/local_test_setup 62 | pylintrc 63 | pylintrc.test 64 | test/ 65 | credentials.json 66 | 67 | # Mac 68 | .DS_Store 69 | 70 | # IDEs/editors 71 | *.sw[op] 72 | *~ 73 | .vscode 74 | .idea 75 | 76 | # Built documentation 77 | docs/_build 78 | docs.metadata 79 | 80 | # Virtual environment 81 | env/ 82 | -------------------------------------------------------------------------------- /cloud/python/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | .dockerignore 11 | .gitignore 12 | 13 | # Source code control files 14 | .git/ 15 | .gitignore 16 | .hgignore 17 | .hg/ 18 | 19 | # README/text files 20 | LICENSE 21 | *.md 22 | 23 | # Tests/results (not in .gitignore) 24 | noxfile.py 25 | test_translate.py 26 | pylintrc 27 | pylintrc.test 28 | 29 | # most of .gitignore (except `lib`) 30 | # 31 | # Python 32 | *.py[cod] 33 | __pycache__/ 34 | /setup.cfg 35 | 36 | # C extensions 37 | *.so 38 | 39 | # Packages 40 | *.egg 41 | *.egg-info 42 | dist 43 | build 44 | eggs 45 | .eggs 46 | parts 47 | bin 48 | var 49 | sdist 50 | develop-eggs 51 | .installed.cfg 52 | lib64 53 | 54 | # Installer logs 55 | pip-log.txt 56 | 57 | # Tests/results 58 | .nox/ 59 | .pytest_cache/ 60 | .cache 61 | .pytype 62 | .coverage 63 | coverage.xml 64 | *sponge_log.xml 65 | system_tests/local_test_setup 66 | 67 | # Mac 68 | .DS_Store 69 | 70 | # IDEs/editors 71 | *.sw[op] 72 | *~ 73 | .vscode 74 | .idea 75 | 76 | # Built documentation 77 | docs/_build 78 | docs.metadata 79 | 80 | # Virtual environment 81 | env/ 82 | -------------------------------------------------------------------------------- /noncloud/python/sheets/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | .dockerignore 11 | .gitignore 12 | 13 | # Source code control files 14 | .git/ 15 | .gitignore 16 | .hgignore 17 | .hg/ 18 | 19 | # README/text files 20 | LICENSE 21 | *.md 22 | 23 | # Tests/results (not in .gitignore) 24 | noxfile.py 25 | test_translate.py 26 | pylintrc 27 | pylintrc.test 28 | 29 | # most of .gitignore (except `lib`) 30 | # 31 | # Python 32 | *.py[cod] 33 | __pycache__/ 34 | /setup.cfg 35 | 36 | # C extensions 37 | *.so 38 | 39 | # Packages 40 | *.egg 41 | *.egg-info 42 | dist 43 | build 44 | eggs 45 | .eggs 46 | parts 47 | bin 48 | var 49 | sdist 50 | develop-eggs 51 | .installed.cfg 52 | lib64 53 | 54 | # Installer logs 55 | pip-log.txt 56 | 57 | # Tests/results 58 | .nox/ 59 | .pytest_cache/ 60 | .cache 61 | .pytype 62 | .coverage 63 | coverage.xml 64 | *sponge_log.xml 65 | system_tests/local_test_setup 66 | 67 | # Mac 68 | .DS_Store 69 | 70 | # IDEs/editors 71 | *.sw[op] 72 | *~ 73 | .vscode 74 | .idea 75 | 76 | # Built documentation 77 | docs/_build 78 | docs.metadata 79 | 80 | # Virtual environment 81 | env/ 82 | -------------------------------------------------------------------------------- /noncloud/python/maps/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | .dockerignore 11 | .gitignore 12 | 13 | # Source code control files 14 | .git/ 15 | .gitignore 16 | .hgignore 17 | .hg/ 18 | 19 | # README/text files 20 | LICENSE 21 | *.md 22 | 23 | # Tests/results (not in .gitignore) 24 | noxfile.py 25 | test_translate.py 26 | pylintrc 27 | pylintrc.test 28 | 29 | # most of .gitignore (except `lib`) 30 | # 31 | # Python 32 | *.py[cod] 33 | __pycache__/ 34 | /setup.cfg 35 | #settings.py 36 | 37 | # C extensions 38 | *.so 39 | 40 | # Packages 41 | *.egg 42 | *.egg-info 43 | dist 44 | build 45 | eggs 46 | .eggs 47 | parts 48 | bin 49 | var 50 | sdist 51 | develop-eggs 52 | .installed.cfg 53 | lib64 54 | 55 | # Installer logs 56 | pip-log.txt 57 | 58 | # Tests/results 59 | .nox/ 60 | .pytest_cache/ 61 | .cache 62 | .pytype 63 | .coverage 64 | coverage.xml 65 | *sponge_log.xml 66 | system_tests/local_test_setup 67 | 68 | # Mac 69 | .DS_Store 70 | 71 | # IDEs/editors 72 | *.sw[op] 73 | *~ 74 | .vscode 75 | .idea 76 | 77 | # Built documentation 78 | docs/_build 79 | docs.metadata 80 | 81 | # Virtual environment 82 | env/ 83 | -------------------------------------------------------------------------------- /noncloud/python/maps/noxfile.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the 'License'); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an 'AS IS' BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import nox 16 | 17 | FLAKE8_COMMON_ARGS = [ 18 | '--show-source', 19 | '--builtin=gettext', 20 | '--max-complexity=20', 21 | '--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py', 22 | '--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202', 23 | '--max-line-length=88', 24 | '.', 25 | ] 26 | 27 | 28 | @nox.session(python=['2.7', '3.8', '3.9']) 29 | def tests(session): 30 | """ 31 | nox test session 32 | """ 33 | session.install('pytest', 'blinker', 'flask', 'googlemaps') 34 | session.run('pytest') 35 | 36 | 37 | @nox.session(python=['2.7', '3.8', '3.9']) 38 | def lint(session): 39 | """ 40 | nox lint session 41 | """ 42 | session.install('flake8') 43 | args = FLAKE8_COMMON_ARGS 44 | session.run('flake8', *args) 45 | -------------------------------------------------------------------------------- /cloud/python/noxfile.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the 'License'); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an 'AS IS' BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import nox 16 | 17 | FLAKE8_COMMON_ARGS = [ 18 | '--show-source', 19 | '--builtin=gettext', 20 | '--max-complexity=20', 21 | '--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py', 22 | '--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202', 23 | '--max-line-length=88', 24 | '.', 25 | ] 26 | 27 | 28 | @nox.session(python=['2.7', '3.6', '3.9']) 29 | def tests(session): 30 | """ 31 | nox test session 32 | """ 33 | session.install('pytest', 'blinker', 'flask', 'google-cloud-translate') 34 | session.run('pytest') 35 | 36 | 37 | @nox.session(python=['2.7', '3.6', '3.9']) 38 | def lint(session): 39 | """ 40 | nox lint session 41 | """ 42 | session.install('flake8') 43 | args = FLAKE8_COMMON_ARGS 44 | session.run('flake8', *args) 45 | -------------------------------------------------------------------------------- /noncloud/python/sheets/noxfile.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the 'License'); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an 'AS IS' BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import nox 16 | 17 | FLAKE8_COMMON_ARGS = [ 18 | '--show-source', 19 | '--builtin=gettext', 20 | '--max-complexity=20', 21 | '--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py', 22 | '--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202', 23 | '--max-line-length=88', 24 | '.', 25 | ] 26 | 27 | 28 | @nox.session(python=['2.7', '3.8', '3.9']) 29 | def tests(session): 30 | """ 31 | nox test session 32 | """ 33 | session.install('pytest', 'blinker', 'flask', 'google-auth', 'google-api-python-client') 34 | session.run('pytest') 35 | 36 | 37 | @nox.session(python=['2.7', '3.8', '3.9']) 38 | def lint(session): 39 | """ 40 | nox lint session 41 | """ 42 | session.install('flake8') 43 | args = FLAKE8_COMMON_ARGS 44 | session.run('flake8', *args) 45 | -------------------------------------------------------------------------------- /noncloud/python/sheets/main.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from flask import Flask, render_template 16 | import google.auth 17 | from googleapiclient import discovery 18 | 19 | app = Flask(__name__) 20 | 21 | # Use service account; can switch to OAuth client ID if desired 22 | CREDS, _PROJECT_ID = google.auth.default() 23 | SHEETS = discovery.build('sheets', 'v4', credentials=CREDS) 24 | 25 | # Quickstart Sheet: developers.google.com/sheets/api/quickstart/python 26 | SHEET_ID = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms' 27 | 28 | 29 | @app.route('/') 30 | def students(_req=None): 31 | """ 32 | read spreadsheet data and render in template 33 | """ 34 | 35 | # read data from Google Sheet 36 | rows = SHEETS.spreadsheets().values().get( 37 | spreadsheetId=SHEET_ID, 38 | range='Class Data', 39 | fields='values' 40 | ).execute().get('values', ['(no data)']) 41 | 42 | # create context & render template 43 | context = {'headers': rows[0], 'students': rows[1:]} 44 | return render_template('index.html', **context) 45 | 46 | 47 | if __name__ == '__main__': 48 | import os 49 | app.run(debug=True, threaded=True, host='0.0.0.0', 50 | port=int(os.environ.get('PORT', 8080))) 51 | -------------------------------------------------------------------------------- /noncloud/python/sheets/test_sheets.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from contextlib import contextmanager 16 | 17 | from flask import template_rendered 18 | import pytest 19 | 20 | from main import app as flask_app 21 | 22 | 23 | @pytest.fixture 24 | def app(): 25 | """ 26 | fixture connecting Flask app 27 | """ 28 | yield flask_app 29 | 30 | 31 | @contextmanager 32 | def captured_templates(app): 33 | """ 34 | capture template render requests 35 | """ 36 | recorded = [] 37 | 38 | def record(sender, template, context, **extra): 39 | recorded.append((template, context)) 40 | 41 | template_rendered.connect(record, app) 42 | try: 43 | yield recorded 44 | finally: 45 | template_rendered.disconnect(record, app) 46 | 47 | 48 | def test_sheets_get(app): 49 | """ 50 | test GET request to main application 51 | """ 52 | with captured_templates(app) as templates: 53 | rv = app.test_client().get('/') 54 | assert rv.status_code == 200 55 | assert len(templates) == 1 56 | template, context = templates[0] 57 | assert template.name == 'index.html' 58 | assert 'headers' in context and 'students' in context 59 | assert context['headers'][0] == 'Student Name' 60 | -------------------------------------------------------------------------------- /noncloud/python/maps/main.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from flask import Flask, render_template, request 16 | import googlemaps 17 | from settings import API_KEY 18 | 19 | app = Flask(__name__) 20 | GMAPS = googlemaps.Client(key=API_KEY) 21 | ADDRESS = '1600 Amphitheatre Pkwy 94043' 22 | 23 | 24 | @app.route('/', methods=['GET', 'POST']) 25 | def mapsgeo(gcf_request=None): 26 | """ 27 | main handler - show form and possibly previous translation 28 | """ 29 | 30 | # Flask Request object passed in for Cloud Functions 31 | # (use gcf_request for GCF but flask.request otherwise) 32 | local_request = gcf_request if gcf_request else request 33 | 34 | # reset all variables (GET) 35 | address = ADDRESS 36 | results = [] 37 | 38 | # form submission and if there is data to process (POST) 39 | if local_request.method == 'POST': 40 | address = local_request.form['address'].strip() 41 | if not address: 42 | address = ADDRESS 43 | rsp = GMAPS.geocode(address) 44 | if rsp: 45 | for data in rsp: 46 | if 'geometry' in data and 'location' in data['geometry']: 47 | geocode = data['geometry']['location'] 48 | results.append({ 49 | 'full_addr': data['formatted_address'], 50 | 'latlong': '%s, %s' % (geocode['lat'], geocode['lng']), 51 | }) 52 | 53 | # create context & render template 54 | context = {'address': address, 'results': results} 55 | return render_template('index.html', **context) 56 | 57 | 58 | if __name__ == '__main__': 59 | import os 60 | app.run(debug=True, threaded=True, host='0.0.0.0', 61 | port=int(os.environ.get('PORT', 8080))) 62 | -------------------------------------------------------------------------------- /cloud/nodejs/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | const express = require('express'); 16 | const nunjucks = require('nunjucks'); 17 | const {TranslationServiceClient} = require('@google-cloud/translate'); 18 | 19 | const app = express(); 20 | app.use(express.urlencoded({extended: true})); 21 | nunjucks.configure('templates', {autoescape: true, express: app}); 22 | const TRANSLATE = new TranslationServiceClient(); 23 | 24 | const PORT = process.env.PORT || 8080; 25 | const SOURCE = ['en', 'English']; 26 | const TARGET = ['es', 'Spanish']; 27 | let parent; 28 | TRANSLATE.getProjectId().then(result => { 29 | parent = `projects/${result}`; 30 | }); 31 | 32 | 33 | if (!process.env.FUNCTION_TARGET) { 34 | app.listen(PORT, () => 35 | console.log(`Listening on port ${PORT}`) 36 | ); 37 | } 38 | 39 | /** 40 | * main handler - show form and possibly previous translation 41 | */ 42 | async function translate(req, rsp) { 43 | // reset all variables (GET/POST) 44 | let text = null; 45 | let translated = null; 46 | 47 | // form submission and if there is data to process (POST) 48 | if (req.method === 'POST') { 49 | text = req.body.text.trim(); 50 | if (text) { 51 | const data = { 52 | contents: [text], 53 | parent: parent, 54 | targetLanguageCode: TARGET[0] 55 | }; 56 | const [response] = await TRANSLATE.translateText(data); 57 | translated = response.translations[0].translatedText; 58 | } 59 | } 60 | 61 | // create context & render template 62 | const context = { 63 | orig: {text: text, lc: SOURCE}, 64 | trans: {text: translated, lc: TARGET} 65 | }; 66 | rsp.render('index.html', context); 67 | } 68 | 69 | app.all('/', translate); 70 | module.exports = { 71 | app 72 | }; 73 | -------------------------------------------------------------------------------- /cloud/python/test_translate.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from contextlib import contextmanager 16 | 17 | from flask import template_rendered 18 | import pytest 19 | 20 | from main import app as flask_app 21 | 22 | 23 | @pytest.fixture 24 | def app(): 25 | """ 26 | fixture connecting Flask app 27 | """ 28 | yield flask_app 29 | 30 | 31 | @contextmanager 32 | def captured_templates(app): 33 | """ 34 | capture template render requests 35 | """ 36 | recorded = [] 37 | 38 | def record(sender, template, context, **extra): 39 | recorded.append((template, context)) 40 | 41 | template_rendered.connect(record, app) 42 | try: 43 | yield recorded 44 | finally: 45 | template_rendered.disconnect(record, app) 46 | 47 | 48 | def test_translate_get(app): 49 | """ 50 | test GET request to main application 51 | """ 52 | with captured_templates(app) as templates: 53 | rv = app.test_client().get('/') 54 | assert rv.status_code == 200 55 | assert len(templates) == 1 56 | template, context = templates[0] 57 | assert template.name == 'index.html' 58 | assert 'orig' in context and 'trans' in context 59 | assert len(context['orig']) == len(context['trans']) == 2 60 | 61 | 62 | def test_translate_post(app): 63 | """ 64 | test POST request to main application 65 | """ 66 | SOURCE, TARGET = 'hello world', 'Hola Mundo' 67 | with captured_templates(app) as templates: 68 | rv = app.test_client().post('/', data={'text': SOURCE}) 69 | assert rv.status_code == 200 70 | assert len(templates) == 1 71 | template, context = templates[0] 72 | assert template.name == 'index.html' 73 | assert 'orig' in context and 'trans' in context 74 | assert context['orig']['text'] == SOURCE 75 | assert context['trans']['text'] == TARGET 76 | -------------------------------------------------------------------------------- /cloud/python/main.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from flask import Flask, render_template, request 16 | import google.auth 17 | from google.cloud import translate 18 | 19 | app = Flask(__name__) 20 | _, PROJECT_ID = google.auth.default() 21 | TRANSLATE = translate.TranslationServiceClient() 22 | PARENT = 'projects/{}'.format(PROJECT_ID) 23 | SOURCE, TARGET = ('en', 'English'), ('es', 'Spanish') 24 | 25 | 26 | @app.route('/', methods=['GET', 'POST']) 27 | def translate(gcf_request=None): 28 | """ 29 | main handler - show form and possibly previous translation 30 | """ 31 | 32 | # Flask Request object passed in for Cloud Functions 33 | # (use gcf_request for GCF but flask.request otherwise) 34 | local_request = gcf_request if gcf_request else request 35 | 36 | # reset all variables (GET/POST) 37 | text = translated = None 38 | 39 | # form submission and if there is data to process (POST) 40 | if local_request.method == 'POST': 41 | text = local_request.form['text'].strip() 42 | if text: 43 | data = { 44 | 'contents': [text], 45 | 'parent': PARENT, 46 | 'target_language_code': TARGET[0], 47 | } 48 | # handle older call for backwards-compatibility 49 | try: 50 | rsp = TRANSLATE.translate_text(request=data) 51 | except TypeError: 52 | rsp = TRANSLATE.translate_text(**data) 53 | translated = rsp.translations[0].translated_text 54 | 55 | # create context & render template 56 | context = { 57 | 'orig': {'text': text, 'lc': SOURCE}, 58 | 'trans': {'text': translated, 'lc': TARGET}, 59 | } 60 | return render_template('index.html', **context) 61 | 62 | 63 | if __name__ == '__main__': 64 | import os 65 | app.run(debug=True, threaded=True, host='0.0.0.0', 66 | port=int(os.environ.get('PORT', 8080))) 67 | -------------------------------------------------------------------------------- /noncloud/python/maps/test_mapsgeo.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from contextlib import contextmanager 16 | 17 | from flask import template_rendered 18 | import pytest 19 | 20 | from main import ADDRESS, app as flask_app 21 | BAD_ADDR = '1 ave 90000' 22 | MLT_ADDR = 'york' # (1-10), 'springfield' (1-10), 'dakota' (1-2) 23 | 24 | 25 | @pytest.fixture 26 | def app(): 27 | """ 28 | fixture connecting Flask app 29 | """ 30 | yield flask_app 31 | 32 | 33 | @contextmanager 34 | def captured_templates(app): 35 | """ 36 | capture template render requests 37 | """ 38 | recorded = [] 39 | 40 | def record(sender, template, context, **extra): 41 | recorded.append((template, context)) 42 | 43 | template_rendered.connect(record, app) 44 | try: 45 | yield recorded 46 | finally: 47 | template_rendered.disconnect(record, app) 48 | 49 | 50 | def test_mapsgeo_get(app): 51 | """ 52 | test GET request to main application 53 | """ 54 | with captured_templates(app) as templates: 55 | rv = app.test_client().get('/') 56 | assert rv.status_code == 200 57 | assert len(templates) == 1 58 | template, context = templates[0] 59 | assert template.name == 'index.html' 60 | assert len(context['address']) > 1 and len(context['results']) == 0 61 | 62 | 63 | def test_mapsgeo_post(app): 64 | """ 65 | test POST request to main application 66 | """ 67 | with captured_templates(app) as templates: 68 | rv = app.test_client().post('/', data={'address': ADDRESS}) 69 | assert rv.status_code == 200 70 | assert len(templates) == 1 71 | template, context = templates[0] 72 | assert 'address' in context and 'results' in context 73 | assert '37.422388, -122.0841883' == context['results'][0]['latlong'] 74 | 75 | 76 | def test_mapsgeo_post_fail(app): 77 | """ 78 | test POST request to main application 79 | """ 80 | with captured_templates(app) as templates: 81 | rv = app.test_client().post('/', data={'address': BAD_ADDR}) 82 | assert rv.status_code == 200 83 | assert len(templates) == 1 84 | template, context = templates[0] 85 | assert template.name == 'index.html' 86 | assert 'address' in context and 'results' in context 87 | assert len(context['results']) == 0 88 | 89 | 90 | def test_mapsgeo_post_sometimes_multi(app): 91 | """ 92 | test POST request to main application 93 | """ 94 | with captured_templates(app) as templates: 95 | rv = app.test_client().post('/', data={'address': MLT_ADDR}) 96 | assert rv.status_code == 200 97 | assert len(templates) == 1 98 | template, context = templates[0] 99 | assert 'address' in context and 'results' in context 100 | assert len(context['results']) >= 1 101 | -------------------------------------------------------------------------------- /cloud/nodejs/README.md: -------------------------------------------------------------------------------- 1 | # Nebulous serverless Cloud Translation API app 2 | 3 | ## Node.js version 4 | 5 | The Node.js version of this app and its deployments are simpler than the [Python equivalent](../python), primarily due to the differences between Python 2 and 3, and also because Node.js is not an App Engine legacy runtime. As a result, there is a single [Node.js codelab](https://codelabs.developers.google.com/codelabs/cloud-nebulous-serverless-nodejs?utm_source=codelabs&utm_medium=et&utm_campaign=CDR_wes_aap-serverless_nebservnodejs_sms_201130&utm_content=-) for deploying this app while there are seven for Python. 6 | 7 | 8 | ## Deployments and their files 9 | 10 | File | Description 11 | --- | --- 12 | [`index.js`](index.js) | main application file 13 | [`templates/index.html`](templates/index.html) | application HTML template 14 | [`package.json`](package.json) | 3rd-party package requirements file 15 | [`app.yaml`](app.yaml) | App Engine configuration file (only for App Engine deployments) 16 | [`test/test_neb.js`](test/test_neb.js) | unit tests (`mocha` & `supertest`) 17 | [`.gcloudignore`](.gcloudignore) | files to exclude deploying to the cloud (administrative) 18 | `README.md` | this file (administrative) 19 | 20 | Below are the required settings and instructions for all (documented) deployments; administrative files are not discussed. The `app.yaml` file is only used for specific deployments and can be deleted for the others. 21 | 22 | > NOTE: Serverless deployments (as configured here) use [default service accounts](https://cloud.google.com/iam/docs/service-accounts#default) which provide a broad set of permissions to assist you in getting a working prototype. When preparing to launch to production, the Google Cloud team recommends the best practice of "least privileges," and instead use [user-managed service accounts](https://cloud.google.com/iam/docs/service-accounts#user-managed) with the minimal set of permissions allowing your app to function properly. 23 | 24 | 25 | ## **Local Express server (Node 10, 17)** 26 | 27 | **TL;DR:** application files (`index.js` & `package.json`). Instructions: 28 | 29 | 1. **Run** `npm install` (to install packages locally) 30 | 1. **Run** `gcloud auth application-default login` to set your credentials 31 | 1. **Run** `npm start` to run locally 32 | 33 | 34 | ## **App Engine (Node 10, 12, 14, 16)** 35 | 36 | **TL;DR:** application files plus `app.yaml`. You may (first) edit `app.yaml` to specify the desired Node version (default: Node 16). Instruction(s): 37 | 38 | 1. **Run** `gcloud app deploy` to deploy to App Engine 39 | - You'll be prompted for the REGION if deploying to App Engine the first time. 40 | - App Engine apps are tied to one region, so it can't be changed once it's set, meaning you won't be prompted thereafter. 41 | 42 | 43 | ## **Cloud Functions (Node 10, 12, 14, 16)** 44 | 45 | **TL;DR:** Uses only the application files. Instruction(s): 46 | 47 | 1. **Run** `gcloud functions deploy translate --runtime nodejs16 --entry-point app --trigger-http --allow-unauthenticated` to deploy to Cloud Functions (or Node 10, 12, 14) 48 | - You'll be prompted for the REGION if deploying a Cloud Function the first time. 49 | - Cloud Functions can be deployed to different regions within a project, but once the region has been set for a function, it cannot be changed. 50 | 51 | The command creates & deploys a new HTTP-triggered Cloud Function named `translate`. Cloud Functions is directed to call the application object, `app`, via `--entry-point`. During execution `translate()` is called by `app`. In the [Python version](../python), `--entry-point` is unnecessary because `translate()` *is* the application entry point. 52 | 53 | 54 | ## **Cloud Run (Node 10+ via Cloud Buildpacks)** 55 | 56 | **TL;DR:** Uses only the application files. Instruction(s): 57 | 58 | 1. **Run** `gcloud run deploy translate --allow-unauthenticated --platform managed --source .` to deploy to Cloud Run 59 | - You'll be prompted to provide a REGION unless you also add `--region REGION` on the cmd-line which will give you a full non-interactive deploy 60 | - A `Dockerfile` is optional, but if you wish to create one, place it in the top-level folder so the build system can access it. Also see the [Python version's `Dockerfile`](../python/Dockerfile) to get an idea of what a Node equivalent would be similar to. 61 | 62 | 63 | ## References 64 | 65 | These are relevant links only to the app in this folder (for all others, see the [README one level up](../README.md): 66 | 67 | - [Node.js App Engine quickstart](https://cloud.google.com/appengine/docs/standard/nodejs/quickstart) 68 | - [Nodejs App Engine (standard environment) runtime](https://cloud.google.com/appengine/docs/standard/nodejs/runtime) 69 | - [Node.js Cloud Functions quickstart](https://cloud.google.com/functions/docs/quickstart-nodejs) 70 | - [Node.js Cloud Run quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy/nodejs) 71 | - [Express.js](https://expressjs.com) 72 | 73 | 74 | ## Testing 75 | 76 | Testing is driven by [`mocha`](https://mochajs.org) which uses [`supertest`](https://github.com/visionmedia/supertest) for testing and [`eslint`](https://eslint.org) for linting, installing both in virtual environments along with application dependencies, `express`, `nunjucks`, and `@google-cloud/translate`. To run the unit tests (testing `GET` and `POST` requests), run `npm install` followed by `npm test`). 77 | 78 | 79 | ### Expected output 80 | 81 | ``` 82 | $ npm test 83 | 84 | > cloud-nebulous-serverless-nodejs@0.0.1 test 85 | > mocha test/test_neb.js 86 | 87 | Listening on port 8080 88 | 89 | 90 | Our application 91 | ✔ GET / should result in HTML w/"translate" in the body 92 | ✔ POST / should have translated "hello world" correctly (140ms) 93 | DONE 94 | 95 | 96 | 2 passing (170ms) 97 | ``` 98 | 99 | ### Troubleshooting 100 | 101 | When running the test, there's a situation which resulting in the test hanging like this: 102 | 103 | ``` 104 | $ npm test 105 | 106 | > cloud-nebulous-serverless-nodejs@0.0.1 test 107 | > mocha test/test_neb.js 108 | 109 | Listening on port 8080 110 | 111 | 112 | Our application 113 | ✔ GET / should result in HTML w/"translate" in the body 114 | 1) POST / should have translated "hello world" correctly 115 | DONE 116 | 117 | 118 | 1 passing (2s) 119 | 1 failing 120 | 121 | 1) Our application 122 | POST / should have translated "hello world" correctly: 123 | Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/tmp/cloud-nebulous-serverless/cloud/nodejs/test/test_neb.js) 124 | at listOnTimeout (node:internal/timers:557:17) 125 | at processTimers (node:internal/timers:500:7) 126 | 127 | ``` 128 | *(hangs here)* 129 | 130 | If this happens to you, **run** `gcloud auth application-default login` to set your credentials and try again. 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | | :boom: ALERT!! | 2 | |:---------------------------| 3 | | This repo will soon be relocating to [GoogleCloudPlatform](https://github.com/GoogleCloudPlatform) as we better organize these code samples! Stay tuned as more info is coming soon. | 4 | 5 | 6 | # Nebulous Google Cloud serverless & API sample applications 7 | ### Run the same apps locally, on App Engine, Cloud Functions, or Cloud Run 8 | 9 | ## Description 10 | 11 | This is the repo for a set of sample apps and corresponding codelabs (self-paced, hands-on tutorials) demonstrating how to call Google APIs from [Google Cloud serverless compute platforms](https://cloud.google.com/serverless) (App Engine, Cloud Functions, Cloud Run). More on each platform below. Working wth [Cloud APIs](cloud) differs from [non-Cloud Google APIs](noncloud), so that is how the samples are organized. The common aspect of all of sample apps is that they can be run locally or deployed to any of the 3 platforms without code changes (all done in configuration). 12 | 13 | 14 | ## Hosting options 15 | 16 | - **[App Engine](https://cloud.google.com/appengine)** (standard environment) — source-based application deployments (app-hosting in the cloud; "PaaS") 17 | - _App Engine_ is for users who wish to deploy a traditional (but not containerized) web stack (LAMP, MEAN, etc.) application direct from source code. 18 | - **[Cloud Functions](https://cloud.google.com/functions)** — cloud-hosted functions or microservices ("FaaS"), possibly event-driven 19 | - If your app is relatively simple, is a single function, or perhaps some event-driven microservice, _Cloud Functions_ may be the right platform for you. 20 | - **[Cloud Run](https://cloud.run)** — fully-managed serverless container-hosting in the cloud ("CaaS") service 21 | - If your apps are containerized or you have containerization as part of your software development workflow, use _Cloud Run_. Containers free developers from any language, library, or binary restrictions with App Engine or Cloud Functions. 22 | 23 | A "fourth" product, [App Engine flexible environment](https://cloud.google.com/appengine/docs/flexible), which sits somewhere between App Engine standard environment and Cloud Run, is out-of-scope for these sample apps at this time. 24 | 25 | When running on App Engine or Cloud Functions, the Python runtime supplies a default web server (`gunicorn`), but for Node.js, [Express.js](http://expressjs.com) was selected. No default servers are available at all for Cloud Run, so Python developers can either run the [Flask](https://flask.palletsprojects.com) development server (default) or self-bundle `gunicorn` (per your configuration). All Node.js deployments specify Express.js. 26 | 27 | 28 | ## Inspiration and implementation 29 | 30 | These samples were inspired by a [user's suboptimal experience](https://www.mail-archive.com/google-appengine@googlegroups.com/msg94549.html) trying to create a simple App Engine app using a Cloud API. This was followed-up with the realization that there aren't enough examples showing users how to access non-Cloud Google APIs from serverless, hence *those* samples. 31 | 32 | The table below outlines the development languages, supported versions, deployments tested, and selected web frameworks (whose bundled development servers are used for running locally): 33 | 34 | Language | Versions | Deployment | Framework 35 | --- | --- | --- | --- 36 | Python|2.7|local, cloud|Flask 37 | Python|3.6+|local, cloud|Flask 38 | Node.js|10, 17|local|Express.js 39 | Node.js|10, 12, 14, 16|cloud|Express.js 40 | 41 | 42 | ## Cost 43 | 44 | While many Google APIs can be used without fees, use of GCP products & APIs is _not_ free. Certain products do offer an ["Always Free" tier](https://cloud.google.com/free/docs/gcp-free-tier#free-tier-usage-limits) which you have to exceed in order to be billed. Reference any relevant pricing information linked below before doing so. 45 | 46 | - [App Engine](https://cloud.google.com/appengine/pricing) 47 | - [Cloud Functions](https://cloud.google.com/functions/pricing) 48 | - [Cloud Run](https://cloud.google.com/run/pricing) 49 | - [GCP general pricing](https://cloud.google.com/pricing) 50 | - [GCP pricing calculator](https://cloud.google.com/products/calculator) 51 | 52 | When enabling services, you may be asked for an active billing account which requires a financial instrument such as a credit card. Reference relevant pricing information before doing so. While Cloud Functions and Cloud Run share a similar Always Free tier and pricing model, App Engine is slightly different. 53 | 54 | Furthermore, deploying to GCP serverless platforms incur [minor build and storage costs](https://cloud.google.com/appengine/pricing#pricing-for-related-google-cloud-products). [Cloud Build](https://cloud.google.com/build/pricing) has its own free quota as does [Cloud Storage](https://cloud.google.com/storage/pricing#cloud-storage-always-free). For greater transparency, Cloud Build builds your application image which is then sent to the [Cloud Container Registry](https://cloud.google.com/container-registry/pricing), or [Artifact Registry](https://cloud.google.com/artifact-registry/pricing), its successor; storage of that image uses up some of that (Cloud Storage) quota as does network egress when transferring that image to the service you're deploying to. However you may live in region that does not have such a free tier, so be aware of your storage usage to minimize potential costs. (You may look at what storage you're using and how much, including deleting build artifacts via [your Cloud Storage browser](https://console.cloud.google.com/storage/browser).) 55 | 56 | More specific cost information for each sample is available in their respective README files. 57 | 58 | 59 | ### Academic use 60 | 61 | #### Teaching and research grants 62 | 63 | If you are a faculty member or lecturer at a regionally-accredited, degree-granting, and not-for-profit higher ed institution in one of [75+ supported countries worldwide](https://support.google.com/google-cloud-higher-ed/answer/10723190) teaching a course where students are expected to code or use resources in the cloud, you may be eligible to grant your students (plus yourself and teaching assistants) free usage of Google Cloud. Explore faculty resources at to learn more about our _education (teaching and [initial] research) grants_. **@Students:** send your instructors there so they can get you access to Google Cloud. 64 | 65 | 66 | #### Cloud computing curriculum 67 | 68 | If you are an educator who wishes to add cloud computing to your curriculum or seek to enhance it with Google Cloud teaching materials, take a look at our [free 10-module, 40-hour complete course](https://cloud.google.com/edu/curriculum). 69 | 70 | 71 | #### Hands-on training for self-paced online tutorials 72 | 73 | For a hands-on learning experience on all aspects of Google Cloud, both students and faculty can get their hands on free _training credits_ for [Cloud Skills Boost hands-on labs powered by QwikLabs](https://cloudskillsboost.google). Apply for those credits at as well. 74 | 75 | 76 | ## Testing 77 | 78 | Each app has its own testing battery; refer to each sample's folder to learn about implemented tests. 79 | -------------------------------------------------------------------------------- /cloud/README.md: -------------------------------------------------------------------------------- 1 | # Nebulous serverless app demonstrating use of Google Cloud APIs 2 | ### Featuring the Cloud Translation API 3 | 4 | ## Description 5 | 6 | This is the code repo for a set of codelab tutorials highlighting a "nebulous" sample app demonstrating how to access Google Cloud APIs from one of our serverless platforms. There are [Python](python) and [Node.js](nodejs) versions of this app available. 7 | 8 | ### Related content 9 | 10 | - [Cloud serverless platforms intro video](https://youtu.be/gle26fT28Bw?list=PL2pQQBHvYcs0PEecTcLD9_VaLvuhK0_VQ?utm_source=youtube&utm_medium=unpaidsoc&utm_campaign=CDR_wes_aap-serverless_nebservconcept_neb_202007&utm_content=info_card) 11 | - [Nebulous serverless app deployments video](https://youtu.be/eTotLOVR7MQ?list=PL2pQQBHvYcs0PEecTcLD9_VaLvuhK0_VQ?utm_source=youtube&utm_medium=unpaidsoc&utm_campaign=CDR_wes_aap-serverless_nebservdeploy_neb_2021xx&utm_content=info_card) 12 | - [Calling Cloud APIs from serverless blog post](https://cloud.google.com/blog/topics/developers-practitioners/calling-google-apis-serverless-part-i-cloud-apis?utm_source=blog&utm_medium=partner&utm_campaign=CDR_wes_aap-serverless_nebservconcept_neb_202007) 13 | - Nebulous serverless app deployments blog post (_coming soon_) 14 | - Calling non-Cloud Google APIs from serverless blog post (_TBD_) 15 | 16 | 17 | ## Inspiration and implementation 18 | 19 | This code sample was inspired by a [user's suboptimal experience](https://www.mail-archive.com/google-appengine@googlegroups.com/msg94549.html) trying to create a simple App Engine app using a Cloud API. It was also inspired by a [colleague's blog post](https://dev.to/googlecloud/portable-code-migrating-across-google-cloud-s-serverless-platforms-2ifk) showing a similar Node.js example "drifting" between GCP serverless platforms. 20 | 21 | This app shows developers how to use the [Cloud Translation API](https://cloud.google.com/translate), the API for [Google Translate](https://translate.google.com), and one of GCP's [AI/ML "building block" APIs](https://web.archive.org/web/20210308144225/https://cloud.google.com/products/ai/building-blocks). Such APIs are backed by pre-trained machine learning models, allowing developers with little or no background in AI/ML to leverage machine learning with only API calls. The application implements a mini Google Translate "MVP" (minimally-viable product) web service. 22 | 23 | 24 | ## Authentication and authorization: service account credentials (local deployments only) 25 | 26 | Google Cloud compute platforms feature [default service account credentials](https://cloud.google.com/iam/docs/service-accounts#default) which are used for these app deployments. However, such credentials are not available when running locally, so users will need to **run** `gcloud auth application-default login` before starting the server. 27 | 28 | 29 | ## Cost 30 | 31 | While the Translation API does not explicitly list a free quota on the ["Always Free" tier page](https://cloud.google.com/free/docs/gcp-free-tier#free-tier-usage-limits), its [pricing page](https://cloud.google.com/translate/pricing#charged-characters) indicates a certain number of [translated characters](https://cloud.google.com/translate/pricing#charged-characters) as a free monthly quota applied as a credit, so long as you stay within that limit, you should not incur any additional charges. 32 | 33 | 34 | ## Enable Google Cloud services used 35 | 36 | Once you have a billing account, you can enable the services/APIs for each product used. Go to the Cloud console pages for each respective Cloud product used and enable the service: 37 | 38 | 1. [App Engine](https://console.cloud.google.com/appengine) 39 | 1. [Cloud Functions](https://console.cloud.google.com/functions) 40 | 1. [Cloud Run](https://console.cloud.google.com/run) 41 | 1. [Cloud Translation](https://console.cloud.google.com/apis/api/translate.googleapis.com) 42 | 43 | Alternatively, you use the [`gcloud` CLI (command-line interface)](https://cloud.google.com/sdk/gcloud) available from the [Cloud SDK](https://cloud.google.com/sdk). Review the [Cloud SDK install instructions](https://cloud.google.com/sdk/docs/quickstart) if needed. New users should also reference the [`gcloud` cheatsheet](https://cloud.google.com/sdk/docs/cheatsheet). 44 | 45 | Enable all 4 services with this one `gcloud` command: `gcloud services enable translate.googleapis.com run.googleapis.com cloudfunctions.googleapis.com appengine.googleapis.com` 46 | 47 | 48 | ## The application itself 49 | 50 | The app consists of a simple web page prompting the user for a phrase to translate from English to Spanish. The translated results along with the original phrase are presented along with an empty form for a follow-up translation if desired. 51 | 52 | This is what the app looks like after completing one translation (Cloud Run version): 53 | 54 | ![app screenshot](https://user-images.githubusercontent.com/1102504/133918482-fc66d512-aeb7-4982-bcd2-75794cd21349.png) 55 | 56 | 57 | ## References 58 | 59 | 1. Google Cloud serverless product pages 60 | - App Engine 61 | - [App Engine home page](https://cloud.google.com/appengine) 62 | - [App Engine documentation](https://cloud.google.com/appengine/docs) 63 | - [Default service accounts](https://cloud.google.com/appengine/docs/standard/nodejs/service-account) 64 | - Cloud Functions 65 | - [Cloud Functions home page](https://cloud.google.com/functions) 66 | - [Cloud Functions documentation](https://cloud.google.com/functions/docs) 67 | - [Default service accounts](https://cloud.google.com/functions/docs/concepts/iam#access_control_for_service_accounts) 68 | - Cloud Run 69 | - [Cloud Run home page](https://cloud.run) 70 | - [Cloud Run documentation](https://cloud.google.com/run/docs) 71 | - [Default service accounts](https://cloud.google.com/run/docs/securing/service-identity) 72 | 73 | 1. Cloud SDK and `gcloud` product pages 74 | - [Cloud SDK](https://cloud.google.com/sdk) 75 | - [Cloud SDK installation](https://cloud.google.com/sdk/docs/quickstart) 76 | - [`gcloud` CLI](https://cloud.google.com/sdk/gcloud) (command-line interface) 77 | - [`gcloud` cheatsheet](https://cloud.google.com/sdk/docs/cheatsheet) 78 | 79 | 1. Cloud build-relevant product pages 80 | - [Cloud Buildpacks announcement](https://cloud.google.com/blog/products/containers-kubernetes/google-cloud-now-supports-buildpacks) 81 | - [Cloud Buildpacks repo](https://github.com/GoogleCloudPlatform/buildpacks) 82 | - [Cloud Artifact Registry home page](https://cloud.google.com/artifact-registry) 83 | - [Cloud Artifact Registry documentation](https://cloud.google.com/artifact-registry/docs) 84 | 85 | 1. Google AI/ML API product pages 86 | - [Cloud Translation home page](https://cloud.google.com/translate) 87 | - [Cloud Translation documentation](https://cloud.google.com/translate/docs) 88 | - [Cloud Translation Python client library (v3 for 3.x)](https://cloud.google.com/translate/docs/reference/libraries/v3/python) 89 | - [Cloud Translation Python client library (v2 for 2.x)](https://cloud.google.com/translate/docs/reference/libraries/v2/python) 90 | - [Translation API pricing page](https://cloud.google.com/translate/pricing) 91 | - [All Cloud AI/ML "building block" APIs](https://web.archive.org/web/20210308144225/https://cloud.google.com/products/ai/building-blocks) 92 | - [Google ML Kit (Cloud AI/ML API subset for mobile)](https://developers.google.com/ml-kit) 93 | - [Google ML Kit Translation API](https://developers.google.com/ml-kit/language/translation) 94 | 95 | 1. Other Google Cloud documentation 96 | - [Google Cloud Python support](https://cloud.google.com/python) 97 | - [Google Cloud client libraries](https://cloud.google.com/apis/docs/cloud-client-libraries) 98 | - [Google Cloud "Always Free" tier](https://cloud.google.com/free/docs/gcp-free-tier#free-tier-usage-limits) 99 | - [All Google Cloud documentation](https://cloud.google.com/docs) 100 | 101 | 1. External links 102 | - [Docker](https://docker.com) 103 | - [`Dockerfile` documentation](https://docs.docker.com/engine/reference/builder) 104 | - [`Dockerfile` best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices) 105 | - [`Procfile` documentation](https://devcenter.heroku.com/articles/procfile) 106 | - [CNCF Buildpacks open spec](https://buildpacks.io) 107 | -------------------------------------------------------------------------------- /noncloud/python/maps/README.md: -------------------------------------------------------------------------------- 1 | # Nebulous serverless Google Maps Geocoding API app 2 | ### Python (2 and 3) version 3 | 4 | This sample is offered in Python 3, but the code itself is Python 2-compatible. Instructions on running it under 2.x will leverage the resources of the [Cloud API Python sample](https:/tmp/github.com/googlecodelabs/cloud-nebulous-serverless/tree/main/cloud/python) which provides a more complete experiencce as it has all configuration available to support 2.x deployments. The app is tested against Python 2.7, 3.8, and 3.9; see [`Testing`](#testing) below for more information. 5 | 6 | 7 | > NOTES: 8 | > - For local or Cloud Run deployments, there are little/no updates to go from Python 2 to 3. 9 | > - Neither Cloud Functions nor Cloud Run with Cloud Buildpacks support Python 2. 10 | 11 | 12 | ## Deployments and their files 13 | 14 | File | Description 15 | --- | --- 16 | [`main.py`](main.py)|main application file 17 | `settings.py`|settings file with API key (copy & modify `settings-tmpl.py` template) 18 | [`templates/index.html`](templates/index.html)|application HTML template 19 | [`requirements.txt`](requirements.txt)|3rd-party package requirements file 20 | [`app.yaml`](app.yaml)|App Engine configuration file (only for App Engine deployments) 21 | [`.gcloudignore`](.gcloudignore)|files to exclude deploying to the cloud (administrative) 22 | [`noxfile.py`](noxfile.py) | unit tests `nox` tool setup file 23 | [`test_mapsgeo.py`](test_mapsgeo.py) | unit tests (`pytest`) 24 | [`Procfile`](Procfile) | "Entrypoint" directive [Procfile](https:/tmp/devcenter.heroku.com/articles/procfile) to start app (only for Cloud Run deployments using Cloud Buildpacks) 25 | `README.md`|this file (administrative) 26 | 27 | Below are the required settings and instructions for all documented deployments. The "**TL:DR;**" section at the top of each configuration summarizes the key files (see above) while the table beneath spells out the details. No administrative files are listed. Create your own `settings.py` file with the API your created in the Cloud console and take precautions to protect it, as outlined in [the Google Maps API key documentation](https://developers.google.com/maps/documentation/javascript/get-api-key). 28 | 29 | 30 | ## **Local [Flask](https://flask.palletsprojects.com) server (Python 2 or 3)** 31 | 32 | **TL;DR:** application files (`main.py`, `templates/`, `requirements.txt`) 33 | 34 | 1. **Run** `pip install -U pip -r requirements.txt` to install/update packages locally (or `pip2` for Python 2 or `pip3` for Python 3 explicitly) 35 | 1. **Run** `python main.py` to run on local Flask server (`python2` or `python3` to be explicit) 36 | 37 | 38 | ## **App Engine (Python 3)** 39 | 40 | **TL;DR:** app files plus `app.yaml` 41 | 42 | 1. **Run** `gcloud app deploy` 43 | 44 | 45 | ## **App Engine (Python 2)** 46 | 47 | **TL;DR:** `app2.yaml` (instead of `app.yaml`), app files plus `appengine_config.py`, and `lib`, and other modifications 48 | 49 | 1. **Copy** the [Cloud API Python 2 `appengine_config.py` file](/blob/main/cloud/python/appengine_config.py) 50 | 1. **Uncomment/enable** `requests-toolbelt` in `requirements.txt` 51 | 1. **Run** `pip install -t lib -r requirements.txt` to populate `lib` folder (or `pip2`) 52 | 1. **Edit** `lib/googlemaps/client.py` by adding `import requests_toolbelt.adapters.appengine; requests_toolbelt.adapters.appengine.monkeypatch()` below `import requests` (as documented [here](https://cloud.google.com/appengine/docs/standard/python/issue-requests#requests)) 53 | 1. **Run** `gcloud app deploy app2.yaml` 54 | 55 | 56 | ## **Cloud Functions (Python 3)** 57 | 58 | **TL;DR:** app files 59 | 60 | 1. **Run** `gcloud functions deploy mapsgeo --runtime python39 --trigger-http --allow-unauthenticated` to deploy to Cloud Functions (or Python 3.7 or 3.8) 61 | 62 | - Cloud Functions does not support Python 2. 63 | - The Cloud Function name (here `mapsgeo`) must match the function's name in `main.py` else you need to use [`--entry-point`](https://cloud.google.com/functions/docs/deploying/filesystem). 64 | 65 | 66 | ## **Cloud Run (Python 3 via Cloud Buildpacks)** 67 | 68 | **TL;DR:** app files plus [`Procfile`](https://devcenter.heroku.com/articles/procfile) 69 | 70 | 1. **Run** `gcloud run deploy mapsgeo --allow-unauthenticated --platform managed` to deploy to Cloud Run; optionally add `--source . --region REGION` for non-interactive deploy 71 | 72 | - There is no support for Python 2 with Cloud Buildpacks (2.x developers must use Docker [see below]) 73 | 74 | > **NOTE:** This sample uses the Flask development server by default for prototyping; for production, bundle and deploy a production server like `gunicorn`: 75 | > 1. **Uncomment** `gunicorn` from `requirements.txt` (commented out for App Engine & Cloud Functions) 76 | > 1. **Uncomment** `ENTRYPOINT` entry for `gunicorn` in `Dockerfile` replacing default entry 77 | > 1. Re-use the same deploy command above 78 | 79 | 80 | ## **Cloud Run (Python 3 via Docker)** 81 | 82 | **TL;DR:** app files plus `Dockerfile` (nearly identical to Python 2 deployment) 83 | 84 | 1. **Copy** the [Cloud API Python `Dockerfile` file](/blob/main/cloud/python/Dockerfile) 85 | 1. **Edit** `Dockerfile` and switch the `FROM` entry to the `python:3-slim` base image 86 | 1. **Run** `gcloud run deploy mapsgeo --allow-unauthenticated --platform managed` to deploy to Cloud Run; optionally add `--source . --region REGION` for non-interactive deploy 87 | 88 | - The `gunicorn` sidebar above also applies here. 89 | 90 | 91 | ## **Cloud Run (Python 2 via Docker)** 92 | 93 | **TL;DR:** app files plus `Dockerfile` 94 | 95 | 1. **Copy** the [Cloud API Python `Dockerfile` file](/blob/main/cloud/python/Dockerfile) 96 | 1. **Run** `gcloud run deploy mapsgeo --allow-unauthenticated --platform managed` to deploy to Cloud Run; optionally add `--source . --region REGION` for non-interactive deploy 97 | 98 | - The `gunicorn` sidebar above also applies here. 99 | 100 | 101 | ## References 102 | 103 | These are relevant links only to the app in this folder (for all others, see the [README one level up](../README.md): 104 | 105 | - [Google Maps client library for Python](https://github.com/googlemaps/google-maps-services-python) 106 | - [Google Maps API key documentation](https://developers.google.com/maps/documentation/javascript/get-api-key) 107 | - [Python 3 App Engine quickstart](https://cloud.google.com/appengine/docs/standard/python3/quickstart) 108 | - [Python 3 App Engine (standard environment) runtime](https://cloud.google.com/appengine/docs/standard/python3/runtime) 109 | - [Python 2 App Engine (standard environment) runtime](https://cloud.google.com/appengine/docs/standard/python/runtime) 110 | - [Python Cloud Functions quickstart](https://cloud.google.com/functions/docs/quickstart-python) 111 | - [Python Cloud Run quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python) 112 | - [Differences between Python 2 & 3 App Engine (standard environment) runtimes](https://cloud.google.com/appengine/docs/standard/runtimes) 113 | - [Python 2 to 3 App Engine (standard environment) migration guide](http://cloud.google.com/appengine/docs/standard/python/migrate-to-python3) 114 | - [App Engine (standard environment) to Cloud Run codelab tutorial](http://g.co/codelabs/pae-migrate-rundocker) (Docker) 115 | - [App Engine (standard environment) to Cloud Run codelab tutorial](http://g.co/codelabs/pae-migrate-runbldpks) (Cloud Buildpacks) 116 | 117 | 118 | ## Testing 119 | 120 | Testing is driven by [`nox`](http://nox.thea.codes) which uses [`pytest`](https://pytest.org) for testing and [`flake8`](https://flake8.pycqa.org) for linting, installing both in virtual environments along with application dependencies, `flask` and [`googlemaps`](https://github.com/googlemaps/google-maps-services-python) and finally, [`blinker`](https://pythonhosted.org/blinker), a signaling framework integrated into Flask. To run the lint and unit tests (testing `GET` and `POST` requests), install `nox` (with the expected `pip install -U nox`) and run it from the command line in the application folder and ensuring `noxfile.py` is present. 121 | 122 | ### Expected output 123 | 124 | ``` 125 | $ nox 126 | nox > Running session tests-2.7 127 | nox > Creating virtual environment (virtualenv) using python2.7 in .nox/tests-2-7 128 | nox > python -m pip install pytest blinker flask googlemaps 129 | nox > pytest 130 | =============================================== test session starts ================================================ 131 | platform darwin -- Python 2.7.16, pytest-4.6.11, py-1.11.0, pluggy-0.13.1 132 | rootdir: /tmp/noncloud/python/maps 133 | collected 4 items 134 | 135 | test_mapsgeo.py .... [100%] 136 | 137 | ============================================= 4 passed in 2.69 seconds ============================================= 138 | nox > Session tests-2.7 was successful. 139 | nox > Running session tests-3.8 140 | nox > Creating virtual environment (virtualenv) using python3.8 in .nox/tests-3-8 141 | nox > python -m pip install pytest blinker flask googlemaps 142 | nox > pytest 143 | =============================================== test session starts ================================================ 144 | platform darwin -- Python 3.8.2, pytest-7.0.1, pluggy-1.0.0 145 | rootdir: /tmp/noncloud/python/maps 146 | collected 4 items 147 | 148 | test_mapsgeo.py .... [100%] 149 | 150 | ================================================ 4 passed in 1.26s ================================================= 151 | nox > Session tests-3.8 was successful. 152 | nox > Running session tests-3.9 153 | nox > Creating virtual environment (virtualenv) using python3.9 in .nox/tests-3-9 154 | nox > python -m pip install pytest blinker flask googlemaps 155 | nox > pytest 156 | =============================================== test session starts ================================================ 157 | platform darwin -- Python 3.9.9, pytest-7.0.1, pluggy-1.0.0 158 | rootdir: /tmp/noncloud/python/maps 159 | collected 4 items 160 | 161 | test_mapsgeo.py .... [100%] 162 | 163 | ================================================ 4 passed in 1.05s ================================================= 164 | nox > Session tests-3.9 was successful. 165 | nox > Running session lint-2.7 166 | nox > Creating virtual environment (virtualenv) using python2.7 in .nox/lint-2-7 167 | nox > python -m pip install flake8 168 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 169 | nox > Session lint-2.7 was successful. 170 | nox > Running session lint-3.8 171 | nox > Creating virtual environment (virtualenv) using python3.8 in .nox/lint-3-8 172 | nox > python -m pip install flake8 173 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 174 | nox > Session lint-3.8 was successful. 175 | nox > Running session lint-3.9 176 | nox > Creating virtual environment (virtualenv) using python3.9 in .nox/lint-3-9 177 | nox > python -m pip install flake8 178 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 179 | nox > Session lint-3.9 was successful. 180 | nox > Ran multiple sessions: 181 | nox > * tests-2.7: success 182 | nox > * tests-3.8: success 183 | nox > * tests-3.9: success 184 | nox > * lint-2.7: success 185 | nox > * lint-3.8: success 186 | nox > * lint-3.9: success 187 | ``` 188 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /noncloud/python/sheets/README.md: -------------------------------------------------------------------------------- 1 | # Nebulous serverless Google Sheets API app 2 | 3 | ## Python 3 version (Python 2 compatible) 4 | 5 | This sample is offered in Python 3, but the code itself is Python 2-compatible. Instructions on running it under 2.x will leverage the resources of the [Cloud API Python sample](https://github.com/googlecodelabs/cloud-nebulous-serverless/tree/main/cloud/python) which provides a more complete experience as it has all configuration available to support 2.x deployments. 6 | 7 | > NOTES: 8 | > - For local or Cloud Run deployments, there are little/no updates to go from Python 2 to 3. 9 | > - Neither Cloud Functions nor Cloud Run with Cloud Buildpacks support Python 2. 10 | 11 | 12 | ## Deployments and their files 13 | 14 | File | Description 15 | --- | --- 16 | [`main.py`](main.py) | main application file 17 | [`templates/index.html`](templates/index.html) | application HTML template 18 | [`requirements.txt`](requirements.txt) | 3rd-party package requirements file 19 | [`app.yaml`](app.yaml) | App Engine configuration file (only for App Engine deployments) 20 | [`.gcloudignore`](.gcloudignore) | files to exclude deploying to the cloud (administrative) 21 | [`noxfile.py`](noxfile.py) | unit tests `nox` tool setup file 22 | [`test_sheets.py`](test_sheets.py) | unit tests (`pytest`) 23 | [`Procfile`](Procfile) | "Entrypoint" directive [Procfile](https://devcenter.heroku.com/articles/procfile) to start app (only for Cloud Run deployments using Cloud Buildpacks) 24 | `README.md` | this file (administrative) 25 | 26 | Below are the required settings and instructions for all documented deployments. The "**TL:DR;**" section at the top of each configuration summarizes the key files (see above) while the table beneath spells out the details. No administrative files are listed. 27 | 28 | 29 | ## **Local [Flask](https://flask.palletsprojects.com) server (Python 2 or 3)** 30 | 31 | **TL;DR:** application files (`main.py` & `requirements.txt`) 32 | 33 | 1. **Login using [Application Default Credentials](https://cloud.google.com/docs/authentication/production#automatically)**: run the `gcloud auth application-default login` command and ensure you do **not** set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable; also see the [command reference](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login). 34 | 1. (2.x only) **Uncomment/enable** the line for `google-auth` in `requirements.txt` 35 | 1. **Run** `pip install -U pip -r requirements.txt` to install/update packages locally (or `pip2` for Python 2 or `pip3` for Python 3 explicitly) 36 | 1. **Run** `python main.py` to run on local Flask server (`python2` or `python3` to be explicit) 37 | 38 | **Troubleshooting**: if you're on a VM running `pip install`, you may get an error like this: 39 | 40 | ``` 41 | ERROR: Could not install packages due to an OSError: [Errno 13] Permission denied: 'PKG-INFO' 42 | Consider using the `--user` option or check the permissions. 43 | ``` 44 | 45 | _Solution_: If this is the case: 46 | - **Update** the line for `flask` in `requirements.txt` to: `flask<2.0dev` 47 | 48 | 49 | ## **App Engine (Python 3)** 50 | 51 | **TL;DR:** app files plus `app.yaml` 52 | 53 | 1. **Run** `gcloud app deploy` 54 | 55 | 56 | ## **App Engine (Python 2)** 57 | 58 | **TL;DR:** app files plus `app.yaml`, `appengine_config.py`, and `lib` 59 | 60 | 1. **Copy** the [Cloud API Python 2 `app.yaml` file](/cloud/python/app.yaml) 61 | 1. **Copy** the [Cloud API Python 2 `appengine_config.py` file](/cloud/python/appengine_config.py) 62 | 1. **Uncomment/enable** the line for `google-auth` in `requirements.txt` 63 | 1. **Uncomment** "<1.12.0" on the line for `google-api-python-client` in `requirements.txt` 64 | 1. **Run** `pip install -t lib -r requirements.txt` to populate `lib` folder (or `pip2`) 65 | 1. **Run** `gcloud app deploy` 66 | 67 | 68 | ## **Cloud Functions (Python 3)** 69 | 70 | **TL;DR:** app files 71 | 72 | 1. **Run** `gcloud functions deploy students --runtime python39 --trigger-http --allow-unauthenticated` to deploy to Cloud Functions (or Python 3.7 or 3.8) 73 | 74 | - Cloud Functions does not support Python 2. 75 | - The Cloud Function name must match the function's name in `main.py` else you need to use [`--entry-point`](https://cloud.google.com/functions/docs/deploying/filesystem#deploy_using_the). 76 | 77 | 78 | ## **Cloud Run (Python 3 via Cloud Buildpacks)** 79 | 80 | **TL;DR:** app files plus [`Procfile`](https://devcenter.heroku.com/articles/procfile) 81 | 82 | 1. **Run** `gcloud run deploy students --allow-unauthenticated --platform managed` to deploy to Cloud Run; optionally add `--source . --region REGION` for non-interactive deploy 83 | 84 | - There is no support for Python 2 with Cloud Buildpacks (2.x developers must use Docker) 85 | 86 | > **NOTE:** This sample uses the Flask development server by default for prototyping; for production, bundle and deploy a production server like `gunicorn`: 87 | > 1. **Uncomment** `gunicorn` from `requirements.txt` (commented out for App Engine & Cloud Functions) 88 | > 1. **Uncomment** `ENTRYPOINT` entry for `gunicorn` in `Dockerfile` replacing default entry 89 | > 1. Re-use the same deploy command above 90 | 91 | 92 | ## **Cloud Run (Python 3 via Docker)** 93 | 94 | **TL;DR:** app files plus `Dockerfile` (nearly identical to Python 2 deployment) 95 | 96 | 1. **Copy** the [Cloud API Python `Dockerfile` file](/cloud/python/Dockerfile) 97 | 1. **Edit** `Dockerfile` and switch the `FROM` entry to the `python:3-slim` base image 98 | 1. **Run** `gcloud run deploy students --allow-unauthenticated --platform managed` to deploy to Cloud Run; optionally add `--source . --region REGION` for non-interactive deploy 99 | 100 | - The `gunicorn` sidebar above also applies here. 101 | 102 | 103 | ## **Cloud Run (Python 2 via Docker)** 104 | 105 | **TL;DR:** app files plus `Dockerfile` 106 | 107 | 1. **Copy** the [Cloud API Python `Dockerfile` file](/cloud/python/Dockerfile) 108 | 1. **Run** `gcloud run deploy students --allow-unauthenticated --platform managed` to deploy to Cloud Run; optionally add `--source . --region REGION` for non-interactive deploy 109 | 110 | - The `gunicorn` sidebar above also applies here. 111 | 112 | 113 | ## Resources 114 | 115 | These are relevant links only to the app in this folder (for all others, see the [README one level up](../README.md): 116 | 117 | ### Google Sheets API and platform client library 118 | 119 | - [Google Sheets API home page](https://developers.google.com/sheets) 120 | - [Google Sheets API Python QuickStart](https://developers.google.com/sheets/api/quickstart/python) (origin of the student spreadsheet) 121 | - [Google Sheets API intro codelab](http://g.co/codelabs/sheets) (Node.js) 122 | - [Google Sheets API videos](https://developers.google.com/sheets/api/videos) (Python or [Apps Script](https://developers.google.com/apps-script) [JavaScript]) 123 | - [Google Workspace APIs home page](https://developers.google.com/gsuite) 124 | - [Google APIs client library for Python](https://github.com/googleapis/google-api-python-client) 125 | 126 | 127 | ### Google Cloud serverless platforms 128 | 129 | - [Google Cloud serverless home page](https://cloud.google.com/serverless) 130 | - [Cloud Functions home page](https://cloud.google.com/functions) 131 | - [Cloud Functions Python quickstart](https://cloud.google.com/functions/docs/quickstart-python) 132 | - [Cloud Run home page](https://cloud.run) 133 | - [Cloud Run Python quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python) 134 | - [App Engine home page](https://cloud.google.com/appengine) 135 | - [App Engine Python 3 quickstart](https://cloud.google.com/appengine/docs/standard/python3/quickstart) 136 | - [App Engine (standard environment) Python 3 runtime](https://cloud.google.com/appengine/docs/standard/python3/runtime) 137 | - [App Engine (standard environment) Python 2 runtime](https://cloud.google.com/appengine/docs/standard/python/runtime) 138 | - [Differences between Python 2 & 3 App Engine (standard environment) runtimes](https://cloud.google.com/appengine/docs/standard/runtimes) 139 | - [App Engine (standard environment) Python 2 to 3 migration guide](http://cloud.google.com/appengine/docs/standard/python/migrate-to-python3) 140 | - [App Engine (standard environment) to Cloud Run codelab tutorial](http://g.co/codelabs/pae-migrate-rundocker) (via Docker) 141 | - [App Engine (standard environment) to Cloud Run codelab tutorial](http://g.co/codelabs/pae-migrate-runbldpks) (no Docker/`Dockerfile` via Cloud Buildpacks) 142 | 143 | 144 | ## Testing 145 | 146 | Testing is driven by [`nox`](http://nox.thea.codes) which uses [`pytest`](https://pytest.org) for testing and [`flake8`](https://flake8.pycqa.org) for linting, installing both in virtual environments along with application dependencies, `flask` and `google-api-python-client`, and finally, [`blinker`](https://pythonhosted.org/blinker), a signaling framework integrated into Flask. To run the lint and unit tests (testing `GET` and `POST` requests), install `nox` (with the expected `pip install -U nox`) and run it from the command line in the application folder and ensuring `noxfile.py` is present. 147 | 148 | 149 | ### Expected output 150 | 151 | ``` 152 | $ nox 153 | nox > Running session tests-2.7 154 | nox > Creating virtual environment (virtualenv) using python2.7 in .nox/tests-2-7 155 | nox > python -m pip install pytest blinker flask google-auth google-api-python-client 156 | nox > pytest 157 | ================================================== test session starts ================================================== 158 | platform darwin -- Python 2.7.16, pytest-4.6.11, py-1.11.0, pluggy-0.13.1 159 | rootdir: /tmp/noncloud/python/sheets 160 | collected 1 item 161 | 162 | test_sheets.py . [100%] 163 | 164 | =============================================== 1 passed in 1.84 seconds ================================================ 165 | nox > Session tests-2.7 was successful. 166 | nox > Running session tests-3.8 167 | nox > Creating virtual environment (virtualenv) using python3.8 in .nox/tests-3-8 168 | nox > python -m pip install pytest blinker flask google-auth google-api-python-client 169 | nox > pytest 170 | ================================================== test session starts ================================================== 171 | platform darwin -- Python 3.8.2, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 172 | rootdir: /tmp/noncloud/python/sheets 173 | collected 1 item 174 | 175 | test_sheets.py . [100%] 176 | 177 | =================================================== 1 passed in 1.04s =================================================== 178 | nox > Session tests-3.8 was successful. 179 | nox > Running session tests-3.9 180 | nox > Creating virtual environment (virtualenv) using python3.9 in .nox/tests-3-9 181 | nox > python -m pip install pytest blinker flask google-auth google-api-python-client 182 | nox > pytest 183 | ================================================== test session starts ================================================== 184 | platform darwin -- Python 3.9.1, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 185 | rootdir: /tmp/noncloud/python/sheets 186 | collected 1 item 187 | 188 | test_sheets.py . [100%] 189 | 190 | =================================================== 1 passed in 0.97s =================================================== 191 | nox > Session tests-3.9 was successful. 192 | nox > Running session lint-2.7 193 | nox > Creating virtual environment (virtualenv) using python2.7 in .nox/lint-2-7 194 | nox > python -m pip install flake8 195 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 196 | nox > Session lint-2.7 was successful. 197 | nox > Running session lint-3.8 198 | nox > Creating virtual environment (virtualenv) using python3.8 in .nox/lint-3-8 199 | nox > python -m pip install flake8 200 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 201 | nox > Session lint-3.8 was successful. 202 | nox > Running session lint-3.9 203 | nox > Creating virtual environment (virtualenv) using python3.9 in .nox/lint-3-9 204 | nox > python -m pip install flake8 205 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 206 | nox > Session lint-3.9 was successful. 207 | nox > Ran multiple sessions: 208 | nox > * tests-2.7: success 209 | nox > * tests-3.8: success 210 | nox > * tests-3.9: success 211 | nox > * lint-2.7: success 212 | nox > * lint-3.8: success 213 | nox > * lint-3.9: success 214 | ``` 215 | -------------------------------------------------------------------------------- /cloud/python/README.md: -------------------------------------------------------------------------------- 1 | # Nebulous serverless Cloud Translation API app 2 | 3 | ## Python (2 and 3) version 4 | 5 | While the majority of this app's deployments are in Python 3, there are still users upgrading from Python 2, so our Python 2 code is meant to help with migration & planning. Admittedly, there may _seem_ to be a bit of "cheating" due to the duplicity of Python 2 and 3, especially since the application is compatible across both language versions without modification or use of compatibility libraries. However there are significant differences between Python 2 and 3 deployment requirements irregardless of language differences. Additional notes: 6 | 7 | - For local or Cloud Run deployments, there are little/no updates to go from Python 2 to 3. 8 | - Neither Cloud Functions nor Cloud Run with Cloud Buildpacks support Python 2. 9 | - There is also an [equivalent Node.js version](../nodejs) of this app. 10 | 11 | 12 | ## Codelab links 13 | 14 | Deployment | Python 2 | Python 3 15 | --- | --- | --- 16 | Local/hosted Flask|[codelab](https://codelabs.developers.google.com/codelabs/cloud-nebulous-serverless-python-flask?utm_source=codelabs&utm_medium=et&utm_campaign=CDR_wes_aap-serverless_nebservflask_sms_201020&utm_content=-)|_same as Python 2_ 17 | App Engine|[codelab](https://codelabs.developers.google.com/codelabs/cloud-nebulous-serverless-python-gae2?utm_source=codelabs&utm_medium=et&utm_campaign=CDR_wes_aap-serverless_nebservgae2_sms_201020&utm_content=-)|[codelab](https://codelabs.developers.google.com/codelabs/cloud-nebulous-serverless-python-gae3?utm_source=codelabs&utm_medium=et&utm_campaign=CDR_wes_aap-serverless_nebservgae3_sms_201020&utm_content=-) 18 | Cloud Functions| _N/A_ |[codelab](https://codelabs.developers.google.com/codelabs/cloud-nebulous-serverless-python-gcf?utm_source=codelabs&utm_medium=et&utm_campaign=CDR_wes_aap-serverless_nebservgcf_sms_201020&utm_content=-) 19 | Cloud Run (Docker)|[codelab](https://codelabs.developers.google.com/codelabs/cloud-nebulous-serverless-python-gcr2?utm_source=codelabs&utm_medium=et&utm_campaign=CDR_wes_aap-serverless_nebservgcr2_sms_201020&utm_content=-)|[codelab](https://codelabs.developers.google.com/codelabs/cloud-nebulous-serverless-python-gcr3?utm_source=codelabs&utm_medium=et&utm_campaign=CDR_wes_aap-serverless_nebservgcr3_sms_201020&utm_content=-) 20 | Cloud Run (Buildpacks)| _N/A_ |[codelab](https://codelabs.developers.google.com/codelabs/cloud-nebulous-serverless-python-gcrbp?utm_source=codelabs&utm_medium=et&utm_campaign=CDR_wes_aap-serverless_nebservgcrbp_sms_201020&utm_content=-) 21 | 22 | 23 | ## Deployments and their files 24 | 25 | These are the files provided in this repo and the deployments they're applicable to: 26 | 27 | ![repository files](https://user-images.githubusercontent.com/1102504/119735453-2f3af500-be31-11eb-9115-3fdeb22ec31c.png) 28 | 29 | > NOTES: 30 | >- * — `requirements.txt` is used for local and App Engine (2.x) package installations and not required in deployments themselves unlike all others 31 | >- `main.py` and `templates/index.html` comprise the entire application and are always required 32 | >- `noxfile.py` and `test_translate.py` are for testing only; see [Testing section](#testing) below 33 | >- All `.*ignore` and `.git*` files/folders are administrative and not listed in table above or deployments below 34 | >- Files applicable only to a specific language version are annotated above 35 | 36 | Below are the required settings and instructions for all documented deployments. The "**TL:DR;**" section at the top of each configuration summarizes the key files (see above) while the table beneath spells out the details. No administrative files are listed. 37 | 38 | > NOTE: Serverless deployments (as configured here) use [default service accounts](https://cloud.google.com/iam/docs/service-accounts#default) which provide a broad set of permissions to assist you in getting a working prototype. When preparing to launch to production, the Google Cloud team recommends the best practice of "least privileges," and instead use [user-managed service accounts](https://cloud.google.com/iam/docs/service-accounts#user-managed) with the minimal set of permissions allowing your app to function properly. 39 | 40 | 41 | ## **Local Flask server (Python 2)** 42 | 43 | **TL;DR:** application files (`main.py` & `requirements.txt`) 44 | 45 | File | Description 46 | --- | --- 47 | `main.py`|**use as-is** from repo 48 | `app.yaml`|_unused_ (delete or leave as-is) 49 | `appengine_config.py`|_unused_ (delete or leave as-is; only for Python 2 App Engine) 50 | `requirements.txt`|**use as-is** to install packages locally (see below) but _unused_ thereafter 51 | `lib`|_unused_ (delete or leave as-is if it exists) 52 | `Dockerfile`|_unused_ (delete or leave as-is) 53 | `Procfile`|_unused_ (delete or leave as-is) 54 | 55 | Instructions: 56 | 57 | 1. **Run** `pip install -U pip -r requirements.txt` to install/update packages locally (or `pip2`) 58 | 1. **Run** `gcloud auth application-default login` to set your credentials 59 | 1. **Run** `python main.py` to run on local Flask server (or `python2`) 60 | 61 | 62 | ## **Local Flask server (Python 3)** 63 | 64 | **TL;DR:** app files (identical to Python 2 deployment) 65 | 66 | File | Description 67 | --- | --- 68 | `main.py`|**use as-is** from repo 69 | `app.yaml`|_unused_ (delete or leave as-is) 70 | `appengine_config.py`|_unused_ (delete or leave as-is; only for Python 2 App Engine) 71 | `requirements.txt`|**use as-is** to install packages locally (see below) but _unused_ thereafter 72 | `lib`|_unused_ (delete or leave as-is if it exists) 73 | `Dockerfile`|_unused_ (delete or leave as-is) 74 | `Procfile`|_unused_ (delete or leave as-is) 75 | 76 | Instructions: 77 | 78 | 1. **Run** `pip install -U pip -r requirements.txt` to install/update packages locally (or `pip3`) 79 | 1. **Run** `gcloud auth application-default login` to set your credentials 80 | 1. **Run** `python main.py` to run on local Flask server (or `python3`) 81 | 82 | 83 | ## **App Engine (Python 2)** 84 | 85 | **TL;DR:** app files plus `app.yaml`, `appengine_config.py`, and `lib` 86 | 87 | File | Description 88 | --- | --- 89 | `main.py`|**use as-is** from repo 90 | `app.yaml`|**use as-is** from repo (ensure `#runtime:python310` commented out) 91 | `appengine_config.py`|**use as-is** from repo 92 | `requirements.txt`|**use as-is** to install packages locally (see below) but _unused_ thereafter 93 | `lib`|**create folder** per instructions below 94 | `Dockerfile`|_unused_ (delete or leave as-is) 95 | `Procfile`|_unused_ (delete or leave as-is) 96 | 97 | Instructions: 98 | 99 | 1. **Run** `pip install -t lib -r requirements.txt` to populate `lib` folder (or `pip2`) 100 | 1. **Run** `gcloud app deploy` to deploy to Python 2 App Engine 101 | - You'll be prompted for the REGION if deploying to App Engine the first time. 102 | - App Engine apps are tied to one region, so it can't be changed once it's set, meaning you won't be prompted thereafter. 103 | 104 | 105 | ## **App Engine (Python 3)** 106 | 107 | **TL;DR:** app files plus `app.yaml` 108 | 109 | File | Description 110 | --- | --- 111 | `main.py`|**use as-is** from repo 112 | `app.yaml`|**uncomment** `runtime:python310` (or Python 3.7-3.9); **delete** all other lines 113 | `appengine_config.py`|_unused_ (delete or leave as-is; only for Python 2 App Engine) 114 | `requirements.txt`|**use as-is** from repo 115 | `lib`|**delete** (or rename) this folder if it exists (not used with Python 3 App Engine) 116 | `Dockerfile`|_unused_ (delete or leave as-is) 117 | `Procfile`|_unused_ (delete or leave as-is) 118 | 119 | Instructions: 120 | 121 | 1. **Edit** `app.yaml` (see above) 122 | 1. (optional) **Delete** `app.yaml`, `lib` and `appengine_config.py` (unused) 123 | 1. **Run** `gcloud app deploy` to deploy to Python 3 App Engine 124 | - You'll be prompted for the REGION if deploying to App Engine the first time. 125 | - App Engine apps are tied to one region, so it can't be changed once it's set, meaning you won't be prompted thereafter. 126 | 127 | 128 | ## **Cloud Functions (Python 3)** 129 | 130 | **TL;DR:** app files 131 | 132 | File | Description 133 | --- | --- 134 | `main.py`|**use as-is** from repo 135 | `app.yaml`|_unused_ (delete or leave as-is; only for App Engine) 136 | `appengine_config.py`|_unused_ (delete or leave as-is; only for Python 2 App Engine) 137 | `requirements.txt`|**use as-is** from repo 138 | `lib`|**delete** (or rename) this folder if it exists (not used with Cloud Functions) 139 | `Dockerfile`|_unused_ (delete or leave as-is) 140 | `Procfile`|_unused_ (delete or leave as-is) 141 | 142 | Instructions: 143 | 144 | 1. (optional) **Delete** `app.yaml`, `lib` and `appengine_config.py` (unused) 145 | 1. **Run** `gcloud functions deploy translate --runtime python310 --trigger-http --allow-unauthenticated` to deploy to Cloud Functions (or Python 3.7-3.9) 146 | - That command creates & deploys a new HTTP-triggered Cloud Function (name must match what's in `main.py`) 147 | - You'll be prompted for the REGION if deploying a Cloud Function the first time. 148 | - Cloud Functions can be deployed to different regions within a project, but once the region has been set for a function, it cannot be changed. 149 | 1. There is no support for Python 2 with Cloud Functions 150 | 151 | 152 | ## **Cloud Run (Python 2 via Docker)** 153 | 154 | **TL;DR:** app files plus `Dockerfile` 155 | 156 | File | Description 157 | --- | --- 158 | `main.py`|**use as-is** from repo 159 | `app.yaml`|_unused_ (delete or leave as-is; only for App Engine) 160 | `appengine_config.py`|_unused_ (delete or leave as-is; only for Python 2 App Engine) 161 | `requirements.txt`|`grpcio<1.40.0` applies to this deployment 162 | `lib`|**delete** (or rename) this folder if it exists (not used with Cloud Run) 163 | `Dockerfile`|**use as-is** from repo (ensure `#FROM python:3-slim` commented out) 164 | `Procfile`|_unused_ (delete or leave as-is) 165 | 166 | Instructions: 167 | 168 | 1. (optional) **Delete** `app.yaml`, `lib` and `appengine_config.py` (unused) 169 | 1. **Run** `gcloud run deploy translate --allow-unauthenticated --platform managed` to deploy to Cloud Run 170 | - The above command wraps `docker build` and `docker push`, deploying the image to [Cloud Artifact Registry](https://cloud.google.com/artifact-registry) (must be enabled), and finally `docker run` to deploy the service, all in one convenient command. 171 | - You'll be prompted to provide a REGION unless you also add `--region REGION` on the cmd-line 172 | - You'll be prompted to provide a SOURCE folder unless you also add `--source FOLDER`, e.g., `--source .` on the cmd-line 173 | - Supplying both `--region` and `--source` options provide a fully non-interactive deploy (unless you don't have a repository, in which case you'll be prompted to create one) 174 | 1. You can also use this shortcut to deploy to Cloud Run: 175 | [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 176 | 1. By default, App Engine & Cloud Functions launch production servers; with Cloud Run, the Flask development server is used for prototyping. For production, bundle and deploy a production server like `gunicorn`: 177 | 1. **Uncomment** `gunicorn` from `requirements.txt` (commented out for App Engine & Cloud Functions) 178 | 1. **Uncomment** the `ENTRYPOINT` entry for `gunicorn` replacing the default entry in `Dockerfile` 179 | 1. Re-use the same deploy command 180 | 181 | 182 | ## **Cloud Run (Python 3 via Docker)** 183 | 184 | **TL;DR:** app files plus `Dockerfile` (nearly identical to Python 2 deployment) 185 | 186 | File | Description 187 | --- | --- 188 | `main.py`|**use as-is** from repo 189 | `app.yaml`|_unused_ (delete or leave as-is; only for App Engine) 190 | `appengine_config.py`|_unused_ (delete or leave as-is; only for Python 2 App Engine) 191 | `requirements.txt`|**use as-is** from repo 192 | `lib`|**delete** (or rename) this folder if it exists (not used with Cloud Run) 193 | `Dockerfile`|**replace** `FROM python:2-slim` with `FROM python:3-slim` (commented out) but **keep all other lines** 194 | `Procfile`|_unused_ (delete or leave as-is) 195 | 196 | Instructions: 197 | 198 | 1. (optional) **Delete** `app.yaml`, `lib` and `appengine_config.py` (unused) 199 | 1. **Edit** `Dockerfile` (see above) 200 | 1. **Run** `gcloud run deploy translate --allow-unauthenticated --platform managed` to deploy to Cloud Run 201 | - The above command wraps `docker build` and `docker push`, deploying the image to [Cloud Artifact Registry](https://cloud.google.com/artifact-registry) (must be enabled), and finally `docker run` to deploy the service, all in one convenient command. 202 | - You'll be prompted to provide a REGION unless you also add `--region REGION` on the cmd-line 203 | - You'll be prompted to provide a SOURCE folder unless you also add `--source FOLDER`, e.g., `--source .` on the cmd-line 204 | - Supplying both `--region` and `--source` options provide a fully non-interactive deploy (unless you don't have a repository, in which case you'll be prompted to create one) 205 | 1. The shortcut "button" above can be customized for Python 3 if you make the `Dockerfile` update above and commit it to your fork/clone. 206 | 1. By default, App Engine & Cloud Functions launch production servers; with Cloud Run, the Flask development server is used for prototyping. For production, bundle and deploy a production server like `gunicorn`: 207 | 1. **Uncomment** `gunicorn` from `requirements.txt` (commented out for App Engine & Cloud Functions) 208 | 1. **Uncomment** the `ENTRYPOINT` entry for `gunicorn` replacing the default entry in `Dockerfile` 209 | 1. Re-use the same deploy command 210 | 211 | 212 | ## **Cloud Run (Python 3 via Cloud Buildpacks)** 213 | 214 | **TL;DR:** app files plus [`Procfile`](https://devcenter.heroku.com/articles/procfile) 215 | 216 | File | Description 217 | --- | --- 218 | `main.py`|**use as-is** from repo 219 | `app.yaml`|_unused_ (delete or leave as-is; only for App Engine) 220 | `appengine_config.py`|_unused_ (delete or leave as-is; only for Python 2 App Engine) 221 | `requirements.txt`|**use as-is** from repo 222 | `lib`|**delete** (or rename) this folder if it exists (not used with Cloud Run) 223 | `Dockerfile`|**delete** (or rename) this file (_required_) 224 | `Procfile`|**use as-is** from repo 225 | 226 | Instructions: 227 | 228 | 1. (optional) **Delete** `app.yaml`, `lib` and `appengine_config.py` (unused) 229 | 1. **Delete** `Dockerfile` (or rename it) 230 | - There is no support for Python 2 with Cloud Buildpacks (2.x developers must use Docker) 231 | 1. **Run** `gcloud run deploy translate --allow-unauthenticated --platform managed` to deploy to Cloud Run 232 | - The above command wraps `docker build` and `docker push`, deploying the image to [Cloud Artifact Registry](https://cloud.google.com/artifact-registry) (must be enabled), and finally `docker run` to deploy the service, all in one convenient command. 233 | - You'll be prompted to provide a REGION unless you also add `--region REGION` on the cmd-line 234 | - You'll be prompted to provide a SOURCE folder unless you also add `--source FOLDER`, e.g., `--source .` on the cmd-line 235 | - Supplying both `--region` and `--source` options provide a fully non-interactive deploy (unless you don't have a repository, in which case you'll be prompted to create one) 236 | 1. By default, App Engine & Cloud Functions launch production servers; with Cloud Run, the Flask development server is used for prototyping. For production, bundle and deploy a production server like `gunicorn`: 237 | 1. **Uncomment** `gunicorn` from `requirements.txt` (commented out for App Engine & Cloud Functions) 238 | 1. **Uncomment** the `web:` entry for `gunicorn` replacing the default entry in `Procfile` 239 | 1. Re-use the same deploy command 240 | 241 | 242 | ## References 243 | 244 | These are relevant links only to the app in this folder (for all others, see the [README one level up](../README.md): 245 | 246 | - [Python 3 App Engine quickstart](https://cloud.google.com/appengine/docs/standard/python3/quickstart) 247 | - [Python 3 App Engine (standard environment) runtime](https://cloud.google.com/appengine/docs/standard/python3/runtime) 248 | - [Python 2 App Engine (standard environment) runtime](https://cloud.google.com/appengine/docs/standard/python/runtime) 249 | - [Python Cloud Functions quickstart](https://cloud.google.com/functions/docs/quickstart-python) 250 | - [Python Cloud Run quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python) 251 | - [Differences between Python 2 & 3 App Engine (standard environment) runtimes](https://cloud.google.com/appengine/docs/standard/runtimes) 252 | - [Python 2 to 3 App Engine (standard environment) migration guide](http://cloud.google.com/appengine/docs/standard/python/migrate-to-python3) 253 | - [App Engine (standard environment) to Cloud Run codelab tutorial](http://g.co/codelabs/pae-migrate-rundocker) (Docker) 254 | - [App Engine (standard environment) to Cloud Run codelab tutorial](http://g.co/codelabs/pae-migrate-runbldpks) (Cloud Buildpacks) 255 | - [Flask](https://flask.palletsprojects.com) 256 | 257 | 258 | ## Testing 259 | 260 | Testing is driven by [`nox`](http://nox.thea.codes) which uses [`pytest`](https://pytest.org) for testing and [`flake8`](https://flake8.pycqa.org) for linting, installing both in virtual environments along with application dependencies, `flask` and `google-cloud-translate`, and finally, `blinker`, a signaling framework integrated into Flask. To run the lint and unit tests (testing `GET` and `POST` requests), install `nox` (with the expected `pip install -U nox`) and run it from the command line in the application folder and ensuring `noxfile.py` is present. 261 | 262 | ### Expected output 263 | 264 | ``` 265 | $ nox 266 | nox > Running session tests-2.7 267 | nox > Creating virtual environment (virtualenv) using python2.7 in .nox/tests-2-7 268 | nox > python -m pip install pytest blinker flask google-cloud-translate 269 | nox > pytest 270 | ============================================ test session starts ============================================= 271 | platform darwin -- Python 2.7.16, pytest-4.6.11, py-1.10.0, pluggy-0.13.1 272 | rootdir: /private/tmp/cloud-nebulous-serverless-python 273 | collected 2 items 274 | 275 | test_translate.py .. [100%] 276 | 277 | ============================================== warnings summary ============================================== 278 | .nox/tests-2-7/lib/python2.7/site-packages/google/cloud/translate_v3/__init__.py:32 279 | /private/tmp/cloud-nebulous-serverless-python/.nox/tests-2-7/lib/python2.7/site-packages/google/cloud/translate_v3/__init__.py:32: DeprecationWarning: A future version of this library will drop support for Python 2.7. More details about Python 2 support for Google Cloud Client Libraries can be found at https://cloud.google.com/python/docs/python2-sunset/ 280 | warnings.warn(message, DeprecationWarning) 281 | 282 | -- Docs: https://docs.pytest.org/en/latest/warnings.html 283 | ==================================== 2 passed, 1 warnings in 1.02 seconds ==================================== 284 | nox > Session tests-2.7 was successful. 285 | nox > Running session tests-3.6 286 | nox > Creating virtual environment (virtualenv) using python3.6 in .nox/tests-3-6 287 | nox > python -m pip install pytest blinker flask google-cloud-translate 288 | nox > pytest 289 | ============================================ test session starts ============================================= 290 | platform darwin -- Python 3.6.8, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 291 | rootdir: /private/tmp/cloud-nebulous-serverless-python 292 | collected 2 items 293 | 294 | test_translate.py .. [100%] 295 | 296 | ============================================= 2 passed in 1.22s ============================================== 297 | nox > Session tests-3.6 was successful. 298 | nox > Running session tests-3.9 299 | nox > Creating virtual environment (virtualenv) using python3.9 in .nox/tests-3-9 300 | nox > python -m pip install pytest blinker flask google-cloud-translate 301 | nox > pytest 302 | ============================================ test session starts ============================================= 303 | platform darwin -- Python 3.9.1, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 304 | rootdir: /private/tmp/cloud-nebulous-serverless-python 305 | collected 2 items 306 | 307 | test_translate.py .. [100%] 308 | 309 | ============================================= 2 passed in 1.04s ============================================== 310 | nox > Session tests-3.9 was successful. 311 | nox > Running session lint-2.7 312 | nox > Creating virtual environment (virtualenv) using python2.7 in .nox/lint-2-7 313 | nox > python -m pip install flake8 314 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 315 | nox > Session lint-2.7 was successful. 316 | nox > Running session lint-3.6 317 | nox > Creating virtual environment (virtualenv) using python3.6 in .nox/lint-3-6 318 | nox > python -m pip install flake8 319 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 320 | nox > Session lint-3.6 was successful. 321 | nox > Running session lint-3.9 322 | nox > Creating virtual environment (virtualenv) using python3.9 in .nox/lint-3-9 323 | nox > python -m pip install flake8 324 | nox > flake8 --show-source --builtin=gettext --max-complexity=20 --exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py --ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202 --max-line-length=88 . 325 | nox > Session lint-3.9 was successful. 326 | nox > Ran multiple sessions: 327 | nox > * tests-2.7: success 328 | nox > * tests-3.6: success 329 | nox > * tests-3.9: success 330 | nox > * lint-2.7: success 331 | nox > * lint-3.6: success 332 | nox > * lint-3.9: success 333 | ``` 334 | --------------------------------------------------------------------------------