mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-24 09:33:44 +03:00
Merge commit '939651499803dc326cc787ad1b3ee48a5f1192ba' into releases
This commit is contained in:
commit
008f9d5517
|
@ -23,8 +23,7 @@ gulp.task('build', function (callback) {
|
||||||
}
|
}
|
||||||
return runSequence(
|
return runSequence(
|
||||||
'clean',
|
'clean',
|
||||||
'tsc',
|
'transpile',
|
||||||
'inlineTemplates',
|
|
||||||
'bundle',
|
'bundle',
|
||||||
'concatDeps',
|
'concatDeps',
|
||||||
'copyDebug',
|
'copyDebug',
|
||||||
|
@ -32,6 +31,14 @@ gulp.task('build', function (callback) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task('transpile', function(cb) {
|
||||||
|
return runSequence(
|
||||||
|
'tsc',
|
||||||
|
'inlineTemplates',
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task('copyDebug', () => {
|
gulp.task('copyDebug', () => {
|
||||||
if (!argv.prod) {
|
if (!argv.prod) {
|
||||||
// copy for be accessible from demo for debug
|
// copy for be accessible from demo for debug
|
||||||
|
@ -39,16 +46,6 @@ gulp.task('copyDebug', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('rebuild', function(done) {
|
|
||||||
return runSequence(
|
|
||||||
'inlineTemplates',
|
|
||||||
'bundle',
|
|
||||||
'concatDeps',
|
|
||||||
'copyDebug',
|
|
||||||
done
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('tsc', function() {
|
gulp.task('tsc', function() {
|
||||||
exec('tsc -p ./tsconfig.json');
|
exec('tsc -p ./tsconfig.json');
|
||||||
});
|
});
|
||||||
|
@ -81,7 +78,7 @@ gulp.task('tsc', function() {
|
||||||
// .pipe(gulp.dest(config.tmp));
|
// .pipe(gulp.dest(config.tmp));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
gulp.task('inlineTemplates', ['tsc', 'sass'], function() {
|
gulp.task('inlineTemplates', ['sass'], function() {
|
||||||
return gulp.src('.tmp/**/*.js', { base: './tmp' })
|
return gulp.src('.tmp/**/*.js', { base: './tmp' })
|
||||||
.pipe(replace(/'(.*?)\.css'/g, '\'$1.scss\''))
|
.pipe(replace(/'(.*?)\.css'/g, '\'$1.scss\''))
|
||||||
.pipe(inlineNg2Template({
|
.pipe(inlineNg2Template({
|
||||||
|
@ -98,14 +95,14 @@ gulp.task('inlineTemplates', ['tsc', 'sass'], function() {
|
||||||
.pipe(gulp.dest(paths.tmp));
|
.pipe(gulp.dest(paths.tmp));
|
||||||
});
|
});
|
||||||
|
|
||||||
function compileSass(ext, file) {
|
function compileSass(ext, file, cb) {
|
||||||
file = file.replace('../../shared/styles/variables', 'lib/shared/styles/variables');
|
file = file.replace('../../shared/styles/variables', 'lib/shared/styles/variables');
|
||||||
file = file.replace('json-schema-common', 'lib/components/JsonSchema/json-schema-common');
|
file = file.replace('json-schema-common', 'lib/components/JsonSchema/json-schema-common');
|
||||||
file = file.replace('../../shared/styles/share-link', 'lib/shared/styles/share-link');
|
file = file.replace('../../shared/styles/share-link', 'lib/shared/styles/share-link');
|
||||||
file = file.replace('../JsonSchema/lib/components/JsonSchema/json-schema-common', 'lib/components/JsonSchema/json-schema-common');
|
file = file.replace('../JsonSchema/lib/components/JsonSchema/json-schema-common', 'lib/components/JsonSchema/json-schema-common');
|
||||||
file = file.replace('../../styles/variables', 'lib/shared/styles/variables');
|
file = file.replace('../../styles/variables', 'lib/shared/styles/variables');
|
||||||
|
|
||||||
return sassCopm.renderSync({data: file}).css;
|
cb(null, sassCopm.renderSync({data: file}).css);
|
||||||
}
|
}
|
||||||
|
|
||||||
var JS_DEPS = argv.prod ? [
|
var JS_DEPS = argv.prod ? [
|
||||||
|
@ -116,7 +113,7 @@ var JS_DEPS = argv.prod ? [
|
||||||
]: [
|
]: [
|
||||||
'lib/utils/browser-update.js',
|
'lib/utils/browser-update.js',
|
||||||
'node_modules/zone.js/dist/zone.js',
|
'node_modules/zone.js/dist/zone.js',
|
||||||
//'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||||
'node_modules/reflect-metadata/Reflect.js',
|
'node_modules/reflect-metadata/Reflect.js',
|
||||||
'node_modules/babel-polyfill/dist/polyfill.js'
|
'node_modules/babel-polyfill/dist/polyfill.js'
|
||||||
];
|
];
|
||||||
|
@ -138,7 +135,7 @@ gulp.task('concatDeps', ['concatPrism'], function() {
|
||||||
.pipe(gulp.dest('.'))
|
.pipe(gulp.dest('.'))
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('bundle', function bundle(done) {
|
gulp.task('bundle', ['injectVersionFile'], function bundle(done) {
|
||||||
mkdir('-p', 'dist');
|
mkdir('-p', 'dist');
|
||||||
cp('lib/index.js', path.join(paths.tmp, paths.sourceEntryPoint));
|
cp('lib/index.js', path.join(paths.tmp, paths.sourceEntryPoint));
|
||||||
var builder = new Builder('./', 'system.config.js');
|
var builder = new Builder('./', 'system.config.js');
|
||||||
|
@ -188,7 +185,8 @@ gulp.task('concatPrism', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// needs inlineTemplates run before to create .tmp/lib folder
|
// needs inlineTemplates run before to create .tmp/lib folder
|
||||||
gulp.task('injectVersionFile', ['inlineTemplates'], function() {
|
gulp.task('injectVersionFile', function() {
|
||||||
var version = require('../../package.json').version;
|
var version = require('../../package.json').version;
|
||||||
fs.writeFileSync(path.join(paths.tmp, 'lib/version.json'), JSON.stringify(version));
|
var exportStatement = `export var redocVersion = "${version}"`;
|
||||||
|
fs.writeFileSync(path.join(paths.tmp, 'lib/version.js'), exportStatement);
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
var gulp = require('gulp');
|
var gulp = require('gulp');
|
||||||
|
var runSequence = require('run-sequence');
|
||||||
var Server = require('karma').Server;
|
var Server = require('karma').Server;
|
||||||
var remapIstanbul = require('remap-istanbul/lib/gulpRemapIstanbul');
|
var remapIstanbul = require('remap-istanbul/lib/gulpRemapIstanbul');
|
||||||
|
|
||||||
|
gulp.task('prepare-test', function(cb) {
|
||||||
|
return runSequence(
|
||||||
|
'clean',
|
||||||
|
'transpile',
|
||||||
|
'concatPrism',
|
||||||
|
'injectVersionFile',
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
})
|
||||||
/**
|
/**
|
||||||
* Run test once and exit
|
* Run test once and exit
|
||||||
*/
|
*/
|
||||||
gulp.task('test', ['concatPrism', 'inlineTemplates', 'injectVersionFile'], function (done) {
|
gulp.task('test', ['prepare-test'], function (done) {
|
||||||
new Server({
|
new Server({
|
||||||
configFile: __dirname + '/../../karma.conf.js',
|
configFile: __dirname + '/../../karma.conf.js',
|
||||||
singleRun: true
|
singleRun: true
|
||||||
|
|
|
@ -7,8 +7,8 @@ function changed(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
gulp.task('watch', ['build'], function () {
|
gulp.task('watch', ['build'], function () {
|
||||||
gulp.watch([ paths.source ], [ 'rebuild', browserSync.reload ]).on('change', changed);
|
gulp.watch([ paths.source ], [ 'build', browserSync.reload ]).on('change', changed);
|
||||||
gulp.watch([ paths.html ], [ 'rebuild', browserSync.reload]).on('change', changed);
|
gulp.watch([ paths.html ], [ 'build', browserSync.reload]).on('change', changed);
|
||||||
gulp.watch([ paths.scss ], [ 'rebuild', browserSync.reload]).on('change', changed);
|
gulp.watch([ paths.scss ], [ 'build', browserSync.reload]).on('change', changed);
|
||||||
gulp.watch([ paths.demo ], [ '', browserSync.reload ]).on('change', changed);
|
gulp.watch([ paths.demo ], [ '', browserSync.reload ]).on('change', changed);
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,6 +24,6 @@
|
||||||
<redoc scroll-y-offset="body > nav" spec-url='swagger.yaml'></redoc>
|
<redoc scroll-y-offset="body > nav" spec-url='swagger.yaml'></redoc>
|
||||||
|
|
||||||
<script src="main.js"> </script>
|
<script src="main.js"> </script>
|
||||||
<script src="../dist/redoc.min.js"> </script>
|
<script src="./dist/redoc.min.js"> </script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -25,18 +25,12 @@ tags:
|
||||||
Sometimes you just can't get enough. For this reason, we've provided a convenient way to access more data in any request for sequential data. Simply call the url in the next_url parameter and we'll respond with the next set of data.
|
Sometimes you just can't get enough. For this reason, we've provided a convenient way to access more data in any request for sequential data. Simply call the url in the next_url parameter and we'll respond with the next set of data.
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
"pagination": {
|
"pagination": {
|
||||||
|
|
||||||
"next_url":
|
"next_url":
|
||||||
"https://api.instagram.com/v1/tags/puppy/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&max_id=13872296",
|
"https://api.instagram.com/v1/tags/puppy/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&max_id=13872296",
|
||||||
|
|
||||||
"next_max_id": "13872296"
|
"next_max_id": "13872296"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ module.exports = function (config) {
|
||||||
loadFiles: ['.tmp/tests/setup.js', '.tmp/tests/helpers.js', '.tmp/lib/**/*.js',
|
loadFiles: ['.tmp/tests/setup.js', '.tmp/tests/helpers.js', '.tmp/lib/**/*.js',
|
||||||
'.tmp/tests/unit/*.js'],
|
'.tmp/tests/unit/*.js'],
|
||||||
serveFiles: ['tests/schemas/**/*.json','tests/schemas/**/*.yml', 'lib/**/*.html',
|
serveFiles: ['tests/schemas/**/*.json','tests/schemas/**/*.yml', 'lib/**/*.html',
|
||||||
'.tmp/lib/**/*.json', '.tmp/*js', '.tmp/lib/**/*.css']
|
'.tmp/*js', '.tmp/lib/**/*.css']
|
||||||
},
|
},
|
||||||
|
|
||||||
proxies: {
|
proxies: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div>
|
<div>
|
||||||
<h1 class="api-info-header">{{data.title}} ({{data.version}})</h1>
|
<h1 class="api-info-header">{{data.title}} ({{data.version}})</h1>
|
||||||
<p *ngIf="data.description" innerHtml="{{data.description | marked}}"> </p>
|
<p *ngIf="data.description" [innerHtml]="data.description | marked"> </p>
|
||||||
<p>
|
<p>
|
||||||
<!-- TODO: create separate components for contact and license ? -->
|
<!-- TODO: create separate components for contact and license ? -->
|
||||||
<span *ngIf="data.contact"> Contact:
|
<span *ngIf="data.contact"> Contact:
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
import { TestComponentBuilder } from '@angular/compiler/testing';
|
import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
|
|
||||||
import { ApiInfo } from './api-info';
|
import { ApiInfo } from './api-info';
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';
|
import { SpecManager } from '../../utils/SpecManager';
|
||||||
import { OptionsService } from '../../services/index';
|
import { OptionsService } from '../../services/index';
|
||||||
|
|
||||||
describe('Redoc components', () => {
|
describe('Redoc components', () => {
|
||||||
|
@ -24,13 +24,13 @@ describe('Redoc components', () => {
|
||||||
let component;
|
let component;
|
||||||
let fixture;
|
let fixture;
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: new SchemaManager()}),
|
provide(SpecManager, {useValue: new SpecManager()}),
|
||||||
provide(OptionsService, {useClass: OptionsService})
|
provide(OptionsService, {useClass: OptionsService})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
|
beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, specMgr) => {
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
return schemaMgr.load('/tests/schemas/api-info-test.json');
|
return specMgr.load('/tests/schemas/api-info-test.json');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { SchemaManager, RedocComponent, BaseComponent } from '../base';
|
import { SpecManager, RedocComponent, BaseComponent } from '../base';
|
||||||
import { OptionsService } from '../../services/index';
|
import { OptionsService } from '../../services/index';
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
|
@ -11,8 +11,8 @@ import { OptionsService } from '../../services/index';
|
||||||
export class ApiInfo extends BaseComponent {
|
export class ApiInfo extends BaseComponent {
|
||||||
data: any;
|
data: any;
|
||||||
specUrl: String;
|
specUrl: String;
|
||||||
constructor(schemaMgr:SchemaManager, private optionsService:OptionsService) {
|
constructor(specMgr:SpecManager, private optionsService:OptionsService) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareModel() {
|
prepareModel() {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
import { TestComponentBuilder } from '@angular/compiler/testing';
|
import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
|
|
||||||
import { ApiLogo } from './api-logo';
|
import { ApiLogo } from './api-logo';
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';
|
import { SpecManager } from '../../utils/SpecManager';
|
||||||
|
|
||||||
|
|
||||||
describe('Redoc components', () => {
|
describe('Redoc components', () => {
|
||||||
|
@ -22,16 +22,16 @@ describe('Redoc components', () => {
|
||||||
let builder;
|
let builder;
|
||||||
let component;
|
let component;
|
||||||
let fixture;
|
let fixture;
|
||||||
let schemaMgr;
|
let specMgr;
|
||||||
|
|
||||||
let schemaUrl = '/tests/schemas/api-info-test.json';
|
let schemaUrl = '/tests/schemas/api-info-test.json';
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: new SchemaManager()})
|
provide(SpecManager, {useValue: new SpecManager()})
|
||||||
]);
|
]);
|
||||||
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, _schemaMgr) => {
|
beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, _specMgr) => {
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
schemaMgr = _schemaMgr;
|
specMgr = _specMgr;
|
||||||
return schemaMgr.load(schemaUrl);
|
return specMgr.load(schemaUrl);
|
||||||
})));
|
})));
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
builder.createAsync(TestAppComponent).then(_fixture => {
|
builder.createAsync(TestAppComponent).then(_fixture => {
|
||||||
|
@ -70,7 +70,7 @@ describe('Redoc components', () => {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'test-app',
|
selector: 'test-app',
|
||||||
directives: [ApiLogo],
|
directives: [ApiLogo],
|
||||||
providers: [SchemaManager],
|
providers: [SpecManager],
|
||||||
template:
|
template:
|
||||||
`<api-logo></api-logo>`
|
`<api-logo></api-logo>`
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
|
import {RedocComponent, BaseComponent, SpecManager} from '../base';
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
selector: 'api-logo',
|
selector: 'api-logo',
|
||||||
|
@ -10,8 +10,8 @@ import {RedocComponent, BaseComponent, SchemaManager} from '../base';
|
||||||
export class ApiLogo extends BaseComponent {
|
export class ApiLogo extends BaseComponent {
|
||||||
data:any = {};
|
data:any = {};
|
||||||
|
|
||||||
constructor(schemaMgr:SchemaManager) {
|
constructor(specMgr:SpecManager) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareModel() {
|
prepareModel() {
|
||||||
|
|
|
@ -11,16 +11,6 @@ $param-name-height: 20px;
|
||||||
|
|
||||||
$sub-schema-offset: ($bullet-size/2) + $bullet-margin;
|
$sub-schema-offset: ($bullet-size/2) + $bullet-margin;
|
||||||
|
|
||||||
/*
|
|
||||||
.param-schema {
|
|
||||||
padding-left: $sub-schema-offset - $lines-width;
|
|
||||||
border-left: $line-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-wrap {
|
|
||||||
position: relative;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
.param-name {
|
.param-name {
|
||||||
|
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
|
@ -34,7 +24,7 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.param-name-content {
|
.param-name-wrap {
|
||||||
padding-right: $cell-spacing;
|
padding-right: $cell-spacing;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-family: $headers-font, $headers-font-family;
|
font-family: $headers-font, $headers-font-family;
|
||||||
|
@ -45,6 +35,7 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-bottom: 1px solid #ccc;
|
border-bottom: 1px solid #ccc;
|
||||||
width: 75%;
|
width: 75%;
|
||||||
|
line-height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.param-range {
|
.param-range {
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import { getChildDebugElement } from '../../../tests/helpers';
|
import { getChildDebugElement } from '../../../tests/helpers';
|
||||||
import { Component, provide } from '@angular/core';
|
import { Component, provide } from '@angular/core';
|
||||||
import { DynamicComponentLoader } from '@angular/core';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
inject,
|
inject,
|
||||||
|
@ -15,42 +14,30 @@ import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
|
|
||||||
|
|
||||||
import { JsonSchemaLazy } from './json-schema-lazy';
|
import { JsonSchemaLazy } from './json-schema-lazy';
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';
|
import { SpecManager } from '../../utils/SpecManager';
|
||||||
|
|
||||||
describe('Redoc components', () => {
|
describe('Redoc components', () => {
|
||||||
describe('JsonSchemaLazy Component', () => {
|
describe('JsonSchemaLazy Component', () => {
|
||||||
let builder;
|
let builder;
|
||||||
let component;
|
let component;
|
||||||
let schemaMgr = new SchemaManager();
|
let specMgr = new SpecManager();
|
||||||
let fixture;
|
let fixture;
|
||||||
let loader;
|
|
||||||
let appRefMock = {
|
|
||||||
instance: {
|
|
||||||
pointer: ''
|
|
||||||
},
|
|
||||||
hostView: { changeDetectorRef: {detectChanges : () => undefined} }
|
|
||||||
};
|
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: schemaMgr})
|
provide(SpecManager, {useValue: specMgr})
|
||||||
]);
|
]);
|
||||||
beforeEach(inject([TestComponentBuilder, DynamicComponentLoader], (tcb, dcl) => {
|
beforeEach(inject([TestComponentBuilder], (tcb, dcl) => {
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
loader = dcl;
|
|
||||||
spyOn(loader, 'loadNextToLocation').and.returnValue({then: (fn) => fn(appRefMock)});
|
|
||||||
}));
|
}));
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
builder.createAsync(TestAppComponent).then(_fixture => {
|
builder.createAsync(TestAppComponent).then(_fixture => {
|
||||||
fixture = _fixture;
|
fixture = _fixture;
|
||||||
let debugEl = getChildDebugElement(fixture.debugElement, 'json-schema-lazy');
|
let debugEl = getChildDebugElement(fixture.debugElement, 'json-schema-lazy');
|
||||||
component = <JsonSchemaLazy>debugEl.componentInstance;
|
component = <JsonSchemaLazy>debugEl.componentInstance;
|
||||||
|
spyOn(component, '_loadAfterSelf').and.callThrough();
|
||||||
done();
|
done();
|
||||||
}, err => done.fail(err));
|
}, err => done.fail(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
loader.loadNextToLocation.and.callThrough();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should init component', () => {
|
it('should init component', () => {
|
||||||
expect(component).not.toBeNull();
|
expect(component).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
@ -59,7 +46,7 @@ describe('Redoc components', () => {
|
||||||
component.pointer = '#/def';
|
component.pointer = '#/def';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.load();
|
component.load();
|
||||||
expect(loader.loadNextToLocation).toHaveBeenCalled();
|
expect(component._loadAfterSelf).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not run loadNextToLocation if already loaded', () => {
|
it('should not run loadNextToLocation if already loaded', () => {
|
||||||
|
@ -67,14 +54,7 @@ describe('Redoc components', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.load();
|
component.load();
|
||||||
component.load();
|
component.load();
|
||||||
expect(loader.loadNextToLocation.calls.count()).toEqual(1);
|
expect(component._loadAfterSelf.calls.count()).toEqual(1);
|
||||||
});
|
|
||||||
|
|
||||||
it('should init json-schema with correct pointer', () => {
|
|
||||||
component.pointer = '#/def';
|
|
||||||
fixture.detectChanges();
|
|
||||||
component.load();
|
|
||||||
expect(appRefMock.instance.pointer).toEqual(component.pointer);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { Component, ElementRef, ViewContainerRef, OnDestroy, AfterViewInit } from '@angular/core';
|
import { Component, ElementRef, ViewContainerRef, OnDestroy, Input,
|
||||||
|
AfterViewInit, ComponentResolver, Renderer } from '@angular/core';
|
||||||
import { CORE_DIRECTIVES } from '@angular/common';
|
import { CORE_DIRECTIVES } from '@angular/common';
|
||||||
import { DynamicComponentLoader, Input } from '@angular/core';
|
|
||||||
|
|
||||||
import { JsonSchema } from './json-schema';
|
import { JsonSchema } from './json-schema';
|
||||||
import { OptionsService } from '../../services/options.service';
|
import { OptionsService } from '../../services/options.service';
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';
|
import { SpecManager } from '../../utils/SpecManager';
|
||||||
|
|
||||||
|
|
||||||
var cache = {};
|
var cache = {};
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'json-schema-lazy',
|
selector: 'json-schema-lazy',
|
||||||
template: '',
|
template: '',
|
||||||
|
@ -21,23 +19,33 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit {
|
||||||
@Input() pointer: string;
|
@Input() pointer: string;
|
||||||
@Input() auto: boolean;
|
@Input() auto: boolean;
|
||||||
@Input() isRequestSchema: boolean;
|
@Input() isRequestSchema: boolean;
|
||||||
|
@Input() final: boolean = false;
|
||||||
|
@Input() nestOdd: boolean;
|
||||||
|
@Input() childFor: string;
|
||||||
|
@Input() isArray: boolean;
|
||||||
loaded: boolean = false;
|
loaded: boolean = false;
|
||||||
constructor(private schemaMgr:SchemaManager, private viewRef:ViewContainerRef, private elementRef:ElementRef,
|
constructor(private specMgr:SpecManager, private location:ViewContainerRef, private elementRef:ElementRef,
|
||||||
private dcl:DynamicComponentLoader, private optionsService:OptionsService) {
|
private resolver:ComponentResolver, private optionsService:OptionsService, private _renderer: Renderer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizePointer() {
|
normalizePointer() {
|
||||||
let schema = this.schemaMgr.byPointer(this.pointer);
|
let schema = this.specMgr.byPointer(this.pointer);
|
||||||
return schema && schema.$ref || this.pointer;
|
return schema && schema.$ref || this.pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadAfterSelf() {
|
_loadAfterSelf() {
|
||||||
return this.dcl.loadNextToLocation(JsonSchema, this.viewRef).then((compRef) => {
|
// FIXME: get rid of DynamicComponentLoader as it is deprecated
|
||||||
this.initComponent(compRef);
|
return this.resolver.resolveComponent(JsonSchema).then(componentFactory => {
|
||||||
if (compRef.changeDetectorRef) {
|
let contextInjector = this.location.parentInjector;
|
||||||
compRef.changeDetectorRef.detectChanges();
|
let compRef = this.location.createComponent(
|
||||||
}
|
componentFactory, null, contextInjector, null);
|
||||||
|
this.initComponent(compRef.instance);
|
||||||
|
this._renderer.setElementAttribute(compRef.location.nativeElement, 'class', this.location.element.nativeElement.className);
|
||||||
|
compRef.changeDetectorRef.detectChanges();
|
||||||
return compRef;
|
return compRef;
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,9 +66,10 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit {
|
||||||
setTimeout( ()=> {
|
setTimeout( ()=> {
|
||||||
let $element = compRef.location.nativeElement;
|
let $element = compRef.location.nativeElement;
|
||||||
|
|
||||||
// skip caching view with tabs inside (discriminator) as it needs attached controller
|
// skip caching view with tabs inside (discriminator)
|
||||||
// FIXME: get rid of dependency on selector
|
// as it needs attached controller
|
||||||
if ($element.querySelector('.discriminator-wrap')) {
|
if (compRef.instance.hasDescendants || compRef.instance._hasSubSchemas) {
|
||||||
|
this._loadAfterSelf();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
insertAfter($element.cloneNode(true), this.elementRef.nativeElement);
|
insertAfter($element.cloneNode(true), this.elementRef.nativeElement);
|
||||||
|
@ -71,12 +80,15 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initComponent(compRef) {
|
initComponent(instance:JsonSchema) {
|
||||||
compRef.instance.pointer = this.pointer;
|
Object.assign(instance, this);
|
||||||
compRef.instance.isRequestSchema = this.isRequestSchema;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
|
if (this.optionsService.options.disableLazySchemas) {
|
||||||
|
this._loadAfterSelf();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.auto) return;
|
if (!this.auto) return;
|
||||||
this.loadCached();
|
this.loadCached();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,20 @@
|
||||||
</span>
|
</span>
|
||||||
<table *ngIf="!schema.isTrivial" class="params-wrap" [ngClass]="{'params-array': schema._isArray}">
|
<table *ngIf="!schema.isTrivial" class="params-wrap" [ngClass]="{'params-array': schema._isArray}">
|
||||||
<!-- <caption> {{_displayType}} </caption> -->
|
<!-- <caption> {{_displayType}} </caption> -->
|
||||||
<template ngFor [ngForOf]="schema.properties" let-prop="$implicit" let-last="last">
|
<template ngFor [ngForOf]="schema._properties" let-prop="$implicit" let-last="last" [ngForTrackBy]="trackByName">
|
||||||
<tr class="param" [ngClass]="{'last': last,
|
<tr class="param" [ngClass]="{'last': last,
|
||||||
'discriminator': prop.isDiscriminator && !derivedEmtpy,
|
'discriminator': prop.isDiscriminator,
|
||||||
'complex': prop._pointer,
|
'complex': prop._pointer,
|
||||||
'additional': prop._additional
|
'additional': prop._additional,
|
||||||
|
'expanded': subSchema.visible
|
||||||
}">
|
}">
|
||||||
<td class="param-name">
|
<td class="param-name">
|
||||||
<span class="param-name-content">{{prop._name}}</span>
|
<span class="param-name-wrap">
|
||||||
|
<span (click)="subSchema.toggle()" class="param-name-content" >{{prop._name}}</span>
|
||||||
|
<svg *ngIf="prop._pointer" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" viewBox="0 0 24 24" xml:space="preserve">
|
||||||
|
<polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="param-info">
|
<td class="param-info">
|
||||||
<div>
|
<div>
|
||||||
|
@ -34,35 +40,37 @@
|
||||||
title="{{prop._displayTypeHint}}"> {{prop._displayType}} {{prop._displayFormat}}
|
title="{{prop._displayTypeHint}}"> {{prop._displayType}} {{prop._displayFormat}}
|
||||||
<span class="param-range" *ngIf="prop._range"> {{prop._range}} </span>
|
<span class="param-range" *ngIf="prop._range"> {{prop._range}} </span>
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="prop.required" class="param-required">Required</span>
|
<span *ngIf="prop._required" class="param-required">Required</span>
|
||||||
<div *ngIf="prop.default">Default: {{prop.default | json}}</div>
|
<div *ngIf="prop.default">Default: {{prop.default | json}}</div>
|
||||||
<div *ngIf="prop.enum && !prop.isDiscriminator" class="param-enum">
|
<div *ngIf="prop.enum && !prop.isDiscriminator" class="param-enum">
|
||||||
<span *ngFor="let enumItem of prop.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
|
<span *ngFor="let enumItem of prop.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-description" innerHtml="{{prop.description | marked}}"></div>
|
<div class="param-description" [innerHtml]="prop.description | marked"></div>
|
||||||
<div class="discriminator-info" *ngIf="prop.isDiscriminator">
|
<div class="discriminator-info" *ngIf="prop.isDiscriminator">
|
||||||
<span>This field value determines the exact schema:</span>
|
<span>This field value determines the exact schema:</span>
|
||||||
<drop-down (change)="selectDerived($event)">
|
<drop-down (change)="selectDescendant($event)">
|
||||||
<option *ngFor="let derived of schema.derived; let i=index"
|
<option *ngFor="let descendant of schema._descendants; let i=index"
|
||||||
[value]="i">{{derived.name}}</option>
|
[value]="i">{{descendant.name}}</option>
|
||||||
</drop-down>
|
</drop-down>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="param-schema" [ngClass]="{'param-array': prop._isArray, 'last': last}" *ngIf="prop._pointer">
|
<tr class="param-schema" [ngClass]="{'param-array': prop._isArray, 'last': last}" [hidden]="!prop._pointer">
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<json-schema class="nested-schema" pointer="{{prop._pointer}}" [isArray]='prop._isArray'
|
<zippy #subSchema title="test" [headless]="true" (open)="lazySchema.load()">
|
||||||
[nestOdd]="!nestOdd" [isRequestSchema]="isRequestSchema" [attr.nesteven]="!nestOdd">
|
<json-schema-lazy #lazySchema class="nested-schema" pointer="{{prop._pointer}}" [isArray]='prop._isArray'
|
||||||
</json-schema>
|
[nestOdd]="!nestOdd" [isRequestSchema]="isRequestSchema">
|
||||||
|
</json-schema-lazy>
|
||||||
|
</zippy>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
<tr *ngIf="schema.derived.length" class="param-wrap discriminator-wrap" [ngClass]="{'empty': derivedEmtpy}">
|
<tr *ngIf="hasDescendants" class="param-wrap discriminator-wrap">
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<div class="derived-schema" *ngFor="let derived of schema.derived" [ngClass]="{active: derived.active}">
|
<div class="derived-schema" *ngFor="let descendant of schema._descendants" [ngClass]="{active: descendant.active, empty: activeDescendant.empty}">
|
||||||
<json-schema class="discriminator-part" *ngIf="!derived.empty" [childFor]="pointer"
|
<json-schema class="discriminator-part" *ngIf="!descendant.empty" [childFor]="pointer"
|
||||||
pointer="{{derived.$ref}}" [final]="derived.final" [isRequestSchema]="isRequestSchema">
|
pointer="{{descendant.$ref}}" [final]="descendant.final" [isRequestSchema]="isRequestSchema">
|
||||||
</json-schema>
|
</json-schema>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -19,7 +19,7 @@ $array-marker-line-height: 1.5;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
json-schema.nested-schema {
|
:host.nested-schema {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -50,7 +50,7 @@ json-schema.nested-schema {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json-schema[nesteven="true"] {
|
:host[nestodd="true"] {
|
||||||
background-color: $side-menu-active-bg-color;
|
background-color: $side-menu-active-bg-color;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
@ -69,10 +69,29 @@ json-schema[nesteven="true"] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.param.complex > .param-info {
|
.zippy-content-wrap {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param.complex.expanded > .param-info {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.param.complex > .param-name .param-name-content {
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param.complex > .param-name svg {
|
||||||
|
height: 1.2em;
|
||||||
|
vertical-align: middle;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param.complex.expanded > .param-name svg{
|
||||||
|
transform: rotateZ(-180deg);
|
||||||
|
}
|
||||||
|
|
||||||
.param.additional > .param-name {
|
.param.additional > .param-name {
|
||||||
color: rgba($black, 0.4);
|
color: rgba($black, 0.4);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +146,6 @@ json-schema[nesteven="true"] {
|
||||||
.param.discriminator {
|
.param.discriminator {
|
||||||
> .param-info {
|
> .param-info {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
border-bottom: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> .param-name:after {
|
> .param-name:after {
|
||||||
|
|
|
@ -13,16 +13,16 @@ import { getChildDebugElement } from '../../../tests/helpers';
|
||||||
|
|
||||||
|
|
||||||
import { JsonSchema } from './json-schema';
|
import { JsonSchema } from './json-schema';
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';;
|
import { SpecManager } from '../../utils/SpecManager';;
|
||||||
|
|
||||||
describe('Redoc components', () => {
|
describe('Redoc components', () => {
|
||||||
describe('JsonSchema Component', () => {
|
describe('JsonSchema Component', () => {
|
||||||
let builder;
|
let builder;
|
||||||
let component;
|
let component;
|
||||||
let schemaMgr = new SchemaManager();
|
let specMgr = new SpecManager();
|
||||||
let fixture;
|
let fixture;
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: schemaMgr})
|
provide(SpecManager, {useValue: specMgr})
|
||||||
]);
|
]);
|
||||||
beforeEach(inject([TestComponentBuilder], (tcb) => {
|
beforeEach(inject([TestComponentBuilder], (tcb) => {
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
|
@ -38,25 +38,25 @@ describe('Redoc components', () => {
|
||||||
|
|
||||||
it('should init component', () => {
|
it('should init component', () => {
|
||||||
component.pointer = '';
|
component.pointer = '';
|
||||||
(<any>schemaMgr)._schema = {type: 'object'};
|
(<any>specMgr)._schema = {type: 'object'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(component).not.toBeNull();
|
expect(component).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set isTrivial for non-object/array types', () => {
|
it('should set isTrivial for non-object/array types', () => {
|
||||||
component.pointer = '';
|
component.pointer = '';
|
||||||
(<any>schemaMgr)._schema = {type: 'string'};
|
(<any>specMgr)._schema = {type: 'string'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.schema.isTrivial.should.be.true();
|
component.schema.isTrivial.should.be.true();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use < * > notation for prop without type', () => {
|
it('should use < * > notation for prop without type', () => {
|
||||||
component.pointer = '';
|
component.pointer = '';
|
||||||
(<any>schemaMgr)._schema = {type: 'object', properties: {
|
(<any>specMgr)._schema = {type: 'object', properties: {
|
||||||
test: {}
|
test: {}
|
||||||
}};
|
}};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.schema.properties[0]._displayType.should.be.equal('< * >');
|
component.schema._properties[0]._displayType.should.be.equal('< * >');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -66,7 +66,7 @@ describe('Redoc components', () => {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'test-app',
|
selector: 'test-app',
|
||||||
directives: [JsonSchema],
|
directives: [JsonSchema],
|
||||||
providers: [SchemaManager],
|
providers: [SpecManager],
|
||||||
template:
|
template:
|
||||||
`<json-schema></json-schema>`
|
`<json-schema></json-schema>`
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,285 +1,96 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { ElementRef, Input } from '@angular/core';
|
import { Input, Renderer, ElementRef, forwardRef } from '@angular/core';
|
||||||
|
|
||||||
import { RedocComponent, BaseComponent, SchemaManager } from '../base';
|
import { RedocComponent, BaseComponent, SpecManager } from '../base';
|
||||||
import { DropDown } from '../../shared/components/index';
|
import { DropDown } from '../../shared/components/index';
|
||||||
import JsonPointer from '../../utils/JsonPointer';
|
import { SchemaNormalizer, SchemaHelper } from '../../services/index';
|
||||||
|
import { JsonSchemaLazy } from './json-schema-lazy';
|
||||||
|
import { Zippy } from '../../shared/components/Zippy/zippy';
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
selector: 'json-schema',
|
selector: 'json-schema',
|
||||||
templateUrl: './json-schema.html',
|
templateUrl: './json-schema.html',
|
||||||
styleUrls: ['./json-schema.css'],
|
styleUrls: ['./json-schema.css'],
|
||||||
directives: [JsonSchema, DropDown],
|
directives: [JsonSchema, DropDown, forwardRef(() => JsonSchemaLazy), Zippy],
|
||||||
detect: true
|
detect: true
|
||||||
})
|
})
|
||||||
export class JsonSchema extends BaseComponent {
|
export class JsonSchema extends BaseComponent {
|
||||||
$element: any;
|
|
||||||
schema: any;
|
schema: any;
|
||||||
derivedEmtpy: boolean;
|
activeDescendant:any = {};
|
||||||
|
hasDescendants: boolean = false;
|
||||||
|
_hasSubSchemas: boolean = false;
|
||||||
@Input() isArray: boolean;
|
@Input() isArray: boolean;
|
||||||
@Input() final: boolean = false;
|
@Input() final: boolean = false;
|
||||||
@Input() nestOdd: boolean;
|
@Input() nestOdd: boolean;
|
||||||
@Input() childFor: string;
|
@Input() childFor: string;
|
||||||
@Input() isRequestSchema: boolean;
|
@Input() isRequestSchema: boolean;
|
||||||
|
normalizer: SchemaNormalizer;
|
||||||
|
|
||||||
static injectPropertyData(propertySchema, propertyName, propPointer, hostPointer?) {
|
constructor(specMgr:SpecManager, private _renderer: Renderer, private _elementRef: ElementRef) {
|
||||||
propertySchema = Object.assign({}, propertySchema);
|
super(specMgr);
|
||||||
|
this.normalizer = new SchemaNormalizer(specMgr);
|
||||||
propertySchema._name = propertyName;
|
|
||||||
runInjectors(propertySchema, propertySchema, propPointer, hostPointer);
|
|
||||||
|
|
||||||
return propertySchema;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(schemaMgr:SchemaManager, elementRef:ElementRef) {
|
get normPointer() {
|
||||||
super(schemaMgr);
|
return this.schema._pointer || this.pointer;
|
||||||
this.$element = elementRef.nativeElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selectDerived(subClassIdx) {
|
selectDescendant(idx) {
|
||||||
let subClass = this.schema.derived[subClassIdx];
|
let activeDescendant = this.schema._descendants[idx];
|
||||||
if (!subClass || subClass.active) return;
|
if (!activeDescendant || activeDescendant.active) return;
|
||||||
this.schema.derived.forEach((subSchema) => {
|
this.schema._descendants.forEach(subSchema => {
|
||||||
subSchema.active = false;
|
subSchema.active = false;
|
||||||
});
|
});
|
||||||
subClass.active = true;
|
activeDescendant.active = true;
|
||||||
this.derivedEmtpy = false;
|
this.activeDescendant = activeDescendant;
|
||||||
if (subClass.empty) this.derivedEmtpy = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unwrapArray(schema) {
|
initDescendants() {
|
||||||
var res = schema;
|
if (!this.schema._descendants || !this.schema._descendants.length) {
|
||||||
if (schema && schema.type === 'array') {
|
return;
|
||||||
let ptr = schema.items._pointer
|
|
||||||
|| JsonPointer.join(schema._pointer || this.pointer, ['items']);
|
|
||||||
res = schema.items;
|
|
||||||
res._isArray = true;
|
|
||||||
res._pointer = ptr;
|
|
||||||
res = this.unwrapArray(res);
|
|
||||||
}
|
}
|
||||||
return res;
|
this.hasDescendants = true;
|
||||||
}
|
let enumArr = this.schema._properties[this.schema._properties.length - 1].enum;
|
||||||
|
|
||||||
prepareModel() {
|
|
||||||
if (!this.componentSchema) {
|
|
||||||
throw new Error(`Can't load component schema at ${this.pointer}`);
|
|
||||||
}
|
|
||||||
this.dereference();
|
|
||||||
|
|
||||||
let schema = this.componentSchema;
|
|
||||||
BaseComponent.joinAllOf(schema, {omitParent: true});
|
|
||||||
this.schema = schema = this.unwrapArray(schema);
|
|
||||||
runInjectors(schema, schema, schema._pointer || this.pointer, this.pointer);
|
|
||||||
|
|
||||||
schema.derived = schema.derived || [];
|
|
||||||
|
|
||||||
if (!schema.isTrivial) {
|
|
||||||
this.prepareObjectPropertiesData(schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.initDerived();
|
|
||||||
}
|
|
||||||
|
|
||||||
initDerived() {
|
|
||||||
if (!this.schema.derived.length) return;
|
|
||||||
let enumArr = this.schema.properties[this.schema.properties.length - 1].enum;
|
|
||||||
if (enumArr) {
|
if (enumArr) {
|
||||||
let enumOrder = {};
|
let enumOrder = {};
|
||||||
enumArr.forEach((enumItem, idx) => {
|
enumArr.forEach((enumItem, idx) => {
|
||||||
enumOrder[enumItem.val] = idx;
|
enumOrder[enumItem.val] = idx;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.schema.derived.sort((a, b) => {
|
this.schema._descendants.sort((a, b) => {
|
||||||
return enumOrder[a.name] > enumOrder[b.name];
|
return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.selectDerived(0);
|
this.selectDescendant(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareObjectPropertiesData(schema) {
|
prepareModel() {
|
||||||
let requiredMap = {};
|
if (this.nestOdd) {
|
||||||
if (schema.required) {
|
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'nestodd', 'true');
|
||||||
schema.required.forEach(prop => requiredMap[prop] = true);
|
}
|
||||||
|
this.schema = this.componentSchema;
|
||||||
|
if (!this.schema) {
|
||||||
|
throw new Error(`Can't load component schema at ${this.pointer}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let discriminatorFieldIdx = -1;
|
this.schema = this.normalizer.normalize(this.schema, this.normPointer);
|
||||||
let props = schema.properties && Object.keys(schema.properties).map((prop, idx) => {
|
this.schema = SchemaHelper.unwrapArray(this.schema, this.normPointer);
|
||||||
let propertySchema = schema.properties[prop];
|
SchemaHelper.preprocess(this.schema, this.normPointer, this.pointer);
|
||||||
let propPointer = propertySchema._pointer ||
|
|
||||||
JsonPointer.join(schema._pointer || this.pointer, ['properties', prop]);
|
|
||||||
propertySchema = JsonSchema.injectPropertyData(propertySchema, prop, propPointer);
|
|
||||||
// stop endless discriminator recursion
|
|
||||||
if (propertySchema._pointer === this.childFor) {
|
|
||||||
propertySchema._pointer = null;
|
|
||||||
}
|
|
||||||
propertySchema.required = !!requiredMap[prop];
|
|
||||||
propertySchema.isDiscriminator = (schema.discriminator === prop);
|
|
||||||
if (propertySchema.isDiscriminator) {
|
|
||||||
discriminatorFieldIdx = idx;
|
|
||||||
}
|
|
||||||
return propertySchema;
|
|
||||||
});
|
|
||||||
|
|
||||||
props = props || [];
|
if (!this.schema.isTrivial) {
|
||||||
|
SchemaHelper.preprocessProperties(this.schema, this.normPointer, {
|
||||||
if (schema.additionalProperties && schema.additionalProperties !== false) {
|
childFor: this.childFor,
|
||||||
let propsSchema = this.prepareAdditionalProperties(schema);
|
skipReadOnly: this.isRequestSchema
|
||||||
propsSchema._additional = true;
|
});
|
||||||
props.push(propsSchema);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move discriminator field to the end of properties list
|
this.initDescendants();
|
||||||
if (discriminatorFieldIdx > -1) {
|
this._hasSubSchemas = this.schema._properties && this.schema._properties.some(
|
||||||
let discrProp = props.splice(discriminatorFieldIdx, 1);
|
propSchema => propSchema.type === 'object' && propSchema._pointer);
|
||||||
props.push(discrProp[0]);
|
|
||||||
}
|
|
||||||
// filter readOnly props for request schemas
|
|
||||||
if (this.isRequestSchema) {
|
|
||||||
props = props.filter(prop => !prop.readOnly);
|
|
||||||
}
|
|
||||||
schema.properties = props;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareAdditionalProperties(schema) {
|
trackByName(index: number, item: any): string {
|
||||||
var addProps = schema.additionalProperties;
|
return item['name'];
|
||||||
return JsonSchema.injectPropertyData(addProps, '<Additional Properties> *',
|
|
||||||
JsonPointer.join(addProps._pointer || schema._pointer || this.pointer, ['additionalProperties']));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const injectors = {
|
|
||||||
general: {
|
|
||||||
check: () => true,
|
|
||||||
inject: (injectTo, propertySchema, pointer) => {
|
|
||||||
injectTo._pointer = propertySchema._pointer || pointer;
|
|
||||||
injectTo._displayType = propertySchema.type;
|
|
||||||
if (propertySchema.format) injectTo._displayFormat = `<${propertySchema.format}>`;
|
|
||||||
if (propertySchema.enum) {
|
|
||||||
injectTo.enum = propertySchema.enum.map((value) => {
|
|
||||||
return {val: value, type: typeof value};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
discriminator: {
|
|
||||||
check: (propertySchema) => propertySchema.discriminator,
|
|
||||||
inject: (injectTo, propertySchema = injectTo, pointer) => {
|
|
||||||
injectTo.derived = SchemaManager.instance().findDerivedDefinitions(pointer);
|
|
||||||
injectTo.discriminator = propertySchema.discriminator;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
array: {
|
|
||||||
check: (propertySchema) => {
|
|
||||||
return propertySchema.type === 'array';
|
|
||||||
},
|
|
||||||
inject: (injectTo, propertySchema = injectTo, propPointer) => {
|
|
||||||
injectTo._isArray = true;
|
|
||||||
injectTo._pointer = propertySchema.items._pointer
|
|
||||||
|| JsonPointer.join(propertySchema._pointer || propPointer, ['items']);
|
|
||||||
|
|
||||||
runInjectors(injectTo, propertySchema.items, propPointer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
object: {
|
|
||||||
check: (propertySchema) => {
|
|
||||||
return propertySchema.type === 'object' && propertySchema.properties;
|
|
||||||
},
|
|
||||||
inject: (injectTo, propertySchema = injectTo) => {
|
|
||||||
let baseName = propertySchema._pointer && JsonPointer.baseName(propertySchema._pointer);
|
|
||||||
injectTo._displayType = propertySchema.title || baseName || 'object';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
noType: {
|
|
||||||
check: (propertySchema) => !propertySchema.type,
|
|
||||||
inject: (injectTo) => {
|
|
||||||
injectTo._displayType = '< * >';
|
|
||||||
injectTo._displayTypeHint = 'This field may contain data of any type';
|
|
||||||
injectTo.isTrivial = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
simpleType: {
|
|
||||||
check: (propertySchema) => {
|
|
||||||
if (propertySchema.type === 'object') {
|
|
||||||
return (!propertySchema.properties || !Object.keys(propertySchema.properties).length)
|
|
||||||
&& (typeof propertySchema.additionalProperties !== 'object');
|
|
||||||
}
|
|
||||||
return (propertySchema.type !== 'array') && propertySchema.type;
|
|
||||||
},
|
|
||||||
inject: (injectTo, propertySchema = injectTo) => {
|
|
||||||
injectTo.isTrivial = true;
|
|
||||||
if (injectTo._pointer) {
|
|
||||||
injectTo._pointer = undefined;
|
|
||||||
injectTo._displayType = propertySchema.title ?
|
|
||||||
`${propertySchema.title} (${propertySchema.type})` : propertySchema.type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
integer: {
|
|
||||||
check: (propertySchema) => (propertySchema.type === 'integer' || propertySchema.type === 'number'),
|
|
||||||
inject: (injectTo, propertySchema = injectTo) => {
|
|
||||||
var range = '';
|
|
||||||
if (propertySchema.minimum && propertySchema.maximum) {
|
|
||||||
range += propertySchema.exclusiveMinimum ? '( ' : '[ ';
|
|
||||||
range += propertySchema.minimum;
|
|
||||||
range += ' .. ';
|
|
||||||
range += propertySchema.maximum;
|
|
||||||
range += propertySchema.exclusiveMaximum ? ' )' : ' ]';
|
|
||||||
} else if (propertySchema.maximum) {
|
|
||||||
range += propertySchema.exclusiveMaximum? '< ' : '<= ';
|
|
||||||
range += propertySchema.maximum;
|
|
||||||
} else if (propertySchema.minimum) {
|
|
||||||
range += propertySchema.exclusiveMinimum ? '> ' : '>= ';
|
|
||||||
range += propertySchema.minimum;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (range) {
|
|
||||||
injectTo._range = range;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
string: {
|
|
||||||
check: propertySchema => (propertySchema.type === 'string'),
|
|
||||||
inject: (injectTo, propertySchema = injectTo) => {
|
|
||||||
var range;
|
|
||||||
if (propertySchema.minLength && propertySchema.maxLength) {
|
|
||||||
range = `[ ${propertySchema.minLength} .. ${propertySchema.maxLength} ]`;
|
|
||||||
} else if (propertySchema.maxLength) {
|
|
||||||
range = '<= ' + propertySchema.maxLength;
|
|
||||||
} else if (propertySchema.minimum) {
|
|
||||||
range = '>= ' + propertySchema.minLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (range) {
|
|
||||||
injectTo._range = range + ' characters';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
check: propertySchema => (propertySchema.type === 'file'),
|
|
||||||
inject: (injectTo, propertySchema = injectTo, propPointer, hostPointer) => {
|
|
||||||
injectTo.isFile = true;
|
|
||||||
let parentPtr;
|
|
||||||
if (propertySchema.in === 'formData') {
|
|
||||||
parentPtr = JsonPointer.dirName(hostPointer, 1);
|
|
||||||
} else {
|
|
||||||
parentPtr = JsonPointer.dirName(hostPointer, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
let parentParam = SchemaManager.instance().byPointer(parentPtr);
|
|
||||||
let root = SchemaManager.instance().schema;
|
|
||||||
injectTo._produces = parentParam && parentParam.produces || root.produces;
|
|
||||||
injectTo._consumes = parentParam && parentParam.consumes || root.consumes;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function runInjectors(injectTo, propertySchema, propertyPointer, hostPointer?) {
|
|
||||||
for (var injName in injectors) {
|
|
||||||
let injector = injectors[injName];
|
|
||||||
if (injector.check(propertySchema)) {
|
|
||||||
injector.inject(injectTo, propertySchema, propertyPointer, hostPointer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<a *ngFor="let tag of data.methodInfo.tags" attr.href="#{{tag}}"> {{tag}} </a>
|
<a *ngFor="let tag of data.methodInfo.tags" attr.href="#{{tag}}"> {{tag}} </a>
|
||||||
</div>
|
</div>
|
||||||
<p *ngIf="data.methodInfo.description" class="method-description"
|
<p *ngIf="data.methodInfo.description" class="method-description"
|
||||||
innerHtml="{{data.methodInfo.description | marked}}">
|
[innerHtml]="data.methodInfo.description | marked">
|
||||||
</p>
|
</p>
|
||||||
<params-list pointer="{{pointer}}/parameters"> </params-list>
|
<params-list pointer="{{pointer}}/parameters"> </params-list>
|
||||||
<responses-list pointer="{{pointer}}/responses"> </responses-list>
|
<responses-list pointer="{{pointer}}/responses"> </responses-list>
|
||||||
|
|
|
@ -13,18 +13,18 @@ import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
import { getChildDebugElement } from '../../../tests/helpers';
|
import { getChildDebugElement } from '../../../tests/helpers';
|
||||||
|
|
||||||
import { Method } from './method';
|
import { Method } from './method';
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';;
|
import { SpecManager } from '../../utils/SpecManager';;
|
||||||
|
|
||||||
describe('Redoc components', () => {
|
describe('Redoc components', () => {
|
||||||
describe('Method Component', () => {
|
describe('Method Component', () => {
|
||||||
let builder;
|
let builder;
|
||||||
let component;
|
let component;
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: new SchemaManager()})
|
provide(SpecManager, {useValue: new SpecManager()})
|
||||||
]);
|
]);
|
||||||
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
|
beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, specMgr) => {
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
return schemaMgr.load('/tests/schemas/extended-petstore.yml');
|
return specMgr.load('/tests/schemas/extended-petstore.yml');
|
||||||
})));
|
})));
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
builder.createAsync(TestAppComponent).then(fixture => {
|
builder.createAsync(TestAppComponent).then(fixture => {
|
||||||
|
@ -57,7 +57,7 @@ describe('Redoc components', () => {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'test-app',
|
selector: 'test-app',
|
||||||
directives: [Method],
|
directives: [Method],
|
||||||
providers: [SchemaManager],
|
providers: [SpecManager],
|
||||||
template:
|
template:
|
||||||
`<method pointer='#/paths/~1user~1{username}/put'></method>`
|
`<method pointer='#/paths/~1user~1{username}/put'></method>`
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
import { Input } from '@angular/core';
|
import { Input } from '@angular/core';
|
||||||
import JsonPointer from '../../utils/JsonPointer';
|
import JsonPointer from '../../utils/JsonPointer';
|
||||||
import { RedocComponent, BaseComponent, SchemaManager} from '../base';
|
import { RedocComponent, BaseComponent, SpecManager} from '../base';
|
||||||
|
|
||||||
import { ParamsList } from '../ParamsList/params-list';
|
import { ParamsList } from '../ParamsList/params-list';
|
||||||
import { ResponsesList } from '../ResponsesList/responses-list';
|
import { ResponsesList } from '../ResponsesList/responses-list';
|
||||||
|
@ -19,13 +19,13 @@ import { RequestSamples } from '../RequestSamples/request-samples';
|
||||||
export class Method extends BaseComponent {
|
export class Method extends BaseComponent {
|
||||||
data:any;
|
data:any;
|
||||||
@Input() tag:string;
|
@Input() tag:string;
|
||||||
constructor(schemaMgr:SchemaManager) {
|
constructor(specMgr:SpecManager) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareModel() {
|
prepareModel() {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
this.data.apiUrl = this.schemaMgr.apiUrl;
|
this.data.apiUrl = this.specMgr.apiUrl;
|
||||||
this.data.httpMethod = JsonPointer.baseName(this.pointer);
|
this.data.httpMethod = JsonPointer.baseName(this.pointer);
|
||||||
this.data.path = JsonPointer.baseName(this.pointer, 2);
|
this.data.path = JsonPointer.baseName(this.pointer, 2);
|
||||||
this.data.methodInfo = this.componentSchema;
|
this.data.methodInfo = this.componentSchema;
|
||||||
|
@ -39,13 +39,13 @@ export class Method extends BaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
filterMainTags(tags) {
|
filterMainTags(tags) {
|
||||||
var tagsMap = this.schemaMgr.getTagsMap();
|
var tagsMap = this.specMgr.getTagsMap();
|
||||||
if (!tags) return [];
|
if (!tags) return [];
|
||||||
return tags.filter(tag => tagsMap[tag] && tagsMap[tag]['x-traitTag']);
|
return tags.filter(tag => tagsMap[tag] && tagsMap[tag]['x-traitTag']);
|
||||||
}
|
}
|
||||||
|
|
||||||
findBodyParam() {
|
findBodyParam() {
|
||||||
let pathParams = this.schemaMgr.getMethodParams(this.pointer, true);
|
let pathParams = this.specMgr.getMethodParams(this.pointer, true);
|
||||||
let bodyParam = pathParams.find(param => param.in === 'body');
|
let bodyParam = pathParams.find(param => param.in === 'body');
|
||||||
return bodyParam;
|
return bodyParam;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="tag" *ngFor="let tag of data.tags">
|
<div class="tag" *ngFor="let tag of data.tags">
|
||||||
<div class="tag-info" [attr.tag]="tag.name">
|
<div class="tag-info" [attr.tag]="tag.name">
|
||||||
<h1 class="sharable-header"> <a class="share-link" href="#tag/{{tag.name | encodeURIComponent}}"></a>{{tag.name}} </h1>
|
<h1 class="sharable-header"> <a class="share-link" href="#tag/{{tag.name | encodeURIComponent}}"></a>{{tag.name}} </h1>
|
||||||
<p *ngIf="tag.description" innerHtml="{{ tag.description | marked }}"> </p>
|
<p *ngIf="tag.description" [innerHtml]="tag.description | marked"> </p>
|
||||||
</div>
|
</div>
|
||||||
<method *ngFor="let method of tag.methods" [pointer]="method.pointer" [attr.pointer]="method.pointer"
|
<method *ngFor="let method of tag.methods" [pointer]="method.pointer" [attr.pointer]="method.pointer"
|
||||||
[attr.tag]="method.tag" [tag]="method.tag" [attr.operation-id]="method.operationId"></method>
|
[attr.tag]="method.tag" [tag]="method.tag" [attr.operation-id]="method.operationId"></method>
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { getChildDebugElement } from '../../../tests/helpers';
|
||||||
|
|
||||||
|
|
||||||
import { MethodsList } from './methods-list';
|
import { MethodsList } from './methods-list';
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';
|
import { SpecManager } from '../../utils/SpecManager';
|
||||||
|
|
||||||
describe('Redoc components', () => {
|
describe('Redoc components', () => {
|
||||||
describe('MethodsList Component', () => {
|
describe('MethodsList Component', () => {
|
||||||
|
@ -22,11 +22,11 @@ describe('Redoc components', () => {
|
||||||
let component;
|
let component;
|
||||||
let fixture;
|
let fixture;
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: new SchemaManager()})
|
provide(SpecManager, {useValue: new SpecManager()})
|
||||||
]);
|
]);
|
||||||
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
|
beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, specMgr) => {
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
return schemaMgr.load('/tests/schemas/methods-list-component.json');
|
return specMgr.load('/tests/schemas/methods-list-component.json');
|
||||||
})));
|
})));
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
builder.createAsync(TestAppComponent).then(_fixture => {
|
builder.createAsync(TestAppComponent).then(_fixture => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { forwardRef } from '@angular/core';
|
import { forwardRef } from '@angular/core';
|
||||||
import { RedocComponent, BaseComponent, SchemaManager } from '../base';
|
import { RedocComponent, BaseComponent, SpecManager } from '../base';
|
||||||
import { Method } from '../Method/method';
|
import { Method } from '../Method/method';
|
||||||
import { EncodeURIComponentPipe } from '../../utils/pipes';
|
import { EncodeURIComponentPipe } from '../../utils/pipes';
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ import { EncodeURIComponentPipe } from '../../utils/pipes';
|
||||||
})
|
})
|
||||||
export class MethodsList extends BaseComponent {
|
export class MethodsList extends BaseComponent {
|
||||||
data:any;
|
data:any;
|
||||||
constructor(schemaMgr:SchemaManager) {
|
constructor(specMgr:SpecManager) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareModel() {
|
prepareModel() {
|
||||||
|
@ -24,7 +24,7 @@ export class MethodsList extends BaseComponent {
|
||||||
// follow SwaggerUI behavior for cases when one method has more than one tag:
|
// follow SwaggerUI behavior for cases when one method has more than one tag:
|
||||||
// duplicate methods
|
// duplicate methods
|
||||||
|
|
||||||
let menuStructure = this.schemaMgr.buildMenuTree();
|
let menuStructure = this.specMgr.buildMenuTree();
|
||||||
let tags = Array.from<any>(menuStructure.entries())
|
let tags = Array.from<any>(menuStructure.entries())
|
||||||
.map((entry) => {
|
.map((entry) => {
|
||||||
let [tag, {description, methods}] = entry;
|
let [tag, {description, methods}] = entry;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="params-wrap">
|
<div class="params-wrap">
|
||||||
<div *ngFor="let param of paramType.params" class="param">
|
<div *ngFor="let param of paramType.params" class="param">
|
||||||
<div class="param-name">
|
<div class="param-name">
|
||||||
<span class="param-name-content"> {{param.name}} </span>
|
<span class="param-name-wrap"> {{param.name}} </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-info">
|
<div class="param-info">
|
||||||
<div>
|
<div>
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
<span *ngFor="let enumItem of param.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
|
<span *ngFor="let enumItem of param.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-description" innerHtml="{{param.description | marked}}"></div>
|
<div class="param-description" [innerHtml]="param.description | marked"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
<div *ngIf="data.bodyParam">
|
<div *ngIf="data.bodyParam">
|
||||||
<h5 class="param-list-header" *ngIf="data.bodyParam"> Request Body </h5>
|
<h5 class="param-list-header" *ngIf="data.bodyParam"> Request Body </h5>
|
||||||
|
|
||||||
<div class="body-param-description" innerHtml="{{data.bodyParam.description | marked}}"></div>
|
<div class="body-param-description" [innerHtml]="data.bodyParam.description | marked"></div>
|
||||||
<div>
|
<div>
|
||||||
<br>
|
<br>
|
||||||
<json-schema-lazy [isRequestSchema]="true" [auto]="true" pointer="{{data.bodyParam.pointer}}/schema">
|
<json-schema-lazy [isRequestSchema]="true" [auto]="true" pointer="{{data.bodyParam.pointer}}/schema">
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { RedocComponent, BaseComponent, SchemaManager } from '../base';
|
import { RedocComponent, BaseComponent, SpecManager } from '../base';
|
||||||
import { JsonSchema } from '../JsonSchema/json-schema';
|
import { JsonSchema } from '../JsonSchema/json-schema';
|
||||||
import {JsonSchemaLazy} from '../JsonSchema/json-schema-lazy';
|
import { JsonSchemaLazy } from '../JsonSchema/json-schema-lazy';
|
||||||
|
import { SchemaHelper } from '../../services/schema-helper.service';
|
||||||
|
|
||||||
function safePush(obj, prop, item) {
|
function safePush(obj, prop, item) {
|
||||||
if (!obj[prop]) obj[prop] = [];
|
if (!obj[prop]) obj[prop] = [];
|
||||||
|
@ -19,18 +20,19 @@ export class ParamsList extends BaseComponent {
|
||||||
|
|
||||||
data:any;
|
data:any;
|
||||||
|
|
||||||
constructor(schemaMgr:SchemaManager) {
|
constructor(specMgr:SpecManager) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareModel() {
|
prepareModel() {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
let paramsList = this.schemaMgr.getMethodParams(this.pointer, true);
|
let paramsList = this.specMgr.getMethodParams(this.pointer, true);
|
||||||
|
|
||||||
paramsList = paramsList.map((paramData) => {
|
paramsList = paramsList.map(paramSchema => {
|
||||||
let propPointer = paramData._pointer;
|
let propPointer = paramSchema._pointer;
|
||||||
if (paramData.in === 'body') return paramData;
|
if (paramSchema.in === 'body') return paramSchema;
|
||||||
return JsonSchema.injectPropertyData(paramData, paramData.name, propPointer, this.pointer);
|
paramSchema._name = paramSchema.name;
|
||||||
|
return SchemaHelper.preprocess(paramSchema,propPointer, this.pointer);
|
||||||
});
|
});
|
||||||
|
|
||||||
let paramsMap = this.orderParams(paramsList);
|
let paramsMap = this.orderParams(paramsList);
|
||||||
|
|
|
@ -160,7 +160,8 @@ footer {
|
||||||
pre {
|
pre {
|
||||||
font-family: Courier, monospace;
|
font-family: Courier, monospace;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
background-color: rgba(38,50,56,0.04);
|
background-color: #263238;
|
||||||
|
color: white;
|
||||||
padding: 12px 14px 15px 14px;
|
padding: 12px 14px 15px 14px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
import { TestComponentBuilder } from '@angular/compiler/testing';
|
import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
|
|
||||||
import { Redoc } from './redoc';
|
import { Redoc } from './redoc';
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';
|
import { SpecManager } from '../../utils/SpecManager';
|
||||||
import { OptionsService } from '../../services/index';
|
import { OptionsService } from '../../services/index';
|
||||||
|
|
||||||
let optsMgr:OptionsService;
|
let optsMgr:OptionsService;
|
||||||
|
@ -23,19 +23,19 @@ let optsMgr:OptionsService;
|
||||||
describe('Redoc components', () => {
|
describe('Redoc components', () => {
|
||||||
describe('Redoc Component', () => {
|
describe('Redoc Component', () => {
|
||||||
let builder;
|
let builder;
|
||||||
let schemaMgr;
|
let specMgr;
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: new SchemaManager()}),
|
provide(SpecManager, {useValue: new SpecManager()}),
|
||||||
]);
|
]);
|
||||||
beforeEach(async(inject([TestComponentBuilder, SchemaManager, OptionsService],
|
beforeEach(async(inject([TestComponentBuilder, SpecManager, OptionsService],
|
||||||
(tcb, _schemaMgr, _optsMgr) => {
|
(tcb, _specMgr, _optsMgr) => {
|
||||||
optsMgr = _optsMgr;
|
optsMgr = _optsMgr;
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
schemaMgr = _schemaMgr;
|
specMgr = _specMgr;
|
||||||
})));
|
})));
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
return schemaMgr.load('/tests/schemas/extended-petstore.yml')
|
return specMgr.load('/tests/schemas/extended-petstore.yml')
|
||||||
.then(() => done())
|
.then(() => done())
|
||||||
.catch(err => done.fail(err));
|
.catch(err => done.fail(err));
|
||||||
});
|
});
|
||||||
|
@ -106,13 +106,13 @@ describe('Redoc components', () => {
|
||||||
let destroySpy;
|
let destroySpy;
|
||||||
let dom = new BrowserDomAdapter();
|
let dom = new BrowserDomAdapter();
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: new SchemaManager()}),
|
provide(SpecManager, {useValue: new SpecManager()}),
|
||||||
provide(BrowserDomAdapter, {useValue: new BrowserDomAdapter()}),
|
provide(BrowserDomAdapter, {useValue: new BrowserDomAdapter()}),
|
||||||
provide(OptionsService, {useValue: optsMgr})
|
provide(OptionsService, {useValue: optsMgr})
|
||||||
]);
|
]);
|
||||||
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
|
beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, specMgr) => {
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
return schemaMgr.load('/tests/schemas/methods-list-component.json');
|
return specMgr.load('/tests/schemas/methods-list-component.json');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
|
|
|
@ -14,9 +14,8 @@ import { MethodsList } from '../MethodsList/methods-list';
|
||||||
import { SideMenu } from '../SideMenu/side-menu';
|
import { SideMenu } from '../SideMenu/side-menu';
|
||||||
|
|
||||||
import { StickySidebar } from '../../shared/components/index';
|
import { StickySidebar } from '../../shared/components/index';
|
||||||
import {SchemaManager} from '../../utils/SchemaManager';
|
import {SpecManager} from '../../utils/SpecManager';
|
||||||
import { OptionsService, RedocEventsService } from '../../services/index';
|
import { OptionsService, RedocEventsService } from '../../services/index';
|
||||||
//import redocVersion from '../../version.js';
|
|
||||||
|
|
||||||
var dom = new BrowserDomAdapter();
|
var dom = new BrowserDomAdapter();
|
||||||
var _modeLocked = false;
|
var _modeLocked = false;
|
||||||
|
@ -24,7 +23,7 @@ var _modeLocked = false;
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
selector: 'redoc',
|
selector: 'redoc',
|
||||||
providers: [
|
providers: [
|
||||||
SchemaManager,
|
SpecManager,
|
||||||
BrowserDomAdapter,
|
BrowserDomAdapter,
|
||||||
RedocEventsService
|
RedocEventsService
|
||||||
],
|
],
|
||||||
|
@ -48,6 +47,7 @@ export class Redoc extends BaseComponent implements AfterViewInit {
|
||||||
|
|
||||||
static hideLoadingAnimation() {
|
static hideLoadingAnimation() {
|
||||||
let redocEl = dom.query('redoc');
|
let redocEl = dom.query('redoc');
|
||||||
|
if (!redocEl) return;
|
||||||
dom.addClass(redocEl, 'loading-remove');
|
dom.addClass(redocEl, 'loading-remove');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dom.removeClass(redocEl, 'loading-remove');
|
dom.removeClass(redocEl, 'loading-remove');
|
||||||
|
@ -67,7 +67,7 @@ export class Redoc extends BaseComponent implements AfterViewInit {
|
||||||
Redoc.destroy();
|
Redoc.destroy();
|
||||||
}
|
}
|
||||||
Redoc.showLoadingAnimation();
|
Redoc.showLoadingAnimation();
|
||||||
return SchemaManager.instance().load(specUrl)
|
return SpecManager.instance().load(specUrl)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (!_modeLocked && !optionsService.options.debugMode) {
|
if (!_modeLocked && !optionsService.options.debugMode) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
|
@ -99,6 +99,7 @@ export class Redoc extends BaseComponent implements AfterViewInit {
|
||||||
static displayError(err) {
|
static displayError(err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
let redocEl = dom.query('redoc');
|
let redocEl = dom.query('redoc');
|
||||||
|
if (!redocEl) return;
|
||||||
let heading = 'Oops... ReDoc failed to render this spec';
|
let heading = 'Oops... ReDoc failed to render this spec';
|
||||||
let details = err.message;
|
let details = err.message;
|
||||||
let erroHtml = `<div class="redoc-error">
|
let erroHtml = `<div class="redoc-error">
|
||||||
|
@ -107,10 +108,6 @@ export class Redoc extends BaseComponent implements AfterViewInit {
|
||||||
redocEl.innerHTML = erroHtml;
|
redocEl.innerHTML = erroHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static get version() {
|
|
||||||
// return redocVersion;
|
|
||||||
// }
|
|
||||||
|
|
||||||
static destroy() {
|
static destroy() {
|
||||||
let el = dom.query('redoc');
|
let el = dom.query('redoc');
|
||||||
let elClone;
|
let elClone;
|
||||||
|
@ -133,9 +130,9 @@ export class Redoc extends BaseComponent implements AfterViewInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(schemaMgr: SchemaManager, optionsMgr:OptionsService, elementRef:ElementRef,
|
constructor(specMgr: SpecManager, optionsMgr:OptionsService, elementRef:ElementRef,
|
||||||
public events:RedocEventsService) {
|
public events:RedocEventsService) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
this.element = elementRef.nativeElement;
|
this.element = elementRef.nativeElement;
|
||||||
//parse options (top level component doesn't support inputs)
|
//parse options (top level component doesn't support inputs)
|
||||||
optionsMgr.parseOptions( this.element );
|
optionsMgr.parseOptions( this.element );
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
<schema-sample [pointer]="data.schemaPointer" [skipReadOnly]="true"> </schema-sample>
|
<schema-sample [pointer]="data.schemaPointer" [skipReadOnly]="true"> </schema-sample>
|
||||||
</tab>
|
</tab>
|
||||||
<tab *ngFor="let sample of data.samples" [tabTitle]="sample.lang">
|
<tab *ngFor="let sample of data.samples" [tabTitle]="sample.lang">
|
||||||
<pre innerHtml="{{sample.source | prism:sample.lang}}"></pre>
|
<pre [innerHtml]="sample.source | prism:sample.lang"></pre>
|
||||||
</tab>
|
</tab>
|
||||||
</tabs>
|
</tabs>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { ViewChildren, QueryList, EventEmitter, Input} from '@angular/core';
|
import { ViewChildren, QueryList, EventEmitter, Input} from '@angular/core';
|
||||||
|
|
||||||
import { RedocComponent, BaseComponent, SchemaManager } from '../base';
|
import { RedocComponent, BaseComponent, SpecManager } from '../base';
|
||||||
import JsonPointer from '../../utils/JsonPointer';
|
import JsonPointer from '../../utils/JsonPointer';
|
||||||
import { Tabs, Tab } from '../../shared/components/index';
|
import { Tabs, Tab } from '../../shared/components/index';
|
||||||
import { SchemaSample } from '../SchemaSample/schema-sample';
|
import { SchemaSample } from '../SchemaSample/schema-sample';
|
||||||
|
@ -25,8 +25,8 @@ export class RequestSamples extends BaseComponent {
|
||||||
data: any;
|
data: any;
|
||||||
@Input() schemaPointer:string;
|
@Input() schemaPointer:string;
|
||||||
@ViewChildren(Tabs) childQuery:QueryList<Tabs>;
|
@ViewChildren(Tabs) childQuery:QueryList<Tabs>;
|
||||||
constructor(schemaMgr:SchemaManager, public events:RedocEventsService) {
|
constructor(specMgr:SpecManager, public events:RedocEventsService) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
|
|
||||||
this.selectedLang = this.events.samplesLanguageChanged;
|
this.selectedLang = this.events.samplesLanguageChanged;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,12 @@
|
||||||
<div class="header-name"> {{header.name}} </div>
|
<div class="header-name"> {{header.name}} </div>
|
||||||
<div class="header-type"> {{header.type}} </div>
|
<div class="header-type"> {{header.type}} </div>
|
||||||
<div *ngIf="header.default" class="header-default"> Default: {{header.default}} </div>
|
<div *ngIf="header.default" class="header-default"> Default: {{header.default}} </div>
|
||||||
<div class="header-description" innerHtml="{{header.description | marked}}"> </div>
|
<div class="header-description" [innerHtml]="header.description | marked"> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<header *ngIf="response.headers">
|
<header *ngIf="response.headers">
|
||||||
Response Schema
|
Response Schema
|
||||||
</header>
|
</header>
|
||||||
<json-schema *ngIf="response.schema && options.disableLazySchemas" class="schema type" pointer="{{response.pointer}}/schema">
|
|
||||||
</json-schema>
|
|
||||||
<json-schema-lazy #lazySchema pointer="{{response.schema ? response.pointer + '/schema' : null}}">
|
<json-schema-lazy #lazySchema pointer="{{response.schema ? response.pointer + '/schema' : null}}">
|
||||||
</json-schema-lazy>
|
</json-schema-lazy>
|
||||||
</zippy>
|
</zippy>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
|
import {RedocComponent, BaseComponent, SpecManager} from '../base';
|
||||||
import JsonPointer from '../../utils/JsonPointer';
|
import JsonPointer from '../../utils/JsonPointer';
|
||||||
import { JsonSchema } from '../JsonSchema/json-schema';
|
import { JsonSchema } from '../JsonSchema/json-schema';
|
||||||
import { JsonSchemaLazy } from '../JsonSchema/json-schema-lazy';
|
import { JsonSchemaLazy } from '../JsonSchema/json-schema-lazy';
|
||||||
|
@ -22,8 +22,8 @@ function isNumeric(n) {
|
||||||
export class ResponsesList extends BaseComponent {
|
export class ResponsesList extends BaseComponent {
|
||||||
data: any;
|
data: any;
|
||||||
options: any;
|
options: any;
|
||||||
constructor(schemaMgr:SchemaManager, optionsMgr:OptionsService) {
|
constructor(specMgr:SpecManager, optionsMgr:OptionsService) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
this.options = optionsMgr.options;
|
this.options = optionsMgr.options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ export class ResponsesList extends BaseComponent {
|
||||||
resp.pointer = JsonPointer.join(this.pointer, respCode);
|
resp.pointer = JsonPointer.join(this.pointer, respCode);
|
||||||
if (resp.$ref) {
|
if (resp.$ref) {
|
||||||
let ref = resp.$ref;
|
let ref = resp.$ref;
|
||||||
resp = this.schemaMgr.byPointer(resp.$ref);
|
resp = this.specMgr.byPointer(resp.$ref);
|
||||||
resp.pointer = ref;
|
resp.pointer = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { forwardRef } from '@angular/core';
|
import { forwardRef } from '@angular/core';
|
||||||
import { RedocComponent, BaseComponent, SchemaManager } from '../base';
|
import { RedocComponent, BaseComponent, SpecManager } from '../base';
|
||||||
import JsonPointer from '../../utils/JsonPointer';
|
import JsonPointer from '../../utils/JsonPointer';
|
||||||
import { Tabs, Tab } from '../../shared/components/index';
|
import { Tabs, Tab } from '../../shared/components/index';
|
||||||
import { SchemaSample } from '../index';
|
import { SchemaSample } from '../index';
|
||||||
|
@ -25,8 +25,8 @@ function hasExample(response) {
|
||||||
})
|
})
|
||||||
export class ResponsesSamples extends BaseComponent {
|
export class ResponsesSamples extends BaseComponent {
|
||||||
data: any;
|
data: any;
|
||||||
constructor(schemaMgr:SchemaManager) {
|
constructor(specMgr:SpecManager) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareModel() {
|
prepareModel() {
|
||||||
|
@ -44,7 +44,7 @@ export class ResponsesSamples extends BaseComponent {
|
||||||
resp.pointer = JsonPointer.join(this.pointer, respCode);
|
resp.pointer = JsonPointer.join(this.pointer, respCode);
|
||||||
if (resp.$ref) {
|
if (resp.$ref) {
|
||||||
let ref = resp.$ref;
|
let ref = resp.$ref;
|
||||||
resp = this.schemaMgr.byPointer(resp.$ref);
|
resp = this.specMgr.byPointer(resp.$ref);
|
||||||
resp.pointer = ref;
|
resp.pointer = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="snippet">
|
<div class="snippet">
|
||||||
<!-- in case sample is not available for some reason -->
|
<!-- in case sample is not available for some reason -->
|
||||||
<pre *ngIf="data.sample == undefined"> Sample unavailable </pre>
|
<pre *ngIf="data.sample == undefined"> Sample unavailable </pre>
|
||||||
<pre innerHtml="{{data.sample | jsonFormatter}}"></pre>
|
<pre [innerHtml]="data.sample | jsonFormatter"></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { ElementRef, Input } from '@angular/core';
|
||||||
|
|
||||||
import * as OpenAPISampler from 'openapi-sampler';
|
import * as OpenAPISampler from 'openapi-sampler';
|
||||||
|
|
||||||
import { RedocComponent, BaseComponent, SchemaManager } from '../base';
|
import { RedocComponent, BaseComponent, SpecManager } from '../base';
|
||||||
import { JsonFormatter } from '../../utils/JsonFormatterPipe';
|
import { JsonFormatter } from '../../utils/JsonFormatterPipe';
|
||||||
|
import { SchemaNormalizer } from '../../services/schema-normalizer.service';
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
selector: 'schema-sample',
|
selector: 'schema-sample',
|
||||||
|
@ -17,12 +18,17 @@ export class SchemaSample extends BaseComponent {
|
||||||
element: any;
|
element: any;
|
||||||
data: any;
|
data: any;
|
||||||
@Input() skipReadOnly:boolean;
|
@Input() skipReadOnly:boolean;
|
||||||
constructor(schemaMgr:SchemaManager, elementRef:ElementRef) {
|
|
||||||
super(schemaMgr);
|
private _normalizer:SchemaNormalizer;
|
||||||
|
|
||||||
|
constructor(specMgr:SpecManager, elementRef:ElementRef) {
|
||||||
|
super(specMgr);
|
||||||
this.element = elementRef.nativeElement;
|
this.element = elementRef.nativeElement;
|
||||||
|
this._normalizer = new SchemaNormalizer(specMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
this.bindEvents();
|
||||||
this.data = {};
|
this.data = {};
|
||||||
|
|
||||||
let base:any = {};
|
let base:any = {};
|
||||||
|
@ -37,7 +43,10 @@ export class SchemaSample extends BaseComponent {
|
||||||
if (base.examples && base.examples['application/json']) {
|
if (base.examples && base.examples['application/json']) {
|
||||||
sample = base.examples['application/json'];
|
sample = base.examples['application/json'];
|
||||||
} else {
|
} else {
|
||||||
this.dereference(this.componentSchema);
|
this.componentSchema = this._normalizer.normalize(this.componentSchema, this.pointer);
|
||||||
|
if (this.fromCache()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
sample = OpenAPISampler.sample(this.componentSchema, {
|
sample = OpenAPISampler.sample(this.componentSchema, {
|
||||||
skipReadOnly: this.skipReadOnly
|
skipReadOnly: this.skipReadOnly
|
||||||
|
@ -46,10 +55,30 @@ export class SchemaSample extends BaseComponent {
|
||||||
// no sample available
|
// no sample available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.cache(sample);
|
||||||
this.data.sample = sample;
|
this.data.sample = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache(sample) {
|
||||||
|
if (this.skipReadOnly) {
|
||||||
|
this.componentSchema['x-redoc-ro-sample'] = sample;
|
||||||
|
} else {
|
||||||
|
this.componentSchema['x-redoc-rw-sample'] = sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fromCache() {
|
||||||
|
if (this.skipReadOnly && this.componentSchema['x-redoc-ro-sample']) {
|
||||||
|
this.data.sample = this.componentSchema['x-redoc-ro-sample'];
|
||||||
|
return true;
|
||||||
|
} else if (this.componentSchema['x-redoc-rw-sample']) {
|
||||||
|
this.data.sample = this.componentSchema['x-redoc-rw-sample'];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
this.element.addEventListener('click', (event) => {
|
this.element.addEventListener('click', (event) => {
|
||||||
var collapsed, target = event.target;
|
var collapsed, target = event.target;
|
||||||
if (event.target.className === 'collapser') {
|
if (event.target.className === 'collapser') {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<div *ngFor="let cat of data.menu; let idx = index" class="menu-cat">
|
<div *ngFor="let cat of data.menu; let idx = index" class="menu-cat">
|
||||||
|
|
||||||
<label class="menu-cat-header" (click)="activateAndScroll(idx, -1)" [ngClass]="{active: cat.active}"> {{cat.name}}</label>
|
<label class="menu-cat-header" (click)="activateAndScroll(idx, -1)" [ngClass]="{active: cat.active}"> {{cat.name}}</label>
|
||||||
<ul class="menu-subitems" [ngClass]="{active: cat.active}">
|
<ul class="menu-subitems" @itemAnimation="cat.active ? 'expanded' : 'collapsed'">
|
||||||
<li *ngFor="let method of cat.methods; let methIdx = index"
|
<li *ngFor="let method of cat.methods; let methIdx = index"
|
||||||
[ngClass]="{active: method.active}"
|
[ngClass]="{active: method.active}"
|
||||||
(click)="activateAndScroll(idx, methIdx)">
|
(click)="activateAndScroll(idx, methIdx)">
|
||||||
|
|
|
@ -38,7 +38,6 @@ $mobile-menu-compact-breakpoint: 550px;
|
||||||
font-weight: $light;
|
font-weight: $light;
|
||||||
color: rgba($text-color, .9);
|
color: rgba($text-color, .9);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 0;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
|
|
||||||
import { MethodsList, SideMenu } from '../index';
|
import { MethodsList, SideMenu } from '../index';
|
||||||
|
|
||||||
import { SchemaManager } from '../../utils/SchemaManager';;
|
import { SpecManager } from '../../utils/SpecManager';;
|
||||||
|
|
||||||
let testOptions;
|
let testOptions;
|
||||||
|
|
||||||
|
@ -26,17 +26,17 @@ describe('Redoc components', () => {
|
||||||
let component;
|
let component;
|
||||||
let fixture;
|
let fixture;
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(SchemaManager, {useValue: new SchemaManager()})
|
provide(SpecManager, {useValue: new SpecManager()})
|
||||||
]);
|
]);
|
||||||
beforeEach(async(inject([TestComponentBuilder, SchemaManager, OptionsService],
|
beforeEach(async(inject([TestComponentBuilder, SpecManager, OptionsService],
|
||||||
(tcb, schemaMgr, opts) => {
|
(tcb, specMgr, opts) => {
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
testOptions = opts;
|
testOptions = opts;
|
||||||
testOptions.options = {
|
testOptions.options = {
|
||||||
scrollYOffset: () => 0,
|
scrollYOffset: () => 0,
|
||||||
scrollParent: window
|
scrollParent: window
|
||||||
};
|
};
|
||||||
return schemaMgr.load('/tests/schemas/extended-petstore.yml');
|
return specMgr.load('/tests/schemas/extended-petstore.yml');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
import { ElementRef, ChangeDetectorRef } from '@angular/core';
|
import { ElementRef, ChangeDetectorRef } from '@angular/core';
|
||||||
import { BrowserDomAdapter } from '@angular/platform-browser/src/browser/browser_adapter';
|
import { BrowserDomAdapter } from '@angular/platform-browser/src/browser/browser_adapter';
|
||||||
import { global } from '@angular/core/src/facade/lang';
|
import { global } from '@angular/core/src/facade/lang';
|
||||||
|
import { trigger, state, animate, transition, style } from '@angular/core';
|
||||||
import { RedocComponent, BaseComponent, SchemaManager } from '../base';
|
import { RedocComponent, BaseComponent, SpecManager } from '../base';
|
||||||
import { ScrollService, Hash, MenuService, OptionsService } from '../../services/index';
|
import { ScrollService, Hash, MenuService, OptionsService } from '../../services/index';
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
|
@ -13,7 +13,18 @@ import { ScrollService, Hash, MenuService, OptionsService } from '../../services
|
||||||
providers: [ScrollService, MenuService, Hash],
|
providers: [ScrollService, MenuService, Hash],
|
||||||
styleUrls: ['./side-menu.css'],
|
styleUrls: ['./side-menu.css'],
|
||||||
detect: true,
|
detect: true,
|
||||||
onPushOnly: false
|
onPushOnly: false,
|
||||||
|
animations: [
|
||||||
|
trigger('itemAnimation', [
|
||||||
|
state('collapsed, void',
|
||||||
|
style({ height: '0px' })),
|
||||||
|
state('expanded',
|
||||||
|
style({ height: '*' })),
|
||||||
|
transition('collapsed <=> expanded', [
|
||||||
|
animate(200)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class SideMenu extends BaseComponent {
|
export class SideMenu extends BaseComponent {
|
||||||
$element: any;
|
$element: any;
|
||||||
|
@ -24,10 +35,10 @@ export class SideMenu extends BaseComponent {
|
||||||
activeItemCaption: string;
|
activeItemCaption: string;
|
||||||
options: any;
|
options: any;
|
||||||
data: any;
|
data: any;
|
||||||
constructor(schemaMgr:SchemaManager, elementRef:ElementRef, private dom:BrowserDomAdapter,
|
constructor(specMgr:SpecManager, elementRef:ElementRef, private dom:BrowserDomAdapter,
|
||||||
private scrollService:ScrollService, private menuService:MenuService, private hash:Hash,
|
private scrollService:ScrollService, private menuService:MenuService, private hash:Hash,
|
||||||
optionsService:OptionsService, private detectorRef:ChangeDetectorRef) {
|
optionsService:OptionsService, private detectorRef:ChangeDetectorRef) {
|
||||||
super(schemaMgr);
|
super(specMgr);
|
||||||
this.$element = elementRef.nativeElement;
|
this.$element = elementRef.nativeElement;
|
||||||
|
|
||||||
this.activeCatCaption = '';
|
this.activeCatCaption = '';
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { SchemaManager } from '../utils/SchemaManager';
|
import { SpecManager } from '../utils/SpecManager';
|
||||||
import { BaseComponent } from '../components/base';
|
import { BaseComponent } from '../components/base';
|
||||||
|
|
||||||
describe('Redoc components', () => {
|
describe('Redoc components', () => {
|
||||||
describe('BaseComponent', () => {
|
describe('BaseComponent', () => {
|
||||||
let schemaMgr;
|
let specMgr;
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
schemaMgr = new SchemaManager();
|
specMgr = new SpecManager();
|
||||||
schemaMgr._schema = {tags: []};
|
specMgr._schema = {tags: []};
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component = new BaseComponent(schemaMgr);
|
component = new BaseComponent(specMgr);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set instance properties', () => {
|
it('should set instance properties', () => {
|
||||||
component.schemaMgr.should.be.equal(schemaMgr);
|
component.specMgr.should.be.equal(specMgr);
|
||||||
//component.schema.should.be.of.type('object');
|
//component.schema.should.be.of.type('object');
|
||||||
expect(component.componentSchema).toBeNull();
|
expect(component.componentSchema).toBeNull();
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@ describe('Redoc components', () => {
|
||||||
it('should set componentSchema based on pointer on ngOnInit', () => {
|
it('should set componentSchema based on pointer on ngOnInit', () => {
|
||||||
component.pointer = '/tags';
|
component.pointer = '/tags';
|
||||||
component.ngOnInit();
|
component.ngOnInit();
|
||||||
component.componentSchema.should.be.deepEqual(schemaMgr._schema.tags);
|
component.componentSchema.should.be.deepEqual(specMgr._schema.tags);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call prepareModel and init virtual methods after init', () => {
|
it('should call prepareModel and init virtual methods after init', () => {
|
||||||
|
@ -39,253 +39,5 @@ describe('Redoc components', () => {
|
||||||
component.prepareModel.and.callThrough();
|
component.prepareModel.and.callThrough();
|
||||||
component.init.and.callThrough();
|
component.init.and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('dereference', () => {
|
|
||||||
beforeAll((done) => {
|
|
||||||
schemaMgr.load('/tests/schemas/base-component-dereference.json').then(
|
|
||||||
() => done()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('simple dereference', () => {
|
|
||||||
let paramWithRef;
|
|
||||||
beforeAll(() => {
|
|
||||||
component.pointer = '/paths/test1/get';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
paramWithRef = component.componentSchema.parameters[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not contain $ref property', () => {
|
|
||||||
expect(paramWithRef.$ref).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inject Title if not exist based on reference', () => {
|
|
||||||
paramWithRef.title.should.be.equal('Simple');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inject pointer', () => {
|
|
||||||
paramWithRef._pointer.should.be.equal('#/definitions/Simple');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should insert correct definition instead of reference', () => {
|
|
||||||
delete paramWithRef.title;
|
|
||||||
delete paramWithRef._pointer;
|
|
||||||
paramWithRef.should.be.deepEqual(schemaMgr.schema.definitions.Simple);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('nested dereference', () => {
|
|
||||||
let paramWithRef;
|
|
||||||
beforeAll(() => {
|
|
||||||
component.pointer = '/paths/test2/get';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
paramWithRef = component.componentSchema.parameters[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not touch title if exist', () => {
|
|
||||||
paramWithRef.title.should.be.equal('NesteTitle');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve nested schema', () => {
|
|
||||||
expect(paramWithRef.properties.subschema.$ref).toBeUndefined();
|
|
||||||
paramWithRef._pointer.should.be.equal('#/definitions/Nested');
|
|
||||||
paramWithRef.properties.subschema._pointer.should.be.equal('#/definitions/Simple');
|
|
||||||
paramWithRef.properties.subschema.type.should.be.equal('object');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('array schema dereference', () => {
|
|
||||||
let paramWithRef;
|
|
||||||
beforeAll(() => {
|
|
||||||
component.pointer = '/paths/test3/get';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
paramWithRef = component.componentSchema.parameters[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve array schema', () => {
|
|
||||||
expect(paramWithRef.$ref).toBeUndefined();
|
|
||||||
expect(paramWithRef.items.schema.$ref).toBeUndefined();
|
|
||||||
paramWithRef.type.should.be.equal('array');
|
|
||||||
paramWithRef._pointer.should.be.equal('#/definitions/ArrayOfSimple');
|
|
||||||
paramWithRef.items.schema._pointer.should.be.equal('#/definitions/Simple');
|
|
||||||
paramWithRef.items.schema.type.should.be.equal('object');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('circular dereference', () => {
|
|
||||||
let paramWithRef;
|
|
||||||
beforeAll(() => {
|
|
||||||
component.pointer = '/paths/test4/get';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
paramWithRef = component.componentSchema.parameters[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve circular schema', () => {
|
|
||||||
expect(paramWithRef.$ref).toBeUndefined();
|
|
||||||
expect(paramWithRef.items.schema.$ref).toBeUndefined();
|
|
||||||
paramWithRef.type.should.be.equal('array');
|
|
||||||
paramWithRef._pointer.should.be.equal('#/definitions/Circular');
|
|
||||||
expect(paramWithRef.items.schema._pointer).toBeUndefined();
|
|
||||||
paramWithRef.items.schema.title.should.be.equal('Circular');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('$ref with other fields on the same level', () => {
|
|
||||||
let paramWithRef;
|
|
||||||
beforeAll(() => {
|
|
||||||
component.pointer = '/paths/test5/get';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
paramWithRef = component.componentSchema.parameters[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should skip other fields', () => {
|
|
||||||
expect(paramWithRef.$ref).toBeUndefined();
|
|
||||||
expect(paramWithRef.title).toBeDefined();
|
|
||||||
paramWithRef.title.should.be.equal('Simple');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should preserve description field', () => {
|
|
||||||
expect(paramWithRef.$ref).toBeUndefined();
|
|
||||||
expect(paramWithRef.description).toBeDefined();
|
|
||||||
paramWithRef.description.should.be.equal('test');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mergeAllOf', () => {
|
|
||||||
beforeAll((done) => {
|
|
||||||
schemaMgr.load('tests/schemas/base-component-joinallof.json').then(() => done());
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Simple allOf merge', () => {
|
|
||||||
let joined;
|
|
||||||
beforeAll(() => {
|
|
||||||
component.pointer = '/definitions/SimpleAllOf';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
BaseComponent.joinAllOf(component.componentSchema);
|
|
||||||
joined = component.componentSchema;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove $allOf field', () => {
|
|
||||||
expect(joined.allOf).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set type object', () => {
|
|
||||||
joined.type.should.be.equal('object');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should merge properties', () => {
|
|
||||||
Object.keys(joined.properties).length.should.be.equal(3);
|
|
||||||
Object.keys(joined.properties).should.be.deepEqual(['prop1', 'prop2', 'prop3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should merge required', () => {
|
|
||||||
joined.required.length.should.be.equal(2);
|
|
||||||
joined.required.should.be.deepEqual(['prop1', 'prop3']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('AllOf with refrence', () => {
|
|
||||||
let joined;
|
|
||||||
beforeAll(() => {
|
|
||||||
component.pointer = '/definitions/AllOfWithRef';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
BaseComponent.joinAllOf(component.componentSchema);
|
|
||||||
joined = component.componentSchema;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove $allOf field', () => {
|
|
||||||
expect(joined.allOf).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set type object', () => {
|
|
||||||
joined.type.should.be.equal('object');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should merge properties', () => {
|
|
||||||
Object.keys(joined.properties).length.should.be.equal(2);
|
|
||||||
Object.keys(joined.properties).should.be.deepEqual(['id', 'prop3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should merge required', () => {
|
|
||||||
joined.required.length.should.be.equal(2);
|
|
||||||
joined.required.should.be.deepEqual(['id', 'prop3']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('AllOf with other properties on the allOf level', () => {
|
|
||||||
let joined;
|
|
||||||
beforeAll(() => {
|
|
||||||
component.pointer = '/definitions/AllOfWithOther';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
BaseComponent.joinAllOf(component.componentSchema);
|
|
||||||
joined = component.componentSchema;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove $allOf field', () => {
|
|
||||||
expect(joined.allOf).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set type object', () => {
|
|
||||||
joined.type.should.be.equal('object');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should merge properties', () => {
|
|
||||||
Object.keys(joined.properties).length.should.be.equal(1);
|
|
||||||
Object.keys(joined.properties).should.be.deepEqual(['id']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should merge required', () => {
|
|
||||||
joined.required.length.should.be.equal(1);
|
|
||||||
joined.required.should.be.deepEqual(['id']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should preserve parent properties', () => {
|
|
||||||
joined.description.should.be.equal('Test');
|
|
||||||
joined.readOnly.should.be.equal(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('allOf edgecases', () => {
|
|
||||||
it('should merge properties and required when defined on allOf level', () => {
|
|
||||||
component.pointer = '/definitions/PropertiesOnAllOfLevel';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
(() => BaseComponent.joinAllOf(component.componentSchema)).should.not.throw();
|
|
||||||
let joined = component.componentSchema;
|
|
||||||
Object.keys(joined.properties).length.should.be.equal(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw when merging schemas with different types', () => {
|
|
||||||
component.pointer = '/definitions/BadAllOf1';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
(() => BaseComponent.joinAllOf(component.componentSchema)).should.throw();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle nested allOF', () => {
|
|
||||||
component.pointer = '/definitions/NestedAllOf';
|
|
||||||
component.ngOnInit();
|
|
||||||
component.dereference();
|
|
||||||
(() => BaseComponent.joinAllOf(component.componentSchema)).should.not.throw();
|
|
||||||
let joined = component.componentSchema;
|
|
||||||
Object.keys(joined.properties).length.should.be.equal(4);
|
|
||||||
Object.keys(joined.properties).should.be.deepEqual(['prop1', 'prop2', 'prop3', 'prop4']);
|
|
||||||
joined.required.should.be.deepEqual(['prop1', 'prop3']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
xdescribe('Merge array allOf', () => {
|
|
||||||
//emtpy
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
|
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { CORE_DIRECTIVES, JsonPipe, AsyncPipe } from '@angular/common';
|
import { CORE_DIRECTIVES, JsonPipe, AsyncPipe } from '@angular/common';
|
||||||
import { SchemaManager } from '../utils/SchemaManager';
|
import { SpecManager } from '../utils/SpecManager';
|
||||||
import JsonPointer from '../utils/JsonPointer';
|
|
||||||
import { MarkedPipe, JsonPointerEscapePipe } from '../utils/pipes';
|
import { MarkedPipe, JsonPointerEscapePipe } from '../utils/pipes';
|
||||||
|
|
||||||
export { SchemaManager };
|
export { SpecManager };
|
||||||
|
|
||||||
// common inputs for all components
|
// common inputs for all components
|
||||||
let commonInputs = ['pointer']; // json pointer to the schema chunk
|
let commonInputs = ['pointer']; // json pointer to the schema chunk
|
||||||
|
@ -17,21 +16,6 @@ function safeConcat(a, b) {
|
||||||
return res.concat(b);
|
return res.concat(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaults(target, src) {
|
|
||||||
var props = Object.keys(src);
|
|
||||||
|
|
||||||
var index = -1,
|
|
||||||
length = props.length;
|
|
||||||
|
|
||||||
while (++index < length) {
|
|
||||||
var key = props[index];
|
|
||||||
if (target[key] === undefined) {
|
|
||||||
target[key] = src[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
function snapshot(obj) {
|
function snapshot(obj) {
|
||||||
if(obj == undefined || typeof(obj) !== 'object') {
|
if(obj == undefined || typeof(obj) !== 'object') {
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -79,6 +63,7 @@ export function RedocComponent(options) {
|
||||||
changeDetection: options.detect ?
|
changeDetection: options.detect ?
|
||||||
(options.onPushOnly ? ChangeDetectionStrategy.OnPush : ChangeDetectionStrategy.Default) :
|
(options.onPushOnly ? ChangeDetectionStrategy.OnPush : ChangeDetectionStrategy.Default) :
|
||||||
ChangeDetectionStrategy.Detached,
|
ChangeDetectionStrategy.Detached,
|
||||||
|
animations: options.animations,
|
||||||
templateUrl: options.templateUrl,
|
templateUrl: options.templateUrl,
|
||||||
template: options.template,
|
template: options.template,
|
||||||
styles: options.styles,
|
styles: options.styles,
|
||||||
|
@ -97,78 +82,16 @@ export function RedocComponent(options) {
|
||||||
export class BaseComponent implements OnInit, OnDestroy {
|
export class BaseComponent implements OnInit, OnDestroy {
|
||||||
componentSchema: any = null;
|
componentSchema: any = null;
|
||||||
pointer: String;
|
pointer: String;
|
||||||
|
dereferencedCache = {};
|
||||||
|
|
||||||
static joinAllOf(schema: any, opts?: any) {
|
constructor(public specMgr: SpecManager) {
|
||||||
function merge(into, schemas) {
|
|
||||||
for (let subSchema of schemas) {
|
|
||||||
if (opts && opts.omitParent && subSchema.discriminator) continue;
|
|
||||||
// TODO: add support for merge array schemas
|
|
||||||
if (typeof subSchema !== 'object') {
|
|
||||||
let errMessage = `Items of allOf should be Object: ${typeof subSchema} found
|
|
||||||
${subSchema}`;
|
|
||||||
throw new Error(errMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (into.type && subSchema.type && into.type !== subSchema.type) {
|
|
||||||
let errMessage = `allOf merging error: schemas with different types can't be merged`;
|
|
||||||
throw new Error(errMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (into.type === 'array') {
|
|
||||||
console.warn('allOf: subschemas with type array are not supported yet');
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add check if can be merged correctly (no different properties with the same name)
|
|
||||||
into.type = into.type || subSchema.type;
|
|
||||||
if (into.type === 'object' && subSchema.properties) {
|
|
||||||
if (!into.properties) into.properties = {};
|
|
||||||
Object.assign(into.properties, subSchema.properties);
|
|
||||||
Object.keys(subSchema.properties).forEach(propName => {
|
|
||||||
if (!subSchema.properties[propName]._pointer) {
|
|
||||||
subSchema.properties[propName]._pointer = subSchema._pointer ?
|
|
||||||
JsonPointer.join(subSchema._pointer, ['properties', propName]) : null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (into.type === 'object' && subSchema.required) {
|
|
||||||
if (!into.required) into.required = [];
|
|
||||||
into.required.push(...subSchema.required);
|
|
||||||
}
|
|
||||||
// don't merge _pointer
|
|
||||||
subSchema._pointer = null;
|
|
||||||
defaults(into, subSchema);
|
|
||||||
}
|
|
||||||
into.allOf = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function traverse(obj) {
|
|
||||||
if (obj == undefined || typeof(obj) !== 'object') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(var key in obj) {
|
|
||||||
if (obj.hasOwnProperty(key)) {
|
|
||||||
traverse(obj[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.allOf) {
|
|
||||||
merge(obj, obj.allOf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
traverse(schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(public schemaMgr: SchemaManager) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onInit method is run by angular2 after all component inputs are resolved
|
* onInit method is run by angular2 after all component inputs are resolved
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.componentSchema = snapshot(this.schemaMgr.byPointer(this.pointer || ''));
|
this.componentSchema = this.specMgr.byPointer(this.pointer || '');
|
||||||
this.prepareModel();
|
this.prepareModel();
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
@ -177,61 +100,6 @@ export class BaseComponent implements OnInit, OnDestroy {
|
||||||
this.destroy();
|
this.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* simple in-place schema dereferencing. Schema is already bundled so no need in global dereferencing.
|
|
||||||
*/
|
|
||||||
dereference(schema = Object.assign({}, this.componentSchema)) {
|
|
||||||
let dereferencedCache = {};
|
|
||||||
|
|
||||||
let resolve = (schema) => {
|
|
||||||
let resolvedRef;
|
|
||||||
if (schema && schema.$ref) {
|
|
||||||
resolvedRef = schema.$ref;
|
|
||||||
let resolved = this.schemaMgr.byPointer(schema.$ref);
|
|
||||||
let baseName = JsonPointer.baseName(schema.$ref);
|
|
||||||
if (!dereferencedCache[schema.$ref]) {
|
|
||||||
// if resolved schema doesn't have title use name from ref
|
|
||||||
resolved = Object.assign({}, resolved);
|
|
||||||
resolved._pointer = schema.$ref;
|
|
||||||
} else {
|
|
||||||
// for circular referenced save only title and type
|
|
||||||
resolved = {
|
|
||||||
title: resolved.title,
|
|
||||||
type: resolved.type
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
dereferencedCache[schema.$ref] = dereferencedCache[schema.$ref] ? dereferencedCache[schema.$ref] + 1 : 1;
|
|
||||||
|
|
||||||
resolved.title = resolved.title || baseName;
|
|
||||||
|
|
||||||
let keysCount = Object.keys(schema).length;
|
|
||||||
if ( keysCount > 2 || (keysCount === 2 && !schema.description) ) {
|
|
||||||
// allow only description field on the same level as $ref because it is
|
|
||||||
// common pattern over specs in the wild
|
|
||||||
console.warn(`other properties defined at the same level as $ref at '${this.pointer}'.
|
|
||||||
They are IGNORRED according to JsonSchema spec`);
|
|
||||||
}
|
|
||||||
|
|
||||||
schema = schema.description ? {
|
|
||||||
description: schema.description
|
|
||||||
} : {};
|
|
||||||
Object.assign(schema, resolved);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(schema).forEach((key) => {
|
|
||||||
let value = schema[key];
|
|
||||||
if (value && typeof value === 'object') {
|
|
||||||
schema[key] = resolve(value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (resolvedRef) dereferencedCache[resolvedRef] = dereferencedCache[resolvedRef] ? dereferencedCache[resolvedRef] - 1 : 0;
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.componentSchema = snapshot(resolve(schema));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to prepare model based on component schema
|
* Used to prepare model based on component schema
|
||||||
* @abstract
|
* @abstract
|
||||||
|
|
|
@ -3,9 +3,12 @@ import 'dropkickjs/build/css/dropkick.css!css';
|
||||||
import 'prismjs/themes/prism-dark.css!css';
|
import 'prismjs/themes/prism-dark.css!css';
|
||||||
import 'hint.css/hint.base.css!css';
|
import 'hint.css/hint.base.css!css';
|
||||||
import './components/Redoc/redoc-initial-styles.css!css';
|
import './components/Redoc/redoc-initial-styles.css!css';
|
||||||
|
import { redocVersion } from './version.js';
|
||||||
|
|
||||||
import { Redoc } from './components/index';
|
import { Redoc } from './components/index';
|
||||||
|
|
||||||
|
Redoc.version = redocVersion;
|
||||||
|
|
||||||
export var init = Redoc.init;
|
export var init = Redoc.init;
|
||||||
|
|
||||||
window['Redoc'] = Redoc;
|
window['Redoc'] = Redoc;
|
||||||
|
|
|
@ -5,3 +5,5 @@ export * from './options.service';
|
||||||
export * from './menu.service';
|
export * from './menu.service';
|
||||||
export * from './scroll.service';
|
export * from './scroll.service';
|
||||||
export * from './hash.service';
|
export * from './hash.service';
|
||||||
|
export * from './schema-normalizer.service';
|
||||||
|
export * from './schema-helper.service';
|
||||||
|
|
|
@ -6,57 +6,54 @@ import {
|
||||||
beforeEach,
|
beforeEach,
|
||||||
describe,
|
describe,
|
||||||
beforeEachProviders,
|
beforeEachProviders,
|
||||||
it
|
it,
|
||||||
|
async
|
||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
|
|
||||||
import { TestComponentBuilder } from '@angular/compiler/testing';
|
import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
|
|
||||||
import { OptionsService } from './options.service';
|
|
||||||
import { MenuService } from './menu.service';
|
import { MenuService } from './menu.service';
|
||||||
import { Hash } from './hash.service';
|
import { Hash } from './hash.service';
|
||||||
import { ScrollService } from './scroll.service';
|
import { ScrollService } from './scroll.service';
|
||||||
import { RedocEventsService } from './events.service';
|
|
||||||
import { MethodsList } from '../components/index';
|
import { MethodsList } from '../components/index';
|
||||||
import { SchemaManager } from '../utils/SchemaManager';;
|
import { SpecManager } from '../utils/SpecManager';;
|
||||||
|
|
||||||
describe('Menu service', () => {
|
describe('Menu service', () => {
|
||||||
let menu, hashService, scroll;
|
let menu, hashService, scroll;
|
||||||
let builder;
|
let builder;
|
||||||
let schemaMgr;
|
let specMgr = new SpecManager();
|
||||||
|
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
provide(BrowserDomAdapter, {useClass: BrowserDomAdapter}),
|
provide(BrowserDomAdapter, {useClass: BrowserDomAdapter}),
|
||||||
provide(OptionsService, {useClass: OptionsService}),
|
provide(Hash, {useClass: Hash}),
|
||||||
provide(Hash, {useClass: Hash}),
|
provide(ScrollService, {useClass: ScrollService}),
|
||||||
provide(ScrollService, {useClass: ScrollService}),
|
provide(SpecManager, {useValue: new SpecManager()})
|
||||||
provide(RedocEventsService, {useClass: RedocEventsService}),
|
|
||||||
provide(SchemaManager, {useClass: SchemaManager})
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
beforeEach(inject([Hash, ScrollService, SchemaManager, TestComponentBuilder],
|
beforeEach(async(inject([Hash, ScrollService, TestComponentBuilder, SpecManager],
|
||||||
(_hash, _scroll, _schemaMgr, tcb) => {
|
(_hash, _scroll, tcb, _specMgr) => {
|
||||||
hashService = _hash;
|
hashService = _hash;
|
||||||
scroll = _scroll;
|
scroll = _scroll;
|
||||||
schemaMgr = _schemaMgr;
|
|
||||||
builder = tcb;
|
builder = tcb;
|
||||||
}));
|
specMgr = _specMgr;
|
||||||
|
})));
|
||||||
|
|
||||||
|
beforeEach(done => {
|
||||||
beforeEach((done) => {
|
specMgr.load('/tests/schemas/extended-petstore.yml').then(r => {
|
||||||
schemaMgr.load('/tests/schemas/extended-petstore.yml').then(() => {
|
|
||||||
menu = new MenuService(hashService, scroll, schemaMgr);
|
|
||||||
done();
|
done();
|
||||||
}).catch((err) => done.fail(err));
|
}).catch(e => {
|
||||||
|
done.fail(e);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach(done => {
|
||||||
builder.createAsync(TestAppComponent).then((fixture) => {
|
menu = new MenuService(hashService, scroll, specMgr);
|
||||||
|
builder.createAsync(TestAppComponent).then(fixture => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
done();
|
done();
|
||||||
}).catch((err) => done.fail(err));
|
}).catch(err => done.fail(err) );
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should run hashScroll when hash changed', (done) => {
|
it('should run hashScroll when hash changed', (done) => {
|
||||||
spyOn(menu, 'hashScroll').and.callThrough();
|
spyOn(menu, 'hashScroll').and.callThrough();
|
||||||
hashService.changed.subscribe(() => {
|
hashService.changed.subscribe(() => {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { Injectable, EventEmitter } from '@angular/core';
|
import { Injectable, EventEmitter } from '@angular/core';
|
||||||
import { ScrollService, INVIEW_POSITION } from './scroll.service';
|
import { ScrollService, INVIEW_POSITION } from './scroll.service';
|
||||||
import { Hash } from './hash.service';
|
import { Hash } from './hash.service';
|
||||||
import { SchemaManager } from '../utils/SchemaManager';
|
import { SpecManager } from '../utils/SpecManager';
|
||||||
|
|
||||||
const CHANGE = {
|
const CHANGE = {
|
||||||
NEXT : 1,
|
NEXT : 1,
|
||||||
|
@ -19,10 +19,9 @@ export class MenuService {
|
||||||
activeMethodIdx: number = -1;
|
activeMethodIdx: number = -1;
|
||||||
activeMethodPtr: string;
|
activeMethodPtr: string;
|
||||||
|
|
||||||
constructor(private hash:Hash, private scrollService:ScrollService, schemaMgr:SchemaManager) {
|
constructor(private hash:Hash, private scrollService:ScrollService, specMgr:SpecManager) {
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
|
this.categories = Array.from(specMgr.buildMenuTree().entries()).map(
|
||||||
this.categories = Array.from(schemaMgr.buildMenuTree().entries()).map(
|
|
||||||
el => ({name: el[0], description: el[1].description, methods: el[1].methods})
|
el => ({name: el[0], description: el[1].description, methods: el[1].methods})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
221
lib/services/schema-helper.service.ts
Normal file
221
lib/services/schema-helper.service.ts
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
'use strict';
|
||||||
|
import { JsonPointer } from '../utils/JsonPointer';
|
||||||
|
import { SpecManager } from '../utils/SpecManager';
|
||||||
|
|
||||||
|
interface PropertyPreprocessOptions {
|
||||||
|
childFor: string;
|
||||||
|
skipReadOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const injectors = {
|
||||||
|
general: {
|
||||||
|
check: () => true,
|
||||||
|
inject: (injectTo, propertySchema, pointer) => {
|
||||||
|
injectTo._pointer = propertySchema._pointer || pointer;
|
||||||
|
injectTo._displayType = propertySchema.type;
|
||||||
|
if (propertySchema.format) injectTo._displayFormat = `<${propertySchema.format}>`;
|
||||||
|
if (propertySchema.enum) {
|
||||||
|
injectTo.enum = propertySchema.enum.map((value) => {
|
||||||
|
return {val: value, type: typeof value};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
discriminator: {
|
||||||
|
check: (propertySchema) => propertySchema.discriminator,
|
||||||
|
inject: (injectTo, propertySchema = injectTo, pointer) => {
|
||||||
|
injectTo._descendants = SpecManager.instance().findDerivedDefinitions(pointer);
|
||||||
|
injectTo.discriminator = propertySchema.discriminator;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
array: {
|
||||||
|
check: (propertySchema) => {
|
||||||
|
return propertySchema.type === 'array';
|
||||||
|
},
|
||||||
|
inject: (injectTo, propertySchema = injectTo, propPointer) => {
|
||||||
|
injectTo._isArray = true;
|
||||||
|
injectTo._pointer = propertySchema.items._pointer
|
||||||
|
|| JsonPointer.join(propertySchema._pointer || propPointer, ['items']);
|
||||||
|
|
||||||
|
SchemaHelper.runInjectors(injectTo, propertySchema.items, propPointer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
object: {
|
||||||
|
check: (propertySchema) => {
|
||||||
|
return propertySchema.type === 'object' && propertySchema.properties;
|
||||||
|
},
|
||||||
|
inject: (injectTo, propertySchema = injectTo) => {
|
||||||
|
let baseName = propertySchema._pointer && JsonPointer.baseName(propertySchema._pointer);
|
||||||
|
injectTo._displayType = propertySchema.title || baseName || 'object';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
noType: {
|
||||||
|
check: (propertySchema) => !propertySchema.type,
|
||||||
|
inject: (injectTo) => {
|
||||||
|
injectTo._displayType = '< * >';
|
||||||
|
injectTo._displayTypeHint = 'This field may contain data of any type';
|
||||||
|
injectTo.isTrivial = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
simpleType: {
|
||||||
|
check: (propertySchema) => {
|
||||||
|
if (propertySchema.type === 'object') {
|
||||||
|
return (!propertySchema.properties || !Object.keys(propertySchema.properties).length)
|
||||||
|
&& (typeof propertySchema.additionalProperties !== 'object');
|
||||||
|
}
|
||||||
|
return (propertySchema.type !== 'array') && propertySchema.type;
|
||||||
|
},
|
||||||
|
inject: (injectTo, propertySchema = injectTo) => {
|
||||||
|
injectTo.isTrivial = true;
|
||||||
|
if (injectTo._pointer) {
|
||||||
|
injectTo._pointer = undefined;
|
||||||
|
injectTo._displayType = propertySchema.title ?
|
||||||
|
`${propertySchema.title} (${propertySchema.type})` : propertySchema.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
integer: {
|
||||||
|
check: (propertySchema) => (propertySchema.type === 'integer' || propertySchema.type === 'number'),
|
||||||
|
inject: (injectTo, propertySchema = injectTo) => {
|
||||||
|
var range = '';
|
||||||
|
if (propertySchema.minimum && propertySchema.maximum) {
|
||||||
|
range += propertySchema.exclusiveMinimum ? '( ' : '[ ';
|
||||||
|
range += propertySchema.minimum;
|
||||||
|
range += ' .. ';
|
||||||
|
range += propertySchema.maximum;
|
||||||
|
range += propertySchema.exclusiveMaximum ? ' )' : ' ]';
|
||||||
|
} else if (propertySchema.maximum) {
|
||||||
|
range += propertySchema.exclusiveMaximum? '< ' : '<= ';
|
||||||
|
range += propertySchema.maximum;
|
||||||
|
} else if (propertySchema.minimum) {
|
||||||
|
range += propertySchema.exclusiveMinimum ? '> ' : '>= ';
|
||||||
|
range += propertySchema.minimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range) {
|
||||||
|
injectTo._range = range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
string: {
|
||||||
|
check: propertySchema => (propertySchema.type === 'string'),
|
||||||
|
inject: (injectTo, propertySchema = injectTo) => {
|
||||||
|
var range;
|
||||||
|
if (propertySchema.minLength && propertySchema.maxLength) {
|
||||||
|
range = `[ ${propertySchema.minLength} .. ${propertySchema.maxLength} ]`;
|
||||||
|
} else if (propertySchema.maxLength) {
|
||||||
|
range = '<= ' + propertySchema.maxLength;
|
||||||
|
} else if (propertySchema.minimum) {
|
||||||
|
range = '>= ' + propertySchema.minLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range) {
|
||||||
|
injectTo._range = range + ' characters';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
file: {
|
||||||
|
check: propertySchema => (propertySchema.type === 'file'),
|
||||||
|
inject: (injectTo, propertySchema = injectTo, propPointer, hostPointer) => {
|
||||||
|
injectTo.isFile = true;
|
||||||
|
let parentPtr;
|
||||||
|
if (propertySchema.in === 'formData') {
|
||||||
|
parentPtr = JsonPointer.dirName(hostPointer, 1);
|
||||||
|
} else {
|
||||||
|
parentPtr = JsonPointer.dirName(hostPointer, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parentParam = SpecManager.instance().byPointer(parentPtr);
|
||||||
|
let root = SpecManager.instance().schema;
|
||||||
|
injectTo._produces = parentParam && parentParam.produces || root.produces;
|
||||||
|
injectTo._consumes = parentParam && parentParam.consumes || root.consumes;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SchemaHelper {
|
||||||
|
static preprocess(schema, pointer, hostPointer?) {
|
||||||
|
//propertySchema = Object.assign({}, propertySchema);
|
||||||
|
if (schema['x-redoc-schema-precompiled']) {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
SchemaHelper.runInjectors(schema, schema, pointer, hostPointer);
|
||||||
|
schema['x-redoc-schema-precompiled'] = true;
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
static runInjectors(injectTo, schema, pointer, hostPointer?) {
|
||||||
|
for (var injName of Object.keys(injectors)) {
|
||||||
|
let injector = injectors[injName];
|
||||||
|
if (injector.check(schema)) {
|
||||||
|
injector.inject(injectTo, schema, pointer, hostPointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static preprocessProperties(schema:any, pointer:string, opts: PropertyPreprocessOptions) {
|
||||||
|
let requiredMap = {};
|
||||||
|
if (schema.required) {
|
||||||
|
schema.required.forEach(prop => requiredMap[prop] = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let discriminatorFieldIdx = -1;
|
||||||
|
let props = schema.properties && Object.keys(schema.properties).map((propName, idx) => {
|
||||||
|
let propertySchema = Object.assign({}, schema.properties[propName]);
|
||||||
|
let propPointer = propertySchema._pointer ||
|
||||||
|
JsonPointer.join(pointer, ['properties', propName]);
|
||||||
|
propertySchema = SchemaHelper.preprocess(propertySchema, propPointer);
|
||||||
|
propertySchema._name = propName;
|
||||||
|
// stop endless discriminator recursion
|
||||||
|
if (propertySchema._pointer === opts.childFor) {
|
||||||
|
propertySchema._pointer = null;
|
||||||
|
}
|
||||||
|
propertySchema._required = !!requiredMap[propName];
|
||||||
|
propertySchema.isDiscriminator = (schema.discriminator === propName);
|
||||||
|
if (propertySchema.isDiscriminator) {
|
||||||
|
discriminatorFieldIdx = idx;
|
||||||
|
}
|
||||||
|
return propertySchema;
|
||||||
|
});
|
||||||
|
|
||||||
|
props = props || [];
|
||||||
|
|
||||||
|
if (schema.additionalProperties && (typeof schema.additionalProperties === 'object')) {
|
||||||
|
let propsSchema = SchemaHelper.preprocessAdditionalProperties(schema, pointer);
|
||||||
|
propsSchema._additional = true;
|
||||||
|
props.push(propsSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move discriminator field to the end of properties list
|
||||||
|
if (discriminatorFieldIdx > -1) {
|
||||||
|
let discrProp = props.splice(discriminatorFieldIdx, 1);
|
||||||
|
props.push(discrProp[0]);
|
||||||
|
}
|
||||||
|
// filter readOnly props for request schemas
|
||||||
|
if (opts.skipReadOnly) {
|
||||||
|
props = props.filter(prop => !prop.readOnly);
|
||||||
|
}
|
||||||
|
schema._properties = props;
|
||||||
|
}
|
||||||
|
|
||||||
|
static preprocessAdditionalProperties(schema:any, pointer:string) {
|
||||||
|
var addProps = schema.additionalProperties;
|
||||||
|
let ptr = addProps._pointer || JsonPointer.join(pointer, ['additionalProperties']);
|
||||||
|
let res = SchemaHelper.preprocess(addProps, ptr);
|
||||||
|
res._name = '<Additional Properties> *';
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unwrapArray(schema, pointer) {
|
||||||
|
var res = schema;
|
||||||
|
if (schema && schema.type === 'array') {
|
||||||
|
let ptr = schema.items._pointer || JsonPointer.join(pointer, ['items']);
|
||||||
|
res = schema.items;
|
||||||
|
res._isArray = true;
|
||||||
|
res._pointer = ptr;
|
||||||
|
res = SchemaHelper.unwrapArray(res, ptr);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
251
lib/services/schema-normalizer.service.spec.ts
Normal file
251
lib/services/schema-normalizer.service.spec.ts
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
'use strict';
|
||||||
|
import { SchemaNormalizer } from './schema-normalizer.service';
|
||||||
|
import {
|
||||||
|
describe,
|
||||||
|
it
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SpecManager } from '../utils/SpecManager';;
|
||||||
|
|
||||||
|
describe('Spec Helper', () => {
|
||||||
|
let specMgr:SpecManager = new SpecManager();
|
||||||
|
let normalizer = new SchemaNormalizer(specMgr);
|
||||||
|
|
||||||
|
describe('Dereference', () => {
|
||||||
|
beforeAll(done => {
|
||||||
|
specMgr.load('/tests/schemas/base-component-dereference.json').then(
|
||||||
|
() => done()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('simple dereference', () => {
|
||||||
|
let resolved;
|
||||||
|
let pointer;
|
||||||
|
beforeAll(() => {
|
||||||
|
pointer = '/paths/test1/get/parameters/0';
|
||||||
|
resolved = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not contain $ref property', () => {
|
||||||
|
expect(resolved.$ref).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inject Title if not exist based on reference', () => {
|
||||||
|
resolved.title.should.be.equal('Simple');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inject pointer', () => {
|
||||||
|
resolved._pointer.should.be.equal('#/definitions/Simple');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert correct definition instead of reference', () => {
|
||||||
|
delete resolved.title;
|
||||||
|
delete resolved._pointer;
|
||||||
|
resolved.should.be.deepEqual(specMgr.schema.definitions.Simple);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('nested dereference', () => {
|
||||||
|
let resolved;
|
||||||
|
beforeAll(() => {
|
||||||
|
let pointer = '/paths/test2/get/parameters/0';
|
||||||
|
resolved = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not touch title if exist', () => {
|
||||||
|
resolved.title.should.be.equal('NesteTitle');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve nested schema', () => {
|
||||||
|
expect(resolved.properties.subschema.$ref).toBeUndefined();
|
||||||
|
resolved._pointer.should.be.equal('#/definitions/Nested');
|
||||||
|
resolved.properties.subschema._pointer.should.be.equal('#/definitions/Simple');
|
||||||
|
resolved.properties.subschema.type.should.be.equal('object');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('array schema dereference', () => {
|
||||||
|
let resolved;
|
||||||
|
beforeAll(() => {
|
||||||
|
let pointer = '/paths/test3/get/parameters/0';
|
||||||
|
resolved = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve array schema', () => {
|
||||||
|
expect(resolved.$ref).toBeUndefined();
|
||||||
|
expect(resolved.items.$ref).toBeUndefined();
|
||||||
|
resolved.type.should.be.equal('array');
|
||||||
|
resolved._pointer.should.be.equal('#/definitions/ArrayOfSimple');
|
||||||
|
resolved.items._pointer.should.be.equal('#/definitions/Simple');
|
||||||
|
resolved.items.type.should.be.equal('object');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('circular dereference', () => {
|
||||||
|
let resolved;
|
||||||
|
beforeAll(() => {
|
||||||
|
let pointer = '/paths/test4/get/parameters/0';
|
||||||
|
resolved = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve circular schema', () => {
|
||||||
|
expect(resolved.$ref).toBeUndefined();
|
||||||
|
expect(resolved.items.$ref).toBeUndefined();
|
||||||
|
resolved.type.should.be.equal('array');
|
||||||
|
resolved._pointer.should.be.equal('#/definitions/Circular');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove _pointer when detect circularity', () => {
|
||||||
|
expect(resolved.items._pointer).toBeUndefined();
|
||||||
|
resolved.items.title.should.be.equal('Circular');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve transitive circular ref', () => {
|
||||||
|
let pointer = '/paths/test6/get/parameters/0';
|
||||||
|
resolved = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
expect(resolved.additionalProperties.$ref).toBeUndefined();
|
||||||
|
expect(resolved.additionalProperties.items.additionalProperties.$ref).toBeUndefined();
|
||||||
|
resolved.additionalProperties.type.should.be.equal('array');
|
||||||
|
resolved.additionalProperties._pointer.should.be.equal('#/definitions/CircularTransitive2');
|
||||||
|
expect(resolved.additionalProperties.items.additionalProperties._pointer).toBeUndefined();
|
||||||
|
resolved.additionalProperties.items.additionalProperties.title.should.be.equal('CircularTransitive');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('$ref with other fields on the same level', () => {
|
||||||
|
let resolved;
|
||||||
|
beforeAll(() => {
|
||||||
|
let pointer = '/paths/test5/get/parameters/0';
|
||||||
|
resolved = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip other fields', () => {
|
||||||
|
expect(resolved.$ref).toBeUndefined();
|
||||||
|
expect(resolved.title).toBeDefined();
|
||||||
|
resolved.title.should.be.equal('Simple');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should preserve description field', () => {
|
||||||
|
expect(resolved.$ref).toBeUndefined();
|
||||||
|
expect(resolved.description).toBeDefined();
|
||||||
|
resolved.description.should.be.equal('test');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mergeAllOf', () => {
|
||||||
|
beforeAll((done) => {
|
||||||
|
specMgr.load('tests/schemas/base-component-joinallof.json').then(() => done());
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Simple allOf merge', () => {
|
||||||
|
let joined;
|
||||||
|
beforeAll(() => {
|
||||||
|
let pointer = '/definitions/SimpleAllOf';
|
||||||
|
joined = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove $allOf field', () => {
|
||||||
|
expect(joined.allOf).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set type object', () => {
|
||||||
|
joined.type.should.be.equal('object');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge properties', () => {
|
||||||
|
Object.keys(joined.properties).length.should.be.equal(3);
|
||||||
|
Object.keys(joined.properties).should.be.deepEqual(['prop1', 'prop2', 'prop3']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge required', () => {
|
||||||
|
joined.required.length.should.be.equal(2);
|
||||||
|
joined.required.should.be.deepEqual(['prop1', 'prop3']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AllOf with refrence', () => {
|
||||||
|
let joined;
|
||||||
|
beforeAll(() => {
|
||||||
|
let pointer = '/definitions/AllOfWithRef';
|
||||||
|
joined = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove $allOf field', () => {
|
||||||
|
expect(joined.allOf).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set type object', () => {
|
||||||
|
joined.type.should.be.equal('object');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge properties', () => {
|
||||||
|
Object.keys(joined.properties).length.should.be.equal(2);
|
||||||
|
Object.keys(joined.properties).should.be.deepEqual(['id', 'prop3']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge required', () => {
|
||||||
|
joined.required.length.should.be.equal(2);
|
||||||
|
joined.required.should.be.deepEqual(['id', 'prop3']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AllOf with other properties on the allOf level', () => {
|
||||||
|
let joined;
|
||||||
|
beforeAll(() => {
|
||||||
|
let pointer = '/definitions/AllOfWithOther';
|
||||||
|
joined = normalizer.normalize(specMgr.byPointer(pointer), pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove $allOf field', () => {
|
||||||
|
expect(joined.allOf).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set type object', () => {
|
||||||
|
joined.type.should.be.equal('object');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge properties', () => {
|
||||||
|
Object.keys(joined.properties).length.should.be.equal(1);
|
||||||
|
Object.keys(joined.properties).should.be.deepEqual(['id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge required', () => {
|
||||||
|
joined.required.length.should.be.equal(1);
|
||||||
|
joined.required.should.be.deepEqual(['id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should preserve parent properties', () => {
|
||||||
|
joined.description.should.be.equal('Test');
|
||||||
|
joined.readOnly.should.be.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('allOf edgecases', () => {
|
||||||
|
it('should merge properties and required when defined on allOf level', () => {
|
||||||
|
let pointer = '/definitions/PropertiesOnAllOfLevel';
|
||||||
|
let joined;
|
||||||
|
(() => joined = normalizer.normalize(specMgr.byPointer(pointer), pointer)).should.not.throw();
|
||||||
|
Object.keys(joined.properties).length.should.be.equal(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when merging schemas with different types', () => {
|
||||||
|
let pointer = '/definitions/BadAllOf1';
|
||||||
|
(() => normalizer.normalize(specMgr.byPointer(pointer), pointer)).should.throw();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle nested allOF', () => {
|
||||||
|
let pointer = '/definitions/NestedAllOf';
|
||||||
|
let joined;
|
||||||
|
(() => joined = normalizer.normalize(specMgr.byPointer(pointer), pointer)).should.not.throw();
|
||||||
|
Object.keys(joined.properties).length.should.be.equal(4);
|
||||||
|
Object.keys(joined.properties).should.be.deepEqual(['prop1', 'prop2', 'prop3', 'prop4']);
|
||||||
|
joined.required.should.be.deepEqual(['prop1', 'prop3']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
xdescribe('Merge array allOf', () => {
|
||||||
|
//emtpy
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
202
lib/services/schema-normalizer.service.ts
Normal file
202
lib/services/schema-normalizer.service.ts
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
'use strict';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { SpecManager } from '../utils/SpecManager';
|
||||||
|
import { JsonPointer } from '../utils/JsonPointer';
|
||||||
|
import { defaults } from '../utils/helpers';
|
||||||
|
|
||||||
|
interface Reference {
|
||||||
|
$ref: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Schema {
|
||||||
|
properties: any;
|
||||||
|
allOf: any;
|
||||||
|
items: any;
|
||||||
|
additionalProperties: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SchemaNormalizer {
|
||||||
|
_dereferencer:SchemaDereferencer;
|
||||||
|
constructor(private _schema:any) {
|
||||||
|
this._dereferencer = new SchemaDereferencer(_schema, this);
|
||||||
|
}
|
||||||
|
normalize(schema, ptr) {
|
||||||
|
if (schema['x-redoc-normalized']) return schema;
|
||||||
|
let res = SchemaWalker.walk(schema, ptr, (subSchema, ptr) => {
|
||||||
|
let resolved = this._dereferencer.dereference(subSchema, ptr);
|
||||||
|
if (resolved.allOf) {
|
||||||
|
resolved._pointer = resolved._pointer || ptr;
|
||||||
|
AllOfMerger.merge(resolved, resolved.allOf, {omitParent: true});
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
|
});
|
||||||
|
res['x-redoc-normalized'] = true;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SchemaWalker {
|
||||||
|
static walk(obj:Schema, pointer:string, visitor:Function) {
|
||||||
|
if (obj == undefined || typeof(obj) !== 'object') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (obj.properties) {
|
||||||
|
let ptr = JsonPointer.join(pointer, ['properties']);
|
||||||
|
SchemaWalker.walkEach(obj.properties, ptr, visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.additionalProperties) {
|
||||||
|
let ptr = JsonPointer.join(pointer, ['additionalProperties']);
|
||||||
|
if (Array.isArray(obj.additionalProperties)) {
|
||||||
|
SchemaWalker.walkEach(obj.additionalProperties, ptr, visitor);
|
||||||
|
} else {
|
||||||
|
let res = SchemaWalker.walk(obj.additionalProperties, ptr, visitor);
|
||||||
|
if (res) obj.additionalProperties = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.allOf) {
|
||||||
|
let ptr = JsonPointer.join(pointer, ['allOf']);
|
||||||
|
SchemaWalker.walkEach(obj.allOf, ptr, visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.items) {
|
||||||
|
let ptr = JsonPointer.join(pointer, ['items']);
|
||||||
|
if (Array.isArray(obj.items)) {
|
||||||
|
SchemaWalker.walkEach(obj.items, ptr, visitor);
|
||||||
|
} else {
|
||||||
|
let res = SchemaWalker.walk(obj.items, ptr, visitor);
|
||||||
|
if (res) obj.items = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return visitor(obj, pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static walkEach(obj:Object, pointer:string, visitor:Function) {
|
||||||
|
for(let key of Object.keys(obj)) {
|
||||||
|
let ptr = JsonPointer.join(pointer, [key]);
|
||||||
|
let res = SchemaWalker.walk(obj[key], ptr, visitor);
|
||||||
|
if (res) obj[key] = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AllOfMerger {
|
||||||
|
static merge(into, schemas, opts) {
|
||||||
|
into['x-derived-from'] = [];
|
||||||
|
for (let i=0; i < schemas.length; i++) {
|
||||||
|
let subSchema = schemas[i];
|
||||||
|
into['x-derived-from'].push(subSchema._pointer);
|
||||||
|
if (opts && opts.omitParent && subSchema.discriminator) continue;
|
||||||
|
|
||||||
|
AllOfMerger.checkCanMerge(subSchema, into);
|
||||||
|
|
||||||
|
into.type = into.type || subSchema.type;
|
||||||
|
if (into.type === 'object') {
|
||||||
|
AllOfMerger.mergeObject(into, subSchema, i);
|
||||||
|
}
|
||||||
|
// don't merge _pointer
|
||||||
|
subSchema._pointer = null;
|
||||||
|
defaults(into, subSchema);
|
||||||
|
}
|
||||||
|
into.allOf = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static mergeObject(into, subSchema, allOfNumber) {
|
||||||
|
if (subSchema.properties) {
|
||||||
|
if (!into.properties) into.properties = {};
|
||||||
|
Object.assign(into.properties, subSchema.properties);
|
||||||
|
Object.keys(subSchema.properties).forEach(propName => {
|
||||||
|
let prop = subSchema.properties[propName];
|
||||||
|
if (!prop._pointer) {
|
||||||
|
let schemaPtr = subSchema._pointer || JsonPointer.join(into._pointer, ['allOf', allOfNumber]);
|
||||||
|
prop._pointer = prop._pointer || JsonPointer.join(schemaPtr, ['properties', propName]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (subSchema.required) {
|
||||||
|
if (!into.required) into.required = [];
|
||||||
|
into.required.push(...subSchema.required);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static checkCanMerge(subSchema, into) {
|
||||||
|
// TODO: add support for merge array schemas
|
||||||
|
if (typeof subSchema !== 'object') {
|
||||||
|
let errMessage = `Items of allOf should be Object: ${typeof subSchema} found
|
||||||
|
${subSchema}`;
|
||||||
|
throw new Error(errMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (into.type && subSchema.type && into.type !== subSchema.type) {
|
||||||
|
let errMessage = `allOf merging error: schemas with different types can't be merged`;
|
||||||
|
throw new Error(errMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (into.type === 'array') {
|
||||||
|
console.warn('allOf: subschemas with type array are not supported yet');
|
||||||
|
}
|
||||||
|
// TODO: add check if can be merged correctly (no different properties with the same name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RefCounter {
|
||||||
|
private _counter = {};
|
||||||
|
|
||||||
|
reset():void {
|
||||||
|
this._counter = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(ref:string):void {
|
||||||
|
this._counter[ref] = this._counter[ref] ? this._counter[ref] + 1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(ref:string):void {
|
||||||
|
this._counter[ref] = this._counter[ref] && this._counter[ref] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
visited(ref:string):boolean {
|
||||||
|
return !!this._counter[ref];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SchemaDereferencer {
|
||||||
|
private _refCouner = new RefCounter();
|
||||||
|
|
||||||
|
constructor(private _spec: SpecManager, private normalizator: SchemaNormalizer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
dereference(schema: Reference, pointer:string):any {
|
||||||
|
if (!schema || !schema.$ref) return schema;
|
||||||
|
window['derefCount'] = window['derefCount'] ? window['derefCount'] + 1 : 1;
|
||||||
|
let $ref = schema.$ref;
|
||||||
|
let resolved = this._spec.byPointer($ref);
|
||||||
|
if (!this._refCouner.visited($ref)) {
|
||||||
|
resolved._pointer = $ref;
|
||||||
|
} else {
|
||||||
|
// for circular referenced save only title and type
|
||||||
|
resolved = {
|
||||||
|
title: resolved.title,
|
||||||
|
type: resolved.type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this._refCouner.visit($ref);
|
||||||
|
// if resolved schema doesn't have title use name from ref
|
||||||
|
resolved.title = resolved.title || JsonPointer.baseName($ref);
|
||||||
|
|
||||||
|
let keysCount = Object.keys(schema).length;
|
||||||
|
if ( keysCount > 2 || (keysCount === 2 && !schema.description) ) {
|
||||||
|
console.warn(`other properties defined at the same level as $ref at '${pointer}'.
|
||||||
|
They are IGNORRED according to JsonSchema spec`);
|
||||||
|
resolved.description = resolved.description || schema.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved = this.normalizator.normalize(resolved, $ref);
|
||||||
|
this._refCouner.exit($ref);
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,13 @@
|
||||||
<div class="zippy zippy-{{type}}" [ngClass]="{'zippy-empty': empty}">
|
<div class="zippy zippy-{{type}}" [ngClass]="{'zippy-empty': empty, 'zippy-hidden': !visible}">
|
||||||
<div class="zippy-title" (click)="toggle()">
|
<div *ngIf='!headless' class="zippy-title" (click)="toggle()">
|
||||||
<span class="zippy-indicator">{{ visible ? '▾' : '▸' }}</span>
|
<span class="zippy-indicator">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" viewBox="0 0 24 24" xml:space="preserve">
|
||||||
|
<polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
{{title}}
|
{{title}}
|
||||||
</div>
|
</div>
|
||||||
<div class="zippy-content" [ngClass]="{'zippy-hidden': !visible}">
|
<div class="zippy-content">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -38,12 +38,39 @@ $zippy-redirect-bg-color: rgba($zippy-redirect-color, .08);
|
||||||
background-color: $zippy-info-bg-color;
|
background-color: $zippy-info-bg-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.zippy-indicator svg {
|
||||||
|
height: 1.2em;
|
||||||
|
vertical-align: middle;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
transform: rotateZ(-180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.zippy-hidden > .zippy-title svg {
|
||||||
|
transform: rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.zippy-title polygon {
|
||||||
|
.zippy-success > & {
|
||||||
|
fill: $zippy-success-color;
|
||||||
|
}
|
||||||
|
.zippy-error > & {
|
||||||
|
fill: $zippy-error-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zippy-redirect > & {
|
||||||
|
fill: $zippy-redirect-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zippy-info > & {
|
||||||
|
fill: $zippy-info-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
span.zippy-indicator {
|
span.zippy-indicator {
|
||||||
|
width: 1em;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
margin-right: 0.2em;
|
text-align: center;
|
||||||
position: relative;
|
display: inline-block;
|
||||||
top: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.zippy-content {
|
.zippy-content {
|
||||||
|
@ -56,7 +83,13 @@ span.zippy-indicator {
|
||||||
}
|
}
|
||||||
|
|
||||||
.zippy-indicator {
|
.zippy-indicator {
|
||||||
display: none;
|
svg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
content: "—";
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.zippy-content {
|
.zippy-content {
|
||||||
|
@ -64,7 +97,7 @@ span.zippy-indicator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.zippy-hidden {
|
.zippy-hidden > .zippy-content {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
|
@ -14,12 +14,16 @@ export class Zippy {
|
||||||
@Input() visible = false;
|
@Input() visible = false;
|
||||||
@Input() empty = false;
|
@Input() empty = false;
|
||||||
@Input() title;
|
@Input() title;
|
||||||
|
@Input() headless: boolean = false;
|
||||||
@Output() open = new EventEmitter();
|
@Output() open = new EventEmitter();
|
||||||
@Output() close = new EventEmitter();
|
@Output() close = new EventEmitter();
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
this.visible = !this.visible;
|
this.visible = !this.visible;
|
||||||
if (this.empty) return;
|
if (this.empty) return;
|
||||||
(this.visible) ? this.open.next({}) : this.close.next({});
|
if (this.visible) {
|
||||||
|
this.open.next({});
|
||||||
|
} else {
|
||||||
|
this.close.next({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
import { isBlank } from '@angular/core/src/facade/lang';
|
import { isBlank } from '@angular/core/src/facade/lang';
|
||||||
|
import { DomSanitizationService } from '@angular/platform-browser';
|
||||||
|
|
||||||
var level = 1;
|
var level = 1;
|
||||||
const COLLAPSE_LEVEL = 2;
|
const COLLAPSE_LEVEL = 2;
|
||||||
|
|
||||||
@Pipe({ name: 'jsonFormatter' })
|
@Pipe({ name: 'jsonFormatter' })
|
||||||
export class JsonFormatter implements PipeTransform {
|
export class JsonFormatter implements PipeTransform {
|
||||||
|
constructor(private sanitizer: DomSanitizationService) {}
|
||||||
transform(value) {
|
transform(value) {
|
||||||
if (isBlank(value)) return value;
|
if (isBlank(value)) return value;
|
||||||
return jsonToHTML(value);
|
return this.sanitizer.bypassSecurityTrustHtml(jsonToHTML(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,21 @@ import JsonSchemaRefParser from 'json-schema-ref-parser';
|
||||||
import JsonPointer from './JsonPointer';
|
import JsonPointer from './JsonPointer';
|
||||||
import {methods as swaggerMethods} from './swagger-defs';
|
import {methods as swaggerMethods} from './swagger-defs';
|
||||||
|
|
||||||
export class SchemaManager {
|
export class SpecManager {
|
||||||
public _schema:any = {};
|
public _schema:any = {};
|
||||||
public apiUrl: string;
|
public apiUrl: string;
|
||||||
private _instance:any;
|
private _instance:any;
|
||||||
|
|
||||||
static instance() {
|
static instance() {
|
||||||
return new SchemaManager();
|
return new SpecManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (SchemaManager.prototype._instance) {
|
if (SpecManager.prototype._instance) {
|
||||||
return SchemaManager.prototype._instance;
|
return SpecManager.prototype._instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
SchemaManager.prototype._instance = this;
|
SpecManager.prototype._instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
load(url) {
|
load(url) {
|
||||||
|
@ -26,14 +26,11 @@ export class SchemaManager {
|
||||||
this._schema = {};
|
this._schema = {};
|
||||||
|
|
||||||
JsonSchemaRefParser.bundle(url, {http: {withCredentials: false}})
|
JsonSchemaRefParser.bundle(url, {http: {withCredentials: false}})
|
||||||
.then(
|
.then(schema => {
|
||||||
(schema) => {
|
this._schema = schema;
|
||||||
this._schema = schema;
|
resolve(this._schema);
|
||||||
resolve(this._schema);
|
this.init();
|
||||||
this.init();
|
}, err => reject(err));
|
||||||
},
|
|
||||||
(err) => reject(err)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
|
@ -49,14 +46,12 @@ export class SchemaManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
get schema() {
|
get schema() {
|
||||||
// TODO: consider returning promise
|
|
||||||
return this._schema;
|
return this._schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
byPointer(pointer) {
|
byPointer(pointer) {
|
||||||
let res = null;
|
let res = null;
|
||||||
try {
|
try {
|
||||||
// TODO: remove decodeURIComponent after this issue is fixed: https://github.com/BigstickCarpet/swagger-parser/issues/31
|
|
||||||
res = JsonPointer.get(this._schema, decodeURIComponent(pointer));
|
res = JsonPointer.get(this._schema, decodeURIComponent(pointer));
|
||||||
} catch(e) {/*skip*/ }
|
} catch(e) {/*skip*/ }
|
||||||
return res;
|
return res;
|
||||||
|
@ -173,13 +168,11 @@ export class SchemaManager {
|
||||||
let globalDefs = this._schema.definitions || {};
|
let globalDefs = this._schema.definitions || {};
|
||||||
let res = [];
|
let res = [];
|
||||||
for (let defName of Object.keys(globalDefs)) {
|
for (let defName of Object.keys(globalDefs)) {
|
||||||
if (!globalDefs[defName].allOf) continue;
|
if (!globalDefs[defName].allOf &&
|
||||||
|
!globalDefs[defName]['x-derived-from']) continue;
|
||||||
let subTypes = globalDefs[defName].allOf;
|
let subTypes = globalDefs[defName]['x-derived-from'] ||
|
||||||
let idx = subTypes.findIndex((subType) => {
|
globalDefs[defName].allOf.map(subType => subType.$ref);
|
||||||
if (subType.$ref === defPointer) return true;
|
let idx = subTypes.findIndex(ref => ref === defPointer);
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (idx < 0) continue;
|
if (idx < 0) continue;
|
||||||
|
|
||||||
let empty = false;
|
let empty = false;
|
|
@ -14,3 +14,18 @@ export function statusCodeType(statusCode) {
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function defaults(target, src) {
|
||||||
|
var props = Object.keys(src);
|
||||||
|
|
||||||
|
var index = -1,
|
||||||
|
length = props.length;
|
||||||
|
|
||||||
|
while (++index < length) {
|
||||||
|
var key = props[index];
|
||||||
|
if (target[key] === undefined) {
|
||||||
|
target[key] = src[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { DomSanitizationService } from '@angular/platform-browser';
|
||||||
import { isString, stringify, isBlank } from '@angular/core/src/facade/lang';
|
import { isString, stringify, isBlank } from '@angular/core/src/facade/lang';
|
||||||
import { BaseException } from '@angular/core/src/facade/exceptions';
|
import { BaseException } from '@angular/core/src/facade/exceptions';
|
||||||
import JsonPointer from './JsonPointer';
|
import JsonPointer from './JsonPointer';
|
||||||
|
|
||||||
declare var Prism: any;
|
declare var Prism: any;
|
||||||
|
|
||||||
import marked from 'marked';
|
import Remarkable from 'remarkable';
|
||||||
|
|
||||||
// in gfm mode marked doesn't parse #Heading (without space after #) as heading
|
const md = new Remarkable({
|
||||||
// https://github.com/chjj/marked/issues/642
|
html: true,
|
||||||
marked['Lexer'].rules.gfm.heading = marked['Lexer'].rules.normal.heading;
|
linkify: true,
|
||||||
marked['Lexer'].rules.tables.heading = marked['Lexer'].rules.normal.heading;
|
|
||||||
|
|
||||||
marked.setOptions({
|
|
||||||
renderer: new marked.Renderer(),
|
|
||||||
gfm: true,
|
|
||||||
tables: true,
|
|
||||||
breaks: false,
|
breaks: false,
|
||||||
pedantic: false,
|
typographer: false,
|
||||||
smartLists: true,
|
highlight: (str, lang) => {
|
||||||
smartypants: false
|
if (lang === 'json') lang = 'js';
|
||||||
|
let grammar = Prism.languages[lang];
|
||||||
|
//fallback to clike
|
||||||
|
if (!grammar) return str;
|
||||||
|
return Prism.highlight(str, grammar);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class InvalidPipeArgumentException extends BaseException {
|
class InvalidPipeArgumentException extends BaseException {
|
||||||
|
@ -65,12 +65,16 @@ export class JsonPointerEscapePipe implements PipeTransform {
|
||||||
|
|
||||||
@Pipe({ name: 'marked' })
|
@Pipe({ name: 'marked' })
|
||||||
export class MarkedPipe implements PipeTransform {
|
export class MarkedPipe implements PipeTransform {
|
||||||
|
constructor(private sanitizer: DomSanitizationService) {}
|
||||||
transform(value) {
|
transform(value) {
|
||||||
if (isBlank(value)) return value;
|
if (isBlank(value)) return value;
|
||||||
if (!isString(value)) {
|
if (!isString(value)) {
|
||||||
throw new InvalidPipeArgumentException(JsonPointerEscapePipe, value);
|
throw new InvalidPipeArgumentException(JsonPointerEscapePipe, value);
|
||||||
}
|
}
|
||||||
return `<span class="redoc-markdown-block">${marked(value)}</span>`;
|
|
||||||
|
return this.sanitizer.bypassSecurityTrustHtml(
|
||||||
|
`<span class="redoc-markdown-block">${md.render(value)}</span>`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +88,7 @@ const langMap = {
|
||||||
|
|
||||||
@Pipe({ name: 'prism' })
|
@Pipe({ name: 'prism' })
|
||||||
export class PrismPipe implements PipeTransform {
|
export class PrismPipe implements PipeTransform {
|
||||||
|
constructor(private sanitizer: DomSanitizationService) {}
|
||||||
transform(value, args) {
|
transform(value, args) {
|
||||||
if (isBlank(args) || args.length === 0) {
|
if (isBlank(args) || args.length === 0) {
|
||||||
throw new BaseException('Prism pipe requires one argument');
|
throw new BaseException('Prism pipe requires one argument');
|
||||||
|
@ -98,7 +103,7 @@ export class PrismPipe implements PipeTransform {
|
||||||
let grammar = Prism.languages[lang];
|
let grammar = Prism.languages[lang];
|
||||||
//fallback to clike
|
//fallback to clike
|
||||||
if (!grammar) grammar = Prism.languages.clike;
|
if (!grammar) grammar = Prism.languages.clike;
|
||||||
return Prism.highlight(value, grammar);
|
return this.sanitizer.bypassSecurityTrustHtml(Prism.highlight(value, grammar));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
46
package.json
46
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"description": "Swagger-generated API Reference Documentation",
|
"description": "Swagger-generated API Reference Documentation",
|
||||||
"version": "0.14.0",
|
"version": "0.15.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/Rebilly/ReDoc"
|
"url": "git://github.com/Rebilly/ReDoc"
|
||||||
|
@ -32,20 +32,26 @@
|
||||||
"jspm": {
|
"jspm": {
|
||||||
"configFile": "system.config.js",
|
"configFile": "system.config.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/common": "npm:@angular/common@^2.0.0-rc.1",
|
"@angular/common": "npm:@angular/common@^2.0.0-rc.2",
|
||||||
"@angular/compiler": "npm:@angular/compiler@^2.0.0-rc.1",
|
"@angular/common@2.0.0-rc.3": "npm:@angular/common@2.0.0-rc.3",
|
||||||
"@angular/core": "npm:@angular/core@^2.0.0-rc.1",
|
"@angular/compiler": "npm:@angular/compiler@^2.0.0-rc.2",
|
||||||
"@angular/platform-browser": "npm:@angular/platform-browser@^2.0.0-rc.1",
|
"@angular/compiler@2.0.0-rc.3": "npm:@angular/compiler@2.0.0-rc.3",
|
||||||
"@angular/platform-browser-dynamic": "npm:@angular/platform-browser-dynamic@^2.0.0-rc.1",
|
"@angular/core": "npm:@angular/core@^2.0.0-rc.2",
|
||||||
|
"@angular/core@2.0.0-rc.3": "npm:@angular/core@2.0.0-rc.3",
|
||||||
|
"@angular/platform-browser": "npm:@angular/platform-browser@^2.0.0-rc.2",
|
||||||
|
"@angular/platform-browser-dynamic": "npm:@angular/platform-browser-dynamic@^2.0.0-rc.2",
|
||||||
|
"@angular/platform-browser-dynamic@2.0.0-rc.3": "npm:@angular/platform-browser-dynamic@2.0.0-rc.3",
|
||||||
|
"@angular/platform-browser@2.0.0-rc.3": "npm:@angular/platform-browser@2.0.0-rc.3",
|
||||||
|
"@angular/platform-server@2.0.0-rc.3": "npm:@angular/platform-server@2.0.0-rc.3",
|
||||||
"dropkickjs": "npm:dropkickjs@^2.1.8",
|
"dropkickjs": "npm:dropkickjs@^2.1.8",
|
||||||
"es6-shim": "github:es-shims/es6-shim@^0.33.6",
|
"es6-shim": "github:es-shims/es6-shim@^0.33.6",
|
||||||
"hint.css": "npm:hint.css@^2.2.1",
|
"hint.css": "npm:hint.css@^2.2.1",
|
||||||
"json": "github:systemjs/plugin-json@^0.1.0",
|
"json": "github:systemjs/plugin-json@^0.1.0",
|
||||||
"json-pointer": "npm:json-pointer@^0.3.0",
|
"json-pointer": "npm:json-pointer@^0.3.0",
|
||||||
"json-schema-ref-parser": "npm:json-schema-ref-parser@^3.1.2",
|
"json-schema-ref-parser": "npm:json-schema-ref-parser@^3.1.2",
|
||||||
"marked": "npm:marked@^0.3.5",
|
|
||||||
"openapi-sampler": "npm:openapi-sampler@0.2.0",
|
"openapi-sampler": "npm:openapi-sampler@0.2.0",
|
||||||
"prismjs": "npm:prismjs@^1.3.0",
|
"prismjs": "npm:prismjs@^1.3.0",
|
||||||
|
"remarkable": "npm:remarkable@^1.6.2",
|
||||||
"rxjs": "npm:rxjs@5.0.0-beta.6",
|
"rxjs": "npm:rxjs@5.0.0-beta.6",
|
||||||
"scrollparent": "npm:scrollparent@^0.1.0",
|
"scrollparent": "npm:scrollparent@^0.1.0",
|
||||||
"stream-http": "npm:stream-http@^2.3.0",
|
"stream-http": "npm:stream-http@^2.3.0",
|
||||||
|
@ -76,13 +82,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/common": "2.0.0-rc.1",
|
"@angular/common": "^2.0.0-rc.3",
|
||||||
"@angular/compiler": "2.0.0-rc.1",
|
"@angular/compiler": "^2.0.0-rc.3",
|
||||||
"@angular/core": "2.0.0-rc.1",
|
"@angular/core": "^2.0.0-rc.2",
|
||||||
"@angular/platform-browser": "2.0.0-rc.1",
|
"@angular/platform-browser": "^2.0.0-rc.3",
|
||||||
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
|
"@angular/platform-browser-dynamic": "^2.0.0-rc.3",
|
||||||
"@angular/platform-server": "2.0.0-rc.1",
|
"@angular/platform-server": "^2.0.0-rc.3",
|
||||||
"babel-eslint": "^4.1.6",
|
|
||||||
"babel-polyfill": "^6.3.14",
|
"babel-polyfill": "^6.3.14",
|
||||||
"branch-release": "^1.0.3",
|
"branch-release": "^1.0.3",
|
||||||
"browser-sync": "^2.10.1",
|
"browser-sync": "^2.10.1",
|
||||||
|
@ -92,9 +97,8 @@
|
||||||
"deploy-to-gh-pages": "^1.1.2",
|
"deploy-to-gh-pages": "^1.1.2",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-concat": "^2.6.0",
|
"gulp-concat": "^2.6.0",
|
||||||
"gulp-eslint": "^1.1.1",
|
|
||||||
"gulp-if": "^2.0.1",
|
"gulp-if": "^2.0.1",
|
||||||
"gulp-inline-ng2-template": "^1.1.5",
|
"gulp-inline-ng2-template": "^2.0.4",
|
||||||
"gulp-protractor": "^2.1.0",
|
"gulp-protractor": "^2.1.0",
|
||||||
"gulp-rename": "^1.2.2",
|
"gulp-rename": "^1.2.2",
|
||||||
"gulp-replace": "^0.5.4",
|
"gulp-replace": "^0.5.4",
|
||||||
|
@ -106,14 +110,13 @@
|
||||||
"jasmine-core": "^2.4.1",
|
"jasmine-core": "^2.4.1",
|
||||||
"jasmine-spec-reporter": "^2.4.0",
|
"jasmine-spec-reporter": "^2.4.0",
|
||||||
"json-pointer": "^0.5.0",
|
"json-pointer": "^0.5.0",
|
||||||
"json-schema-instantiator": "^0.3.0",
|
|
||||||
"json-schema-ref-parser": "^3.1.2",
|
"json-schema-ref-parser": "^3.1.2",
|
||||||
"jspm": "^0.16.36",
|
"jspm": "^0.16.36",
|
||||||
"karma": "^0.13.15",
|
"karma": "^0.13.15",
|
||||||
"karma-babel-preprocessor": "^5.2.2",
|
"karma-babel-preprocessor": "^5.2.2",
|
||||||
"karma-chrome-launcher": "^0.2.2",
|
"karma-chrome-launcher": "^1.0.1",
|
||||||
"karma-coverage": "github:douglasduteil/karma-coverage#next",
|
"karma-coverage": "github:douglasduteil/karma-coverage#next",
|
||||||
"karma-jasmine": "^0.3.6",
|
"karma-jasmine": "^1.0.2",
|
||||||
"karma-jspm": "^2.1.1",
|
"karma-jspm": "^2.1.1",
|
||||||
"karma-mocha-reporter": "^2.0.0",
|
"karma-mocha-reporter": "^2.0.0",
|
||||||
"karma-phantomjs-launcher": "^1.0.0",
|
"karma-phantomjs-launcher": "^1.0.0",
|
||||||
|
@ -121,20 +124,19 @@
|
||||||
"karma-regex-preprocessor": "github:makern/karma-regex-preprocessor",
|
"karma-regex-preprocessor": "github:makern/karma-regex-preprocessor",
|
||||||
"karma-should": "^1.0.0",
|
"karma-should": "^1.0.0",
|
||||||
"karma-sinon": "^1.0.4",
|
"karma-sinon": "^1.0.4",
|
||||||
"marked": "^0.3.5",
|
|
||||||
"node-sass": "^3.7.0",
|
"node-sass": "^3.7.0",
|
||||||
"openapi-sampler": "^0.2.0",
|
"openapi-sampler": "^0.2.0",
|
||||||
"phantomjs-prebuilt": "^2.1.7",
|
"phantomjs-prebuilt": "^2.1.7",
|
||||||
"protractor": "^3.0.0",
|
"protractor": "^3.0.0",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"remap-istanbul": "^0.6.4",
|
"remap-istanbul": "^0.6.4",
|
||||||
|
"remarkable": "^1.6.2",
|
||||||
"require-dir": "^0.3.0",
|
"require-dir": "^0.3.0",
|
||||||
"rollup-plugin-commonjs": "^2.2.1",
|
|
||||||
"run-sequence": "^1.1.5",
|
"run-sequence": "^1.1.5",
|
||||||
"rxjs": "5.0.0-beta.6",
|
"rxjs": "5.0.0-beta.6",
|
||||||
"scrollparent": "^1.0.0",
|
"scrollparent": "^1.0.0",
|
||||||
"shelljs": "^0.7.0",
|
"shelljs": "^0.7.0",
|
||||||
"should": "^8.0.2",
|
"should": "^9.0.2",
|
||||||
"sinon": "^1.17.2",
|
"sinon": "^1.17.2",
|
||||||
"systemjs-builder": "^0.15.16",
|
"systemjs-builder": "^0.15.16",
|
||||||
"tslint": "^3.11.0",
|
"tslint": "^3.11.0",
|
||||||
|
|
|
@ -16,11 +16,17 @@ System.config({
|
||||||
},
|
},
|
||||||
|
|
||||||
map: {
|
map: {
|
||||||
"@angular/common": "npm:@angular/common@2.0.0-rc.1",
|
"@angular/common": "npm:@angular/common@2.0.0-rc.3",
|
||||||
"@angular/compiler": "npm:@angular/compiler@2.0.0-rc.1",
|
"@angular/common@2.0.0-rc.3": "npm:@angular/common@2.0.0-rc.3",
|
||||||
"@angular/core": "npm:@angular/core@2.0.0-rc.1",
|
"@angular/compiler": "npm:@angular/compiler@2.0.0-rc.3",
|
||||||
"@angular/platform-browser": "npm:@angular/platform-browser@2.0.0-rc.1",
|
"@angular/compiler@2.0.0-rc.3": "npm:@angular/compiler@2.0.0-rc.3",
|
||||||
"@angular/platform-browser-dynamic": "npm:@angular/platform-browser-dynamic@2.0.0-rc.1",
|
"@angular/core": "npm:@angular/core@2.0.0-rc.3",
|
||||||
|
"@angular/core@2.0.0-rc.3": "npm:@angular/core@2.0.0-rc.3",
|
||||||
|
"@angular/platform-browser": "npm:@angular/platform-browser@2.0.0-rc.3",
|
||||||
|
"@angular/platform-browser-dynamic": "npm:@angular/platform-browser-dynamic@2.0.0-rc.3",
|
||||||
|
"@angular/platform-browser-dynamic@2.0.0-rc.3": "npm:@angular/platform-browser-dynamic@2.0.0-rc.3",
|
||||||
|
"@angular/platform-browser@2.0.0-rc.3": "npm:@angular/platform-browser@2.0.0-rc.3",
|
||||||
|
"@angular/platform-server@2.0.0-rc.3": "npm:@angular/platform-server@2.0.0-rc.3",
|
||||||
"babel": "npm:babel-core@5.8.34",
|
"babel": "npm:babel-core@5.8.34",
|
||||||
"babel-runtime": "npm:babel-runtime@5.8.34",
|
"babel-runtime": "npm:babel-runtime@5.8.34",
|
||||||
"clean-css": "npm:clean-css@3.4.17",
|
"clean-css": "npm:clean-css@3.4.17",
|
||||||
|
@ -33,9 +39,9 @@ System.config({
|
||||||
"json-formatter-js": "npm:json-formatter-js@0.2.0",
|
"json-formatter-js": "npm:json-formatter-js@0.2.0",
|
||||||
"json-pointer": "npm:json-pointer@0.3.0",
|
"json-pointer": "npm:json-pointer@0.3.0",
|
||||||
"json-schema-ref-parser": "npm:json-schema-ref-parser@3.1.2",
|
"json-schema-ref-parser": "npm:json-schema-ref-parser@3.1.2",
|
||||||
"marked": "npm:marked@0.3.5",
|
|
||||||
"openapi-sampler": "npm:openapi-sampler@0.2.0",
|
"openapi-sampler": "npm:openapi-sampler@0.2.0",
|
||||||
"prismjs": "npm:prismjs@1.3.0",
|
"prismjs": "npm:prismjs@1.3.0",
|
||||||
|
"remarkable": "npm:remarkable@1.6.2",
|
||||||
"rxjs": "npm:rxjs@5.0.0-beta.6",
|
"rxjs": "npm:rxjs@5.0.0-beta.6",
|
||||||
"scrollparent": "npm:scrollparent@0.1.0",
|
"scrollparent": "npm:scrollparent@0.1.0",
|
||||||
"stream-http": "npm:stream-http@2.3.0",
|
"stream-http": "npm:stream-http@2.3.0",
|
||||||
|
@ -116,38 +122,54 @@ System.config({
|
||||||
"github:jspm/nodelibs-zlib@0.1.0": {
|
"github:jspm/nodelibs-zlib@0.1.0": {
|
||||||
"browserify-zlib": "npm:browserify-zlib@0.1.4"
|
"browserify-zlib": "npm:browserify-zlib@0.1.4"
|
||||||
},
|
},
|
||||||
"npm:@angular/common@2.0.0-rc.1": {
|
"npm:@angular/common@2.0.0-rc.3": {
|
||||||
"@angular/core": "npm:@angular/core@2.0.0-rc.1",
|
"@angular/core": "npm:@angular/core@2.0.0-rc.3",
|
||||||
"process": "github:jspm/nodelibs-process@0.1.2"
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
},
|
},
|
||||||
"npm:@angular/compiler@2.0.0-rc.1": {
|
"npm:@angular/compiler@2.0.0-rc.3": {
|
||||||
"@angular/core": "npm:@angular/core@2.0.0-rc.1",
|
"@angular/core": "npm:@angular/core@2.0.0-rc.3",
|
||||||
"process": "github:jspm/nodelibs-process@0.1.2"
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
},
|
},
|
||||||
"npm:@angular/core@2.0.0-rc.1": {
|
"npm:@angular/core@2.0.0-rc.3": {
|
||||||
"process": "github:jspm/nodelibs-process@0.1.2",
|
"process": "github:jspm/nodelibs-process@0.1.2",
|
||||||
"rxjs": "npm:rxjs@5.0.0-beta.6",
|
"rxjs": "npm:rxjs@5.0.0-beta.6",
|
||||||
"zone.js": "npm:zone.js@0.6.12"
|
"zone.js": "npm:zone.js@0.6.12"
|
||||||
},
|
},
|
||||||
"npm:@angular/platform-browser-dynamic@2.0.0-rc.1": {
|
"npm:@angular/platform-browser-dynamic@2.0.0-rc.3": {
|
||||||
"@angular/common": "npm:@angular/common@2.0.0-rc.1",
|
"@angular/common": "npm:@angular/common@2.0.0-rc.3",
|
||||||
"@angular/compiler": "npm:@angular/compiler@2.0.0-rc.1",
|
"@angular/compiler": "npm:@angular/compiler@2.0.0-rc.3",
|
||||||
"@angular/core": "npm:@angular/core@2.0.0-rc.1",
|
"@angular/core": "npm:@angular/core@2.0.0-rc.3",
|
||||||
"@angular/platform-browser": "npm:@angular/platform-browser@2.0.0-rc.1",
|
"@angular/platform-browser": "npm:@angular/platform-browser@2.0.0-rc.3",
|
||||||
"process": "github:jspm/nodelibs-process@0.1.2"
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
},
|
},
|
||||||
"npm:@angular/platform-browser@2.0.0-rc.1": {
|
"npm:@angular/platform-browser@2.0.0-rc.3": {
|
||||||
"@angular/common": "npm:@angular/common@2.0.0-rc.1",
|
"@angular/common": "npm:@angular/common@2.0.0-rc.3",
|
||||||
"@angular/compiler": "npm:@angular/compiler@2.0.0-rc.1",
|
"@angular/compiler": "npm:@angular/compiler@2.0.0-rc.3",
|
||||||
"@angular/core": "npm:@angular/core@2.0.0-rc.1",
|
"@angular/core": "npm:@angular/core@2.0.0-rc.3",
|
||||||
"process": "github:jspm/nodelibs-process@0.1.2"
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
},
|
},
|
||||||
|
"npm:@angular/platform-server@2.0.0-rc.3": {
|
||||||
|
"@angular/common": "npm:@angular/common@2.0.0-rc.3",
|
||||||
|
"@angular/compiler": "npm:@angular/compiler@2.0.0-rc.3",
|
||||||
|
"@angular/core": "npm:@angular/core@2.0.0-rc.3",
|
||||||
|
"@angular/platform-browser": "npm:@angular/platform-browser@2.0.0-rc.3",
|
||||||
|
"parse5": "npm:parse5@1.3.2"
|
||||||
|
},
|
||||||
"npm:amdefine@1.0.0": {
|
"npm:amdefine@1.0.0": {
|
||||||
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||||
"module": "github:jspm/nodelibs-module@0.1.0",
|
"module": "github:jspm/nodelibs-module@0.1.0",
|
||||||
"path": "github:jspm/nodelibs-path@0.1.0",
|
"path": "github:jspm/nodelibs-path@0.1.0",
|
||||||
"process": "github:jspm/nodelibs-process@0.1.2"
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
},
|
},
|
||||||
|
"npm:argparse@0.1.16": {
|
||||||
|
"assert": "github:jspm/nodelibs-assert@0.1.0",
|
||||||
|
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||||
|
"path": "github:jspm/nodelibs-path@0.1.0",
|
||||||
|
"process": "github:jspm/nodelibs-process@0.1.2",
|
||||||
|
"underscore": "npm:underscore@1.7.0",
|
||||||
|
"underscore.string": "npm:underscore.string@2.4.0",
|
||||||
|
"util": "github:jspm/nodelibs-util@0.1.0"
|
||||||
|
},
|
||||||
"npm:argparse@1.0.7": {
|
"npm:argparse@1.0.7": {
|
||||||
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||||
"path": "github:jspm/nodelibs-path@0.1.0",
|
"path": "github:jspm/nodelibs-path@0.1.0",
|
||||||
|
@ -193,6 +215,10 @@ System.config({
|
||||||
"npm:async@1.5.2": {
|
"npm:async@1.5.2": {
|
||||||
"process": "github:jspm/nodelibs-process@0.1.2"
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
},
|
},
|
||||||
|
"npm:autolinker@0.15.3": {
|
||||||
|
"child_process": "github:jspm/nodelibs-child_process@0.1.0",
|
||||||
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
|
},
|
||||||
"npm:aws-sign2@0.6.0": {
|
"npm:aws-sign2@0.6.0": {
|
||||||
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
|
"crypto": "github:jspm/nodelibs-crypto@0.1.0",
|
||||||
"url": "github:jspm/nodelibs-url@0.1.0"
|
"url": "github:jspm/nodelibs-url@0.1.0"
|
||||||
|
@ -660,6 +686,9 @@ System.config({
|
||||||
"pbkdf2": "npm:pbkdf2@3.0.4",
|
"pbkdf2": "npm:pbkdf2@3.0.4",
|
||||||
"systemjs-json": "github:systemjs/plugin-json@0.1.2"
|
"systemjs-json": "github:systemjs/plugin-json@0.1.2"
|
||||||
},
|
},
|
||||||
|
"npm:parse5@1.3.2": {
|
||||||
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
|
},
|
||||||
"npm:path-browserify@0.0.0": {
|
"npm:path-browserify@0.0.0": {
|
||||||
"process": "github:jspm/nodelibs-process@0.1.2"
|
"process": "github:jspm/nodelibs-process@0.1.2"
|
||||||
},
|
},
|
||||||
|
@ -743,6 +772,16 @@ System.config({
|
||||||
"string_decoder": "npm:string_decoder@0.10.31",
|
"string_decoder": "npm:string_decoder@0.10.31",
|
||||||
"util-deprecate": "npm:util-deprecate@1.0.2"
|
"util-deprecate": "npm:util-deprecate@1.0.2"
|
||||||
},
|
},
|
||||||
|
"npm:remarkable@1.6.2": {
|
||||||
|
"argparse": "npm:argparse@0.1.16",
|
||||||
|
"autolinker": "npm:autolinker@0.15.3",
|
||||||
|
"buffer": "github:jspm/nodelibs-buffer@0.1.0",
|
||||||
|
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||||
|
"path": "github:jspm/nodelibs-path@0.1.0",
|
||||||
|
"process": "github:jspm/nodelibs-process@0.1.2",
|
||||||
|
"systemjs-json": "github:systemjs/plugin-json@0.1.2",
|
||||||
|
"util": "github:jspm/nodelibs-util@0.1.0"
|
||||||
|
},
|
||||||
"npm:request@2.72.0": {
|
"npm:request@2.72.0": {
|
||||||
"aws-sign2": "npm:aws-sign2@0.6.0",
|
"aws-sign2": "npm:aws-sign2@0.6.0",
|
||||||
"aws4": "npm:aws4@1.4.1",
|
"aws4": "npm:aws4@1.4.1",
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<script>
|
<script>
|
||||||
window.redocError = null;
|
window.redocError = null;
|
||||||
/* init redoc */
|
/* init redoc */
|
||||||
var url = window.location.search.substr(5) || 'http://rebilly.github.io:80/SwaggerTemplateRepo/swagger.json';
|
var url = window.location.search.substr(5) || 'http://rebilly.github.io/SwaggerTemplateRepo/swagger.json';
|
||||||
Redoc.init(decodeURIComponent(url), {disableLazySchemas: true}).then(function() {}, function(err) {
|
Redoc.init(decodeURIComponent(url), {disableLazySchemas: true}).then(function() {}, function(err) {
|
||||||
window.redocError = err;
|
window.redocError = err;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
console.log('here');
|
|
||||||
const verifyNoBrowserErrors = require('./helpers').verifyNoBrowserErrors;
|
const verifyNoBrowserErrors = require('./helpers').verifyNoBrowserErrors;
|
||||||
const scrollToEl = require('./helpers').scrollToEl;
|
const scrollToEl = require('./helpers').scrollToEl;
|
||||||
const fixFFTest = require('./helpers').fixFFTest;
|
const fixFFTest = require('./helpers').fixFFTest;
|
||||||
|
@ -92,7 +91,8 @@ if (process.env.JOB === 'e2e-guru') {
|
||||||
delete apisGuruList['googleapis.com:mirror']; // bad urls in images
|
delete apisGuruList['googleapis.com:mirror']; // bad urls in images
|
||||||
delete apisGuruList['googleapis.com:discovery']; // non-string references
|
delete apisGuruList['googleapis.com:discovery']; // non-string references
|
||||||
delete apisGuruList['clarify.io']; // non-string references
|
delete apisGuruList['clarify.io']; // non-string references
|
||||||
delete apisGuruList['pushpay.com']; // https://github.com/Rebilly/ReDoc/issues/30
|
//delete apisGuruList['pushpay.com']; // https://github.com/Rebilly/ReDoc/issues/30
|
||||||
|
delete apisGuruList['bbci.co.uk']; // too big
|
||||||
|
|
||||||
// run quick version of e2e test on all builds except releases
|
// run quick version of e2e test on all builds except releases
|
||||||
if (process.env.TRAVIS && !process.env.TRAVIS_TAG) {
|
if (process.env.TRAVIS && !process.env.TRAVIS_TAG) {
|
||||||
|
|
|
@ -27,16 +27,26 @@
|
||||||
"ArrayOfSimple": {
|
"ArrayOfSimple": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"schema": {
|
"$ref": "#/definitions/Simple"
|
||||||
"$ref": "#/definitions/Simple"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Circular": {
|
"Circular": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"schema": {
|
"$ref": "#/definitions/Circular"
|
||||||
"$ref": "#/definitions/Circular"
|
}
|
||||||
|
},
|
||||||
|
"CircularTransitive": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"$ref": "#/definitions/CircularTransitive2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CircularTransitive2": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"additionalProperties": {
|
||||||
|
"$ref": "#/definitions/CircularTransitive"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +103,18 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"test6": {
|
||||||
|
"get": {
|
||||||
|
"summary": "test get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/CircularTransitive",
|
||||||
|
"title": "test",
|
||||||
|
"description": "test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { SchemaManager } from '../../lib/utils/SchemaManager';
|
import { SpecManager } from '../../lib/utils/SpecManager';
|
||||||
describe('Utils', () => {
|
describe('Utils', () => {
|
||||||
describe('Schema manager', () => {
|
describe('Schema manager', () => {
|
||||||
let schemaMgr;
|
let specMgr;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
schemaMgr = new SchemaManager();
|
specMgr = new SpecManager();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be a singleton', ()=> {
|
it('Should be a singleton', ()=> {
|
||||||
(new SchemaManager()).should.be.equal(schemaMgr);
|
(new SpecManager()).should.be.equal(specMgr);
|
||||||
SchemaManager.instance().should.be.equal(schemaMgr);
|
SpecManager.instance().should.be.equal(specMgr);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('load should return a promise', ()=> {
|
it('load should return a promise', ()=> {
|
||||||
schemaMgr.load('/tests/schemas/extended-petstore.yml').should.be.instanceof(Promise);
|
specMgr.load('/tests/schemas/extended-petstore.yml').should.be.instanceof(Promise);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('load should reject promise for invalid url', (done)=> {
|
it('load should reject promise for invalid url', (done)=> {
|
||||||
schemaMgr.load('/nonexisting/schema.json').then(() => {
|
specMgr.load('/nonexisting/schema.json').then(() => {
|
||||||
throw new Error('Succees handler should not be called');
|
throw new Error('Succees handler should not be called');
|
||||||
}, () => {
|
}, () => {
|
||||||
done();
|
done();
|
||||||
|
@ -27,7 +27,7 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('load should resolve promise for valid url', (done)=> {
|
it('load should resolve promise for valid url', (done)=> {
|
||||||
schemaMgr.load('/tests/schemas/extended-petstore.yml').then(() => {
|
specMgr.load('/tests/schemas/extended-petstore.yml').then(() => {
|
||||||
done();
|
done();
|
||||||
}, () => {
|
}, () => {
|
||||||
throw new Error('Error handler should not be called');
|
throw new Error('Error handler should not be called');
|
||||||
|
@ -36,7 +36,7 @@ describe('Utils', () => {
|
||||||
|
|
||||||
describe('Schema manager basic functionality', ()=> {
|
describe('Schema manager basic functionality', ()=> {
|
||||||
beforeAll(function (done) {
|
beforeAll(function (done) {
|
||||||
schemaMgr.load('/tests/schemas/extended-petstore.yml').then(() => {
|
specMgr.load('/tests/schemas/extended-petstore.yml').then(() => {
|
||||||
done();
|
done();
|
||||||
}, () => {
|
}, () => {
|
||||||
throw new Error('Error handler should not be called');
|
throw new Error('Error handler should not be called');
|
||||||
|
@ -45,23 +45,23 @@ describe('Utils', () => {
|
||||||
|
|
||||||
|
|
||||||
it('should contain non-empty schema', ()=> {
|
it('should contain non-empty schema', ()=> {
|
||||||
schemaMgr.schema.should.be.an.Object();
|
specMgr.schema.should.be.an.Object();
|
||||||
schemaMgr.schema.should.be.not.empty();
|
specMgr.schema.should.be.not.empty();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly init api url', ()=> {
|
it('should correctly init api url', ()=> {
|
||||||
schemaMgr.apiUrl.should.be.equal('http://petstore.swagger.io/v2');
|
specMgr.apiUrl.should.be.equal('http://petstore.swagger.io/v2');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('byPointer method', () => {
|
describe('byPointer method', () => {
|
||||||
it('should return correct schema part', ()=> {
|
it('should return correct schema part', ()=> {
|
||||||
let part = schemaMgr.byPointer('/tags/3');
|
let part = specMgr.byPointer('/tags/3');
|
||||||
part.should.be.deepEqual(schemaMgr.schema.tags[3]);
|
part.should.be.deepEqual(specMgr.schema.tags[3]);
|
||||||
part.should.be.equal(schemaMgr.schema.tags[3]);
|
part.should.be.equal(specMgr.schema.tags[3]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return null for incorrect pointer', ()=> {
|
it('should return null for incorrect pointer', ()=> {
|
||||||
let part = schemaMgr.byPointer('/incorrect/pointer');
|
let part = specMgr.byPointer('/incorrect/pointer');
|
||||||
expect(part).toBeNull();
|
expect(part).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -69,7 +69,7 @@ describe('Utils', () => {
|
||||||
|
|
||||||
describe('getTagsMap method', () => {
|
describe('getTagsMap method', () => {
|
||||||
beforeAll(function () {
|
beforeAll(function () {
|
||||||
schemaMgr._schema = {
|
specMgr._schema = {
|
||||||
tags: [
|
tags: [
|
||||||
{name: 'tag1', description: 'info1'},
|
{name: 'tag1', description: 'info1'},
|
||||||
{name: 'tag2', description: 'info2', 'x-traitTag': true}
|
{name: 'tag2', description: 'info2', 'x-traitTag': true}
|
||||||
|
@ -78,7 +78,7 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return correct tags map', () => {
|
it('should return correct tags map', () => {
|
||||||
let tagsMap = schemaMgr.getTagsMap();
|
let tagsMap = specMgr.getTagsMap();
|
||||||
let expectedResult = {
|
let expectedResult = {
|
||||||
tag1: {description: 'info1', 'x-traitTag': false},
|
tag1: {description: 'info1', 'x-traitTag': false},
|
||||||
tag2: {description: 'info2', 'x-traitTag': true}
|
tag2: {description: 'info2', 'x-traitTag': true}
|
||||||
|
@ -87,8 +87,8 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return empty array for non-specified tags', () => {
|
it('should return empty array for non-specified tags', () => {
|
||||||
delete schemaMgr._schema.tags;
|
delete specMgr._schema.tags;
|
||||||
let tagsMap = schemaMgr.getTagsMap();
|
let tagsMap = specMgr.getTagsMap();
|
||||||
tagsMap.should.be.empty();
|
tagsMap.should.be.empty();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -121,8 +121,8 @@ describe('Utils', () => {
|
||||||
let entries;
|
let entries;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
schemaMgr._schema = suitSchema;
|
specMgr._schema = suitSchema;
|
||||||
menuTree = schemaMgr.buildMenuTree();
|
menuTree = specMgr.buildMenuTree();
|
||||||
entries = Array.from(menuTree.entries());
|
entries = Array.from(menuTree.entries());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ describe('Utils', () => {
|
||||||
info.methods.should.be.an.Array();
|
info.methods.should.be.an.Array();
|
||||||
for (let methodInfo of info.methods) {
|
for (let methodInfo of info.methods) {
|
||||||
methodInfo.should.have.properties(['pointer', 'summary']);
|
methodInfo.should.have.properties(['pointer', 'summary']);
|
||||||
let methSchema = schemaMgr.byPointer(methodInfo.pointer);
|
let methSchema = specMgr.byPointer(methodInfo.pointer);
|
||||||
expect(methSchema).not.toBeNull();
|
expect(methSchema).not.toBeNull();
|
||||||
if (methSchema.summary) {
|
if (methSchema.summary) {
|
||||||
methSchema.summary.should.be.equal(methodInfo.summary);
|
methSchema.summary.should.be.equal(methodInfo.summary);
|
||||||
|
@ -174,7 +174,7 @@ describe('Utils', () => {
|
||||||
|
|
||||||
describe('getMethodParams method', () => {
|
describe('getMethodParams method', () => {
|
||||||
beforeAll((done) => {
|
beforeAll((done) => {
|
||||||
schemaMgr.load('/tests/schemas/schema-mgr-methodparams.json').then(() => {
|
specMgr.load('/tests/schemas/schema-mgr-methodparams.json').then(() => {
|
||||||
done();
|
done();
|
||||||
}, () => {
|
}, () => {
|
||||||
done(new Error('Error handler should not be called'));
|
done(new Error('Error handler should not be called'));
|
||||||
|
@ -182,26 +182,26 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should propagate path parameters', () => {
|
it('should propagate path parameters', () => {
|
||||||
let params = schemaMgr.getMethodParams('/paths/test1/get');
|
let params = specMgr.getMethodParams('/paths/test1/get');
|
||||||
params.length.should.be.equal(2);
|
params.length.should.be.equal(2);
|
||||||
params[0].name.should.be.equal('methodParam');
|
params[0].name.should.be.equal('methodParam');
|
||||||
params[1].name.should.be.equal('pathParam');
|
params[1].name.should.be.equal('pathParam');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inject correct pointers', () => {
|
it('should inject correct pointers', () => {
|
||||||
let params = schemaMgr.getMethodParams('/paths/test1/get');
|
let params = specMgr.getMethodParams('/paths/test1/get');
|
||||||
params[0]._pointer.should.be.equal('/paths/test1/get/parameters/0');
|
params[0]._pointer.should.be.equal('/paths/test1/get/parameters/0');
|
||||||
params[1]._pointer.should.be.equal('/paths/test1/parameters/0');
|
params[1]._pointer.should.be.equal('/paths/test1/parameters/0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept pointer directly to parameters', () => {
|
it('should accept pointer directly to parameters', () => {
|
||||||
let params = schemaMgr.getMethodParams('/paths/test1/get/parameters', true);
|
let params = specMgr.getMethodParams('/paths/test1/get/parameters', true);
|
||||||
expect(params).not.toBeNull();
|
expect(params).not.toBeNull();
|
||||||
params.length.should.be.equal(2);
|
params.length.should.be.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve path params from Parameters Definitions Object', () => {
|
it('should resolve path params from Parameters Definitions Object', () => {
|
||||||
let params = schemaMgr.getMethodParams('/paths/test2/get', true);
|
let params = specMgr.getMethodParams('/paths/test2/get', true);
|
||||||
params.length.should.be.equal(2);
|
params.length.should.be.equal(2);
|
||||||
params[0].name.should.be.equal('methodParam');
|
params[0].name.should.be.equal('methodParam');
|
||||||
params[1].name.should.be.equal('extParam');
|
params[1].name.should.be.equal('extParam');
|
||||||
|
@ -209,21 +209,21 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve method params from Parameters Definitions Object', () => {
|
it('should resolve method params from Parameters Definitions Object', () => {
|
||||||
let params = schemaMgr.getMethodParams('/paths/test3/get', true);
|
let params = specMgr.getMethodParams('/paths/test3/get', true);
|
||||||
params.length.should.be.equal(1);
|
params.length.should.be.equal(1);
|
||||||
params[0].name.should.be.equal('extParam');
|
params[0].name.should.be.equal('extParam');
|
||||||
params[0]._pointer.should.be.equal('#/parameters/extparam');
|
params[0]._pointer.should.be.equal('#/parameters/extparam');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw for parameters other than array', () => {
|
it('should throw for parameters other than array', () => {
|
||||||
let func = () => schemaMgr.getMethodParams('/paths/test4/get', true);
|
let func = () => specMgr.getMethodParams('/paths/test4/get', true);
|
||||||
expect(func).toThrow();
|
expect(func).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findDerivedDefinitions method', () => {
|
describe('findDerivedDefinitions method', () => {
|
||||||
beforeAll((done) => {
|
beforeAll((done) => {
|
||||||
schemaMgr.load('/tests/schemas/extended-petstore.yml').then(() => {
|
specMgr.load('/tests/schemas/extended-petstore.yml').then(() => {
|
||||||
done();
|
done();
|
||||||
}, () => {
|
}, () => {
|
||||||
done(new Error('Error handler should not be called'));
|
done(new Error('Error handler should not be called'));
|
||||||
|
@ -231,7 +231,7 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find derived definitions for Pet', () => {
|
it('should find derived definitions for Pet', () => {
|
||||||
let deriveDefs = schemaMgr.findDerivedDefinitions('#/definitions/Pet');
|
let deriveDefs = specMgr.findDerivedDefinitions('#/definitions/Pet');
|
||||||
deriveDefs.should.be.instanceof(Array);
|
deriveDefs.should.be.instanceof(Array);
|
||||||
deriveDefs.should.not.be.empty();
|
deriveDefs.should.not.be.empty();
|
||||||
deriveDefs.should.be.deepEqual([
|
deriveDefs.should.be.deepEqual([
|
||||||
|
@ -241,7 +241,7 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return emtpy array for definitions that dont have discriminator', () => {
|
it('should return emtpy array for definitions that dont have discriminator', () => {
|
||||||
let deriveDefs = schemaMgr.findDerivedDefinitions('#/definitions/Order');
|
let deriveDefs = specMgr.findDerivedDefinitions('#/definitions/Order');
|
||||||
deriveDefs.should.be.instanceof(Array);
|
deriveDefs.should.be.instanceof(Array);
|
||||||
deriveDefs.should.be.empty();
|
deriveDefs.should.be.empty();
|
||||||
});
|
});
|
7
typings/marked.d.ts
vendored
7
typings/marked.d.ts
vendored
|
@ -1,7 +0,0 @@
|
||||||
// Generated by typings
|
|
||||||
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/json-pointer/json-pointer.d.ts
|
|
||||||
|
|
||||||
declare module "marked" {
|
|
||||||
var x: any;
|
|
||||||
export default x;
|
|
||||||
}
|
|
4
typings/remarkable.d.ts
vendored
Normal file
4
typings/remarkable.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
declare module 'remarkable' {
|
||||||
|
var x: any;
|
||||||
|
export default x;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user