mirror of
https://github.com/Redocly/redoc.git
synced 2025-02-07 13:30:33 +03:00
Refactor menu generation + handle methods without tags
This commit is contained in:
parent
4a475677f7
commit
b22b8d2122
|
@ -1,7 +1,7 @@
|
||||||
<div class="method">
|
<div class="method">
|
||||||
<div class="method-content">
|
<div class="method-content">
|
||||||
<h2 class="method-header sharable-header">
|
<h2 class="method-header sharable-header">
|
||||||
<a class="share-link" href="#{{data.methodAnchor}}"></a>{{data.methodInfo.summary}}
|
<a class="share-link" href="#{{data.methodAnchor}}"></a>{{data.summary}}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="method-tags" *ngIf="data.methodInfo.tags.length">
|
<div class="method-tags" *ngIf="data.methodInfo.tags.length">
|
||||||
<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>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { ResponsesList } from '../ResponsesList/responses-list';
|
||||||
import { ResponsesSamples } from '../ResponsesSamples/responses-samples';
|
import { ResponsesSamples } from '../ResponsesSamples/responses-samples';
|
||||||
import { SchemaSample } from '../SchemaSample/schema-sample';
|
import { SchemaSample } from '../SchemaSample/schema-sample';
|
||||||
import { RequestSamples } from '../RequestSamples/request-samples';
|
import { RequestSamples } from '../RequestSamples/request-samples';
|
||||||
|
import { SchemaHelper } from '../../services/schema-helper.service';
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
selector: 'method',
|
selector: 'method',
|
||||||
|
@ -31,6 +32,7 @@ export class Method extends BaseComponent {
|
||||||
this.data.methodInfo = this.componentSchema;
|
this.data.methodInfo = this.componentSchema;
|
||||||
this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags);
|
this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags);
|
||||||
this.data.bodyParam = this.findBodyParam();
|
this.data.bodyParam = this.findBodyParam();
|
||||||
|
this.data.summary = SchemaHelper.methodSummary(this.componentSchema);
|
||||||
if (this.componentSchema.operationId) {
|
if (this.componentSchema.operationId) {
|
||||||
this.data.methodAnchor = 'operation/' + encodeURIComponent(this.componentSchema.operationId);
|
this.data.methodAnchor = 'operation/' + encodeURIComponent(this.componentSchema.operationId);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="methods">
|
<div class="methods">
|
||||||
<div class="tag" *ngFor="let tag of data.tags;trackBy:trackByTagName">
|
<div class="tag" *ngFor="let tag of data.tags;trackBy:trackByTagName">
|
||||||
<div class="tag-info" [attr.tag]="tag.name">
|
<div class="tag-info" [attr.tag]="tag.name" *ngIf="!tag.empty">
|
||||||
<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>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { forwardRef } from '@angular/core';
|
||||||
import { RedocComponent, BaseComponent, SpecManager } 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';
|
||||||
|
import { SchemaHelper } from '../../services/index';
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
selector: 'methods-list',
|
selector: 'methods-list',
|
||||||
|
@ -24,20 +25,13 @@ 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.specMgr.buildMenuTree();
|
let tags = SchemaHelper.buildMenuTree(this.specMgr.schema);
|
||||||
let tags = Array.from<any>(menuStructure.entries())
|
tags.forEach(tagInfo => {
|
||||||
.map((entry) => {
|
|
||||||
let [tag, {description, methods}] = entry;
|
|
||||||
// inject tag name into method info
|
// inject tag name into method info
|
||||||
methods = methods || [];
|
tagInfo.methods = tagInfo.methods || [];
|
||||||
methods.forEach(method => {
|
tagInfo.methods.forEach(method => {
|
||||||
method.tag = tag;
|
method.tag = name;
|
||||||
});
|
});
|
||||||
return {
|
|
||||||
name: tag,
|
|
||||||
description: description,
|
|
||||||
methods: methods
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
this.data.tags = tags;
|
this.data.tags = tags;
|
||||||
// TODO: check $ref field
|
// TODO: check $ref field
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
<h5 class="menu-header"> API reference </h5>
|
<h5 class="menu-header"> API reference </h5>
|
||||||
<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)" [hidden]="cat.empty"
|
||||||
|
[ngClass]="{active: cat.active}"> {{cat.name}}</label>
|
||||||
<ul class="menu-subitems" @itemAnimation="cat.active ? 'expanded' : 'collapsed'">
|
<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}"
|
||||||
|
|
|
@ -29,6 +29,10 @@ $mobile-menu-compact-breakpoint: 550px;
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
background-color: $side-menu-active-bg-color;
|
background-color: $side-menu-active-bg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-subitems {
|
.menu-subitems {
|
||||||
|
|
|
@ -3,6 +3,7 @@ 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 { SpecManager } from '../utils/SpecManager';
|
import { SpecManager } from '../utils/SpecManager';
|
||||||
|
import { SchemaHelper } from './schema-helper.service';
|
||||||
|
|
||||||
const CHANGE = {
|
const CHANGE = {
|
||||||
NEXT : 1,
|
NEXT : 1,
|
||||||
|
@ -21,9 +22,7 @@ export class MenuService {
|
||||||
|
|
||||||
constructor(private hash:Hash, private scrollService:ScrollService, specMgr:SpecManager) {
|
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 = SchemaHelper.buildMenuTree(specMgr.schema);
|
||||||
el => ({name: el[0], description: el[1].description, methods: el[1].methods})
|
|
||||||
);
|
|
||||||
|
|
||||||
scrollService.scroll.subscribe((evt) => {
|
scrollService.scroll.subscribe((evt) => {
|
||||||
this.scrollUpdate(evt.isScrolledDown);
|
this.scrollUpdate(evt.isScrolledDown);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
import { JsonPointer } from '../utils/JsonPointer';
|
import { JsonPointer } from '../utils/JsonPointer';
|
||||||
import { SpecManager } from '../utils/SpecManager';
|
import { SpecManager } from '../utils/SpecManager';
|
||||||
|
import {methods as swaggerMethods} from '../utils/swagger-defs';
|
||||||
|
|
||||||
interface PropertyPreprocessOptions {
|
interface PropertyPreprocessOptions {
|
||||||
childFor: string;
|
childFor: string;
|
||||||
|
@ -218,4 +219,57 @@ export class SchemaHelper {
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static methodSummary(method) {
|
||||||
|
return method.summary || method.operationId || method.description.substring(0, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildMenuTree(schema) {
|
||||||
|
let tag2MethodMapping = {};
|
||||||
|
|
||||||
|
let definedTags = schema.tags || [];
|
||||||
|
// add tags into map to preserve order
|
||||||
|
for (let tag of definedTags) {
|
||||||
|
tag2MethodMapping[tag.name] = {
|
||||||
|
'description': tag.description,
|
||||||
|
'x-traitTag': tag['x-traitTag'],
|
||||||
|
'methods': []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let paths = schema.paths;
|
||||||
|
for (let path of Object.keys(paths)) {
|
||||||
|
let methods = Object.keys(paths[path]).filter((k) => swaggerMethods.has(k));
|
||||||
|
for (let method of methods) {
|
||||||
|
let methodInfo = paths[path][method];
|
||||||
|
let tags = methodInfo.tags;
|
||||||
|
|
||||||
|
//TODO: mb need to do something cleverer
|
||||||
|
if (!tags || !tags.length) {
|
||||||
|
tags = [''];
|
||||||
|
}
|
||||||
|
let methodPointer = JsonPointer.compile(['paths', path, method]);
|
||||||
|
let methodSummary = SchemaHelper.methodSummary(methodInfo);
|
||||||
|
for (let tag of tags) {
|
||||||
|
let tagDetails = tag2MethodMapping[tag];
|
||||||
|
if (!tagDetails) {
|
||||||
|
tagDetails = {
|
||||||
|
name: tag,
|
||||||
|
empty: !tag
|
||||||
|
};
|
||||||
|
tag2MethodMapping[tag] = tagDetails;
|
||||||
|
}
|
||||||
|
if (tagDetails['x-traitTag']) continue;
|
||||||
|
if (!tagDetails.methods) tagDetails.methods = [];
|
||||||
|
tagDetails.methods.push({
|
||||||
|
pointer: methodPointer,
|
||||||
|
summary: methodSummary,
|
||||||
|
operationId: methodInfo.operationId,
|
||||||
|
tag: tag
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.keys(tag2MethodMapping).map(tag => tag2MethodMapping[tag]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import JsonSchemaRefParser from 'json-schema-ref-parser';
|
import JsonSchemaRefParser from 'json-schema-ref-parser';
|
||||||
import JsonPointer from './JsonPointer';
|
import JsonPointer from './JsonPointer';
|
||||||
import {methods as swaggerMethods} from './swagger-defs';
|
|
||||||
|
|
||||||
export class SpecManager {
|
export class SpecManager {
|
||||||
public _schema:any = {};
|
public _schema:any = {};
|
||||||
|
@ -114,52 +113,6 @@ export class SpecManager {
|
||||||
return tagsMap;
|
return tagsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns ES6 Map */
|
|
||||||
buildMenuTree():Map<string, any> {
|
|
||||||
let tag2MethodMapping = new Map();
|
|
||||||
|
|
||||||
let definedTags = this._schema.tags || [];
|
|
||||||
// add tags into map to preserve order
|
|
||||||
for (let tag of definedTags) {
|
|
||||||
tag2MethodMapping.set(tag.name, {
|
|
||||||
'description': tag.description,
|
|
||||||
'x-traitTag': tag['x-traitTag'],
|
|
||||||
'methods': []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let paths = this._schema.paths;
|
|
||||||
for (let path of Object.keys(paths)) {
|
|
||||||
let methods = Object.keys(paths[path]).filter((k) => swaggerMethods.has(k));
|
|
||||||
for (let method of methods) {
|
|
||||||
let methodInfo = paths[path][method];
|
|
||||||
let tags = methodInfo.tags;
|
|
||||||
|
|
||||||
//TODO: mb need to do something cleverer
|
|
||||||
if (!tags || !tags.length) {
|
|
||||||
tags = ['[Other]'];
|
|
||||||
}
|
|
||||||
let methodPointer = JsonPointer.compile(['paths', path, method]);
|
|
||||||
let methodSummary = methodInfo.summary || methodInfo.operationId;
|
|
||||||
for (let tag of tags) {
|
|
||||||
let tagDetails = tag2MethodMapping.get(tag);
|
|
||||||
if (!tagDetails) {
|
|
||||||
tagDetails = {};
|
|
||||||
tag2MethodMapping.set(tag, tagDetails);
|
|
||||||
}
|
|
||||||
if (tagDetails['x-traitTag']) continue;
|
|
||||||
if (!tagDetails.methods) tagDetails.methods = [];
|
|
||||||
tagDetails.methods.push({
|
|
||||||
pointer: methodPointer,
|
|
||||||
summary: methodSummary,
|
|
||||||
operationId: methodInfo.operationId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tag2MethodMapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
findDerivedDefinitions(defPointer) {
|
findDerivedDefinitions(defPointer) {
|
||||||
let definition = this.byPointer(defPointer);
|
let definition = this.byPointer(defPointer);
|
||||||
if (!definition) throw new Error(`Can't load schema at ${defPointer}`);
|
if (!definition) throw new Error(`Can't load schema at ${defPointer}`);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user