Refactor menu generation + handle methods without tags

This commit is contained in:
Roman Hotsiy 2016-07-01 17:24:59 +03:00
parent 4a475677f7
commit b22b8d2122
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
9 changed files with 74 additions and 67 deletions

View File

@ -1,7 +1,7 @@
<div class="method">
<div class="method-content">
<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>
<div class="method-tags" *ngIf="data.methodInfo.tags.length">
<a *ngFor="let tag of data.methodInfo.tags" attr.href="#{{tag}}"> {{tag}} </a>

View File

@ -8,6 +8,7 @@ import { ResponsesList } from '../ResponsesList/responses-list';
import { ResponsesSamples } from '../ResponsesSamples/responses-samples';
import { SchemaSample } from '../SchemaSample/schema-sample';
import { RequestSamples } from '../RequestSamples/request-samples';
import { SchemaHelper } from '../../services/schema-helper.service';
@RedocComponent({
selector: 'method',
@ -31,6 +32,7 @@ export class Method extends BaseComponent {
this.data.methodInfo = this.componentSchema;
this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags);
this.data.bodyParam = this.findBodyParam();
this.data.summary = SchemaHelper.methodSummary(this.componentSchema);
if (this.componentSchema.operationId) {
this.data.methodAnchor = 'operation/' + encodeURIComponent(this.componentSchema.operationId);
} else {

View File

@ -1,6 +1,6 @@
<div class="methods">
<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>
<p *ngIf="tag.description" [innerHtml]="tag.description | marked"> </p>
</div>

View File

@ -4,6 +4,7 @@ import { forwardRef } from '@angular/core';
import { RedocComponent, BaseComponent, SpecManager } from '../base';
import { Method } from '../Method/method';
import { EncodeURIComponentPipe } from '../../utils/pipes';
import { SchemaHelper } from '../../services/index';
@RedocComponent({
selector: 'methods-list',
@ -24,21 +25,14 @@ export class MethodsList extends BaseComponent {
// follow SwaggerUI behavior for cases when one method has more than one tag:
// duplicate methods
let menuStructure = this.specMgr.buildMenuTree();
let tags = Array.from<any>(menuStructure.entries())
.map((entry) => {
let [tag, {description, methods}] = entry;
// inject tag name into method info
methods = methods || [];
methods.forEach(method => {
method.tag = tag;
});
return {
name: tag,
description: description,
methods: methods
};
let tags = SchemaHelper.buildMenuTree(this.specMgr.schema);
tags.forEach(tagInfo => {
// inject tag name into method info
tagInfo.methods = tagInfo.methods || [];
tagInfo.methods.forEach(method => {
method.tag = name;
});
});
this.data.tags = tags;
// TODO: check $ref field
}

View File

@ -9,7 +9,8 @@
<h5 class="menu-header"> API reference </h5>
<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'">
<li *ngFor="let method of cat.methods; let methIdx = index"
[ngClass]="{active: method.active}"

View File

@ -29,6 +29,10 @@ $mobile-menu-compact-breakpoint: 550px;
color: $primary-color;
background-color: $side-menu-active-bg-color;
}
&[hidden] {
display: none;
}
}
.menu-subitems {

View File

@ -3,6 +3,7 @@ import { Injectable, EventEmitter } from '@angular/core';
import { ScrollService, INVIEW_POSITION } from './scroll.service';
import { Hash } from './hash.service';
import { SpecManager } from '../utils/SpecManager';
import { SchemaHelper } from './schema-helper.service';
const CHANGE = {
NEXT : 1,
@ -21,9 +22,7 @@ export class MenuService {
constructor(private hash:Hash, private scrollService:ScrollService, specMgr:SpecManager) {
this.hash = hash;
this.categories = Array.from(specMgr.buildMenuTree().entries()).map(
el => ({name: el[0], description: el[1].description, methods: el[1].methods})
);
this.categories = SchemaHelper.buildMenuTree(specMgr.schema);
scrollService.scroll.subscribe((evt) => {
this.scrollUpdate(evt.isScrolledDown);

View File

@ -1,6 +1,7 @@
'use strict';
import { JsonPointer } from '../utils/JsonPointer';
import { SpecManager } from '../utils/SpecManager';
import {methods as swaggerMethods} from '../utils/swagger-defs';
interface PropertyPreprocessOptions {
childFor: string;
@ -218,4 +219,57 @@ export class SchemaHelper {
}
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]);
}
}

View File

@ -2,7 +2,6 @@
import JsonSchemaRefParser from 'json-schema-ref-parser';
import JsonPointer from './JsonPointer';
import {methods as swaggerMethods} from './swagger-defs';
export class SpecManager {
public _schema:any = {};
@ -114,52 +113,6 @@ export class SpecManager {
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) {
let definition = this.byPointer(defPointer);
if (!definition) throw new Error(`Can't load schema at ${defPointer}`);