{ "info": { "author": "thingsplode", "author_email": "tamas.csaba@gmail.com", "bugtrack_url": null, "classifiers": [ "Environment :: Console", "Environment :: Web Environment", "Framework :: Flask", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX", "Programming Language :: Python" ], "description": "# appkernel - microservice APIs made easy\n\n\n![alt build_status](https://travis-ci.org/accelero-cloud/appkernel.svg?branch=master \"build status\")\n![alt issues](https://img.shields.io/github/issues/accelero-cloud/appkernel.svg \"Open issues\")\n![alt coverage](https://codecov.io/gh/accelero-cloud/appkernel/branch/master/graph/badge.svg \"code coverage\")\n![GitHub license](https://img.shields.io/github/license/accelero-cloud/appkernel.svg \"license\")\n\n## What is Appkernel?\nA super-easy microservice and API framework, which enables API development from zero to production within minutes (no kidding: literally within minutes).\n\n**It provides data serialisation, transformation, validation, security, ORM, RPC and service mash functions out of the box** ([check out the roadmap for more details](https://github.com/accelero-cloud/appkernel/blob/master/docs/roadmap.md)).\n- [Check out the tutorial :)](https://github.com/accelero-cloud/tutorials)\n- [or read the full documentation.](http://appkernel.readthedocs.io/en/latest/)\n\n... and finally **give a vote on [awesome-python](https://github.com/vinta/awesome-python/pull/1103)** if you like the project, so it gets added to the list of RESTful python frameworks. **Only 15 more votes are missing :)**\n\n## Installation\n\n```bash\n pip install appkernel\n```\n\n## Crash Course\nLet's build an awseome mini identity service:\n```python\nclass User(Model, MongoRepository):\n # define the resource schema as class meta data\n id = Property(str)\n name = Property(str, index=UniqueIndex)\n email = Property(str, validators=[Email], index=UniqueIndex)\n password = Property(str, converter=content_hasher(), omit=True)\n roles = Property(list, sub_type=str, default_value=['Login'])\n\n @classmethod\n def before_post(cls, *args, **kwargs):\n # this method is automatically called before persisting the instance\n # one can use after_post for hook after the persistence.\n user = kwargs.get('model')\n print(f'going to create the following user: {user}')\n\n\n\nif __name__ == '__main__':\n # let's expose the user resource\n kernel = AppKernelEngine()\n kernel.register(User)\n\n # let's create and persist a sample user\n user = User(name='Test User', email='test@accelero.cloud', password='some pass')\n user.save()\n\n # and we are all set\n kernel.run()\n```\nThat's all folks, our user service is ready to roll, the entity is saved, we can re-load the object from the database, or we can request its json schema for validation, or metadata to generate an SPA (Single Page Application).\nOf course validation and some more goodies are built-in as well :)\n\n### Retrieving our our User, using HTTP requests\n\n**GET request**:\n```bash\ncurl -i -X GET \\\n 'http://127.0.0.1:5000/users/'\n```\n**And the result**:\n```json\n{\n \"_items\": [\n {\n \"_type\": \"User\",\n \"email\": \"test@appkernel.cloud\",\n \"id\": \"U0590e790-46cf-42a0-bdca-07b0694d08e2\",\n \"name\": \"Test User\",\n \"roles\": [\n \"Login\"\n ]\n }\n ],\n \"_links\": {\n \"self\": {\n \"href\": \"/users/\"\n }\n }\n}\n```\n\nAdding extra and secure methods using the `@action` decorator is easy as well:\n\n```python\n@action(method='POST', require=[CurrentSubject(), Role('admin')])\ndef change_password(self, current_password, new_password):\n if not pbkdf2_sha256.verify(current_password, self.password):\n raise ServiceException(403, _('Current password is not correct'))\n else:\n self.password = new_password\n self.save()\n return _('Password changed')\n```\n\nThe example above exposes the `http://base_url/users//change_password` endpoint and allows the user with admin\nrole or the user with the current user_id to call it.\n\nCreate additional hooks, which are called before and after a HTTP method is executed, by simply adding\na static method to the `Model` class following the convention: `before_{http_method}` and `after_{http_method}`:\n\n**Example**:\n```python\n@classmethod\ndef before_post(cls, *args, **kwargs):\n user = kwargs.get('model')\n print(f'going to create this user: {user}')\n```\n\nor inspect (and alter) the already persisted object:\n\n```python\n@classmethod\ndef after_post(cls, *args, **kwargs):\n user = kwargs.get('model')\n print(f'this user was created: {user}')\n```\n\nWe can also call other services using the built-in REST client proxy. In the snippet bellow we call the `reservations` endpoint\non the Inventory service, by POST-ing a `Reservation` object.\n\n```python\n client = HttpClientServiceProxy('http://127.0.0.1:5000/')\n status_code, rsp_dict = client.reservations.post(Reservation(order_id=order.id, products=order.products))\n```\n\n### Some features of the REST endpoint\n\n- GET /users/12345 - retrieve a User object by its database ID;\n- GET /users/?name=Jane&email=jane@appkernel.cloud - retrieve the User named Jane with e-mail address jane@appkernel.cloud;\n- GET /users/?name=Jane&name=John&logic=OR - retrieve Jane or John;\n- GET /users/?roles=~Admin - retrieve all users which have the role Admin;\n- GET /users/?name=[Jane,John] - retrieve all user with the name Jane or John;\n- GET /users/?inserted=>2018-01-01&inserted=<2018-12-31 - return all users created in 2018;\n- GET /users/?page=1&page_size=5&sort_by=inserted&sort_order=DESC - return the first page of 5 elements;\n- GET /users/?query={\"$or\":[{\"name\": \"Jane\"}, {\"name\":\"John\"}]} - return users filtered with a native Mongo Query;\n- GET /users/meta - retrieve the metadata of the User class for constructing self-generating SPAs;\n- GET /users/schema - return the Json Schema of the User class used for validating objects;\n\nAdditionally the following HTTP methods are supported:\n- POST: create a new user (or updates existing one by replacing it) using a json payload or multipart form data\n- PATCH: add or updates some fields on the User object\n- PUT: replaces a User object\n\n### A few features of the built-in ORM function\nFind one single user matching the query Property:\n```python\nuser = User.where(name=='Some username').find_one()\n```\nReturn the first 5 users which have the role \"Admin\":\n```python\nuser_generator = User.where(User.roles % 'Admin').find(page=0, page_size=5)\n```\nOr use native Mongo Query:\n```python\nuser_generator = Project.find_by_query({'name': 'user name'})\n```\n\nAtomic updates:\n```python\n# reserve 10 products with product code TRS abd size M\nquery = StockInventory.where((StockInventory.product.code == 'TRS') & (StockInventory.product.size == ProductSize.M))\nfor _ in range(10):\n ...\n query.update(available=StockInventory.available - 1, reserved=StockInventory.reserved + 1)\n```\n\nOne could extend the *AuditedMongoRepository* mixin instead of the *MongoRepository* and we would end up with 3 extra fields:\n- **inserted**: the date-time of insertion;\n- **updated**: the date-time of the last update;\n- **version**: the number of versions stored for this document;\n\n## Some more extras baked into the Model\nGenerate the ID value automatically using a uuid generator and a prefix 'U':\n```python\nid = Property(..., generator=uuid_generator('U'))\n```\nAdd a Unique index to the User's name property:\n```python\nname = Property(..., index=UniqueIndex)\n```\nValidate the e-mail property, using the NotEmpty and Email validators\n```python\nemail = Property(..., validators=[Email, NotEmpty])\n```\nAdd schema validation to the database:\n```python\nUser.add_schema_validation(validation_action='error')\n```\nHash the password and omit this attribute from the json representation:\n```python\npassword = Property(..., converter=content_hasher(rounds=10), omit=True)\n```\nRun the generators on the attributes and validate the object (usually not needed, since it is implicitly called by save and dumps methods):\n```python\nuser.finalise_and_validate()\n```\n### Security is also part of the mix\n\nThe following snippet shows the declarative way of access control:\n```python\nuser_service = kernel.register(User, methods=['GET', 'PUT', 'POST', 'PATCH', 'DELETE'])\nuser_service.deny_all().require(Role('user'), methods='GET').require(Role('admin'),\n methods=['PUT', 'POST', 'PATCH', 'DELETE'])\n```\n\n1. user_service.deny_all(): by default access to all methods is forbidden;\n2. require(Role('user'), methods='GET'): GET methods can be used by users having the Role: user (basic login role);\n3. require(Role('admin'), methods=['PUT', 'POST', 'PATCH', 'DELETE']): one needs the Role: admin in order to call other http methods;\n\n[I want to know the current status of the project](docs/roadmap.md)\n\n[For more details feel free to check out the documentation](http://appkernel.readthedocs.io)\n\n## What are we building here?\nThe vision of the project is to provide you with a full-fledged [microservice chassis](http://microservices.io/microservices/news/2016/02/21/microservice-chassis.html),\nas defined by Chris Richardson.\n\n## How does it helps you?\nWe've spent the time on analysing the stack, made the hard choices for you in terms of Database/ORM/Security/Rate Limiting and so on, so\nyou don't have to. You can focus entirely on delivering business value from day one and being the rockstar of your project.\n\nCurrently supported (and fully tested) features:\n- REST endpoints over HTTP\n- Full range of CRUD operations\n- Customizable resource endpoints\n- Customizable, multiple item endpoints\n- Filtering and Sorting\n- Pagination\n- Data Validation\n- Extensible Data Validation\n- Default Values\n- Projections\n- Embedded Resource Serialization\n- Custom ID Fields\n- MongoDB Aggregation Framework\n- Powered by Flask\n\n### Contribute\nBe part of the development: [contribute to the project :)](docs/contributors.md)\n\n### Why did we built this?\n* We had the need to build a myriad of small services in our daily business, ranging from data-aggregation pipelines, to housekeeping services and\nother process automation services. These do share similar requirements and the underlying infrastructure needed to be rebuilt and tested over and over again. The question arose:\nwhat if we avoid spending valuable time on the boilerplate and focus only on the fun part?\n\n* Often time takes a substantial effort to make a valuable internal hack or proof of concept presentable to customers, until it reaches the maturity in terms reliability, fault\ntolerance and security. What if all these non-functional requirements would be taken care by an underlying platform?\n\n* There are several initiatives out there (Flask Admin, Flask Rest Extension and so), which do target parts of the problem, but they either need substantial effort to make them play nice together, either they feel complicated and uneasy to use.\nWe wanted something simple and beautiful, which we love working with.\n\nThese were the major driving question, which lead to the development of App Kernel.\n\n### How does it works?\n\nAppKernel is built around the concepts of Domain Driven Design. You can start the project by laying out the model.\nThe first step is to define the validation and data generations rules. For making life easier, one can also set default values.\nThan one can extend several built-in classes in order to augment the model with extended functionality:\n* extending the Repository class (or its descendants) adds and ORM persistency capability to the model;\n* extending the Service class (or its descendants) add the capablity to expose the model over REST services;\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/accelero-cloud/", "keywords": "microservice,flask,pymongo,serverless,rest,flask,serialisation,orm,mongo,api,rest api", "license": "Apache 2.0", "maintainer": "", "maintainer_email": "", "name": "appkernel", "package_url": "https://pypi.org/project/appkernel/", "platform": "any", "project_url": "https://pypi.org/project/appkernel/", "project_urls": { "Homepage": "https://github.com/accelero-cloud/" }, "release_url": "https://pypi.org/project/appkernel/1.2.4/", "requires_dist": [ "pyyaml", "enum34", "pymongo (==3.7.1)", "simplejson", "Flask (>0.12.3)", "werkzeug", "eventlet", "wrapt", "passlib (==1.7.1)", "jsonschema", "flask-babel", "babel", "pyjwt", "cryptography", "sets", "aiohttp", "cchardet", "aiodns", "requests" ], "requires_python": "", "summary": "An easy to use microservice framework.", "version": "1.2.4" }, "last_serial": 4273460, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "545c110edd5dbcbb9049c6ebf73477b1", "sha256": "d1f37008d334acba4ce3c86bacc4e92ea8b925ee4c17c2359c6ef82ce258ecc8" }, "downloads": -1, "filename": "appkernel-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "545c110edd5dbcbb9049c6ebf73477b1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 52617, "upload_time": "2018-07-08T20:53:28", "url": "https://files.pythonhosted.org/packages/84/07/3fa6d116e70ad6dd36d5ccc1c42d4c651b6526d307efc8f99ed765b54eeb/appkernel-0.1.0-py3-none-any.whl" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "3744411a2fbfa27e7b3cfa1809b06854", "sha256": "690ad955c0c9897fabe016eebd4cd1ad7fca71d98b3c9893987415302a3ba8fd" }, "downloads": -1, "filename": "appkernel-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "3744411a2fbfa27e7b3cfa1809b06854", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 52618, "upload_time": "2018-07-08T20:54:14", "url": "https://files.pythonhosted.org/packages/7d/e3/80aef9fc19f8afa5cbb5e6e8d7c0ad27c4c2e123b7ceaab3868791433e32/appkernel-1.0.0-py3-none-any.whl" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "874c7c8975b7a8c159a8845196c61376", "sha256": "d241b870b4ca468a5af723662dab74fea7d5979df98babded063398133cb3493" }, "downloads": -1, "filename": "appkernel-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "874c7c8975b7a8c159a8845196c61376", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 48369, "upload_time": "2018-09-09T15:29:29", "url": "https://files.pythonhosted.org/packages/1a/70/c99d6eab1b1b7056a78d88811340a127a7a606edbe42ca6b6c13f1f2c510/appkernel-1.0.1-py3-none-any.whl" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "b4694342452e25b847089e8918aeffc4", "sha256": "cb96eda8c9d44569b431661cdb53e5d0ed6019ab32bba3bf0aee49d76221ad64" }, "downloads": -1, "filename": "appkernel-1.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b4694342452e25b847089e8918aeffc4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 48341, "upload_time": "2018-09-09T15:29:31", "url": "https://files.pythonhosted.org/packages/72/fd/a6b7c70c9747bd13c6c637e6e27f9492f088cfeb809790fafe4ee35b554a/appkernel-1.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "712f0ffcaf29fdfaaf935af035b0269a", "sha256": "450150a52427d43d0e016f7f78ab7634b106df15f6bab090d074aa196b16ebf0" }, "downloads": -1, "filename": "appkernel-1.1.0.tar.gz", "has_sig": false, "md5_digest": "712f0ffcaf29fdfaaf935af035b0269a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 48226, "upload_time": "2018-09-09T15:29:33", "url": "https://files.pythonhosted.org/packages/23/0b/bc3fc83538d829e41dfc6899a6c394125e44c094006f32a21332068e7b16/appkernel-1.1.0.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "4382b8764d4421abbca39cc53208273d", "sha256": "b91cbd2bf7bd392675d7e1b158074de516f4c715744796d6eb34a6cd6134192c" }, "downloads": -1, "filename": "appkernel-1.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "4382b8764d4421abbca39cc53208273d", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 48967, "upload_time": "2018-09-14T12:04:33", "url": "https://files.pythonhosted.org/packages/2b/cc/d503f90731b0819e640b8e23f056bbba525e182007a7acba167727dc807a/appkernel-1.2.0-py3-none-any.whl" } ], "1.2.1": [ { "comment_text": "", "digests": { "md5": "e0f74141a1add834abe214c94b2578cb", "sha256": "be07b00d50d173db6134d96246da86b41bd09150b5315953e45aea1c171ee63c" }, "downloads": -1, "filename": "appkernel-1.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "e0f74141a1add834abe214c94b2578cb", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 49032, "upload_time": "2018-09-14T13:38:33", "url": "https://files.pythonhosted.org/packages/a4/66/411649efc64d3cce0a14f87c2d17959b9e86b66ca4dca2278cbe8461d3a8/appkernel-1.2.1-py3-none-any.whl" } ], "1.2.2": [ { "comment_text": "", "digests": { "md5": "3fcc152b8aab4b3dbfdf1e02b2ecc01f", "sha256": "b9a6bce8731cde3e17a2f51b2c63f3990ff3b42f2a4025cea168a0064eb906dc" }, "downloads": -1, "filename": "appkernel-1.2.2-py3-none-any.whl", "has_sig": false, "md5_digest": "3fcc152b8aab4b3dbfdf1e02b2ecc01f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 49098, "upload_time": "2018-09-14T15:04:54", "url": "https://files.pythonhosted.org/packages/fe/80/d5e59b024cd06fb569619901e743dcdbe8f3f748f3f6f3aafca149bcad8a/appkernel-1.2.2-py3-none-any.whl" } ], "1.2.3": [ { "comment_text": "", "digests": { "md5": "de79e60b5cede5e9763bf9663bc34213", "sha256": "80b5d4158bbeabcd47386eeace88c32e5d9eb1ef877fba0b09239577e888c984" }, "downloads": -1, "filename": "appkernel-1.2.3-py3-none-any.whl", "has_sig": false, "md5_digest": "de79e60b5cede5e9763bf9663bc34213", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 49097, "upload_time": "2018-09-14T16:02:54", "url": "https://files.pythonhosted.org/packages/bd/9b/d7102f5f4e910b7a271830ea5aa0bff61333a3b786a461f636dd7731ae39/appkernel-1.2.3-py3-none-any.whl" } ], "1.2.4": [ { "comment_text": "", "digests": { "md5": "6c3ef31731385226f993c147db1b6c14", "sha256": "b0ac8ba3c37dc8878d6cb4036120a23dce45678402d8e51afe40a8f648a467f5" }, "downloads": -1, "filename": "appkernel-1.2.4-py3-none-any.whl", "has_sig": false, "md5_digest": "6c3ef31731385226f993c147db1b6c14", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 49794, "upload_time": "2018-09-14T19:42:40", "url": "https://files.pythonhosted.org/packages/f5/1c/50bedbf1dc072f08740cebb9e68820ecf88cad3ad5aed967c6ef178d5601/appkernel-1.2.4-py3-none-any.whl" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6c3ef31731385226f993c147db1b6c14", "sha256": "b0ac8ba3c37dc8878d6cb4036120a23dce45678402d8e51afe40a8f648a467f5" }, "downloads": -1, "filename": "appkernel-1.2.4-py3-none-any.whl", "has_sig": false, "md5_digest": "6c3ef31731385226f993c147db1b6c14", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 49794, "upload_time": "2018-09-14T19:42:40", "url": "https://files.pythonhosted.org/packages/f5/1c/50bedbf1dc072f08740cebb9e68820ecf88cad3ad5aed967c6ef178d5601/appkernel-1.2.4-py3-none-any.whl" } ] }