UNPKG

4.44 kBPlain TextView Raw
1import {SchemaType} from './parts/Schema'
2import {client} from './parts/Client'
3import {SortItem} from './Sort'
4import {EditorBuilder} from './Editor'
5import {SerializeError, HELP_URL} from './SerializeError'
6import {SerializeOptions, Child} from './StructureNodes'
7import {ChildResolver, ChildResolverOptions, ItemChild} from './ChildResolver'
8import {
9 GenericListBuilder,
10 BuildableGenericList,
11 GenericList,
12 GenericListInput
13} from './GenericList'
14
15const 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
22const 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
39const 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
52export interface PartialDocumentList extends BuildableGenericList {
53 options?: DocumentListOptions
54 schemaTypeName?: string
55}
56
57export interface DocumentListInput extends GenericListInput {
58 options: DocumentListOptions
59}
60
61export interface DocumentList extends GenericList {
62 options: DocumentListOptions
63 child: Child
64 schemaTypeName?: string
65}
66
67interface DocumentListOptions {
68 filter: string
69 params?: {[key: string]: any}
70 defaultOrdering?: SortItem[]
71}
72
73export 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}