mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-28 03:23:44 +03:00
Merge commit 'afa5511973ad01c8f86d11bd080e4ecac2161a65' into releases
This commit is contained in:
commit
7a81326474
|
@ -144,9 +144,9 @@ ReDoc makes use of the following [vendor extensions](http://swagger.io/specifica
|
||||||
## Advanced usage
|
## Advanced usage
|
||||||
Instead of adding `spec-url` attribute to the `<redoc>` element you can initialize ReDoc via globally exposed `Redoc` object:
|
Instead of adding `spec-url` attribute to the `<redoc>` element you can initialize ReDoc via globally exposed `Redoc` object:
|
||||||
```js
|
```js
|
||||||
Redoc.init(specUrl, options)
|
Redoc.init(specOrSpecUrl, options)
|
||||||
```
|
```
|
||||||
|
`specOrSpecUrl` is either JSON object with specification or an URL to the spec in `JSON` or `YAML` format.
|
||||||
`options` is javascript object with camel-cased version of `<redoc>` tag attribute names as the keys, e.g.:
|
`options` is javascript object with camel-cased version of `<redoc>` tag attribute names as the keys, e.g.:
|
||||||
```js
|
```js
|
||||||
Redoc.init('http://petstore.swagger.io/v2/swagger.json', {
|
Redoc.init('http://petstore.swagger.io/v2/swagger.json', {
|
||||||
|
|
|
@ -91,8 +91,8 @@ export class JsonSchema extends BaseSearchableComponent implements OnInit {
|
||||||
}).sort((a, b) => {
|
}).sort((a, b) => {
|
||||||
return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1;
|
return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1;
|
||||||
});
|
});
|
||||||
this.descendants.forEach((d, idx) => d.idx = idx);
|
|
||||||
}
|
}
|
||||||
|
this.descendants.forEach((d, idx) => d.idx = idx);
|
||||||
this.selectDescendantByIdx(0);
|
this.selectDescendantByIdx(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { BaseComponent } from '../base';
|
||||||
import * as detectScollParent from 'scrollparent';
|
import * as detectScollParent from 'scrollparent';
|
||||||
|
|
||||||
import { SpecManager } from '../../utils/spec-manager';
|
import { SpecManager } from '../../utils/spec-manager';
|
||||||
import { SearchService, OptionsService, Hash, AppStateService, SchemaHelper } from '../../services/';
|
import { SearchService, OptionsService, Options, Hash, AppStateService, SchemaHelper } from '../../services/';
|
||||||
import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';
|
import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -29,7 +29,7 @@ export class Redoc extends BaseComponent implements OnInit {
|
||||||
|
|
||||||
error: any;
|
error: any;
|
||||||
specLoaded: boolean;
|
specLoaded: boolean;
|
||||||
options: any;
|
options: Options;
|
||||||
|
|
||||||
loadingProgress: number;
|
loadingProgress: number;
|
||||||
|
|
||||||
|
@ -84,7 +84,8 @@ export class Redoc extends BaseComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
this.specMgr.load(this.options.specUrl).catch(err => {
|
// bunlde spec directly if passsed or load by URL
|
||||||
|
this.specMgr.load(this.options.spec || this.options.specUrl).catch(err => {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { enableProdMode } from '@angular/core';
|
||||||
import { Redoc } from './components/index';
|
import { Redoc } from './components/index';
|
||||||
import { BrowserDomAdapter as DOM } from './utils/browser-adapter';
|
import { BrowserDomAdapter as DOM } from './utils/browser-adapter';
|
||||||
import { disableDebugTools } from '@angular/platform-browser';
|
import { disableDebugTools } from '@angular/platform-browser';
|
||||||
|
import { isString } from './utils/helpers';
|
||||||
|
|
||||||
var bootstrapRedoc;
|
var bootstrapRedoc;
|
||||||
if (AOT) {
|
if (AOT) {
|
||||||
|
@ -21,13 +22,16 @@ if (IS_PRODUCTION) {
|
||||||
export const version = LIB_VERSION;
|
export const version = LIB_VERSION;
|
||||||
|
|
||||||
var moduleRef;
|
var moduleRef;
|
||||||
export function init(specUrl:string, options:any = {}) {
|
export function init(specUrlOrSpec:string|any, options:any = {}) {
|
||||||
if (moduleRef) {
|
if (moduleRef) {
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
Redoc._preOptions = options;
|
Redoc._preOptions = options;
|
||||||
options.specUrl = options.specUrl || specUrl;
|
options.specUrl = options.specUrl || (isString(specUrlOrSpec) ? specUrlOrSpec : '');
|
||||||
|
if (!isString(specUrlOrSpec)) {
|
||||||
|
options.spec = specUrlOrSpec;
|
||||||
|
}
|
||||||
return bootstrapRedoc()
|
return bootstrapRedoc()
|
||||||
.then(appRef => {
|
.then(appRef => {
|
||||||
moduleRef = appRef;
|
moduleRef = appRef;
|
||||||
|
|
|
@ -141,7 +141,7 @@ export class MenuService {
|
||||||
onHashChange(hash?: string) {
|
onHashChange(hash?: string) {
|
||||||
if (hash == undefined) return;
|
if (hash == undefined) return;
|
||||||
let activated = this.activateByHash(hash);
|
let activated = this.activateByHash(hash);
|
||||||
if (!this.tasks.empty) {
|
if (!this.tasks.processed) {
|
||||||
this.tasks.start(this.activeIdx, this);
|
this.tasks.start(this.activeIdx, this);
|
||||||
this.scrollService.setStickElement(this.getCurrentEl());
|
this.scrollService.setStickElement(this.getCurrentEl());
|
||||||
if (activated) this.scrollToActive();
|
if (activated) this.scrollToActive();
|
||||||
|
|
|
@ -19,7 +19,7 @@ const OPTION_NAMES = new Set([
|
||||||
'requiredPropsFirst'
|
'requiredPropsFirst'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
interface Options {
|
export interface Options {
|
||||||
scrollYOffset?: any;
|
scrollYOffset?: any;
|
||||||
disableLazySchemas?: boolean;
|
disableLazySchemas?: boolean;
|
||||||
specUrl?: string;
|
specUrl?: string;
|
||||||
|
@ -29,6 +29,7 @@ interface Options {
|
||||||
expandResponses?: Set<string> | 'all';
|
expandResponses?: Set<string> | 'all';
|
||||||
$scrollParent?: HTMLElement | Window;
|
$scrollParent?: HTMLElement | Window;
|
||||||
requiredPropsFirst?: boolean;
|
requiredPropsFirst?: boolean;
|
||||||
|
spec?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
@ -99,6 +99,7 @@ class SchemaWalker {
|
||||||
export class AllOfMerger {
|
export class AllOfMerger {
|
||||||
static merge(into, schemas) {
|
static merge(into, schemas) {
|
||||||
into['x-derived-from'] = [];
|
into['x-derived-from'] = [];
|
||||||
|
let hadDiscriminator = !!into.discriminator;
|
||||||
for (let i=0; i < schemas.length; i++) {
|
for (let i=0; i < schemas.length; i++) {
|
||||||
let subSchema = schemas[i];
|
let subSchema = schemas[i];
|
||||||
into['x-derived-from'].push(subSchema._pointer);
|
into['x-derived-from'].push(subSchema._pointer);
|
||||||
|
@ -115,7 +116,7 @@ export class AllOfMerger {
|
||||||
defaults(into, subSchema);
|
defaults(into, subSchema);
|
||||||
subSchema._pointer = tmpPtr;
|
subSchema._pointer = tmpPtr;
|
||||||
}
|
}
|
||||||
into.discriminator = null;
|
if (!hadDiscriminator) into.discriminator = null;
|
||||||
into.allOf = null;
|
into.allOf = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +215,8 @@ class SchemaDereferencer {
|
||||||
// if resolved schema doesn't have title use name from ref
|
// if resolved schema doesn't have title use name from ref
|
||||||
resolved.title = resolved.title || JsonPointer.baseName($ref);
|
resolved.title = resolved.title || JsonPointer.baseName($ref);
|
||||||
|
|
||||||
let keysCount = Object.keys(schema).length;
|
let keysCount = Object.keys(schema).filter(key => !key.startsWith('x-redoc')).length;
|
||||||
|
|
||||||
if ( keysCount > 2 || (keysCount === 2 && !schema.description) ) {
|
if ( keysCount > 2 || (keysCount === 2 && !schema.description) ) {
|
||||||
WarningsService.warn(`Other properties are defined at the same level as $ref at "#${pointer}". ` +
|
WarningsService.warn(`Other properties are defined at the same level as $ref at "#${pointer}". ` +
|
||||||
'They are IGNORED according to the JsonSchema spec');
|
'They are IGNORED according to the JsonSchema spec');
|
||||||
|
|
|
@ -186,10 +186,13 @@ export class SearchService {
|
||||||
let title = name;
|
let title = name;
|
||||||
schema = this.normalizer.normalize(schema, schema._pointer || absolutePointer, { childFor: parent });
|
schema = this.normalizer.normalize(schema, schema._pointer || absolutePointer, { childFor: parent });
|
||||||
|
|
||||||
|
// prevent endless discriminator recursion
|
||||||
|
if (schema._pointer && schema._pointer === parent) return;
|
||||||
|
|
||||||
let body = schema.description; // TODO: defaults, examples, etc...
|
let body = schema.description; // TODO: defaults, examples, etc...
|
||||||
|
|
||||||
if (schema.type === 'array') {
|
if (schema.type === 'array') {
|
||||||
this.indexSchema(schema.items, title, JsonPointer.join(absolutePointer, ['items']), menuPointer);
|
this.indexSchema(schema.items, title, JsonPointer.join(absolutePointer, ['items']), menuPointer, parent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,20 +208,18 @@ export class SearchService {
|
||||||
body += ' ' + schema.enum.join(' ');
|
body += ' ' + schema.enum.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parent) {
|
this.index({
|
||||||
// redoc doesn't display top level descriptions and titles
|
pointer: absolutePointer,
|
||||||
this.index({
|
menuId: menuPointer,
|
||||||
pointer: absolutePointer,
|
title,
|
||||||
menuId: menuPointer,
|
body
|
||||||
title,
|
});
|
||||||
body
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schema.properties) {
|
if (schema.properties) {
|
||||||
Object.keys(schema.properties).forEach(propName => {
|
Object.keys(schema.properties).forEach(propName => {
|
||||||
let propPtr = JsonPointer.join(absolutePointer, ['properties', propName]);
|
let propPtr = JsonPointer.join(absolutePointer, ['properties', propName]);
|
||||||
this.indexSchema(schema.properties[propName], propName, propPtr, menuPointer);
|
let prop:SwaggerSchema = schema.properties[propName];
|
||||||
|
this.indexSchema(prop, propName, propPtr, menuPointer, parent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ export class CopyButton implements OnInit {
|
||||||
onClick() {
|
onClick() {
|
||||||
let copied;
|
let copied;
|
||||||
if (this.copyText) {
|
if (this.copyText) {
|
||||||
copied = Clipboard.copyCustom(JSON.stringify(this.copyText));
|
copied = Clipboard.copyCustom(JSON.stringify(this.copyText, null, 2));
|
||||||
} else {
|
} else {
|
||||||
copied = Clipboard.copyElement(this.copyElement);
|
copied = Clipboard.copyElement(this.copyElement);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ export class LazyTasksService {
|
||||||
private _tasks = [];
|
private _tasks = [];
|
||||||
private _current: number = 0;
|
private _current: number = 0;
|
||||||
private _syncCount: number = 0;
|
private _syncCount: number = 0;
|
||||||
|
private _emptyProcessed = false;
|
||||||
private menuService;
|
private menuService;
|
||||||
|
|
||||||
public loadProgress = new BehaviorSubject<number>(0);
|
public loadProgress = new BehaviorSubject<number>(0);
|
||||||
|
@ -38,8 +39,10 @@ export class LazyTasksService {
|
||||||
constructor(public optionsService: OptionsService) {
|
constructor(public optionsService: OptionsService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
get empty() {
|
get processed() {
|
||||||
return this._current === this._tasks.length;
|
let res = this._tasks.length && (this._current >= this._tasks.length) || this._emptyProcessed;
|
||||||
|
if (!this._tasks.length) this._emptyProcessed = true;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
set syncCount(n: number) {
|
set syncCount(n: number) {
|
||||||
|
@ -97,10 +100,17 @@ export class LazyTasksService {
|
||||||
} else {
|
} else {
|
||||||
this.sortTasks(idx);
|
this.sortTasks(idx);
|
||||||
}
|
}
|
||||||
|
syncCount = Math.min(syncCount, this._tasks.length);
|
||||||
if (this.allSync) syncCount = this._tasks.length;
|
if (this.allSync) syncCount = this._tasks.length;
|
||||||
for (var i = this._current; i < syncCount; i++) {
|
for (var i = this._current; i < syncCount; i++) {
|
||||||
this.nextTaskSync();
|
this.nextTaskSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this._tasks.length) {
|
||||||
|
this.loadProgress.next(100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.nextTask();
|
this.nextTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export function stringify(obj:any) {
|
||||||
return JSON.stringify(obj);
|
return JSON.stringify(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isString(str:any) {
|
export function isString(str:any):str is String {
|
||||||
return typeof str === 'string';
|
return typeof str === 'string';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { MdRenderer } from './md-renderer';
|
||||||
|
|
||||||
import { SwaggerOperation, SwaggerParameter } from './swagger-typings';
|
import { SwaggerOperation, SwaggerParameter } from './swagger-typings';
|
||||||
import { snapshot } from './helpers';
|
import { snapshot } from './helpers';
|
||||||
|
import { WarningsService } from '../services/warnings.service';
|
||||||
|
|
||||||
function getDiscriminator(obj) {
|
function getDiscriminator(obj) {
|
||||||
return obj.discriminator || obj['x-extendedDiscriminator'];
|
return obj.discriminator || obj['x-extendedDiscriminator'];
|
||||||
|
@ -181,7 +182,37 @@ export class SpecManager {
|
||||||
|
|
||||||
let globalDefs = this._schema.definitions || {};
|
let globalDefs = this._schema.definitions || {};
|
||||||
let res:DescendantInfo[] = [];
|
let res:DescendantInfo[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
// from the spec: When used, the value MUST be the name of this schema or any schema that inherits it.
|
||||||
|
// but most of people use it as an abstract class so here is workaround to allow using it other way
|
||||||
|
// check if parent definition name is in the enum of possible values
|
||||||
|
if (definition.discriminator) {
|
||||||
|
let prop = definition.properties[definition.discriminator];
|
||||||
|
if (prop.enum && prop.enum.indexOf(JsonPointer.baseName(defPointer)) > -1) {
|
||||||
|
res.push({
|
||||||
|
name: JsonPointer.baseName(defPointer),
|
||||||
|
$ref: defPointer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let extendedDiscriminatorProp = definition['x-extendedDiscriminator'];
|
let extendedDiscriminatorProp = definition['x-extendedDiscriminator'];
|
||||||
|
|
||||||
|
let pointers;
|
||||||
|
if (definition['x-derived-from']) {
|
||||||
|
// support inherited discriminator o_O
|
||||||
|
let derivedDiscriminator = definition['x-derived-from'].filter(ptr => {
|
||||||
|
if (!ptr) return false;
|
||||||
|
let def = this.byPointer(ptr);
|
||||||
|
return def && def.discriminator;
|
||||||
|
});
|
||||||
|
pointers = [defPointer, ...derivedDiscriminator];
|
||||||
|
} else {
|
||||||
|
pointers = [defPointer];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (let defName of Object.keys(globalDefs)) {
|
for (let defName of Object.keys(globalDefs)) {
|
||||||
let def = globalDefs[defName];
|
let def = globalDefs[defName];
|
||||||
if (!def.allOf &&
|
if (!def.allOf &&
|
||||||
|
@ -189,12 +220,6 @@ export class SpecManager {
|
||||||
let subTypes = def['x-derived-from'] ||
|
let subTypes = def['x-derived-from'] ||
|
||||||
def.allOf.map(subType => subType._pointer || subType.$ref);
|
def.allOf.map(subType => subType._pointer || subType.$ref);
|
||||||
|
|
||||||
let pointers;
|
|
||||||
if (definition['x-derived-from']) {
|
|
||||||
pointers = [defPointer, ...definition['x-derived-from']];
|
|
||||||
} else {
|
|
||||||
pointers = [defPointer];
|
|
||||||
}
|
|
||||||
let idx = -1;
|
let idx = -1;
|
||||||
|
|
||||||
for (let ptr of pointers) {
|
for (let ptr of pointers) {
|
||||||
|
@ -204,12 +229,23 @@ export class SpecManager {
|
||||||
|
|
||||||
if (idx < 0) continue;
|
if (idx < 0) continue;
|
||||||
|
|
||||||
let derivedName = defName;
|
let derivedName;
|
||||||
if (extendedDiscriminatorProp) {
|
if (extendedDiscriminatorProp) {
|
||||||
let prop = def.properties && def.properties[extendedDiscriminatorProp];
|
let subDefs = def.allOf || [];
|
||||||
if (prop && prop.enum && prop.enum.length === 1) {
|
for (let def of subDefs) {
|
||||||
derivedName = prop.enum[0];
|
let prop = def.properties && def.properties[extendedDiscriminatorProp];
|
||||||
|
if (prop && prop.enum && prop.enum.length === 1) {
|
||||||
|
derivedName = prop.enum[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (derivedName == undefined) {
|
||||||
|
WarningsService.warn(`Incorrect usage of x-extendedDiscriminator at ${defPointer}: `
|
||||||
|
+ `can't find corresponding enum with single value in definition "${defName}"`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
derivedName = defName;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.push({name: derivedName, $ref: `#/definitions/${defName}`});
|
res.push({name: derivedName, $ref: `#/definitions/${defName}`});
|
||||||
|
|
5
manual-types/index.d.ts
vendored
5
manual-types/index.d.ts
vendored
|
@ -13,6 +13,11 @@ declare module "*.css" {
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "*.json" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
declare var LIB_VERSION: any;
|
declare var LIB_VERSION: any;
|
||||||
declare var IS_PRODUCTION: any;
|
declare var IS_PRODUCTION: any;
|
||||||
declare var AOT: any;
|
declare var AOT: any;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"description": "Swagger-generated API Reference Documentation",
|
"description": "Swagger-generated API Reference Documentation",
|
||||||
"version": "1.10.2",
|
"version": "1.11.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/Rebilly/ReDoc"
|
"url": "git://github.com/Rebilly/ReDoc"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { SpecManager } from '../../lib/utils/spec-manager';
|
import { SpecManager } from '../../lib/utils/spec-manager';
|
||||||
|
import * as xExtendedDefs from './x-extended-defs.json';
|
||||||
|
|
||||||
describe('Utils', () => {
|
describe('Utils', () => {
|
||||||
describe('Schema manager', () => {
|
describe('Schema manager', () => {
|
||||||
let specMgr: SpecManager;
|
let specMgr: SpecManager;
|
||||||
|
@ -175,6 +177,23 @@ describe('Utils', () => {
|
||||||
deriveDefs.should.be.instanceof(Array);
|
deriveDefs.should.be.instanceof(Array);
|
||||||
deriveDefs.should.be.empty();
|
deriveDefs.should.be.empty();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should correctly work with x-extendedDiscriminator', () => {
|
||||||
|
specMgr._schema = {
|
||||||
|
definitions: xExtendedDefs
|
||||||
|
};
|
||||||
|
|
||||||
|
let deriveDefs = specMgr.findDerivedDefinitions('#/definitions/Payment');
|
||||||
|
deriveDefs.should.be.instanceof(Array);
|
||||||
|
deriveDefs.should.be.deepEqual([
|
||||||
|
{
|
||||||
|
name: 'cash',
|
||||||
|
$ref: '#/definitions/CashPayment'
|
||||||
|
}, {
|
||||||
|
name: 'paypal',
|
||||||
|
$ref: '#/definitions/PayPalPayment'
|
||||||
|
}])
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
57
tests/unit/x-extended-defs.json
Normal file
57
tests/unit/x-extended-defs.json
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"Payment": {
|
||||||
|
"x-extendedDiscriminator": "type",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CashPayment": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Payment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"cash"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"PayPalPayment": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Payment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"paypal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"userEmail": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user