praxis 0.20.1 → 0.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/CHANGELOG.md +36 -0
- data/lib/api_browser/Gruntfile.js +33 -3
- data/lib/api_browser/app/index.html +3 -0
- data/lib/api_browser/app/js/factories/Example.js +4 -0
- data/lib/api_browser/app/js/factories/template_for.js +14 -0
- data/lib/api_browser/app/js/filters/attribute_name.js +3 -2
- data/lib/api_browser/app/js/filters/header_info.js +9 -0
- data/lib/api_browser/app/views/action.html +11 -2
- data/lib/api_browser/app/views/controller.html +5 -5
- data/lib/api_browser/app/views/menu.html +1 -1
- data/lib/api_browser/app/views/type.html +4 -23
- data/lib/api_browser/app/views/type/details.html +7 -4
- data/lib/api_browser/app/views/types/main/array.html +22 -0
- data/lib/api_browser/app/views/types/main/default.html +23 -0
- data/lib/api_browser/app/views/types/main/hash.html +23 -0
- data/lib/praxis.rb +2 -0
- data/lib/praxis/action_definition.rb +0 -8
- data/lib/praxis/api_definition.rb +11 -6
- data/lib/praxis/api_general_info.rb +13 -0
- data/lib/praxis/bootloader.rb +11 -5
- data/lib/praxis/docs/generator.rb +31 -11
- data/lib/praxis/docs/link_builder.rb +30 -0
- data/lib/praxis/extensions/field_expansion.rb +2 -2
- data/lib/praxis/media_type.rb +1 -1
- data/lib/praxis/middleware_app.rb +30 -0
- data/lib/praxis/resource_definition.rb +24 -2
- data/lib/praxis/response.rb +2 -1
- data/lib/praxis/response_definition.rb +2 -2
- data/lib/praxis/responses/http.rb +28 -92
- data/lib/praxis/responses/validation_error.rb +4 -1
- data/lib/praxis/tasks/api_docs.rb +11 -24
- data/lib/praxis/trait.rb +12 -7
- data/lib/praxis/validation_handler.rb +2 -1
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +11 -7
- data/spec/api_browser/filters/attribute_name_spec.js +2 -2
- data/spec/praxis/action_definition_spec.rb +23 -1
- data/spec/praxis/bootloader_spec.rb +28 -0
- data/spec/praxis/extensions/field_expansion_spec.rb +10 -0
- data/spec/praxis/middleware_app_spec.rb +55 -0
- data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +8 -3
- data/spec/praxis/resource_definition_spec.rb +51 -2
- data/spec/praxis/response_definition_spec.rb +16 -4
- data/spec/praxis/response_spec.rb +1 -1
- data/spec/praxis/trait_spec.rb +13 -0
- data/spec/spec_app/config/environment.rb +11 -1
- metadata +30 -25
- data/lib/praxis/restful_doc_generator.rb +0 -439
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0a0928d3b9a59294d32767a333b73c03a0a7955
|
4
|
+
data.tar.gz: 840261c0c55f18280f3c32d53a62053ed8582834
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c7d30737b31ec8204e030ca43577d8a4620b4a16ee3f9ec00c40ef740f08db8c7c21d9bc1d06dc7ddc56e2223dc0f9aaa990526a6f92f7297493dcf02158c11
|
7
|
+
data.tar.gz: b1e794d55ef40a616c21adeb8a72cea53b796e4a943636c5a16a576f282298c6f573b730f708ab2a0bbb050784c56c3b1fe53f760488c3100d98958a2b50f6eb
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.3
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,42 @@
|
|
2
2
|
|
3
3
|
## next
|
4
4
|
|
5
|
+
## 0.21
|
6
|
+
|
7
|
+
* Protect against `MediaType`s that do not have any links defined.
|
8
|
+
* More robust scanning of existing types when generating docs. Some types might have not been
|
9
|
+
properly reported in the `schemas` section of the JSON docs if they were only used somewhere
|
10
|
+
deep in some other type or action hierarchy
|
11
|
+
* Build doc browser support for defining top-level home pages for types.
|
12
|
+
Apps can achieve the override by registering templates that respond to the ‘main’ type (instead of
|
13
|
+
the other existing ‘label’,’embedded’ and ‘standalone’ modes).
|
14
|
+
* Enhance doc browser to show header and location expectations on action responses that have them
|
15
|
+
defined
|
16
|
+
* Allow Plugin registration without requiring config_key
|
17
|
+
* registration will select a default config_key based on the class name
|
18
|
+
* A new `documentation_url` global directive is exposed for authors to be able to
|
19
|
+
indicate where documentation will be hosted.
|
20
|
+
* If this is provided, the default *validation handler* will add a `documentation`
|
21
|
+
key to the response pointing at a url that should correspond to the documentation
|
22
|
+
for the resource the user was requesting.
|
23
|
+
* `Praxis::Docs::LinkBuilder` can be used to generate these documentation urls from
|
24
|
+
the praxis application.
|
25
|
+
* You can now switch your doc browser to use HTML5 style urls (i.e.
|
26
|
+
`/1.0/type/V1-MediaTypes-PriceFilter` instead of
|
27
|
+
`/index.html#/1.0/type/V1-MediaTypes-PriceFilter`).
|
28
|
+
* Remove deprecated rake tasks.
|
29
|
+
* Remove some remaining inline styling in doc browser.
|
30
|
+
* Adds a `ExampleProvider.removeHandlersForKey` call. You can use `ExampleProvider.removeHandlersForKey('general')`
|
31
|
+
to get rid of the default example if required.
|
32
|
+
* Make Traits accumulate block definitions for `params`,`headers` and `payload` rather than overriding them.
|
33
|
+
* Switch to lazy evaluation of `base_params` from `ApiDefinition` to properly inherit them into the resources
|
34
|
+
and their corresponding actions even before the application's `MediaTtypes` have been finalized.
|
35
|
+
* Built the `MiddlewareApp` class to make it easy to run a Praxis app mounted as an intercepting
|
36
|
+
middleware which will only forward requests down the stack if they didn't match any of its routes.
|
37
|
+
* Note: it properly skips forwarding when 404s are purposedly returned by the application itself.
|
38
|
+
* Note2: it also respects the `X-Cascade=pass` conventions.
|
39
|
+
|
40
|
+
|
5
41
|
## 0.20.1
|
6
42
|
|
7
43
|
* Doc generation: handle SimpleMediaTypes so that they don’t show up in the generated schemas.
|
@@ -13,7 +13,8 @@ module.exports = function(grunt) {
|
|
13
13
|
pluginPaths = process.env.PLUGIN_PATHS ? process.env.PLUGIN_PATHS.split(':') : [],
|
14
14
|
buildPath = userDocsPath + '/output',
|
15
15
|
browserPort = process.env.DOC_PORT || '9090',
|
16
|
-
bowerPaths = pluginPaths.concat([userDocsPath]).map(function(f) { return f + '/bower.json'; })
|
16
|
+
bowerPaths = pluginPaths.concat([userDocsPath]).map(function(f) { return f + '/bower.json'; }),
|
17
|
+
basePath = process.env.BASE_PATH || '';
|
17
18
|
|
18
19
|
var cssOutput = {};
|
19
20
|
cssOutput[buildPath + '/css/main.css'] = userDocsPath + '/styles.scss';
|
@@ -75,7 +76,28 @@ module.exports = function(grunt) {
|
|
75
76
|
'.tmp',
|
76
77
|
'.data',
|
77
78
|
'app'
|
78
|
-
])
|
79
|
+
]),
|
80
|
+
// This is to handle support of using $locationProvider.html5Mode(true).
|
81
|
+
middleware: function(connect, options, middlewares) {
|
82
|
+
middlewares.push(function(req, res, next) {
|
83
|
+
var segments = require('url').parse(req.url).pathname.split('/'),
|
84
|
+
last = segments[segments.length - 1];
|
85
|
+
if (last.split('.').length === 1) {
|
86
|
+
try {
|
87
|
+
var index = grunt.file.read('.tmp/index.html');
|
88
|
+
res.setHeader("Content-Type", "text/html");
|
89
|
+
res.write(index, 'utf8');
|
90
|
+
res.end()
|
91
|
+
} catch(e) {
|
92
|
+
next(e);
|
93
|
+
}
|
94
|
+
} else {
|
95
|
+
next();
|
96
|
+
}
|
97
|
+
});
|
98
|
+
|
99
|
+
return middlewares;
|
100
|
+
}
|
79
101
|
}
|
80
102
|
},
|
81
103
|
dist: {
|
@@ -362,7 +384,15 @@ module.exports = function(grunt) {
|
|
362
384
|
html: [buildPath + '/index.html'],
|
363
385
|
css: [buildPath + '/css/{,*/}*.css'],
|
364
386
|
options: {
|
365
|
-
assetsDirs: [buildPath]
|
387
|
+
assetsDirs: [buildPath],
|
388
|
+
blockReplacements: {
|
389
|
+
base: function(block) {
|
390
|
+
if (basePath !== '') {
|
391
|
+
return '<base href="' + basePath + '">';
|
392
|
+
}
|
393
|
+
return '';
|
394
|
+
}
|
395
|
+
}
|
366
396
|
}
|
367
397
|
},
|
368
398
|
|
@@ -6,6 +6,9 @@
|
|
6
6
|
<title ng-bind="subtitle ? title + ' – ' + subtitle : title">API Browser</title>
|
7
7
|
<meta name="description" content="">
|
8
8
|
<meta name="viewport" content="width=device-width">
|
9
|
+
<!-- build:base base -->
|
10
|
+
<base src="/">
|
11
|
+
<!-- endbuild -->
|
9
12
|
<!-- build:css({.tmp,app,/}) css/other.css -->
|
10
13
|
<!-- bower:css -->
|
11
14
|
<!-- endbower -->
|
@@ -80,6 +80,20 @@ app.provider('templateFor', function() {
|
|
80
80
|
}
|
81
81
|
});
|
82
82
|
|
83
|
+
this.register(function mainPageResolver($family, $requestedTemplate) {
|
84
|
+
'ngInject';
|
85
|
+
if ($requestedTemplate === 'main') {
|
86
|
+
switch ($family) {
|
87
|
+
case 'hash':
|
88
|
+
return 'views/types/main/hash.html';
|
89
|
+
case 'array':
|
90
|
+
return 'views/types/main/array.html';
|
91
|
+
default:
|
92
|
+
return 'views/types/main/default.html';
|
93
|
+
}
|
94
|
+
}
|
95
|
+
});
|
96
|
+
|
83
97
|
this.$get = function($injector, prepareTemplate) {
|
84
98
|
return function(type, templateType) {
|
85
99
|
for (var i = 0; i < resolvers.length; i++) {
|
@@ -1,8 +1,9 @@
|
|
1
1
|
app.filter('attributeName', function($sce) {
|
2
2
|
return function(input) {
|
3
3
|
var parts = input.split('.');
|
4
|
-
if (parts.length
|
5
|
-
|
4
|
+
if (parts.length > 1) {
|
5
|
+
var prefix = _.take(parts, parts.length-1).join('.');
|
6
|
+
return $sce.trustAsHtml('<span class="attribute-prefix">' + prefix + '.</span>' + _.last(parts));
|
6
7
|
}
|
7
8
|
return $sce.trustAsHtml(input);
|
8
9
|
};
|
@@ -56,12 +56,21 @@
|
|
56
56
|
<h5>Description</h5>
|
57
57
|
<div ng-bind-html="response.description | markdown"></div>
|
58
58
|
</div>
|
59
|
-
<h5>Status</h5>
|
60
|
-
<p>{{ response.status }}</p>
|
59
|
+
<h5>Status: {{ response.status }}</h5>
|
61
60
|
<div ng-if="response.examples.json">
|
62
61
|
<h5>Content-Type</h5>
|
63
62
|
<p>{{ response.examples.json.content_type }}</p>
|
64
63
|
</div>
|
64
|
+
<div ng-if="response.location">
|
65
|
+
<h5>Location:</h5>
|
66
|
+
<p>{{response.location.value}} <em>({{response.location.type}})</em></p>
|
67
|
+
</div>
|
68
|
+
<div ng-hide="response.headers | isEmpty">
|
69
|
+
<h5>Headers:</h5>
|
70
|
+
<div ng-repeat="(hname, hinfo) in response.headers">
|
71
|
+
<p> {{hname}}: {{ hinfo | headerInfo }}</p>
|
72
|
+
</div>
|
73
|
+
</div>
|
65
74
|
<div ng-if="response.payload.id">
|
66
75
|
<h5>Media Type</h5>
|
67
76
|
<p><type-placeholder template="label" type="response.payload"></type-placeholder></p>
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<div class="row">
|
6
6
|
<div class="col-lg-12">
|
7
7
|
<h1 class="page-header">
|
8
|
-
{{ controller.name | resourceName }}
|
8
|
+
{{ controller.display_name || (controller.name | resourceName) }}
|
9
9
|
</h1>
|
10
10
|
|
11
11
|
<p ng-bind-html="controller.description | markdown"></p>
|
@@ -16,7 +16,7 @@
|
|
16
16
|
</p>
|
17
17
|
</div>
|
18
18
|
</div>
|
19
|
-
<div class="row" ng-if="controller.actions.length">
|
19
|
+
<div class="row actions-overview" ng-if="controller.actions.length">
|
20
20
|
<div class="col-lg-12">
|
21
21
|
<h2>Actions</h2>
|
22
22
|
<div class="table-responsive">
|
@@ -30,13 +30,13 @@
|
|
30
30
|
</thead>
|
31
31
|
<tbody>
|
32
32
|
<tr ng-repeat="action in controller.actions">
|
33
|
-
<td>
|
33
|
+
<td class="action-overview-name">
|
34
34
|
<a ui-sref="root.action({action: action.name, controller: controllerName, version: apiVersion})">{{ action.name }}</a>
|
35
35
|
</td>
|
36
|
-
<td>
|
36
|
+
<td class="action-overview-url">
|
37
37
|
<url action="action"></url>
|
38
38
|
</td>
|
39
|
-
<td
|
39
|
+
<td class="action-overview-description">
|
40
40
|
<p ng-bind-html="action.description | markdown"></p>
|
41
41
|
</td>
|
42
42
|
</tr>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<div class="row">
|
3
3
|
<div class="col-sm-12">
|
4
4
|
<p>
|
5
|
-
<div ng-if="versions.length > 1" dropdown
|
5
|
+
<div ng-if="versions.length > 1" dropdown dropdown-append-to-body>
|
6
6
|
<button type="button" class="btn btn-success" dropdown-toggle ng-disabled="disabled">
|
7
7
|
{{:: versionLabel}}: {{selectedVersion}} <span class="caret"></span>
|
8
8
|
</button>
|
@@ -1,25 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
<
|
4
|
-
<p>The requested type could not be found.</p>
|
5
|
-
</div>
|
6
|
-
<div ng-if="type">
|
7
|
-
<div class="row">
|
8
|
-
<div class="col-lg-12">
|
9
|
-
<h1>
|
10
|
-
{{ type.name | resourceName }}
|
11
|
-
</h1>
|
12
|
-
</div>
|
13
|
-
</div>
|
14
|
-
<div class="row" ng-if="controllers.length">
|
15
|
-
<div class="col-lg-12">
|
16
|
-
<h3>Served by</h3>
|
17
|
-
<ul>
|
18
|
-
<li ng-repeat="controller in controllers">
|
19
|
-
<a ui-sref="root.controller({version: apiVersion, controller: controller.id})">{{ controller.name | resourceName }}</a>
|
20
|
-
</li>
|
21
|
-
</ul>
|
22
|
-
</div>
|
1
|
+
<div ng-if="type">
|
2
|
+
<div ng-if="error" class="alert alert-danger">
|
3
|
+
<p>The requested type could not be found.</p>
|
23
4
|
</div>
|
24
|
-
<
|
5
|
+
<type-placeholder type="type" template="main" details="type.attributes"></type-placeholder>
|
25
6
|
</div>
|
@@ -1,10 +1,13 @@
|
|
1
1
|
<div class="row" ng-if="type">
|
2
2
|
<div class="col-lg-12">
|
3
|
-
<
|
4
|
-
|
5
|
-
|
3
|
+
<div ng-if="type.attributes">
|
4
|
+
<h2>Schema</h2>
|
5
|
+
<p ng-if="type.identifier"><b>Media-type identifier: {{ type.identifier }}</b></p>
|
6
|
+
<div ng-if="type.description" ng-bind-html="type.description | markdown"></div>
|
6
7
|
|
7
|
-
|
8
|
+
<attribute-table attributes="type.attributes"></attribute-table>
|
9
|
+
</div>
|
10
|
+
<div ng-if="type.attributes === undefined" type-placeholder type="type" template="standalone" name="type.name" details="type"></div>
|
8
11
|
</div>
|
9
12
|
</div>
|
10
13
|
<div class="row" ng-if="views.length">
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<div ng-if="type">
|
2
|
+
<div class="row">
|
3
|
+
<div class="col-lg-12">
|
4
|
+
<h1>
|
5
|
+
{{ type.name | resourceName }}
|
6
|
+
</h1>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
<div class="row" ng-if="controllers.length">
|
10
|
+
<div class="col-lg-12">
|
11
|
+
<h3>Served by</h3>
|
12
|
+
<ul>
|
13
|
+
<li ng-repeat="controller in controllers">
|
14
|
+
<a ui-sref="root.controller({version: apiVersion, controller: controller.id})">{{ controller.name | resourceName }}</a>
|
15
|
+
</li>
|
16
|
+
</ul>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
<p>A Collection of:</p>
|
20
|
+
|
21
|
+
<type-placeholder type="type.member_attribute.type" template="standalone" details="type.member_attribute.type.attributes"></type-placeholder>
|
22
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<div ng-if="error" class="alert alert-danger">
|
2
|
+
<p>The requested type could not be found.</p>
|
3
|
+
</div>
|
4
|
+
<div ng-if="type">
|
5
|
+
<div class="row">
|
6
|
+
<div class="col-lg-12">
|
7
|
+
<h1>
|
8
|
+
{{ type.name | resourceName }}
|
9
|
+
</h1>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
<div class="row" ng-if="controllers.length">
|
13
|
+
<div class="col-lg-12">
|
14
|
+
<h3>Served by</h3>
|
15
|
+
<ul>
|
16
|
+
<li ng-repeat="controller in controllers">
|
17
|
+
<a ui-sref="root.controller({version: apiVersion, controller: controller.id})">{{ controller.name | resourceName }}</a>
|
18
|
+
</li>
|
19
|
+
</ul>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<ng-include src="'views/type/details.html'" />
|
23
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<div ng-if="error" class="alert alert-danger">
|
2
|
+
<p>The requested type could not be found.</p>
|
3
|
+
</div>
|
4
|
+
<div ng-if="type">
|
5
|
+
<div class="row">
|
6
|
+
<div class="col-lg-12">
|
7
|
+
<h1>
|
8
|
+
{{ type.name | resourceName }}
|
9
|
+
</h1>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
<div class="row" ng-if="controllers.length">
|
13
|
+
<div class="col-lg-12">
|
14
|
+
<h3>Served by</h3>
|
15
|
+
<ul>
|
16
|
+
<li ng-repeat="controller in controllers">
|
17
|
+
<a ui-sref="root.controller({version: apiVersion, controller: controller.id})">{{ controller.name | resourceName }}</a>
|
18
|
+
</li>
|
19
|
+
</ul>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<ng-include src="'views/type/details.html'" />
|
23
|
+
</div>
|
data/lib/praxis.rb
CHANGED
@@ -45,10 +45,12 @@ module Praxis
|
|
45
45
|
|
46
46
|
autoload :Stats, 'praxis/stats'
|
47
47
|
autoload :Notifications, 'praxis/notifications'
|
48
|
+
autoload :MiddlewareApp, 'praxis/middleware_app'
|
48
49
|
|
49
50
|
autoload :RestfulDocGenerator, 'praxis/restful_doc_generator'
|
50
51
|
module Docs
|
51
52
|
autoload :Generator, 'praxis/docs/generator'
|
53
|
+
autoload :LinkBuilder, 'praxis/docs/link_builder'
|
52
54
|
end
|
53
55
|
|
54
56
|
# types
|
@@ -118,14 +118,6 @@ module Praxis
|
|
118
118
|
update_attribute(@params, opts, block)
|
119
119
|
else
|
120
120
|
@params = create_attribute(type, **opts, &block)
|
121
|
-
if (api_info = ApiDefinition.instance.info(resource_definition.version))
|
122
|
-
if api_info.base_params
|
123
|
-
base_attrs = api_info.base_params.attributes
|
124
|
-
@params.attributes.merge!(base_attrs) do |key, oldval, newval|
|
125
|
-
oldval
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
121
|
end
|
130
122
|
|
131
123
|
@params
|
@@ -92,17 +92,22 @@ module Praxis
|
|
92
92
|
end
|
93
93
|
|
94
94
|
define do |api|
|
95
|
-
api.response_template :ok do |media_type: |
|
96
|
-
media_type media_type
|
95
|
+
api.response_template :ok do |media_type: , location: nil, headers: nil, description: nil |
|
97
96
|
status 200
|
98
|
-
description 'Standard response for successful HTTP requests.'
|
97
|
+
description( description || 'Standard response for successful HTTP requests.' )
|
98
|
+
|
99
|
+
media_type media_type
|
100
|
+
location location
|
101
|
+
headers headers if headers
|
99
102
|
end
|
100
103
|
|
101
|
-
api.response_template :created do |location: nil,
|
104
|
+
api.response_template :created do |media_type: nil, location: nil, headers: nil, description: nil|
|
105
|
+
status 201
|
106
|
+
description( description || 'The request has been fulfilled and resulted in a new resource being created.' )
|
107
|
+
|
102
108
|
media_type media_type if media_type
|
103
109
|
location location
|
104
|
-
|
105
|
-
description 'The request has been fulfilled and resulted in a new resource being created.'
|
110
|
+
headers headers if headers
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|