1 | import {SchemaType} from './parts/Schema'
|
2 | import {client} from './parts/Client'
|
3 | import {SortItem} from './Sort'
|
4 | import {EditorBuilder} from './Editor'
|
5 | import {SerializeError, HELP_URL} from './SerializeError'
|
6 | import {SerializeOptions, Child} from './StructureNodes'
|
7 | import {ChildResolver, ChildResolverOptions, ItemChild} from './ChildResolver'
|
8 | import {
|
9 | GenericListBuilder,
|
10 | BuildableGenericList,
|
11 | GenericList,
|
12 | GenericListInput
|
13 | } from './GenericList'
|
14 |
|
15 | const resolveTypeForDocument = (id: string): Promise<string | undefined> => {
|
16 | const query = '*[_id in [$documentId, $draftId]]._type'
|
17 | const documentId = id.replace(/^drafts\./, '')
|
18 | const draftId = `drafts.${documentId}`
|
19 | return client.fetch(query, {documentId, draftId}).then(types => types[0])
|
20 | }
|
21 |
|
22 | const validateFilter = (spec: PartialDocumentList, options: SerializeOptions) => {
|
23 | const filter = spec.options!.filter.trim()
|
24 |
|
25 | if (['*', '{'].includes(filter[0])) {
|
26 | throw new SerializeError(
|
27 | `\`filter\` cannot start with \`${
|
28 | filter[0]
|
29 | }\` - looks like you are providing a query, not a filter`,
|
30 | options.path,
|
31 | spec.id,
|
32 | spec.title
|
33 | ).withHelpUrl(HELP_URL.QUERY_PROVIDED_FOR_FILTER)
|
34 | }
|
35 |
|
36 | return filter
|
37 | }
|
38 |
|
39 | const resolveEditorChildForItem: ChildResolver = (
|
40 | itemId: string,
|
41 | options: ChildResolverOptions
|
42 | ): ItemChild | Promise<ItemChild> | undefined => {
|
43 | const parentItem = options.parent as DocumentList
|
44 | return Promise.resolve(parentItem.schemaTypeName || resolveTypeForDocument(itemId)).then(type =>
|
45 | new EditorBuilder()
|
46 | .id('editor')
|
47 | .documentId(itemId)
|
48 | .schemaType(type || '')
|
49 | )
|
50 | }
|
51 |
|
52 | export interface PartialDocumentList extends BuildableGenericList {
|
53 | options?: DocumentListOptions
|
54 | schemaTypeName?: string
|
55 | }
|
56 |
|
57 | export interface DocumentListInput extends GenericListInput {
|
58 | options: DocumentListOptions
|
59 | }
|
60 |
|
61 | export interface DocumentList extends GenericList {
|
62 | options: DocumentListOptions
|
63 | child: Child
|
64 | schemaTypeName?: string
|
65 | }
|
66 |
|
67 | interface DocumentListOptions {
|
68 | filter: string
|
69 | params?: {[key: string]: any}
|
70 | defaultOrdering?: SortItem[]
|
71 | }
|
72 |
|
73 | export class DocumentListBuilder extends GenericListBuilder<
|
74 | PartialDocumentList,
|
75 | DocumentListBuilder
|
76 | > {
|
77 | protected spec: PartialDocumentList
|
78 |
|
79 | constructor(spec?: DocumentListInput) {
|
80 | super()
|
81 | this.spec = spec ? spec : {}
|
82 | }
|
83 |
|
84 | filter(filter: string): DocumentListBuilder {
|
85 | return this.clone({options: {...(this.spec.options || {}), filter}})
|
86 | }
|
87 |
|
88 | getFilter() {
|
89 | return this.spec.options && this.spec.options.filter
|
90 | }
|
91 |
|
92 | schemaType(type: SchemaType | string): DocumentListBuilder {
|
93 | return this.clone({schemaTypeName: typeof type === 'string' ? type : type.name})
|
94 | }
|
95 |
|
96 | getSchemaType() {
|
97 | return this.spec.schemaTypeName
|
98 | }
|
99 |
|
100 | params(params: {}): DocumentListBuilder {
|
101 | return this.clone({options: {...(this.spec.options || {filter: ''}), params}})
|
102 | }
|
103 |
|
104 | getParams() {
|
105 | return this.spec.options && this.spec.options.params
|
106 | }
|
107 |
|
108 | defaultOrdering(ordering: SortItem[]): DocumentListBuilder {
|
109 | if (!Array.isArray(ordering)) {
|
110 | throw new Error('`defaultOrdering` must be an array of order clauses')
|
111 | }
|
112 |
|
113 | return this.clone({
|
114 | options: {...(this.spec.options || {filter: ''}), defaultOrdering: ordering}
|
115 | })
|
116 | }
|
117 |
|
118 | getDefaultOrdering() {
|
119 | return this.spec.options && this.spec.options.defaultOrdering
|
120 | }
|
121 |
|
122 | serialize(options: SerializeOptions = {path: []}): DocumentList {
|
123 | if (typeof this.spec.id !== 'string' || !this.spec.id) {
|
124 | throw new SerializeError(
|
125 | '`id` is required for document lists',
|
126 | options.path,
|
127 | options.index,
|
128 | this.spec.title
|
129 | ).withHelpUrl(HELP_URL.ID_REQUIRED)
|
130 | }
|
131 |
|
132 | if (!this.spec.options || !this.spec.options.filter) {
|
133 | throw new SerializeError(
|
134 | '`filter` is required for document lists',
|
135 | options.path,
|
136 | this.spec.id,
|
137 | this.spec.title
|
138 | ).withHelpUrl(HELP_URL.FILTER_REQUIRED)
|
139 | }
|
140 |
|
141 | return {
|
142 | ...super.serialize(options),
|
143 | type: 'documentList',
|
144 | schemaTypeName: this.spec.schemaTypeName,
|
145 | child: this.spec.child || resolveEditorChildForItem,
|
146 | options: {
|
147 | ...this.spec.options,
|
148 | filter: validateFilter(this.spec, options)
|
149 | }
|
150 | }
|
151 | }
|
152 |
|
153 | clone(withSpec?: PartialDocumentList): DocumentListBuilder {
|
154 | const builder = new DocumentListBuilder()
|
155 | builder.spec = {...this.spec, ...(withSpec || {})}
|
156 | return builder
|
157 | }
|
158 | }
|