|
1 | 1 | /* |
2 | 2 | * adonis-api-resources |
3 | 3 | * |
4 | | - * (c) Boris Abramov <boris@ideainc.eu> |
| 4 | + * (c) Boris Abramov <boris@rykantas.com> |
5 | 5 | * |
6 | 6 | * For the full copyright and license information, please view the LICENSE |
7 | 7 | * file that was distributed with this source code. |
8 | 8 | */ |
9 | 9 |
|
| 10 | +import { type Data, Processor } from '#common/types' |
| 11 | +import EntityProcessor from '#processors/entity' |
| 12 | +import CollectionProcessor from '#processors/collection' |
| 13 | +import PaginatedProcessor from '#processors/paginated' |
| 14 | +import { paginate } from '#utils/paginate' |
| 15 | + |
10 | 16 | export abstract class Resource { |
11 | | - constructor(private data: any) {} |
| 17 | + constructor(private data: Data) {} |
| 18 | + |
| 19 | + private _processor?: Processor |
12 | 20 |
|
13 | | - abstract defineMap(data: any): object |
| 21 | + abstract defineMap(data: object): object |
14 | 22 |
|
15 | | - private isPaginated(): boolean { |
16 | | - const ormPaginated = 'rows' in this.data && 'currentPage' in this.data |
17 | | - const odmPaginated = 'data' in this.data && 'meta' in this.data |
| 23 | + private isPaginated(data: Data): boolean { |
| 24 | + const ormPaginated = 'rows' in data && 'currentPage' in data |
| 25 | + const odmPaginated = 'data' in data && 'meta' in data |
18 | 26 | const indeedPaginated = ormPaginated || odmPaginated |
19 | | - if (odmPaginated) this.data.getMeta = () => this.data.meta |
| 27 | + if (odmPaginated) (data as any).getMeta = () => (data as any).meta |
20 | 28 | return indeedPaginated |
21 | 29 | } |
22 | 30 |
|
23 | | - private isCollection(): boolean { |
24 | | - return Array.isArray(this.data) |
25 | | - } |
26 | | - |
27 | | - private parsePaginated(meta: object, data: object): object { |
28 | | - return { |
29 | | - meta: meta, |
30 | | - data: data, |
31 | | - } |
32 | | - } |
33 | | - |
34 | | - private redefineEntity(data: any): object { |
35 | | - return this.defineMap(data) |
| 31 | + private isCollection(data: Data): boolean { |
| 32 | + return Array.isArray(data) |
36 | 33 | } |
37 | 34 |
|
38 | | - private redefineCollection(data: any[]): object { |
39 | | - return data.map((item) => { |
40 | | - return this.redefineEntity(item) |
41 | | - }) |
42 | | - } |
43 | | - |
44 | | - private pickEntity(entity: object, keys: string[]) { |
45 | | - return Object.assign({}, ...keys.map((key) => ({ [key]: (entity as any)[key] }))) |
46 | | - } |
47 | | - |
48 | | - private pickCollection(data: object[], keys: string[]) { |
49 | | - let result = [] |
50 | | - for (let entity of data) { |
51 | | - result.push(this.pickEntity(entity, keys)) |
| 35 | + private processor(): Processor { |
| 36 | + if (!this._processor) { |
| 37 | + if (this.isPaginated(this.data)) { |
| 38 | + this._processor = new PaginatedProcessor(new CollectionProcessor()) |
| 39 | + } else if (this.isCollection(this.data)) { |
| 40 | + this._processor = new CollectionProcessor() |
| 41 | + } else { |
| 42 | + this._processor = new EntityProcessor() |
| 43 | + } |
52 | 44 | } |
53 | | - return result |
| 45 | + return this._processor |
54 | 46 | } |
55 | 47 |
|
56 | | - private omitEntity(entity: object, keys: string[]) { |
57 | | - const exclude = new Set(keys) |
58 | | - const o = |
59 | | - 'serialize' in entity && typeof entity.serialize === 'function' ? entity.serialize() : entity |
60 | | - return Object.fromEntries(Object.entries(o).filter((e) => !exclude.has(e[0]))) |
| 48 | + pick(...keys: string[]) { |
| 49 | + const processor = this.processor() |
| 50 | + this.data = processor.pick(this.data, ...keys) |
| 51 | + return this |
61 | 52 | } |
62 | 53 |
|
63 | | - private omitCollection(data: object[], keys: string[]) { |
64 | | - let result = [] |
65 | | - for (let entity of data) { |
66 | | - result.push(this.omitEntity(entity, keys)) |
67 | | - } |
68 | | - return result |
| 54 | + omit(...keys: string[]): this { |
| 55 | + const processor = this.processor() |
| 56 | + this.data = processor.omit(this.data, ...keys) |
| 57 | + return this |
69 | 58 | } |
70 | 59 |
|
71 | | - pick(...keys: string[]): this { |
72 | | - if (this.isPaginated()) { |
73 | | - this.data = this.parsePaginated( |
74 | | - (this.data as any).getMeta(), |
75 | | - this.pickCollection((this.data as any).rows || (this.data as any).data, keys) |
76 | | - ) |
77 | | - } else if (this.isCollection()) { |
78 | | - this.data = this.pickCollection(this.data, keys) |
79 | | - } else { |
80 | | - this.data = this.pickEntity(this.data, keys) |
81 | | - } |
| 60 | + remap(): this { |
| 61 | + const processor = this.processor() |
| 62 | + this.data = processor.remap(this.data, this.defineMap) |
82 | 63 | return this |
83 | 64 | } |
84 | 65 |
|
85 | | - omit(...keys: string[]): this { |
86 | | - if (this.isPaginated()) { |
87 | | - this.data = this.parsePaginated( |
88 | | - (this.data as any).getMeta(), |
89 | | - this.omitCollection((this.data as any).rows || (this.data as any).data, keys) |
90 | | - ) |
91 | | - } else if (this.isCollection()) { |
92 | | - this.data = this.omitCollection(this.data, keys) |
93 | | - } else { |
94 | | - this.data = this.omitEntity(this.data, keys) |
| 66 | + paginate(page: number = 1, limit: number = 10): this { |
| 67 | + if (!this.isCollection(this.data)) { |
| 68 | + throw new Error('Pagination requires an array of objects') |
95 | 69 | } |
| 70 | + this.data = paginate(this.data as Array<object>, page, limit) |
96 | 71 | return this |
97 | 72 | } |
98 | 73 |
|
| 74 | + // Obsolete methods for backward compatibility |
99 | 75 | redefine(): this { |
100 | | - if (this.isPaginated()) { |
101 | | - this.data = this.parsePaginated( |
102 | | - (this.data as any).getMeta(), |
103 | | - this.redefineCollection((this.data as any).rows || (this.data as any).data) |
104 | | - ) |
105 | | - } else if (this.isCollection()) { |
106 | | - this.data = this.redefineCollection(this.data) |
107 | | - } else { |
108 | | - this.data = this.redefineEntity(this.data) |
109 | | - } |
110 | | - return this |
| 76 | + return this.remap() |
111 | 77 | } |
112 | | - |
113 | | - refine(): object { |
114 | | - return this.redefine().get() |
| 78 | + get(): this { |
| 79 | + return this |
115 | 80 | } |
116 | | - |
117 | | - refinePaginate(page: number = 1, limit: number = 10): object { |
118 | | - return this.redefine().paginate(page, limit) |
| 81 | + refine(): this { |
| 82 | + return this.remap() |
119 | 83 | } |
120 | | - |
121 | | - paginate(page: number = 1, limit: number = 10) { |
122 | | - const lastPage = Math.max(Math.ceil(this.data.data.length / limit), 1) |
123 | | - interface PaginationMeta { |
124 | | - total: number |
125 | | - perPage: number |
126 | | - currentPage: number |
127 | | - lastPage: number |
128 | | - firstPage: number |
129 | | - firstPageUrl: string |
130 | | - lastPageUrl: string |
131 | | - nextPageUrl: string | null |
132 | | - previousPageUrl: string | null |
133 | | - } |
134 | | - const meta: PaginationMeta = { |
135 | | - total: this.data.length, |
136 | | - perPage: Math.floor(limit), |
137 | | - currentPage: Math.floor(page), |
138 | | - lastPage: lastPage, |
139 | | - firstPage: 1, |
140 | | - firstPageUrl: '/?page=1', |
141 | | - lastPageUrl: '/?page=' + lastPage, |
142 | | - nextPageUrl: page < lastPage ? `/?page=${page + 1}` : null, |
143 | | - previousPageUrl: page > 1 ? `/?page=${page - 1}` : null, |
144 | | - } |
145 | | - const collection = (this.data.rows || this.data.data).slice((page - 1) * limit, page * limit) |
146 | | - this.data = { |
147 | | - meta: meta, |
148 | | - data: collection, |
149 | | - } |
150 | | - return this.data |
| 84 | + refinePaginate(page: number = 1, limit: number = 10): this { |
| 85 | + return this.remap().paginate(page, limit) |
151 | 86 | } |
| 87 | +} |
152 | 88 |
|
153 | | - get() { |
154 | | - return this.data |
155 | | - } |
| 89 | +;(Resource.prototype as any).toJSON = function () { |
| 90 | + return (this as any).data |
156 | 91 | } |
0 commit comments