Skip to content

Commit f85f1b8

Browse files
committed
[fix] MobX-Strapi circular dependency bug
1 parent c79e65b commit f85f1b8

File tree

5 files changed

+261
-248
lines changed

5 files changed

+261
-248
lines changed

wrapper/Strapi/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mobx-strapi",
3-
"version": "0.8.0",
3+
"version": "0.8.1",
44
"license": "LGPL-3.0",
55
"author": "[email protected]",
66
"description": "MobX SDK for Strapi headless CMS",

wrapper/Strapi/source/List.ts

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { AbstractClass, makeDateRange, TypeKeys } from 'web-utility';
2+
import { stringify } from 'qs';
3+
import { computed, observable } from 'mobx';
4+
import { IDType, NewData, Filter, ListModel, toggle } from 'mobx-restful';
5+
6+
import {
7+
Base,
8+
StrapiDataItem,
9+
StrapiFilter,
10+
StrapiFilterOperator,
11+
StrapiItemWrapper,
12+
StrapiListWrapper,
13+
StrapiPopulateQuery,
14+
StrapiQuery
15+
} from './type';
16+
17+
export abstract class StrapiListModel<
18+
D extends Base,
19+
F extends Filter<D> = Filter<D>
20+
> extends ListModel<D, F> {
21+
operator: Partial<Record<keyof D, StrapiFilterOperator>> = {};
22+
23+
sort: Partial<Record<keyof D, 'asc' | 'desc'>> = {};
24+
25+
populate: StrapiPopulateQuery<D> = {};
26+
27+
dateKeys: readonly TypeKeys<D, string>[] = [];
28+
29+
normalize({ id, documentId, attributes }: StrapiDataItem<D>) {
30+
const data = Object.fromEntries(
31+
Object.entries(attributes).map(([key, value]) => [
32+
key,
33+
value?.data
34+
? Array.isArray(value.data)
35+
? (value as StrapiListWrapper<any>).data.map(item => this.normalize(item))
36+
: this.normalize(value.data)
37+
: value
38+
])
39+
) as D;
40+
41+
return { id, documentId, ...data } as D;
42+
}
43+
44+
@toggle('downloading')
45+
async getOne(id: IDType) {
46+
const { populate } = this;
47+
48+
const { body } = await this.client.get<StrapiItemWrapper<D>>(
49+
`${this.baseURI}/${id}?${stringify({ populate }, { encodeValuesOnly: true })}`
50+
);
51+
return (this.currentOne = this.normalize(body!.data));
52+
}
53+
54+
@toggle('uploading')
55+
async updateOne(data: Partial<NewData<D>>, id?: IDType) {
56+
const { body } = await (id
57+
? this.client.put<StrapiItemWrapper<D>>(`${this.baseURI}/${id}`, {
58+
data
59+
})
60+
: this.client.post<StrapiItemWrapper<D>>(this.baseURI, { data }));
61+
62+
return (this.currentOne = this.normalize(body!.data));
63+
}
64+
65+
makeFilter(pageIndex: number, pageSize: number, filter: F): StrapiQuery<D> {
66+
const { indexKey, operator, populate, dateKeys } = this,
67+
pagination = { page: pageIndex, pageSize };
68+
69+
const filters = Object.fromEntries(
70+
Object.entries(filter).map(([key, value]) => [
71+
key,
72+
key in populate
73+
? { [indexKey]: { $eq: value } }
74+
: dateKeys.includes(key as TypeKeys<D, string>)
75+
? { $between: makeDateRange(value + '') }
76+
: { [key in operator ? operator[key] : '$eq']: value }
77+
])
78+
) as StrapiFilter<typeof indexKey>;
79+
80+
const sort = Object.entries(this.sort).map(([key, value]) => `${key}:${value}`);
81+
return { populate, filters, sort, pagination };
82+
}
83+
84+
async loadPage(pageIndex: number, pageSize: number, filter: F) {
85+
const { body } = await this.client.get<StrapiListWrapper<D>>(
86+
`${this.baseURI}?${stringify(this.makeFilter(pageIndex, pageSize, filter), {
87+
encodeValuesOnly: true
88+
})}`
89+
);
90+
return {
91+
pageData: body!.data.map(item => this.normalize(item)),
92+
totalCount: body!.meta.pagination.total
93+
};
94+
}
95+
}
96+
97+
export type SearchableFilter<D extends Base> = Filter<D> & {
98+
keywords?: string;
99+
};
100+
101+
export function Searchable<
102+
D extends Base,
103+
F extends SearchableFilter<D> = SearchableFilter<D>,
104+
M extends AbstractClass<StrapiListModel<D, F>> = AbstractClass<StrapiListModel<D, F>>
105+
>(Super: M) {
106+
abstract class SearchableListMixin extends Super {
107+
abstract searchKeys: readonly TypeKeys<D, string>[];
108+
109+
@observable
110+
accessor keywords = '';
111+
112+
@computed
113+
get searchFilter() {
114+
const words = this.keywords.split(/\s+/);
115+
116+
type OrFilter = Record<TypeKeys<D, string>, { $containsi: string }>;
117+
118+
const $or = this.searchKeys
119+
.map(key => words.map(word => ({ [key]: { $containsi: word } })))
120+
.flat() as OrFilter[];
121+
122+
return { $or };
123+
}
124+
125+
makeFilter(pageIndex: number, pageSize: number, filter: F) {
126+
const { populate, keywords } = this,
127+
pagination = { page: pageIndex, pageSize };
128+
129+
return keywords
130+
? { populate, filters: this.searchFilter, pagination }
131+
: super.makeFilter(pageIndex, pageSize, filter);
132+
}
133+
134+
getList(
135+
{ keywords, ...filter }: F,
136+
pageIndex = this.pageIndex + 1,
137+
pageSize = this.pageSize
138+
) {
139+
if (keywords) this.keywords = keywords;
140+
141+
return super.getList(filter as F, pageIndex, pageSize);
142+
}
143+
}
144+
return SearchableListMixin;
145+
}

wrapper/Strapi/source/User.ts

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,11 @@
1-
import { components } from '@octokit/openapi-types';
21
import { clear } from 'idb-keyval';
32
import { HTTPClient, HTTPError } from 'koajax';
43
import { observable } from 'mobx';
54
import { Filter, IDType, NewData, persist, restore, toggle } from 'mobx-restful';
65
import { stringify } from 'qs';
76

8-
import { StrapiListModel } from './index';
9-
10-
export type Base = Record<'documentId' | 'createdAt' | 'updatedAt', string> & {
11-
id: number;
12-
};
13-
export type BaseUser = Base &
14-
Record<'username' | 'email' | 'provider', string> &
15-
Record<'confirmed' | 'blocked', boolean>;
16-
17-
export type GithubUser = components['schemas']['public-user'];
18-
19-
export type OAuthProvider =
20-
| 'auth0'
21-
| 'cognito'
22-
| 'cas'
23-
| 'discord'
24-
| 'facebook'
25-
| 'github'
26-
| 'google'
27-
| 'instagram'
28-
| 'keycloak'
29-
| 'linkedin'
30-
| 'patreon'
31-
| 'reddit'
32-
| 'twitch'
33-
| 'twitter'
34-
| 'vk';
35-
export type Media = Base &
36-
Record<
37-
| 'name'
38-
| 'ext'
39-
| 'mime'
40-
| 'hash'
41-
| 'url'
42-
| 'previewUrl'
43-
| 'provider'
44-
| 'alternativeText'
45-
| 'caption',
46-
string
47-
> &
48-
Record<'width' | 'height' | 'formats' | 'size', number> & {
49-
provider_metadata: {};
50-
};
7+
import { StrapiListModel } from './List';
8+
import { BaseUser, GithubUser, Media, OAuthProvider } from './type';
519

5210
export class UserModel<
5311
D extends BaseUser = BaseUser,

0 commit comments

Comments
 (0)