{"id":17038067,"url":"https://github.com/modesty/calllog211","last_synced_at":"2025-04-12T13:52:12.601Z","repository":{"id":18809177,"uuid":"22023667","full_name":"modesty/calllog211","owner":"modesty","description":"A RESTful Web Service Python Starter Project on Google App Engine","archived":false,"fork":false,"pushed_at":"2014-08-26T17:00:49.000Z","size":1055,"stargazers_count":14,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-26T08:37:07.806Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/modesty.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-20T01:48:11.000Z","updated_at":"2024-03-06T21:54:39.000Z","dependencies_parsed_at":"2022-09-24T21:32:09.042Z","dependency_job_id":null,"html_url":"https://github.com/modesty/calllog211","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modesty%2Fcalllog211","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modesty%2Fcalllog211/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modesty%2Fcalllog211/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modesty%2Fcalllog211/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/modesty","download_url":"https://codeload.github.com/modesty/calllog211/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248576075,"owners_count":21127317,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T08:55:53.975Z","updated_at":"2025-04-12T13:52:12.581Z","avatar_url":"https://github.com/modesty.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CallLog211\n\n## A RESTful Web Service Python Starter Project on Google App Engine\n\nThis project is an application skeleton and working example for a typical RESTful Web Service project on \n[Google Cloud Platform](https://cloud.google.com/). You can use it to quickly bootstrap the server side of \n[Service Oriented HTML Application (SOHA)](http://www.codeproject.com/Articles/118683/SOHA-Service-Oriented-HTML-Application-Concepts-an) \napplications and dev environment for these projects.\n\nThe seed contains dependency Python libraries, one API endpoint and a bunch of scripts all pre-configured \nfor instant data persistence development gratification. Just clone the repo (or download the zip/tarball), \n[get a project id from Google](https://developers.google.com/cloud/),\n[activate Google Cloud Datastore](https://developers.google.com/datastore/docs/activate),\nstart up [Google App Engine App Launcher](https://developers.google.com/appengine/downloads),\nand you are ready to develop and test your RESTful API.\n\nThe seed app does two main things, saving and fetching data. It implements one endpoint that takes and validates a HTTP POST request, \nthen save the data to [Google Cloud Datastore - Google's version of NoSQL cloud data storage](https://developers.google.com/datastore/). \nBeyond saving data, it also provides a generic data retrieval mechanism, \nit supports ndb.Model JSON serialization, response packaging, sorting, paging and query for property with great flexibilities,\nit not only query property with actual value, also supports OR operation via dictionary literal in the query string, \nand Range operation through tuple literal in the query string as well, see data retrieval examples below.\n\nThis project shows how to set up 3rd party Python libraries for [Google App Engine](https://developers.google.com/appengine/),\nhow to structure your source code, how to validate data from request, how to handle exceptions and \nhow to response with a customized response JSON object.\n\n_Note: This project is intended to serve as a seed project to develop RESTful Web Service with specific \nPython frameworks and libraries (more on this later), it does not has a full fledged CRUD sample code \nwith NDB, although it can be added later on._\n\n## Up and running\n\nTo run this project in your local development environment, clone the repository first, then edit app.yaml by replacing `\u003cYour Google Cloud Poject ID\u003e` with your own Google App Engine project ID, in terminal, run:\n \n \n    `cd \u003cyour-project-clone-directory\u003e`\n    \n    `dev_appserver.py .`\n\n## What it is built with\n\nThere are many options to built RESTful web service in Python on Google App Engine, I've evaluated \n[Django](https://www.djangoproject.com/), [Django REST framework](http://www.django-rest-framework.org/),\n[Google Cloud Endpoints](https://developers.google.com/appengine/docs/java/endpoints/), since all I need\nis a lightweight, easy to use and plug-able RESTful-only framework, I eventually landed on the following\nstack:\n\n### Flask-RESTful\n\n[Flask-RESTful](https://github.com/twilio/flask-restful) provides a lightweight abstraction for building\nRESTful service, it's an extension for [Flask](http://flask.pocoo.org/) and has all the bells and whistles\nto quickly get a RESTful service up and running on Google App Engine. \n\nAll dependencies of Flask-RESTful and dependencies of dependencies are packaged up in `lib` directory,\nincluding:\n\n* aniso8601\n* flask\n* pytz\n* werkzeug\n* itsdangerous\n\n### RESTful building blocks\n\nAlthough [Flask-RESTful](https://github.com/twilio/flask-restful) has provided an excellent starting point\nfor all sorts of RESTful service building blocks, like useful decorators, Resource base class, easy API routes,\nrequest parsing and validation, error handing, etc., there always are opportunities to add more utilities and\nhelper functions to make it more suitable to your specific project. To be as much as generic as possible, I\nalso include the following building blocks in the `api/common` directory:\n\n* response.py: two base classes to structure all response in a unified form\n* session.py: utilize Google App Engine's memcache to validate a session\n* util.py: lots of utilities methods mainly around data types and ndb model processes\n* validators: validator types that works with [ReqParse](http://flask-restful.readthedocs.org/en/latest/api.html#module-reqparse)\n* [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS): I was trying to use Flask-RESTful\n built-in support for [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS), but when I tested\n it with HTTP OPTIONS, it returns HTTP 200 without access control headers. So I commented out decorators code,\n and implemented OPTIONS in CallLogAPI.py. I should revisit this issue later...\n\nAnd lots more! Clone it and start to hack!\n\n\n### Configurations for Google App Engine\n\nSince Flask-RESTful is not included in Google App Engine by default, we need to configure our project to make\nit 'Cloud Ready'. All the necessary libraries, source trees and scripts/yaml are included:\n\n* lib: this directory includes all the 3rd party Python libraries that Flask-RESTful requires, no special\ninstallation commandline to run, just clone or copy the entire directory, it's ready to go\n* app.yaml: Google App Engine requires app.yaml to run your project, since this project is a backend\n example of [SOHA](http://www.codeproject.com/Articles/118683/SOHA-Service-Oriented-HTML-Application-Concepts-an),\n architecture, this file is essentially empty: it only includes two other yaml files to ensure the front-end\n is separated from back-end, these two other yaml files are:\n    - web_app.yaml: this is a fork of yaml that enables hosting a static site on Google App Engine. This seed\n project does not have UI front-end, this yaml is provided for future reference\n    - web_api.yaml: this yaml tells Google App Engine which Python class to load to run RESTful service if\n the request path matches `/api/*`\n* appengine_config.py: this config file is the key to use Python modules underneath `lib` directory\n* data.datastore: optional data file for local testing, it requires `--datastore_path=\u003cYour Clone Path\u003e/data.datastore`\ncommandline arguments when run the project on localhost.\n* api/config.py: some API level configuration constants and variables are defined here, including:\n    - session time span\n    - datastore version string\n    - api base url for version management\n    - application version, etc.\n\n### Run and deploy the project\n\nGoogle has excellent documentation on [how to run on local development server](https://developers.google.com/appengine/docs/python/tools/devserver),\nand also [how to deploy and manage your app on App Engine](https://developers.google.com/appengine/docs/python/tools/uploadinganapp).\nYou can also reference [Python tutorial](https://developers.google.com/appengine/docs/python/gettingstartedpython27/introduction) for more details.\n\n### Flexible Data Retrieval\n\nAs discussed before, this project provides a generic data retrieval mechanism (defined in modelX.retrieve_list as generic static method), \nit supports ndb.Model JSON serialization, response packaging, sorting, paging and query for property with great flexibilities,\nit can not only query property with actual value, but also supports OR operation via dictionary literal in the query string, \nand Range operation through tuple literal in the query string as well, here are some query examples via HTTP GET:\n\ndefault query (with default limit set to 20: it returns up to 20 entities):\n    \n    curl -isv http://localhost:8080/api/v1.0/calllogs/\n    \norder by name (ASC):\n\n    curl -isv http://localhost:8080/api/v1.0/calllogs/?order=name\n    \norder by name (DESC):\n\n    curl -isv http://localhost:8080/api/v1.0/calllogs/?order=-name\n    \nsort and limit:\n    \n    curl -isv \"http://localhost:8080/api/v1.0/calllogs/?order=-name\u0026limit=2\"\n    notice \"more_cursor\" and \"more_url\" in the response\n       \npaging (with cursor value returned in earlier calls)\n\n    curl -isv \"http://localhost:8080/api/v1.0/calllogs/?cursor=E-ABAOsB8gEEbmFtZfoBChoISGVrdGhvbjXsAYICNWodZGV2fmNhbGwtbG9nLTIxMS1kYXRhLXNlcnZpY2VyFAsSB0NhbGxMb2cYgICAgID0jgoMiAIBFA%3D%3D\u0026limit=2\u0026order=-name\"\n    \nlist all (default limit) entities whose name is \"Hekthon5\":\n\n    curl -isv http://localhost:8080/api/v1.0/calllogs/?name=Hekthon5\n    \nOR: list all (default limit) entities whose name is either 'Hekthon5' or 'Hekthon4':\n    \n    curl -isv -G --data-urlencode \"name=['Hekthon5', 'Hekthon4']\" http://localhost:8080/api/v1.0/calllogs/\n    Notice the square bracket in the query string, this list literal will make the value to be an option ist\n\nRange: list all (default limit) entities which is created between '2014-08-01 00:00:00' and '2014-08-04 23:59:59' (inclusive)\n\n    curl -isv -G --data-urlencode \"created=('2014-08-01 00:00:00', '2014-08-04 23:59:59')\" http://localhost:8080/api/v1.0/calllogs/\n    Notice the parentheses in the query string, this tuple literal will make the value to be a range \n\n### Test the API\n\nHere are some sample `curl` command to test the API in terminal. When testing after deployment, simply replace\n`http://127.0.0.1:8080` with `http://\u003cyour_google_project_id\u003e.appspot.com`:\n\n    curl -isv -H \"Content-Type: application/json\" -X POST -d '{\"name\":\"modesty zhang\", \"zip\":\"92129\"}' http://127.0.0.1:8080/api/v1.0/calllog/\n\nIt should return HTTP 400 Bad Request with:\n\n    {\"message\": \"Missing required parameter callReason in json or the post body or the query string\"}    \n\nThe above `curl` command line shows how the service responds when required fields not found in request body.\n\nThe following `curl` command should return a HTTP 200 OK:\n\n    curl -isv -H \"Content-Type: application/json\" -X POST -d '{\"name\":\"modesty zhang\", \"zip\":\"92129\", \"callReason\": \"GAE RESTful sample\", \"location\": \"Google Cloud Platform\", \"phone\": \"8588889999\"}' http://127.0.0.1:8080/api/v1.0/calllog/\n    \nThe response body is structured by `common/response.py` and returns with datastore newly created entity id:\n\n    {\n        \"result\": {\n            \"created\": \"2014-07-20 01:32:43\", \n            \"id\": 5655612935372800\n        }, \n        \"status\": {\n            \"code\": 200, \n            \"message\": \"OK\"\n        }\n    }\n    \nTest cases for data retrieval are listed in last section.\n\n### CORS Support\n\nThe built-in implementation of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) comes with Flask Restful \nhas issues to automatically handle HTTP OPTIONS calls, it returns 404 with the correct setups for methods decorators. I made \ntwo additions to CORS support in Flask Restful (look for \"MQZ\" pre-fixed comments in lib/flask_restful/utils/cors.py):\n\n* Default HTTP OPTIONS handler:\n\n        # MQZ. 08/12/2014: without this, will get 405 response for OPTIONS request\n        f.required_methods = ['OPTIONS']\n        \n* Default on Cookie support:\n\n        # MQZ. 8/26/2014: Make sure cors support for cookies (with_credentials), default on\n        def crossdomain(origin=None, methods=None, headers=None,\n            max_age=21600, attach_to_all=True,\n            automatic_options=True, with_credentials=True):\n\nThen in line 43:\n\n        if with_credentials:\n            h['Access-Control-Allow-Credentials'] = True\n\nLeave me a message in [GitHub](https://github.com/modesty) if you see issue on CORS.\n\n### Receiving updates from upstream\n\nWhen we upgrade this repo with newer python code or testing library code, you can just\nfetch the changes and merge them into your project with git.\n\n\n## Contact\n\nFor more information on more advanced RESTful services with [Python NDB API](https://developers.google.com/appengine/docs/python/ndb/), \nplease check out https://cloud.google.com/, or leave me a message in [GitHub](https://github.com/modesty).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodesty%2Fcalllog211","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmodesty%2Fcalllog211","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodesty%2Fcalllog211/lists"}