mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-10-30 23:37:28 +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