{ "info": { "author": "Nicolas Esteves", "author_email": "hamstahguru@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "Tools to play with json-schemas defined APIs.\n\nThese tools are based on json-schema draft 3 from http://tools.ietf.org/html/draft-zyp-json-schema-03\nNot all features of the schema are supported and probably won't be.\nHandling of not supported feature varies between the different tools.\n\nAll these tools are proofs of concept and work in progress, they need more extensive testing and documentation.\n\n# datagenerator \n\nClass to generate random values given a json-schema. \nDoesn't support all json-schema monstruousities, only a subset I find useful. \nSee TODO.md for what is likely to be implemented next. \n\n## Examples\n\n```python\nfrom datagenerator import DataGenerator\n\ngenerator = DataGenerator()\n```\n\n### Basic\n\nGenerate random values of each basic type using\n\n```python\n>>> generator.random_value(\"string\")\n'Olzq3LV'\n>>> generator.random_value(\"number\")\n-6.675904074356879\n>>> generator.random_value(\"integer\")\n30\n>>> generator.random_value(\"boolean\")\nTrue\n\n```\n\n### Basic with constraints\n\n`number`\n\n```python\n>>> generator.random_value({\"type\":\"number\", \"minimum\":30})\n32.34295327292445\n>>> generator.random_value({\"type\":\"number\", \"maximum\":30})\n-35.80704939879546\n>>> generator.random_value({\"type\":\"number\", \"maximum\":30, \"minimum\":12})\n16.45747265846327\n```\n\n`integer` supports `minimum` and `maximum` like `number` and more\n```python\n>>> generator.random_value({\"type\":\"integer\", \"maximum\":30, \"divisibleBy\":4, \"minimum\":12})\n24\n>>> generator.random_value({\"type\":\"integer\", \"maximum\":30, \"exclusiveMaximum\":True, \"minimum\":28})\n29\n```\n(same for `exclusiveMinimum`)\n\n`string` supports `minLength`, `maxLength`, `pattern` (ignores `minLength` and `maxLength` if `pattern` is used)\n```python\n>>> generator.random_value({\"type\":\"string\", \"maxLength\":20, \"minLength\":15})\n'VytPCEdAImX11188HU'\n>>> generator.random_value({\"type\":\"string\", \"pattern\":\"[0-9]{3}[a-zA-Z]{2,5}\"})\nu'806FoNP'\n```\n\n`boolean` doesn't have any constraints.\n\n### Arrays\n\nWithout constraints the array size will be picked the same way as a random `integer`. \nEach item in the array is generated using the default generator for the type given in `items`.\n```python\n>>> generator.random_value({\"type\":\"array\", \"items\": {\"type\":\"string\"}})\n[\n\t'39yxcpvS5tfPf6O', \n\t'sNDk7SlGNQstxxx', \n\t'nPcRSD9yIP7j ', \n\t'PWP7KQfjc1', \n\t'tt6F6Z2YEp'\n]\n```\n\n`minItems`, `maxItems` and `uniqueItems` are supported\n\nThe type of object in `items` can be anything that the generator knows about, either one of the basic types\nor a user defined one available from the generator's schemas store. \n\n```python\nfrom schemasstore import SchemasStore\n\n...\n>>> from schemasstore import SchemasStore\n>>> store = SchemasStore()\n>>> generator.schemas_store = store\n>>> store.add_schema({\"type\":\"integer\", \"name\":\"small_integer\", \"minimum\":0,\"maximum\":9})\nTrue\n>>> generator.random_value({\"type\":\"array\", \"uniqueItems\":True, \"minItems\":10, \"items\":{\"type\":\"small_integer\"}})\n[0, 7, 2, 5, 3, 6, 1, 4, 8, 9]\n```\n\nSee [datagenerator](https://github.com/hamstah/apitools/blob/master/datagenerator.py) for other examples.\n\n### Objects\n\nObjects can be generated the same way as the other types.\n\nExample generating [search_result.json](https://github.com/hamstah/apitools/blob/master/data/schemas/search_result.json)\n```python\n>>> store.load_folder(\"data/schemas/\")\n>>> generator.random_value(\"search_result\")\n{u'price': 21.980325774975253, u'name': 'wdvfXYrrt', u'reference': 26}\n```\n\nGenerating arrays of objects is fine as well\n```python\n>>> generator.random_value({\"type\":\"array\", \"maxItems\":3, \"minItems\":2, \"items\":{\"type\":\"search_result\"}})\n[\n {u'price': 20.304440535786522, u'name': 'VUIgjaPbs', u'reference': 40}, \n\t{u'price': 28.45387747055259, u'name': 'JTycBU1V78X1S', u'reference': 27}\n]\n```\n\nOr generating objects with arrays of other objects in them, see\n[search_resuts](https://github.com/hamstah/apitools/blob/master/data/schemas/search_results.json) \nwith an array of [search_result](https://github.com/hamstah/apitools/blob/master/data/schemas/search_result.json)\n```python\n>>> generator.random_value(\"search_results\")\n{\n u'total_results': 41, \n\tu'total_pages': 26, \n\tu'current_page': 33, \n\tu'items_per_page': 27, \n\tu'results': [\n\t {u'price': 26.218704680177446, u'name': 'B4p1Z1pOFQO', u'reference': 38}, \n\t\t{u'price': 21.205089550441276, u'name': 'FQPHdLds', u'reference': 7}, \n\t\t{u'price': 20.610536930894398, u'name': '8D862p1XVupP', u'reference': 38}, \n\t\t{u'price': 9.543934434058526, u'name': 'PmqBA0e DIWisf', u'reference': 32}\n\t]\n}\n```\n\n### Schemas\n\nWhy not generate random schemas?\n```python\n>>> r_schema = generator.random_schema()\n>>> r_schema\n{\n 'type': 'object', \n\t'properties': {\n\t u'viYXjhu': {'required': False, 'type': 'boolean'}, \n\t\tu'TO': {'required': False, 'type': 'string'}, \n\t\tu'NTSd': {'required': False, 'type': 'string'}, \n\t\tu'WjaL': {'required': False, 'type': 'string'}, \n\t\tu'PtvhZ': {'required': False, 'type': 'boolean'}\n\t}, \n\t'name': u'zJllGkKosmocOVO'\n}\n```\nAnd then generate an array of random values of it\n```python\n>>> store.add_schema(r_schema)\nTrue\n>>> generator.random_value({\"type\":\"array\", \"minItems\":1, \"maxItems\":3, \"items\":{\"type\":\"zJllGkKosmocOVO\"}})\n[\n\t{u'TO': 'jamKFpdwY'}, \n\t{u'WjaL': '8LnibWUdsSI', u'PtvhZ': True}, \n\t{}\n]\n```\n\n## Notes on the generation\n\nAll the values are generated using the `random` module, so please don't use the generate values for anything\nrequiring reliable randomness == **don't use it to generate passwords**.\n\nTo generate the data, the generator has to limit the range of possible values, so the values generated don't\nvary too wildly. The ranges are controlled by variables in `DataGenerator`. Feel free to tweak them, especially\nif you need values that don't fall into those ranges without having to set both minimum and maximum on your \nproperties.\n\n---\n\n# urlsgenerator\n\nClass to generate links defined in the links section of a json-schema.\n\n## Example\n\nGenerate links from [book.json](https://github.com/hamstah/apitools/blob/master/data/schemas/book.json)\n\nInput\n```javascript\n...\n\t\"isbn\" : {\n\t \"type\":\"string\",\n\t \"required\":true,\n\t \"pattern\":\"^\\\\d{12}(\\\\d|X)$\"\n\t}\n\n },\n \"links\" : [\n\t{\n\t \"rel\":\"self\",\n\t \"href\":\"books/{isbn}\"\n\t},\n\t{\n\t \"rel\":\"instances\",\n\t \"href\": \"books\"\n\t}\n ]\n...\n\n```\n\nOutput\n```python\n{\n u'instances': [u'books'], \n u'self' : [u'books/525259838909X']\n}\n```\n\n`{isbn}` got replaced by a random value `525259838909X` satisfying the constraints on `isbn` (matches the regex).\n\n---\n\n# invaliddatagenerator\n\nClass to generate invalid data for a given schema\n\nBasically does the opposite of datagenerator. WIP, needs documentation and examples.\n\n---\n\n# modelgenerator\n\nBase class to generate models from a schema, nothing too visible on its own, check `resourceserver`.\n\n---\n\n# flasksqlalchemymodelgenerator\n\nGenerate SQLAlchemy models to be used with flask-sqlalchemy from a schema. Uses `modelgenerator`. \nUsed in `resourceserver` to store and query items.\n\n---\n\n# backbonemodelgenerator\n\nGenerate models and collections for Backbone.js from a schema. \nThe models generated use the primary key defined in the `rel=self` link or `id` by default. \nTo be able to use collections, make sure your schema has a `rel=instances` link or `fetch` won't work. \n\n## Usage\n\n```bash\n$ python backbonemodelgenerator.py -h\nUsage: backbonemodelgenerator.py jsonfile1 [jsonfile2]...\n\nOptions:\n -h, --help show this help message and exit\n -t OUTPUT_TYPE, --type=OUTPUT_TYPE\n Output type (js|wrapped|html)\n```\n\n## Output types\n\n### js\n\nOutputs only the js code for the models/collections\n\n```bash\n$ python backbonemodelgenerator.py -t js data/schemas/message.json\n\nApp.Models.Message = Backbone.Model.extend({\n urlRoot: '/messages',\n idAttribute: 'id'\n});\n\nApp.Collections.Messages = Backbone.Collection.extend({\n model : App.Models.Message,\n url : \"/messages\"\n});\n```\n\n### wrapped\n\nWraps the js code into `$(document).ready()`\n\n```bash\n$ python backbonemodelgenerator.py -t wrapped data/schemas/message.json\n\n$(document).ready(function() {\n\n window.App = { Models : {}, Collections : {} };\n \n App.Models.Message = Backbone.Model.extend({\n urlRoot: '/messages',\n idAttribute: 'id'\n });\n \n App.Collections.Messages = Backbone.Collection.extend({\n model : App.Models.Message,\n url : \"/messages\"\n });\n\n});\n```\n\n### html\n\nSame as wrapped but generate a whole html page including jQuery, Backbone and Underscore to easily test.\n\n## Example usage\n\n### Setup\n\nYou can use it with resource server for example\n```bash\n$ mkdir static\n$ python backbonemodelgenerator.py -t html data/schemas/message.json > static/index.html\n$ python resourceserver.py data/schemas/message.json\nAdded message\n * Running on http://0.0.0.0:5000/\n```\n\nNow open your browser at http://0.0.0.0:5000/static/index.html\nOpen your js console to start playing\n\n### Create a collection and fetch them\n\n```javascript\nvar col = new App.Collections.Messages()\ncol.fetch()\n```\nYou should see backbone talking to the resource server in the server shell\n```bash\n127.0.0.1 - - [20/Nov/2012 01:17:15] \"GET /messages HTTP/1.1\" 200 -\n```\n\nYou can inspect the results using\n```javascript\ncol.models\n```\n\nUsing fetch() only works if your schema includes a link with `rel=instances`\n\n### Create a new message\n\n```javascript\nvar msg = new App.Models.Message({recipient:\"01234567890\", text:\"test message\"})\nmsg.attributes\n```\n\nAt that point the message is not saved yet, you can verify by using\n```javascript\nmsg.isNew()\n```\n\nYou can save it on the server using \n```javascript\nmsg.save()\n```\n\nYou can verify that the message was sent to the server in the server shell\n```bash\n127.0.0.1 - - [20/Nov/2012 01:23:24] \"POST /messages HTTP/1.1\" 201 -\n```\n\nNow you should have an id for the message and it shouldn't be marked as new anymore.\n```javascript\nmsg.id\nmsg.isNew()\n```\n\n### Fetch an existing message\n\nCreate a message with the `id` of the message to fetch\n```javascript\nvar msg = new App.Models.Message({id: 3})\n```\n\nThe message is not marked as new as it has an id. \nWe can then fetch the actual message from the server using \n```javascript\nmsg.fetch()\nmsg.attributes()\n```\n\nYou can see the query in the server shell again\n```bash\n127.0.0.1 - - [20/Nov/2012 01:25:41] \"PUT /messages/3 HTTP/1.1\" 200 -\n```\n\n### Update a message\n\nOnce you have a message object, you can update it using `save`.\n\n```javascript\n> msg.attributes.recipient\n\"01234567890\"\n> msg.save({recipient:\"00123456789\"})\n> msg.attributes.recipient\n\"00123456789\"\n```\n\nThis is done by doing a `PUT` on the server\n```bash\n127.0.0.1 - - [20/Nov/2012 01:33:35] \"PUT /messages/3 HTTP/1.1\" 200 -\n```\n\n### Delete a message\n\nSimply use `destroy` on the object\n```javascript\nmsg.destroy()\n```\n\nAnd see the `DELETE` happening on the server\n```bash\n127.0.0.1 - - [20/Nov/2012 01:34:48] \"DELETE /messages/3 HTTP/1.1\" 204 -\n```\n\n---\n\n# resourceserver\n\nClass to implement the REST api of resources defined in a schema. \nSupports creation, update, retrieval, deletion, listing of instances and schema. \n\n## Usage\n\nRun the server using\n```bash\n$ python resourceserver.py [jsonfile1, jsonfile2, ...]\n```\n\n## Example using data/schemas/message.json\n\n```bash\n$ python resourceserver.py data/schemas/message.json\nAdded message\n * Running on http://0.0.0.0:5000/\n```\n\n### Create a new message\n\n```bash\n$ curl -i -X POST http://0.0.0.0:5000/messages -d \"recipient=07771818335&text=nice message\"\n$ curl -i -X POST http://0.0.0.0:5000/messages -d '{\"recipient\":\"01234567890\", \"text\":\"test\"}' \\\n\t -H \"Content-Type: application/json\"\nHTTP/1.0 201 CREATED\nContent-Type: application/json\nContent-Length: 13\nLocation: http://0.0.0.0:5000/messages/2\nServer: Werkzeug/0.8.3 Python/2.7.3\nDate: Sun, 18 Nov 2012 19:28:56 GMT\n\n{\n \"id\": 2\n}\n```\n\n### List messages\n\n```bash\n$ curl -i -X GET http://0.0.0.0:5000/messages\nHTTP/1.0 200 OK\nContent-Type: application/json\nContent-Length: 126\nServer: Werkzeug/0.8.3 Python/2.7.3\nDate: Sun, 18 Nov 2012 19:32:09 GMT\n\n[\n {\"text\": \"I