Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
mission-liao committed May 7, 2015
2 parents b8d5bd0 + a495f51 commit 2aff7d8
Show file tree
Hide file tree
Showing 16 changed files with 264 additions and 74 deletions.
98 changes: 25 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ For other projects related to Swagger tools in python, check [here](https://gith

Read the [Document](http://pyswagger.readthedocs.org/en/latest/), or just go through this README.

- [Features](https://github.com/mission-liao/pyswagger/blob/master/README.md#features)
- [Quick Start](https://github.com/mission-liao/pyswagger/blob/master/README.md#quick-start)
- [Installation](https://github.com/mission-liao/pyswagger/blob/master/README.md#installation)
- [Reference](https://github.com/mission-liao/pyswagger/blob/master/README.md#reference)
- [Contributors](https://github.com/mission-liao/pyswagger/blob/master/README.md#contributors)
- [Contribution Guideline](https://github.com/mission-liao/pyswagger/blob/master/README.md#contribution-guideline)
- [FAQ](https://github.com/mission-liao/pyswagger/blob/master/README.md#faq)
- [Features](README.md#features)
- [Tutorial](README.md#tutorial)
- [Quick Start](README.md#quick-start)
- [Installation](README.md#installation)
- [Reference](README.md#reference)
- [Contributors](README.md#contributors)
- [Contribution Guideline](README.md#contribution-guildeline)
- [FAQ](README.md#faq)

---------

Expand All @@ -47,6 +48,15 @@ Read the [Document](http://pyswagger.readthedocs.org/en/latest/), or just go thr

---------

##Tutorial

- [Initialization](docs/md/tutorial/init.md)
- [Making a Request](docs/md/tutorial/request.md)
- [Access the Response](docs/md/tutorial/response.md)
- [Testing a Local Server](docs/md/tutorial/local.md)

---------

##Quick Start
```python
from pyswagger import SwaggerApp, SwaggerSecurity
Expand Down Expand Up @@ -79,81 +89,23 @@ assert pet.name == 'Tom'
pet = client.request(app.resolve(jp_compose('/pet/{petId}', base='#/paths')).get(petId=1)).data
assert pet.id == 1
```

---------

##Installation
We support pip installtion.
```bash
pip install pyswagger
```

---------

##Reference
All exported API are described in following sections. ![A diagram about relations between components](https://docs.google.com/drawings/d/1DZiJgl4i9L038UJJp3kpwkWRvcNQktf5h-e4m96_C-k/pub?w=849&h=530)

###SwaggerApp
The initialization of pyswagger starts from **SwaggerApp.\_create_(url)**, where **url** could either be a _url_ or a _file_ path. This function returns a SwaggerApp instance, which would be used to initiate SwaggerSecurity.

**SwaggerApp.op** provides a shortcut to access Operation objects, which will produce a set of request/response for SwaggerClient to access API. The way we provide here would help to minimize the possible difference introduced by Swagger2.0 when everything is merged into one file.
```python
# call an API when its nickname is unique
SwaggerApp.op['getPetById']
# call an API when its nickname collid with other resources
SwaggerApp.op['user', 'getById'] # operationId:'getById', tags:'user' (or a user resource in Swagger 1.2)
SwaggerApp.op['pet', 'getById'] # operationId:'getById', tags:'pet' (or a pet resource in Swagger 1.2)

# utilize SwaggerApp.resolve to do the same thing
SwaggerApp.resolve('#/paths/~1pet~1{petId}').get
# instead of writing JSON-pointers by yourselves, utilize pyswagger.utils.jp_compose
SwaggerApp.resolve(utils.jp_compose('/pet/{petId}', base='#/paths')).get
```
**SwaggerApp.validate(strict=True)** provides validation against the loaded Swagger API definition. When passing _strict=True_, an exception would be raised if validation failed. It returns a list of errors in tuple: _(where, type, msg)_.

**SwaggerApp.resolve(JSON_Reference)** is a new way to access objects. For example, to access a Schema object 'User':
```python
app.resolve('#/definitions/User')
```
This function accepts a [JSON Reference](http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03), which is composed by an url and a [JSON Pointer](http://tools.ietf.org/html/rfc6901), it is the standard way to access a Swagger document. Since a JSON reference contains an url, this means you can access any external document when you need:
```python
app.resolve('http://another_site.com/apis/swagger.json#/definitions/User')
```
`pyswagger` will load that swagger.json, create a new `SwaggerApp`, and group it with the `SwaggerApp` you kept (**app** in code above). Internally, when `pyswagger` encounter some $ref directs to external documents, we just silently handle it in the same way.

###SwaggerClient
You also need **SwaggerClient(security=None)** to access API, this layer wraps the difference between those http libraries in python. where **security**(optional) is `SwaggerSecurity`, which helps to handle authorizations of each request.

```python
client.request(app.op['addPet'](body=dict(id=1, name='Tom')))
```
To make a request, you need to create a pair of request/response from **SwaggerApp.op** by providing essential parameters. Then passing the tuple of (request, response) to **SwaggerClient.request(req_and_resp, opt={})** likes the code segment above. Below is a reference mapping between python objects and Swagger primitives. Check this mapping when you need to construct a parameter set:
- **dict** corresponds to _Model_
- **list** corresponds to _Array_
- **datetime.datetime**, timestamp(float or int), or ISO8601-string for _date-time_ and _date_
- _File_ type is a little bit complex, but just similar to [request](https://github.com/kennethreitz/requests), which uses a dict containing file info.
```python
YouFile = {
# header values used in multipart/form-data according to RFC2388
'header': {
'Content-Type': 'text/plain',

# according to RFC2388, available values are '7bit', '8bit', 'binary'
'Content-Transfer-Encoding': 'binary'
},
'filename': 'a.txt',
'data': None (or any file-like object)
}
```
- other primitives are similar to python's primitives

The return value is a **SwaggerResponse** object, with these attributes:
- status
- data, corresponds to Operation object's return value, or `ResponseMessage` object's _responseModel_ (in Swagger 1.2, `Schema` object of `Response` object in Swagger 2.0) when its status matched.
- header, organized in ```{key: [value1, value2...]}```
- message, corresponds to ResponseMessage object's _message_ when status matched on ResponseMessage object.
- raw, raw data without touching.

###SwaggerSecurity
Holder/Dispatcher for user-provided authorization info. Initialize this object like **SwaggerSecurity(app)**, where **app** is an instance of SwaggerApp. To add authorization, call **SwaggerSecurity.update\_with(name, token)**, where **name** is the name of Authorizations object in Swagger 1.2(Security Scheme Object in Swagger 2.0) , and **token** is different for different kinds of authorizations:
- basic authorization: (username, password)
- api key: the api key
- oauth2: the access\_token
- [SwaggerApp](docs/md/ref/app.md)
- [SwaggerClient](docs/md/ref/client.md)
- [SwaggerSecurity](docs/md/ref/security.md)

---------

Expand Down
28 changes: 28 additions & 0 deletions docs/md/ref/app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
The initialization of pyswagger starts from **SwaggerApp.\_create_(url)**, where **url** could either be a _url_ or a _file_ path. This function returns a SwaggerApp instance, which would be used to initiate SwaggerSecurity.

**SwaggerApp.op** provides a shortcut to access Operation objects, which will produce a set of request/response for SwaggerClient to access API. The way we provide here would help to minimize the possible difference introduced by Swagger2.0 when everything is merged into one file.
```python
# call an API when its nickname is unique
SwaggerApp.op['getPetById']
# call an API when its nickname collid with other resources
SwaggerApp.op['user', 'getById'] # operationId:'getById', tags:'user' (or a user resource in Swagger 1.2)
SwaggerApp.op['pet', 'getById'] # operationId:'getById', tags:'pet' (or a pet resource in Swagger 1.2)

# utilize SwaggerApp.resolve to do the same thing
SwaggerApp.resolve('#/paths/~1pet~1{petId}').get
# instead of writing JSON-pointers by yourselves, utilize pyswagger.utils.jp_compose
SwaggerApp.resolve(utils.jp_compose('/pet/{petId}', base='#/paths')).get
```
**SwaggerApp.validate(strict=True)** provides validation against the loaded Swagger API definition. When passing _strict=True_, an exception would be raised if validation failed. It returns a list of errors in tuple: _(where, type, msg)_.

**SwaggerApp.resolve(JSON_Reference)** is a new way to access objects. For example, to access a Schema object 'User':
```python
app.resolve('#/definitions/User')
```
This function accepts a [JSON Reference](http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03), which is composed by an url and a [JSON Pointer](http://tools.ietf.org/html/rfc6901), it is the standard way to access a Swagger document. Since a JSON reference contains an url, this means you can access any external document when you need:
```python
app.resolve('http://another_site.com/apis/swagger.json#/definitions/User')
```
`pyswagger` will load that swagger.json, create a new `SwaggerApp`, and group it with the `SwaggerApp` you kept (**app** in code above). Internally, when `pyswagger` encounter some $ref directs to external documents, we just silently handle it in the same way.


33 changes: 33 additions & 0 deletions docs/md/ref/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
You also need **SwaggerClient(security=None)** to access API, this layer wraps the difference between those http libraries in python. where **security**(optional) is `SwaggerSecurity`, which helps to handle authorizations of each request.

```python
client.request(app.op['addPet'](body=dict(id=1, name='Tom')))
```
To make a request, you need to create a pair of request/response from **SwaggerApp.op** by providing essential parameters. Then passing the tuple of (request, response) to **SwaggerClient.request(req_and_resp, opt={})** likes the code segment above. Below is a reference mapping between python objects and Swagger primitives. Check this mapping when you need to construct a parameter set:
- **dict** corresponds to _Model_
- **list** corresponds to _Array_
- **datetime.datetime**, timestamp(float or int), or ISO8601-string for _date-time_ and _date_
- _File_ type is a little bit complex, but just similar to [request](https://github.com/kennethreitz/requests), which uses a dict containing file info.
```python
YouFile = {
# header values used in multipart/form-data according to RFC2388
'header': {
'Content-Type': 'text/plain',

# according to RFC2388, available values are '7bit', '8bit', 'binary'
'Content-Transfer-Encoding': 'binary'
},
'filename': 'a.txt',
'data': None (or any file-like object)
}
```
- other primitives are similar to python's primitives

The return value is a **SwaggerResponse** object, with these attributes:
- status
- data, corresponds to Operation object's return value, or `ResponseMessage` object's _responseModel_ (in Swagger 1.2, `Schema` object of `Response` object in Swagger 2.0) when its status matched.
- header, organized in ```{key: [value1, value2...]}```
- message, corresponds to ResponseMessage object's _message_ when status matched on ResponseMessage object.
- raw, raw data without touching.


Empty file added docs/md/ref/response.md
Empty file.
6 changes: 6 additions & 0 deletions docs/md/ref/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Holder/Dispatcher for user-provided authorization info. Initialize this object like **SwaggerSecurity(app)**, where **app** is an instance of SwaggerApp. To add authorization, call **SwaggerSecurity.update\_with(name, token)**, where **name** is the name of Authorizations object in Swagger 1.2(Security Scheme Object in Swagger 2.0) , and **token** is different for different kinds of authorizations:
- basic authorization: (username, password)
- api key: the api key
- oauth2: the access\_token


25 changes: 25 additions & 0 deletions docs/md/tutorial/init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Initialzation

The first step to use pyswagger is creating a pyswagger.SwaggerApp object. You need to provide the path of the resource file. For example, a SwaggerApp for petstore can be initialized in this way:
```python
from pyswagger import SwaggerApp

# utilize SwaggerApp.create
app = SwaggerApp.create('http://petstore.swagger.io/v2/swagger.json')
```

## Initailize with A Local file

The path could be an URI or a absolute path. For example, a path /home/workspace/local/swagger.json could be passed like:
```python
from pyswagger import SwaggerApp

# file URI
app = SwaggerApp.create('file:///home/workspace/local/swagger.json')
# with hostname
app = SwaggerApp.create('file://localhost/home/workspace/local/swagger.json')
# absolute path
app = SwaggerApp.create('/home/workspace/local/swagger.json')
# without the file name, because 'swagger.json' is a predefined name
app = SwaggerApp.create('/home/workspace/local')
```
20 changes: 20 additions & 0 deletions docs/md/tutorial/local.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Testing a Local Server

As a backend developer, you will need to test your API before shipping. We provide a simple way to patch the url before client actually making a request

```python
from pyswagger import SwaggerApp
from pyswagger.contrib.client.request import Client

# create a SwaggerApp with a local resource file
app = SwaggerApp.create('/path/to/your/resource/file/swagger.json')
# init the client
client = Client()

# try to making a request
client.request(
app.op['getUserByName'](username='Tom'),
opt=dict(
url_netloc='localhost:8001' # patch the url of petstore to localhost:8001
))
```
91 changes: 91 additions & 0 deletions docs/md/tutorial/request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
## Making a Request

Three parts are involved with making a request:
- access the Operation object
- provide parameters to the Operation object, it will return (SwaggerRequest, SwaggerResponse) pair
- provide this pair to the client implementation you choose

### Access Operation
There are many ways to access an Operation object. For example, if you want to access 'getUserByName' in petstore.
```python
from pyswagger import SwaggerApp

app = SwaggerApp.create('http://petstore.swagger.io/v2/swagger.json')

# via operationId and tag, they are optional in swagger 2.0
op = app.op['getUserByName'] # when the operationId is unique
op = app.op['user', 'getUserByName'] # tag + operationId

# via JSON Pointer, every object in Swagger can be referenced via its JSON Pointer.
# The JSON pointer of Operation 'getUserByName' is '#/paths/~1user~1{username}/get',
# here we provide a simple way for you to handle JSON pointer.
from pyswagger import utils
# check the place of the Operation 'getUserByName' in petstore
op = app.resolve(utils.jp_compose(['#', 'paths', '/user/{username}', 'get']))

# cascade resolving
username_api = app.resolve(utils.jp_compose(['#', 'paths', '/user/{username}']))
op = username_api.resolve('get')
# there is no special character in 'get',
# just access it like a property
op = username_api.get
```

### Provide parameter
This step involves converting primitives in python to primitives in Swagger,
please refer to [here](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#data-types) for a list of primitives im Swagger.
```python
# a fake operation containing all types of primitives
op = app.op['FakeApi']

req_and_resp = op(
# integer, long,
Id=1,
# float, double
speed=27.3,
# string
name='Tom',
# byte
raw=b'fffffffffff',
# byte, in string, would be encoded to 'utf-8'
raw_s='jdkjldkjlsdfs',
# boolean
like_suki=True,
# date, datetime, in timestamp
next_date_1=0.0,
# date, in datetime.date
next_date_2=datetime.date.today(),
# datetime, in datetime.datetime
next_date_3=datetime.datetime.now(),
# date, in string ISO8601
next_date_4='2007-04-05',
# datetime, in string ISO8601
next_date_5='2007-04-05T12:30:00-02:00',
# array of integer
list_of_ids=[1, 2, 3, 4, 5],
# an object
user=dict(id=1, username='Tom'),
# list of objects
users=[
dict(id=1, username='Tom'),
dict(id=2, username='Mary')
]
)
```
### Pass result to Client
The return value when calling an Operation is a pair of (SwaggerRequest, SwaggerResponse),
just pass it to 'request' function of client. Below is a full example of 'getUserByName'
```python
from pyswagger import SwaggerApp
from pyswagger.contrib.client.request import Client

app = SwaggerApp.create('/path/to/your/resource/file/swagger.json')
client = Client()

# make the request
response = client.request(
app.op['getUserByName']( # access the Operation
username='Tom' # provide the parameter
))

```
35 changes: 35 additions & 0 deletions docs/md/tutorial/response.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Access the Response
The return value of 'request' function of each client implementation is a pyswagger.io.SwaggerResponse object.
You need to access the result of your request via its interface

```python
from pyswagger import SwaggerApp
from pyswagger.contrib.client.requests import Client

app = SwaggerApp.create('/path/to/your/resource/file/swagger.json')
client = Client()

# making a request
resp = client.request(app.op['getUserByName'](username='Tom'))

# Status
assert resp.status == 200

# Data
# it should return a data accord with '#/definitions/User' Schema object
assert resp.data.id == 1
assert resp.data.username == 'Tom'
# Raw
assert resp.raw == '{"id": 1, "username": "Tom"}'

# Header
# header is a dict, its values are lists of values,
# because keys in HTTP header allow duplication.
#
# when the input header is:
# A: 1,
# A: 2,
# B, 1
assert sorted(resp.header['A']) == [1, 2]
assert resp.header['B'] == [1]
```
File renamed without changes.
2 changes: 1 addition & 1 deletion docs/conf.py → docs/rst/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
__root__ = os.path.dirname(os.path.dirname(__file__))
__root__ = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
sys.path.append(__root__)
# -- General configuration ------------------------------------------------

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 2aff7d8

Please sign in to comment.