2018-02-22 12:26:53 +03:00
|
|
|
import { observe } from 'mobx';
|
|
|
|
|
2017-11-14 18:46:50 +03:00
|
|
|
import { OpenAPISpec } from '../types';
|
2017-11-19 13:51:59 +03:00
|
|
|
import { loadAndBundleSpec } from '../utils/loadAndBundleSpec';
|
2018-08-17 14:50:58 +03:00
|
|
|
import { history } from './HistoryService';
|
2018-03-05 18:58:19 +03:00
|
|
|
import { MarkerService } from './MarkerService';
|
2018-01-22 21:30:53 +03:00
|
|
|
import { MenuStore } from './MenuStore';
|
|
|
|
import { SpecStore } from './models';
|
2017-11-21 14:00:33 +03:00
|
|
|
import { RedocNormalizedOptions, RedocRawOptions } from './RedocNormalizedOptions';
|
2018-01-22 21:30:53 +03:00
|
|
|
import { ScrollService } from './ScrollService';
|
2018-02-08 19:41:02 +03:00
|
|
|
import { SearchStore } from './SearchStore';
|
2017-10-12 00:01:37 +03:00
|
|
|
|
2018-08-17 14:41:22 +03:00
|
|
|
import { SecurityDefs } from '../components/SecuritySchemes/SecuritySchemes';
|
|
|
|
import { SECURITY_DEFINITIONS_COMPONENT_NAME } from '../utils/openapi';
|
|
|
|
|
2018-03-07 18:14:00 +03:00
|
|
|
export interface StoreState {
|
2017-11-14 18:46:50 +03:00
|
|
|
menu: {
|
|
|
|
activeItemIdx: number;
|
|
|
|
};
|
|
|
|
spec: {
|
2018-07-17 12:17:06 +03:00
|
|
|
url?: string;
|
2017-11-14 18:46:50 +03:00
|
|
|
data: any;
|
|
|
|
};
|
2018-03-06 14:10:08 +03:00
|
|
|
searchIndex: any;
|
2017-11-21 14:00:33 +03:00
|
|
|
options: RedocRawOptions;
|
2018-01-22 21:30:53 +03:00
|
|
|
}
|
2017-11-14 18:46:50 +03:00
|
|
|
|
2017-11-21 14:00:33 +03:00
|
|
|
export async function createStore(
|
|
|
|
spec: object,
|
|
|
|
specUrl: string | undefined,
|
|
|
|
options: RedocRawOptions = {},
|
|
|
|
) {
|
2017-11-19 13:51:59 +03:00
|
|
|
const resolvedSpec = await loadAndBundleSpec(spec || specUrl);
|
2017-11-21 14:00:33 +03:00
|
|
|
return new AppStore(resolvedSpec, specUrl, options);
|
2017-11-19 13:51:59 +03:00
|
|
|
}
|
|
|
|
|
2017-10-12 00:01:37 +03:00
|
|
|
export class AppStore {
|
2018-01-22 21:30:53 +03:00
|
|
|
/**
|
|
|
|
* deserialize store
|
|
|
|
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
|
|
|
|
*/
|
|
|
|
// TODO:
|
2018-03-07 18:14:00 +03:00
|
|
|
static fromJS(state: StoreState): AppStore {
|
2018-03-06 14:10:08 +03:00
|
|
|
const inst = new AppStore(state.spec.data, state.spec.url, state.options, false);
|
2018-01-22 21:30:53 +03:00
|
|
|
inst.menu.activeItemIdx = state.menu.activeItemIdx || 0;
|
|
|
|
inst.menu.activate(inst.menu.flatItems[inst.menu.activeItemIdx]);
|
2018-07-17 11:07:34 +03:00
|
|
|
if (!inst.options.disableSearch) {
|
|
|
|
inst.search!.load(state.searchIndex);
|
|
|
|
}
|
2018-01-22 21:30:53 +03:00
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
2017-10-12 00:01:37 +03:00
|
|
|
menu: MenuStore;
|
|
|
|
spec: SpecStore;
|
2017-11-21 14:00:33 +03:00
|
|
|
rawOptions: RedocRawOptions;
|
|
|
|
options: RedocNormalizedOptions;
|
2018-07-17 11:07:34 +03:00
|
|
|
search?: SearchStore<string>;
|
2018-02-22 12:26:53 +03:00
|
|
|
marker = new MarkerService();
|
2017-10-12 00:01:37 +03:00
|
|
|
|
2017-11-14 18:46:50 +03:00
|
|
|
private scroll: ScrollService;
|
2018-02-22 12:26:53 +03:00
|
|
|
private disposer;
|
2017-10-12 00:01:37 +03:00
|
|
|
|
2018-03-06 14:10:08 +03:00
|
|
|
constructor(
|
|
|
|
spec: OpenAPISpec,
|
|
|
|
specUrl?: string,
|
|
|
|
options: RedocRawOptions = {},
|
|
|
|
createSearchIndex: boolean = true,
|
|
|
|
) {
|
2017-11-21 14:00:33 +03:00
|
|
|
this.rawOptions = options;
|
2018-08-17 14:41:22 +03:00
|
|
|
this.options = new RedocNormalizedOptions(options, DEFAULT_OPTIONS);
|
2018-01-11 19:08:33 +03:00
|
|
|
this.scroll = new ScrollService(this.options);
|
2018-03-22 18:46:30 +03:00
|
|
|
|
|
|
|
// update position statically based on hash (in case of SSR)
|
2018-08-17 14:50:58 +03:00
|
|
|
MenuStore.updateOnHistory(history.currentId, this.scroll);
|
2018-03-22 18:46:30 +03:00
|
|
|
|
2017-11-21 14:00:33 +03:00
|
|
|
this.spec = new SpecStore(spec, specUrl, this.options);
|
2018-08-17 14:50:58 +03:00
|
|
|
this.menu = new MenuStore(this.spec, this.scroll, history);
|
2018-02-08 19:41:02 +03:00
|
|
|
|
2018-07-17 11:07:34 +03:00
|
|
|
if (!this.options.disableSearch) {
|
|
|
|
this.search = new SearchStore();
|
|
|
|
if (createSearchIndex) {
|
|
|
|
this.search.indexItems(this.menu.items);
|
|
|
|
}
|
2018-02-22 12:26:53 +03:00
|
|
|
|
2018-07-17 11:07:34 +03:00
|
|
|
this.disposer = observe(this.menu, 'activeItemIdx', change => {
|
|
|
|
this.updateMarkOnMenu(change.newValue as number);
|
|
|
|
});
|
|
|
|
}
|
2018-02-22 12:26:53 +03:00
|
|
|
}
|
|
|
|
|
2018-02-22 19:48:50 +03:00
|
|
|
onDidMount() {
|
2018-08-17 14:50:58 +03:00
|
|
|
this.menu.updateOnHistory();
|
2018-02-22 19:48:50 +03:00
|
|
|
this.updateMarkOnMenu(this.menu.activeItemIdx);
|
|
|
|
}
|
|
|
|
|
2017-10-12 00:01:37 +03:00
|
|
|
dispose() {
|
|
|
|
this.scroll.dispose();
|
|
|
|
this.menu.dispose();
|
2018-02-22 12:26:53 +03:00
|
|
|
this.disposer();
|
2017-10-12 00:01:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* serializes store
|
|
|
|
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
|
|
|
|
*/
|
2018-07-26 17:34:44 +03:00
|
|
|
// TODO: improve
|
2018-03-07 18:14:00 +03:00
|
|
|
async toJS(): Promise<StoreState> {
|
2017-10-12 00:01:37 +03:00
|
|
|
return {
|
|
|
|
menu: {
|
2017-11-14 18:46:50 +03:00
|
|
|
activeItemIdx: this.menu.activeItemIdx,
|
2017-10-12 00:01:37 +03:00
|
|
|
},
|
|
|
|
spec: {
|
2017-11-14 18:46:50 +03:00
|
|
|
url: this.spec.parser.specUrl,
|
|
|
|
data: this.spec.parser.spec,
|
2017-10-12 00:01:37 +03:00
|
|
|
},
|
2018-07-17 11:07:34 +03:00
|
|
|
searchIndex: this.search ? await this.search.toJS() : undefined,
|
2017-11-21 14:00:33 +03:00
|
|
|
options: this.rawOptions,
|
2017-10-12 00:01:37 +03:00
|
|
|
};
|
|
|
|
}
|
2018-07-26 17:34:44 +03:00
|
|
|
|
|
|
|
private updateMarkOnMenu(idx: number) {
|
|
|
|
const start = Math.max(0, idx);
|
|
|
|
const end = Math.min(this.menu.flatItems.length, start + 5);
|
|
|
|
|
|
|
|
const elements: Element[] = [];
|
|
|
|
for (let i = start; i < end; i++) {
|
|
|
|
let elem = this.menu.getElementAt(i);
|
|
|
|
if (!elem) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (this.menu.flatItems[i].type === 'section') {
|
|
|
|
elem = elem.parentElement!.parentElement;
|
|
|
|
}
|
|
|
|
if (elem) {
|
|
|
|
elements.push(elem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.marker.addOnly(elements);
|
|
|
|
this.marker.mark();
|
|
|
|
}
|
2017-10-12 00:01:37 +03:00
|
|
|
}
|
2018-08-17 14:41:22 +03:00
|
|
|
|
|
|
|
const DEFAULT_OPTIONS: RedocRawOptions = {
|
|
|
|
allowedMdComponents: {
|
|
|
|
[SECURITY_DEFINITIONS_COMPONENT_NAME]: {
|
|
|
|
component: SecurityDefs,
|
|
|
|
propsSelector: (store: AppStore) => ({
|
|
|
|
securitySchemes: store.spec.securitySchemes,
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|