Skip to content

Commit

Permalink
Allow to have registered resources without controller bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
feugy committed Mar 21, 2013
1 parent 4b41ef2 commit 75f5250
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 41 deletions.
72 changes: 37 additions & 35 deletions src/generator.coffee
Expand Up @@ -94,46 +94,49 @@ addRoutes = (descriptor, resources) ->
# analyze each resources
for resource in resources
# check mandatory informations
unless _.isObject(resource.api) and _.isObject(resource.controller)
throw new Error("Resource must contain 'api' and 'controller' attributes")
unless _.isString(resource.api.resourcePath)
throw new Error('Resource without path - are you kidding')

unless _.isObject(resource.api)
throw new Error("Resource must contain 'api' attribute")

# add models
if _.isObject(resource.api.models)
for id, model of resource.api.models
descriptor.models[id] = validateModel(model, id)

# allow api without controllers, but do not generate routes
if _.isObject(resource.controller)
unless _.isString(resource.api.resourcePath)
throw new Error('Resource without path - are you kidding')

# analyze each api within a given resource
for api in resource.api.apis
unless _.isString(api.path)
throw new Error("Resource #{resource.api.resourcePath} has an api without path - D\'oh'")

# analyze each api within a given resource
for api in resource.api.apis
unless _.isString(api.path)
throw new Error("Resource #{resource.api.resourcePath} has an api without path - D\'oh'")

for operation in api.operations
# check mandatory informations
unless _.isString(operation.httpMethod)
throw new Error("Api #{api.path} has an operation without http method - what is the police doing ?")

verb = operation.httpMethod.toLowerCase()
unless verb in ['get', 'post', 'delete', 'put', 'options', 'head']
throw new Error("Api #{api.path} operation #{operation.httpMethod} is not supported - I\'m so sorry Janice")
for operation in api.operations
# check mandatory informations
unless _.isString(operation.httpMethod)
throw new Error("Api #{api.path} has an operation without http method - what is the police doing ?")

unless _.isString(operation.nickname)
throw new Error("Api #{api.path} operation #{operation.httpMethod} does not specify a nickname - we cannot guess the corresponding controller method")

# Validates parameters
if _.isArray(operation.parameters)
# parameter validations
validateParameters(operation.parameters, descriptor.models, api.path, operation.httpMethod)

route = utils.pathToRoute(api.path)
unless operation.nickname of resource.controller
throw new Error("Api #{api.path} nickname #{operation.nickname} cannot be found in controller")

# load the relevant script that must contain the middelware
routes.push({method:verb, path:route, middleware: resource.controller[operation.nickname]})
if /swagger/.test process.env?.NODE_DEBUG
console.log("found a route #{route} with verb #{verb} bound to exported method #{operation.nickname}")
verb = operation.httpMethod.toLowerCase()
unless verb in ['get', 'post', 'delete', 'put', 'options', 'head']
throw new Error("Api #{api.path} operation #{operation.httpMethod} is not supported - I\'m so sorry Janice")

unless _.isString(operation.nickname)
throw new Error("Api #{api.path} operation #{operation.httpMethod} does not specify a nickname - we cannot guess the corresponding controller method")

# Validates parameters
if _.isArray(operation.parameters)
# parameter validations
validateParameters(operation.parameters, descriptor.models, api.path, operation.httpMethod)

route = utils.pathToRoute(api.path)
unless operation.nickname of resource.controller
throw new Error("Api #{api.path} nickname #{operation.nickname} cannot be found in controller")

# load the relevant script that must contain the middelware
routes.push({method:verb, path:route, middleware: resource.controller[operation.nickname]})
if /swagger/.test process.env?.NODE_DEBUG
console.log("found a route #{route} with verb #{verb} bound to exported method #{operation.nickname}")

# enrich descriptor
descriptor.apis.push(resource.api)
Expand Down Expand Up @@ -189,7 +192,6 @@ module.exports = (app, descriptor, resources, options = {}) ->
app.descriptor = descriptor
catch err
throw new Error("Failed to create routes from resources: #{err.toString()}");


# Express middleware for serving the descRoute.
return (req, res, next) ->
Expand Down
76 changes: 70 additions & 6 deletions test/apiGeneration.coffee
Expand Up @@ -31,7 +31,7 @@ describe 'API generation tests', ->
it 'should fail if no api or controller provided for a resource', ->
assert.throws ->
swagger.generator express(), {}, [{}]
, /Resource must contain 'api' and 'controller' attributes/
, /Resource must contain 'api' attribute/

it 'should fail on missing resource path', ->
assert.throws ->
Expand Down Expand Up @@ -312,7 +312,7 @@ describe 'API generation tests', ->
, /operation DELETE does not allowed body parameters/

it 'should customize the generated descriptor path', (done) ->
# given a properlly
# given a server with api and custom descriptor path
app = express()
app.use(express.cookieParser())
.use(express.methodOverride())
Expand All @@ -328,16 +328,80 @@ describe 'API generation tests', ->
server.listen port, host, _.defer((err) ->
return done err if err?
# when requesting the API description details
request.get
request.get(
url: 'http://'+host+':'+port+'/my-desc'
json: true
, (err, res, body) ->
return done err if err?
# then a json file is returned
assert.equal res.statusCode, 200
assert.deepEqual body, require './fixtures/streamApi.yml'
server.close()
done()
assert.deepEqual body,
apiVersion: '1.0'
basePath: '/api'
apis: [
path: '/my-desc/stream'
]
models: {}
server.close()
done()
)
)

it 'should allow wired and not wired resources', (done) ->
# given a server with wired and not wired api
app = express()
app.use(express.cookieParser())
.use(express.methodOverride())
.use(express.bodyParser())
.use(swagger.generator(app,
apiVersion: '1.0',
basePath: root
, [
api: require './fixtures/streamApi.yml'
controller: stat: (req, res) -> res.json status: 'passed'
,
api: require './fixtures/notwired.yml'
]))
server = http.createServer app
server.listen port, host, _.defer((err) ->
return done err if err?
# when requesting the API description details
request.get(
url: 'http://'+host+':'+port+'/api-docs.json'
json: true
, (err, res, body) ->
return done err if err?
# then a json file is returned
assert.equal res.statusCode, 200
assert.deepEqual body,
apiVersion: '1.0'
basePath: '/api'
apis: [
path: '/api-docs.json/stream'
,
path: '/api-docs.json/source'
]
models: {}
# then the unwired resource details are available
request.get(
url: 'http://'+host+':'+port+'/api-docs.json/source'
json: true
, (err, res, body) ->
return done err if err?
assert.deepEqual body,
apiVersion: '1.0'
basePath: '/api'
apis: [
path: '/source/stats'
operations: [
httpMethod: 'GET'
]
],
resourcePath: '/source'
server.close()
done()
)
)
)

describe 'given a configured server with complex models', ->
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/notwired.yml
@@ -0,0 +1,5 @@
resourcePath: /source
apis:
- path: /source/stats
operations:
- httpMethod: GET

0 comments on commit 75f5250

Please sign in to comment.