diff --git a/example.raml b/example.raml index 0ba8902..5c2ed8b 100644 --- a/example.raml +++ b/example.raml @@ -5,43 +5,11 @@ documentation: - title: Home content: | Welcome to the example API. -baseUri: http://{host}:{port}/{version} +baseUri: http://localhost:6543/api version: v1 mediaType: application/json protocols: [HTTP, HTTPS] -securitySchemes: - - x_token_auth: - description: Authorization header token policy - type: x-ApiKey - - x_ticket_auth: - description: Standard Pyramid Auth Ticket policy - type: x-Ticket - settings: - secret: auth_tkt_secret - hashalg: sha512 - cookie_name: ramses_auth_tkt - http_only: true - - read_only_users: - description: ACL that allows authenticated users to read collection - type: x-ACL - settings: - collection: | - allow admin all - allow authenticated get - item: | - allow admin all - allow authenticated get - - read_only_useritem_everyone: - description: ACL that allows everyone to read item, authenticated users to read collection - type: x-ACL - settings: - collection: | - allow admin all - allow authenticated get - item: | - allow admin all - allow everyone get - allow {{user_self}} patch +securitySchemes: !include inclusions/yaml/security_schemes.yaml securedBy: [x_ticket_auth] /stories: @@ -49,83 +17,317 @@ securedBy: [x_ticket_auth] displayName: All stories get: description: Get all stories + queryParameters: !include inclusions/yaml/collection_query_params.yaml + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/collection_response.json + headers: !include inclusions/yaml/demo_headers.yaml post: description: Create a new story + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml body: application/json: - schema: !include schemas/story.json + schema: !include inclusions/schemas/story.json + example: !include inclusions/examples/story_body_example.json + responses: + 201: + body: + application/json: + schema: !include inclusions/schemas/story_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml patch: description: Update multiple stories - delete: - description: Delete multiple stories + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + _limit: + displayName: Limit + type: integer + required: true + example: 5 + body: + application/json: + example: !include inclusions/examples/story_body_example.json + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/collection_patch_response.json + headers: !include inclusions/yaml/demo_headers.yaml head: description: Determine whether a given resource is available + queryParameters: !include inclusions/yaml/collection_query_params.yaml + responses: + 200: + headers: !include inclusions/yaml/demo_headers.yaml options: description: Retrieve the available HTTP verbs for a given resource - + responses: !include inclusions/yaml/options_responses.yaml + delete: + description: Delete multiple stories + queryParameters: + q: + displayName: Query + type: string + required: false + example: foobar + minLength: 1 + _limit: + displayName: Limit + type: integer + required: true + minimum: 1 + example: 10 + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/collection_delete_response.json + headers: !include inclusions/yaml/demo_headers.yaml /{id}: displayName: One story + uriParameters: + id: + displayName: StoryID + type: integer get: description: Get a particular story + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/story_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml delete: description: Delete a particular story + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/item_delete_response.json + headers: !include inclusions/yaml/demo_headers.yaml patch: - put: description: Update a particular story + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + body: + application/json: + example: !include inclusions/examples/story_body_example.json + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/story_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml + put: + description: Replace a particular story + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + body: + application/json: + schema: !include inclusions/schemas/story.json + example: !include inclusions/examples/story_body_example.json + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/story_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml head: description: Determine whether a given resource is available + responses: + 200: + headers: !include inclusions/yaml/demo_headers.yaml options: description: Retrieve the available HTTP verbs for a given resource + responses: !include inclusions/yaml/options_responses.yaml /users: securedBy: [read_only_useritem_everyone] displayName: All users get: description: Get all users + queryParameters: !include inclusions/yaml/collection_query_params.yaml + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/collection_response.json + headers: !include inclusions/yaml/demo_headers.yaml post: description: Create a new user + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml body: application/json: - schema: !include schemas/user.json + schema: !include inclusions/schemas/user.json + example: !include inclusions/examples/user_body_example.json + responses: + 201: + body: + application/json: + schema: !include inclusions/schemas/user_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml patch: description: Update multiple users + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + _limit: + displayName: Limit + type: integer + required: true + example: 5 + body: + application/json: + example: !include inclusions/examples/user_body_example.json + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/collection_patch_response.json + headers: !include inclusions/yaml/demo_headers.yaml head: description: Determine whether a given resource is available + queryParameters: !include inclusions/yaml/collection_query_params.yaml + responses: + 200: + headers: !include inclusions/yaml/demo_headers.yaml options: description: Retrieve the available HTTP verbs for a given resource + responses: !include inclusions/yaml/options_responses.yaml /{username}: displayName: One user + uriParameters: + id: + displayName: Username + type: string get: description: Get a particular user + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/user_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml patch: - put: description: Update a particular user + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + body: + application/json: + example: !include inclusions/examples/user_body_example.json + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/user_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml + put: + description: Replace a particular user + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + body: + application/json: + schema: !include inclusions/schemas/user.json + example: !include inclusions/examples/user_body_example.json + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/user_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml delete: description: Delete a particular user - + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/item_delete_response.json + headers: !include inclusions/yaml/demo_headers.yaml /settings: displayName: User settings get: description: Get all settings of a particular user + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/settings_response.json + headers: !include inclusions/yaml/demo_headers.yaml post: description: Change a user's settings - + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + body: + application/json: + example: !include inclusions/examples/settings_body_example.json + responses: + 201: + body: + application/json: + schema: !include inclusions/schemas/settings_response.json + headers: !include inclusions/yaml/demo_headers.yaml /groups: displayName: User groups get: description: Get all groups of a particular user + responses: + 200: + headers: !include inclusions/yaml/demo_headers.yaml post: description: Change a user's groups + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + body: + application/json: + example: !include inclusions/examples/groups_body_example.json + responses: + 201: + headers: !include inclusions/yaml/demo_headers.yaml /profile: displayName: User profile get: description: Get a user's profile + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/profile_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml post: description: Create a user's profile + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml body: application/json: - schema: !include schemas/profile.json + schema: !include inclusions/schemas/profile.json + example: !include inclusions/examples/profile_body_example.json + responses: + 201: + body: + application/json: + schema: !include inclusions/schemas/profile_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml + patch: description: Update a user's profile + queryParameters: + _refresh_index: !include inclusions/yaml/refresh_index_param.yaml + body: + application/json: + example: !include inclusions/examples/profile_body_example.json + responses: + 200: + body: + application/json: + schema: !include inclusions/schemas/profile_item_response.json + headers: !include inclusions/yaml/demo_headers.yaml diff --git a/inclusions/examples/groups_body_example.json b/inclusions/examples/groups_body_example.json new file mode 100644 index 0000000..710e732 --- /dev/null +++ b/inclusions/examples/groups_body_example.json @@ -0,0 +1 @@ +{"user": "bar", "admin": "foo"} \ No newline at end of file diff --git a/inclusions/examples/profile_body_example.json b/inclusions/examples/profile_body_example.json new file mode 100644 index 0000000..9b0e8ca --- /dev/null +++ b/inclusions/examples/profile_body_example.json @@ -0,0 +1 @@ +{"address": "Earth, Milky Way"} \ No newline at end of file diff --git a/inclusions/examples/settings_body_example.json b/inclusions/examples/settings_body_example.json new file mode 100644 index 0000000..18d7acf --- /dev/null +++ b/inclusions/examples/settings_body_example.json @@ -0,0 +1 @@ +{"foo": "bar"} \ No newline at end of file diff --git a/inclusions/examples/story_body_example.json b/inclusions/examples/story_body_example.json new file mode 100644 index 0000000..0354f6c --- /dev/null +++ b/inclusions/examples/story_body_example.json @@ -0,0 +1,17 @@ +{ + "due_date": "2015-03-07T10:00:00Z", + "description": "desc1", + "completed": false, + "start_date": "2015-03-04T10:00:00Z", + "progress": 1.0, + "name": "name1", + "signs_number": 12312313123123123, + "valid_date": "2015-03-04", + "valid_time": "14:32:00", + "reads": 10, + "rating": 9, + "available_for": 36000, + "price": 32.99, + "unicode_name": "name2", + "unicode_description": "desc2" +} \ No newline at end of file diff --git a/inclusions/examples/user_body_example.json b/inclusions/examples/user_body_example.json new file mode 100644 index 0000000..e0ee7fa --- /dev/null +++ b/inclusions/examples/user_body_example.json @@ -0,0 +1,8 @@ +{ + "password": "123456", + "first_name": "John", + "last_name": "Doe", + "last_login": "2015-03-04T14:32:00Z", + "groups": ["user"], + "settings": {"foo": "bar"} +} \ No newline at end of file diff --git a/inclusions/schemas/collection_delete_response.json b/inclusions/schemas/collection_delete_response.json new file mode 100644 index 0000000..beed084 --- /dev/null +++ b/inclusions/schemas/collection_delete_response.json @@ -0,0 +1,45 @@ +{ + "id": "collectionDeleteResponseSchema", + "type": "object", + "title": "Collection response schema", + "$schema": "http://json-schema.org/draft-04/schema", + "properties": { + "timestamp": { + "type": "string", + "format": "date-time" + }, + "title": { + "type": "string" + }, + "status_code": { + "type": "integer" + }, + "explanation": { + "type": "string" + }, + "message": { + "type": ["string", "null"] + }, + "extra": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "confirmation_url": { + "type": "string", + "pattern": "http(s?)://" + }, + "method": { + "type": "string", + "enum": ["delete", "DELETE"] + } + }, + "required": ["count", "confirmation_url", "method"] + } + }, + "required": [ + "timestamp", "title", "status_code", "explanation", + "message", "extra" + ] +} diff --git a/inclusions/schemas/collection_patch_response.json b/inclusions/schemas/collection_patch_response.json new file mode 100644 index 0000000..2b31647 --- /dev/null +++ b/inclusions/schemas/collection_patch_response.json @@ -0,0 +1,31 @@ +{ + "id": "collectionPatchResponseSchema", + "type": "object", + "title": "Collection response schema", + "$schema": "http://json-schema.org/draft-04/schema", + "properties": { + "explanation": { + "type": "string" + }, + "message": { + "type": "string" + }, + "request_url": { + "type": "string" + }, + "status_code": { + "type": "integer" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "title": { + "type": "string" + } + }, + "required": [ + "explanation", "message", "request_url", "status_code", + "timestamp", "title" + ] +} \ No newline at end of file diff --git a/inclusions/schemas/collection_response.json b/inclusions/schemas/collection_response.json new file mode 100644 index 0000000..5927802 --- /dev/null +++ b/inclusions/schemas/collection_response.json @@ -0,0 +1,30 @@ +{ + "id": "collectionGetSchema", + "type": "object", + "title": "Collection response schema", + "$schema": "http://json-schema.org/draft-04/schema", + "properties": { + "count": { + "type": "integer", + "minumum": 0 + }, + "fields": { + "type": "string" + }, + "took": { + "type": "integer" + }, + "start": { + "type": "integer", + "minumum": 0 + }, + "total": { + "type": "integer", + "minumum": 0 + }, + "data": { + "type": "array" + } + }, + "required": ["count", "fields", "start", "total", "data"] +} \ No newline at end of file diff --git a/inclusions/schemas/item_delete_response.json b/inclusions/schemas/item_delete_response.json new file mode 100644 index 0000000..a6a1133 --- /dev/null +++ b/inclusions/schemas/item_delete_response.json @@ -0,0 +1,31 @@ +{ + "id": "itemDeleteResponseSchema", + "type": "object", + "title": "Collection response schema", + "$schema": "http://json-schema.org/draft-04/schema", + "properties": { + "explanation": { + "type": "string" + }, + "message": { + "type": ["string", "null"] + }, + "request_url": { + "type": "string" + }, + "status_code": { + "type": "integer" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "title": { + "type": "string" + } + }, + "required": [ + "timestamp", "title", "status_code", "explanation", + "message" + ] +} diff --git a/schemas/profile.json b/inclusions/schemas/profile.json similarity index 100% rename from schemas/profile.json rename to inclusions/schemas/profile.json diff --git a/inclusions/schemas/profile_item_response.json b/inclusions/schemas/profile_item_response.json new file mode 100644 index 0000000..df7965c --- /dev/null +++ b/inclusions/schemas/profile_item_response.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "title": "Story schema", + "$schema": "http://json-schema.org/draft-04/schema", + "properties": { + "_self": { + "type": "string", + "pattern": "http(s?)://" + }, + "id": { + "type": ["string", "integer"], + "minimum": 1 + }, + "_pk": { + "type": "string" + }, + "_type": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "user": { + "type": "string" + }, + "address": { + "type": "string" + }, + "created_at": { + "type": ["string", "null"], + "format": "date-time" + }, + "_version": { + "type": "integer" + }, + "updated_at": { + "type": ["string", "null"], + "format": "date-time" + } + }, + "required": [ + "_self", "id", "_pk", "_type", "user", "address", + "created_at", "_version", "updated_at" + ] +} diff --git a/inclusions/schemas/settings_response.json b/inclusions/schemas/settings_response.json new file mode 100644 index 0000000..22fe881 --- /dev/null +++ b/inclusions/schemas/settings_response.json @@ -0,0 +1,12 @@ +{ + "id": "objectGetSchema", + "type": "object", + "title": "Collection response schema", + "$schema": "http://json-schema.org/draft-04/schema", + "properties": { + "foo": { + "type": "string" + } + }, + "required": ["foo"] +} \ No newline at end of file diff --git a/schemas/story.json b/inclusions/schemas/story.json similarity index 100% rename from schemas/story.json rename to inclusions/schemas/story.json diff --git a/inclusions/schemas/story_item_response.json b/inclusions/schemas/story_item_response.json new file mode 100644 index 0000000..32ccc3b --- /dev/null +++ b/inclusions/schemas/story_item_response.json @@ -0,0 +1,106 @@ +{ + "type": "object", + "title": "Story schema", + "$schema": "http://json-schema.org/draft-04/schema", + "properties": { + "_self": { + "type": "string", + "pattern": "http(s?)://" + }, + "rating": { + "type": ["integer", "null"] + }, + "signs_number": { + "type": ["integer", "null"] + }, + "available_for": { + "type": ["integer", "null"] + }, + "updated_at": { + "type": ["string", "null"], + "format": "date-time" + }, + "assignee_id": { + "type": ["string", "null"] + }, + "assignee": { + "type": ["string", "null"] + }, + "reads": { + "type": ["integer", "null"] + }, + "owner": { + "type": ["string", "null"] + }, + "valid_date": { + "type": ["string", "null"], + "format": "date-time" + }, + "id": { + "type": ["string", "integer"], + "minimum": 1 + }, + "_pk": { + "type": "string" + }, + "attachment": { + "type": ["string", "null"] + }, + "progress": { + "type": ["number", "null"] + }, + "price": { + "type": ["number", "null"] + }, + "owner_id": { + "type": ["string", "null"] + }, + "due_date": { + "type": ["string", "null"], + "format": "date-time" + }, + "_type": { + "type": "string" + }, + "arbitrary_object": { + "type": ["object", "null"] + }, + "description": { + "type": "string" + }, + "valid_time": { + "type": ["string", "null"] + }, + "unicode_description": { + "type": ["string", "null"] + }, + "completed": { + "type": ["boolean", "null"] + }, + "name": { + "type": "string" + }, + "created_at": { + "type": ["string", "null"], + "format": "date-time" + }, + "_version": { + "type": "integer" + }, + "start_date": { + "type": ["string", "null"], + "format": "date-time" + }, + "unicode_name": { + "type": ["string", "null"] + }, + }, + "required": [ + "_self", "rating", "signs_number", "available_for", "updated_at", + "assignee", "reads", "owner", "valid_date", "id", + "_pk", "attachment", "progress", "price", "due_date", + "_type", "arbitrary_object", "description", "valid_time", + "unicode_description", "completed", "name", "created_at", + "_version", "start_date", "unicode_name" + ] +} diff --git a/schemas/user.json b/inclusions/schemas/user.json similarity index 100% rename from schemas/user.json rename to inclusions/schemas/user.json diff --git a/inclusions/schemas/user_item_response.json b/inclusions/schemas/user_item_response.json new file mode 100644 index 0000000..47288bc --- /dev/null +++ b/inclusions/schemas/user_item_response.json @@ -0,0 +1,72 @@ +{ + "type": "object", + "title": "Story schema", + "$schema": "http://json-schema.org/draft-04/schema", + "properties": { + "_self": { + "type": "string", + "pattern": "http(s?)://" + }, + "_pk": { + "type": "string" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "status": { + "type": ["string", "null"] + }, + "first_name": { + "type": ["string", "null"] + }, + "last_name": { + "type": ["string", "null"] + }, + "assigned_stories": { + "type": ["array", "null"] + }, + "stories": { + "type": ["array", "null"] + }, + "settings": { + "type": ["object", "null"] + }, + "created_at": { + "type": ["string", "null"], + "format": "date-time" + }, + "last_login": { + "type": ["string", "null"], + "format": "date-time" + }, + "_version": { + "type": "integer" + }, + "_type": { + "type": "string" + }, + "updated_at": { + "type": ["string", "null"], + "format": "date-time" + }, + "profile": { + "type": ["object", "null"] + }, + "groups": { + "type": ["array", "null"], + } + }, + "required": [ + "_self", "_pk", "username", "password", "email", "status", + "first_name", "last_name", "assigned_stories", "stories", + "settings", "created_at", "last_login", "_version", "_type", + "updated_at", "profile", "groups" + ] +} diff --git a/inclusions/yaml/collection_query_params.yaml b/inclusions/yaml/collection_query_params.yaml new file mode 100644 index 0000000..d7c0baf --- /dev/null +++ b/inclusions/yaml/collection_query_params.yaml @@ -0,0 +1,22 @@ +q: + displayName: Query + type: string + required: false + example: foobar + minLength: 1 +_limit: + displayName: Limit + type: integer + required: false + minimum: 1 + example: 10 +_start: + displayName: Start + type: integer + required: false + minimum: 1 + example: 12 +_count: + displayName: Count + type: boolean + required: false \ No newline at end of file diff --git a/inclusions/yaml/demo_headers.yaml b/inclusions/yaml/demo_headers.yaml new file mode 100644 index 0000000..d94dac6 --- /dev/null +++ b/inclusions/yaml/demo_headers.yaml @@ -0,0 +1,6 @@ +Content-Length: + displayName: ContentLength + description: Demo content length header + type: integer + required: true + minimum: 1 \ No newline at end of file diff --git a/inclusions/yaml/options_responses.yaml b/inclusions/yaml/options_responses.yaml new file mode 100644 index 0000000..db9c121 --- /dev/null +++ b/inclusions/yaml/options_responses.yaml @@ -0,0 +1,14 @@ +200: + headers: + Content-Length: + displayName: ContentLength + description: Demo content length header + type: integer + required: true + minimum: 0 + maximum: 0 + Allow: + displayName: AllowedMethods + description: Allowed methods + type: string + required: true \ No newline at end of file diff --git a/inclusions/yaml/refresh_index_param.yaml b/inclusions/yaml/refresh_index_param.yaml new file mode 100644 index 0000000..72ace41 --- /dev/null +++ b/inclusions/yaml/refresh_index_param.yaml @@ -0,0 +1,3 @@ +displayName: RefreshIndex +type: boolean +required: false \ No newline at end of file diff --git a/inclusions/yaml/security_schemes.yaml b/inclusions/yaml/security_schemes.yaml new file mode 100644 index 0000000..b35c79f --- /dev/null +++ b/inclusions/yaml/security_schemes.yaml @@ -0,0 +1,35 @@ +- x_token_auth: + description: Authorization header token policy + type: x-ApiKey + +- x_ticket_auth: + description: Standard Pyramid Auth Ticket policy + type: x-Ticket + settings: + secret: auth_tkt_secret + hashalg: sha512 + cookie_name: ramses_auth_tkt + http_only: true + +- read_only_users: + description: ACL that allows authenticated users to read collection + type: x-ACL + settings: + collection: | + allow admin all + allow authenticated get + item: | + allow admin all + allow authenticated get + +- read_only_useritem_everyone: + description: ACL that allows everyone to read item, authenticated users to read collection + type: x-ACL + settings: + collection: | + allow admin all + allow authenticated get + item: | + allow admin all + allow everyone get + allow {{user_self}} patch \ No newline at end of file diff --git a/ra_test.py b/ra_test.py new file mode 100644 index 0000000..2cee340 --- /dev/null +++ b/ra_test.py @@ -0,0 +1,9 @@ +from ra import RAMLTester +from pyramid.paster import bootstrap + + +def main(): + application = bootstrap('local.ini')['app'] + RAMLTester(application, 'example.raml').test() + +main()