mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-06 05:10:20 +03:00
Add support of x-permissions vendor extension
This commit is contained in:
parent
278e5d7917
commit
f212145d83
|
@ -157,6 +157,51 @@ description: Pagination description (can use markdown syntax)
|
|||
x-traitTag: true
|
||||
```
|
||||
|
||||
### Permission Object vendor extensions
|
||||
|
||||
#### x-permissions
|
||||
|
||||
| Field Name | Type | Description |
|
||||
| :------------- | :------: | :---------- |
|
||||
| x-permissions | Object | Define the several permissions available for the API and associated to the operations |
|
||||
|
||||
###### Usage in ReDoc
|
||||
`x-permissions` are rendered bellow the API description of ReDoc
|
||||
|
||||
###### x-permissions example (main declaration)
|
||||
|
||||
yaml
|
||||
```yaml
|
||||
swagger: '2.0'
|
||||
info:
|
||||
...
|
||||
tags: [...]
|
||||
x-permissions:
|
||||
admin:
|
||||
title: Pet store admin
|
||||
description: 'Pet store Admin have special privileges on PetStore API allowing them to manage their store and users from their company '
|
||||
user:
|
||||
title: Pet Store user
|
||||
description: 'Pet store user'
|
||||
```
|
||||
|
||||
##### Usage in Redoc operations
|
||||
`x-permissions` are rendered above the Operation description of ReDoc
|
||||
###### x-permissions example (operation declaration)
|
||||
|
||||
yaml
|
||||
```yaml
|
||||
/user/logout:
|
||||
get:
|
||||
tags:
|
||||
- "user"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
- name: "user"
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Operation Object vendor extensions
|
||||
Extends OpenAPI [Operation Object](http://swagger.io/specification/#operationObject)
|
||||
#### x-code-samples
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
<div class="operation-tags" *ngIf="operation.info.tags.length">
|
||||
<a *ngFor="let tag of operation.info.tags" attr.href="#tag/{{tag}}"> {{tag}} </a>
|
||||
</div>
|
||||
<div class="operation-permissions" *ngIf="operation.info.permissions.length">
|
||||
<span><em>Permissions:</em><a *ngFor="let permission of operation.info.permissions" attr.href="#section/Permissions/{{permission.name}}"> {{permission.title}} </a>
|
||||
</span>
|
||||
</div>
|
||||
<p *ngIf="operation.info.description" class="operation-description"
|
||||
[innerHtml]="operation.info.description | marked">
|
||||
</p>
|
||||
|
|
|
@ -53,6 +53,30 @@
|
|||
}
|
||||
}
|
||||
|
||||
.operation-permissions {
|
||||
margin-top: 20px;
|
||||
|
||||
font-size: 14px;
|
||||
padding: 0.2em 0;
|
||||
margin: 3em 0 1.1em;
|
||||
color: #253137;
|
||||
font-weight: normal;
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
font-family: Courier, monospace;
|
||||
background-color: rgba(38, 50, 56, 0.02);
|
||||
border: 1px solid rgba(38, 50, 56, 0.1);
|
||||
margin: 2px 3px;
|
||||
padding: 0px 5px;
|
||||
border-radius: 2px;
|
||||
color: #263238;
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.operation-content, .operation-samples {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
|
|
|
@ -10,8 +10,8 @@ import {
|
|||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
|
||||
import { Operation } from './operation';
|
||||
import { SpecManager } from '../../utils/spec-manager';;
|
||||
import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';;
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';
|
||||
|
||||
describe('Redoc components', () => {
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -12,6 +12,7 @@ export interface OperationInfo {
|
|||
path: string;
|
||||
info: {
|
||||
tags: string[];
|
||||
permissions: string[];
|
||||
description: string;
|
||||
};
|
||||
bodyParam: any;
|
||||
|
@ -20,7 +21,7 @@ export interface OperationInfo {
|
|||
externalDocs?: {
|
||||
url: string;
|
||||
description?: string;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -56,7 +57,8 @@ export class Operation extends BaseComponent implements OnInit {
|
|||
path: JsonPointer.baseName(this.pointer, 2),
|
||||
info: {
|
||||
description: this.componentSchema.description,
|
||||
tags: this.filterMainTags(this.componentSchema.tags)
|
||||
tags: this.filterMainTags(this.componentSchema.tags),
|
||||
permissions: this.reduceMainPermissions(this.componentSchema['x-permissions'])
|
||||
},
|
||||
bodyParam: this.findBodyParam(),
|
||||
summary: SchemaHelper.operationSummary(this.componentSchema),
|
||||
|
@ -77,6 +79,17 @@ export class Operation extends BaseComponent implements OnInit {
|
|||
return tags.filter(tag => tagsMap[tag] && tagsMap[tag]['x-traitTag']);
|
||||
}
|
||||
|
||||
reduceMainPermissions(permissions) {
|
||||
var permissionsMap = this.specMgr.getPermissionsMap();
|
||||
if (!permissions) return [];
|
||||
return permissions.reduce(( filtered, permission) => {
|
||||
if( permissionsMap[permission.name] ) {
|
||||
filtered.push(permissionsMap[permission.name]);
|
||||
}
|
||||
return filtered;
|
||||
}, []);
|
||||
}
|
||||
|
||||
findBodyParam():SwaggerBodyParameter {
|
||||
let params = this.specMgr.getOperationParams(this.pointer);
|
||||
let bodyParam = params.find(param => param.in === 'body');
|
||||
|
|
5
lib/components/Permissions/x-permissions.html
Normal file
5
lib/components/Permissions/x-permissions.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div class="x-permissions" *ngFor="let def of defs">
|
||||
<h2 class="sharable-header" attr.section="section/Permissions/{{def.name}}">
|
||||
<a class="share-link" href="#section/Permissions/{{def.name}}"></a>{{def.title}}</h2>
|
||||
<div [innerHTML]="def.details.description | marked"></div>
|
||||
</div>
|
36
lib/components/Permissions/x-permissions.scss
Normal file
36
lib/components/Permissions/x-permissions.scss
Normal file
|
@ -0,0 +1,36 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.security-definition:not(:last-of-type) {
|
||||
border-bottom: 1px solid rgba($text-color, .3);
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
:host h2 {
|
||||
padding-top: $section-spacing;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1em 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
:host .security-scopes-details, :host .security-details {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
table.details th, table.details td {
|
||||
font-weight: bold;
|
||||
width: 200px;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
table.details th {
|
||||
text-align: left;
|
||||
padding: 6px;
|
||||
text-transform: capitalize;
|
||||
font-weight: normal;
|
||||
}
|
48
lib/components/Permissions/x-permissions.ts
Normal file
48
lib/components/Permissions/x-permissions.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
'use strict';
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { SpecManager, BaseComponent } from '../base';
|
||||
|
||||
import { ComponentParser } from '../../services/component-parser.service';
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'x-permissions',
|
||||
styleUrls: ['./x-permissions.css'],
|
||||
templateUrl: './x-permissions.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class XPermissions extends BaseComponent implements OnInit {
|
||||
info: any = {};
|
||||
specUrl: String;
|
||||
defs: any[];
|
||||
|
||||
static insertTagIntoDescription(md:string) {
|
||||
if (ComponentParser.contains(md, 'x-permissions')) return md;
|
||||
if (/^#\s?Permissions\s*$/mi.test(md)) return md;
|
||||
return md + '\n\n# Permissions \n' + ComponentParser.build('x-permissions');
|
||||
}
|
||||
|
||||
constructor(specMgr:SpecManager) {
|
||||
super(specMgr);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.componentSchema = this.componentSchema['x-permissions'];
|
||||
this.defs = Object.keys(this.componentSchema).map(name => {
|
||||
let details = this.componentSchema[name];
|
||||
let title = details.title ? details.title : name;
|
||||
details.description = details.description ? details.description : 'none';
|
||||
return {
|
||||
name,
|
||||
title,
|
||||
details
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import { OperationsList } from './OperationsList/operations-list';
|
|||
import { Operation } from './Operation/operation';
|
||||
import { Warnings } from './Warnings/warnings';
|
||||
import { SecurityDefinitions } from './SecurityDefinitions/security-definitions';
|
||||
import { XPermissions } from './Permissions/x-permissions';
|
||||
import { LoadingBar } from './LoadingBar/loading-bar';
|
||||
import { RedocSearch } from './Search/redoc-search';
|
||||
import { ExternalDocs } from './ExternalDocs/external-docs';
|
||||
|
@ -24,9 +25,9 @@ import { Redoc } from './Redoc/redoc';
|
|||
export const REDOC_DIRECTIVES = [
|
||||
ApiInfo, ApiLogo, JsonSchema, JsonSchemaLazy, ParamsList, RequestSamples, ResponsesList,
|
||||
ResponsesSamples, SchemaSample, SideMenu, OperationsList, Operation, Warnings, Redoc, SecurityDefinitions,
|
||||
LoadingBar, SideMenuItems, RedocSearch, ExternalDocs, EndpointLink
|
||||
XPermissions, LoadingBar, SideMenuItems, RedocSearch, ExternalDocs, EndpointLink
|
||||
];
|
||||
|
||||
export { ApiInfo, ApiLogo, JsonSchema, JsonSchemaLazy, ParamsList, RequestSamples, ResponsesList,
|
||||
ResponsesSamples, SchemaSample, SideMenu, OperationsList, Operation, Warnings, Redoc, SecurityDefinitions,
|
||||
LoadingBar, SideMenuItems, ExternalDocs, EndpointLink };
|
||||
XPermissions, LoadingBar, SideMenuItems, ExternalDocs, EndpointLink };
|
||||
|
|
|
@ -5,10 +5,10 @@ import { CommonModule } from '@angular/common';
|
|||
|
||||
import { SpecManager } from './utils/spec-manager';
|
||||
|
||||
import { Redoc, SecurityDefinitions, Operation, REDOC_DIRECTIVES } from './components/index';
|
||||
import { Redoc, SecurityDefinitions, XPermissions, Operation, REDOC_DIRECTIVES } from './components/index';
|
||||
import { REDOC_COMMON_DIRECTIVES, DynamicNg2Wrapper, DropDown } from './shared/components/index';
|
||||
import { REDOC_PIPES } from './utils/pipes';
|
||||
import { CustomErrorHandler } from './utils/'
|
||||
import { CustomErrorHandler } from './utils/';
|
||||
import { LazyTasksService } from './shared/components/LazyFor/lazy-for';
|
||||
|
||||
import {
|
||||
|
@ -31,7 +31,7 @@ import {
|
|||
imports: [ CommonModule ],
|
||||
declarations: [ REDOC_DIRECTIVES, REDOC_COMMON_DIRECTIVES, REDOC_PIPES ],
|
||||
bootstrap: [ Redoc ],
|
||||
entryComponents: [ SecurityDefinitions, DynamicNg2Wrapper, Operation ],
|
||||
entryComponents: [ SecurityDefinitions, XPermissions, DynamicNg2Wrapper, Operation ],
|
||||
providers: [
|
||||
ScrollService,
|
||||
Hash,
|
||||
|
@ -42,7 +42,7 @@ import {
|
|||
ContentProjector,
|
||||
{ provide: APP_ID, useValue: 'redoc' },
|
||||
{ provide: ErrorHandler, useClass: CustomErrorHandler },
|
||||
{ provide: COMPONENT_PARSER_ALLOWED, useValue: { 'security-definitions': SecurityDefinitions} }
|
||||
{ provide: COMPONENT_PARSER_ALLOWED, useValue: { 'security-definitions': SecurityDefinitions, 'x-permissions': XPermissions} },
|
||||
],
|
||||
exports: [Redoc, REDOC_DIRECTIVES, REDOC_COMMON_DIRECTIVES, REDOC_PIPES]
|
||||
})
|
||||
|
|
|
@ -101,6 +101,13 @@ export class SpecManager {
|
|||
require('../components/SecurityDefinitions/security-definitions').SecurityDefinitions;
|
||||
mdRender.addPreprocessor(SecurityDefinitions.insertTagIntoDescription);
|
||||
}
|
||||
|
||||
if( this._schema['x-permissions']) {
|
||||
let permissions =
|
||||
require('../components/Permissions/x-permissions').XPermissions;
|
||||
mdRender.addPreprocessor(permissions.insertTagIntoDescription);
|
||||
}
|
||||
|
||||
this._schema.info['x-redoc-html-description'] = mdRender.renderMd(this._schema.info.description);
|
||||
this._schema.info['x-redoc-markdown-headers'] = mdRender.headings;
|
||||
}
|
||||
|
@ -185,6 +192,23 @@ export class SpecManager {
|
|||
return tagsMap;
|
||||
}
|
||||
|
||||
getPermissionsMap() {
|
||||
let permissions = this._schema['x-permissions'] || [];
|
||||
var permissionsMap = {};
|
||||
for (let key in permissions) {
|
||||
if( !permissions.hasOwnProperty(key) ) {
|
||||
continue;
|
||||
}
|
||||
permissionsMap[key] = {
|
||||
name: key,
|
||||
description: permissions[key].description,
|
||||
title: permissions[key].title ? permissions[key].title : key
|
||||
};
|
||||
}
|
||||
|
||||
return permissionsMap;
|
||||
}
|
||||
|
||||
findDerivedDefinitions(defPointer: string, schema?: any): DescendantInfo[] {
|
||||
let definition = schema || this.byPointer(defPointer);
|
||||
if (!definition) throw new Error(`Can't load schema at ${defPointer}`);
|
||||
|
|
|
@ -77,6 +77,8 @@
|
|||
post:
|
||||
tags:
|
||||
- "pet"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Add a new pet to the store"
|
||||
description: ""
|
||||
operationId: "addPet"
|
||||
|
@ -138,6 +140,8 @@
|
|||
put:
|
||||
tags:
|
||||
- "pet"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Update an existing pet"
|
||||
description: ""
|
||||
operationId: "updatePet"
|
||||
|
@ -173,6 +177,9 @@
|
|||
- "pet"
|
||||
- "Pagination"
|
||||
- "JSONP"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
- name: "user"
|
||||
summary: "Finds Pets by status"
|
||||
description: "Multiple status values can be provided with comma seperated strings"
|
||||
operationId: "findPetsByStatus"
|
||||
|
@ -214,6 +221,9 @@
|
|||
- "pet"
|
||||
- "Pagination"
|
||||
- "JSONP"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
- name: "user"
|
||||
summary: "Finds Pets by tags"
|
||||
description: "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing."
|
||||
operationId: "findPetsByTags"
|
||||
|
@ -249,6 +259,9 @@
|
|||
tags:
|
||||
- "pet"
|
||||
- "JSONP"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
- name: "user"
|
||||
summary: "Find pet by ID"
|
||||
description: "Returns a single pet"
|
||||
operationId: "getPetById"
|
||||
|
@ -278,6 +291,8 @@
|
|||
post:
|
||||
tags:
|
||||
- "pet"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Updates a pet in the store with form data"
|
||||
description: ""
|
||||
operationId: "updatePetWithForm"
|
||||
|
@ -317,6 +332,8 @@
|
|||
delete:
|
||||
tags:
|
||||
- "pet"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Deletes a pet"
|
||||
description: ""
|
||||
operationId: "deletePet"
|
||||
|
@ -348,6 +365,8 @@
|
|||
post:
|
||||
tags:
|
||||
- "pet"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "uploads an image"
|
||||
description: ""
|
||||
operationId: "uploadFile"
|
||||
|
@ -390,6 +409,8 @@
|
|||
tags:
|
||||
- "store"
|
||||
- "JSONP"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Returns pet inventories by status"
|
||||
description: "Returns a map of status codes to quantities"
|
||||
operationId: "getInventory"
|
||||
|
@ -411,6 +432,8 @@
|
|||
post:
|
||||
tags:
|
||||
- "store"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Place an order for a pet"
|
||||
description: ""
|
||||
operationId: "placeOrder"
|
||||
|
@ -437,6 +460,8 @@
|
|||
tags:
|
||||
- "store"
|
||||
- "JSONP"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Find purchase order by ID"
|
||||
description: "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions"
|
||||
operationId: "getOrderById"
|
||||
|
@ -465,6 +490,8 @@
|
|||
delete:
|
||||
tags:
|
||||
- "store"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Delete purchase order by ID"
|
||||
description: "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors"
|
||||
operationId: "deleteOrder"
|
||||
|
@ -488,6 +515,8 @@
|
|||
post:
|
||||
tags:
|
||||
- "user"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Create user"
|
||||
description: "This can only be done by the logged in user."
|
||||
operationId: "createUser"
|
||||
|
@ -509,6 +538,8 @@
|
|||
post:
|
||||
tags:
|
||||
- "user"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Creates list of users with given input array"
|
||||
description: ""
|
||||
operationId: "createUsersWithArrayInput"
|
||||
|
@ -532,6 +563,8 @@
|
|||
post:
|
||||
tags:
|
||||
- "user"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Creates list of users with given input array"
|
||||
description: ""
|
||||
operationId: "createUsersWithListInput"
|
||||
|
@ -555,6 +588,9 @@
|
|||
get:
|
||||
tags:
|
||||
- "user"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
- name: "user"
|
||||
summary: "Logs user into the system"
|
||||
description: ""
|
||||
operationId: "loginUser"
|
||||
|
@ -594,6 +630,9 @@
|
|||
get:
|
||||
tags:
|
||||
- "user"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
- name: "user"
|
||||
summary: "Logs out current logged in user session"
|
||||
description: ""
|
||||
operationId: "logoutUser"
|
||||
|
@ -609,6 +648,9 @@
|
|||
tags:
|
||||
- "user"
|
||||
- "JSONP"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
- name: "user"
|
||||
summary: "Get user by user name"
|
||||
description: ""
|
||||
operationId: "getUserByName"
|
||||
|
@ -634,6 +676,9 @@
|
|||
put:
|
||||
tags:
|
||||
- "user"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
- name: "user"
|
||||
summary: "Updated user"
|
||||
description: "This can only be done by the logged in user."
|
||||
operationId: "updateUser"
|
||||
|
@ -662,6 +707,8 @@
|
|||
delete:
|
||||
tags:
|
||||
- "user"
|
||||
x-permissions:
|
||||
- name: "admin"
|
||||
summary: "Delete user"
|
||||
description: "This can only be done by the logged in user."
|
||||
operationId: "deleteUser"
|
||||
|
@ -853,3 +900,11 @@
|
|||
externalDocs:
|
||||
description: "Find out more about Swagger"
|
||||
url: "http://swagger.io"
|
||||
x-permissions:
|
||||
admin:
|
||||
title: Pet store admin
|
||||
description: 'Pet store Admin have special privileges on PetStore API allowing them to manage their store and users from their company '
|
||||
user:
|
||||
title: Pet Store user
|
||||
description: 'Pet store user'
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ beforeEach(function() {
|
|||
services.SearchService,
|
||||
{ provide: sharedComponents.LazyTasksService, useClass: sharedComponents.LazyTasksServiceSync },
|
||||
//{ provide: ErrorHandler, useClass: forwardRef(function() {return services.CustomErrorHandler}) },
|
||||
{ provide: services.COMPONENT_PARSER_ALLOWED, useValue: { 'security-definitions': components.SecurityDefinitions }}
|
||||
{ provide: services.COMPONENT_PARSER_ALLOWED, useValue: { 'security-definitions': components.SecurityDefinitions }},
|
||||
{ provide: services.COMPONENT_PARSER_ALLOWED, useValue: { 'x-permissions': components.XPermissions }}
|
||||
],
|
||||
declarations: [REDOC_PIPES, REDOC_DIRECTIVES, REDOC_COMMON_DIRECTIVES]
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user