@kaspernj/api-maker 1.0.146 → 1.0.149
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -0
- package/index.js +1 -87
- package/package.json +1 -2
- package/src/base-model.cjs +7 -2
- package/src/can-can.cjs +21 -2
- package/src/collection-loader.jsx +226 -0
- package/src/config.js +4 -0
- package/src/deserializer.cjs +9 -1
- package/src/link.jsx +21 -0
- package/src/router.jsx +131 -0
- package/src/resource-route.cjs +0 -82
- package/src/resource-routes.jsx +0 -30
package/.eslintrc.js
CHANGED
|
@@ -239,6 +239,7 @@ module.exports = {
|
|
|
239
239
|
"react/jsx-newline": "off",
|
|
240
240
|
"react/jsx-no-bind": "error",
|
|
241
241
|
"react/jsx-no-constructed-context-values": "error",
|
|
242
|
+
"react/jsx-no-leaked-render": "error",
|
|
242
243
|
"react/jsx-no-literals": "error",
|
|
243
244
|
"react/jsx-no-script-url": "error",
|
|
244
245
|
"react/jsx-no-useless-fragment": "error",
|
|
@@ -316,6 +317,7 @@ module.exports = {
|
|
|
316
317
|
"jest/consistent-test-it": "off",
|
|
317
318
|
"jest/prefer-called-with": "off",
|
|
318
319
|
"jest/prefer-expect-assertions": "off",
|
|
320
|
+
"jest/prefer-hooks-in-order": "error",
|
|
319
321
|
"jest/prefer-strict-equal": "off",
|
|
320
322
|
"no-shadow": "off",
|
|
321
323
|
"jest/prefer-to-have-length": "off",
|
package/index.js
CHANGED
|
@@ -1,87 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const AttributeNotLoadedError = require("./src/attribute-not-loaded-error.cjs")
|
|
3
|
-
const BaseModel = require("./src/base-model.cjs")
|
|
4
|
-
const CableConnectionPool = require("./src/cable-connection-pool.cjs")
|
|
5
|
-
const CanCan = require("./src/can-can.cjs")
|
|
6
|
-
const CanCanLoader = require("./src/can-can-loader.jsx").default
|
|
7
|
-
const Collection = require("./src/collection.cjs")
|
|
8
|
-
const CommandSubmitData = require("./src/command-submit-data.cjs")
|
|
9
|
-
const CommandsPool = require("./src/commands-pool.cjs")
|
|
10
|
-
const CustomError = require("./src/custom-error.cjs")
|
|
11
|
-
const Deserializer = require("./src/deserializer.cjs")
|
|
12
|
-
const Devise = require("./src/devise.cjs")
|
|
13
|
-
const ErrorLogger = require("./src/error-logger.cjs")
|
|
14
|
-
const EventConnection = require("./src/event-connection").default
|
|
15
|
-
const EventCreated = require("./src/event-created").default
|
|
16
|
-
const EventDestroyed = require("./src/event-destroyed").default
|
|
17
|
-
const EventEmitterListener = require("./src/event-emitter-listener").default
|
|
18
|
-
const EventListener = require("./src/event-listener").default
|
|
19
|
-
const EventModelClass = require("./src/event-model-class").default
|
|
20
|
-
const EventUpdated = require("./src/event-updated").default
|
|
21
|
-
const instanceOfClassName = require("./src/instance-of-class-name.cjs")
|
|
22
|
-
const KeyValueStore = require("./src/key-value-store.cjs")
|
|
23
|
-
const Logger = require("./src/logger.cjs")
|
|
24
|
-
const ModelName = require("./src/model-name.cjs")
|
|
25
|
-
const ModelPropType = require("./src/model-prop-type.cjs")
|
|
26
|
-
const ModelsResponseReader = require("./src/models-response-reader.cjs")
|
|
27
|
-
const MoneyFormatter = require("./src/money-formatter.cjs")
|
|
28
|
-
const NotLoadedError = require("./src/not-loaded-error.cjs")
|
|
29
|
-
const Params = require("./src/params.cjs")
|
|
30
|
-
const Preloaded = require("./src/preloaded.cjs")
|
|
31
|
-
const RoutesNative = require("./src/routes-native.cjs")
|
|
32
|
-
const ResourceRoute = require("./src/resource-route.cjs")
|
|
33
|
-
const ResourceRoutes = require("./src/resource-routes").default
|
|
34
|
-
const Result = require("./src/result.cjs")
|
|
35
|
-
const Routes = require("./src/routes.cjs")
|
|
36
|
-
const Serializer = require("./src/serializer.cjs")
|
|
37
|
-
const Services = require("./src/services.cjs")
|
|
38
|
-
const SessionStatusUpdater = require("./src/session-status-updater.cjs")
|
|
39
|
-
const SourceMapsLoader = require("./src/source-maps-loader.cjs")
|
|
40
|
-
const UpdatedAttribute = require("./src/updated-attribute").default
|
|
41
|
-
const ValidationError = require("./src/validation-error.cjs")
|
|
42
|
-
const {ValidationErrors} = require("./src/validation-errors.cjs")
|
|
43
|
-
|
|
44
|
-
export {
|
|
45
|
-
Api,
|
|
46
|
-
AttributeNotLoadedError,
|
|
47
|
-
BaseModel,
|
|
48
|
-
CableConnectionPool,
|
|
49
|
-
CanCan,
|
|
50
|
-
CanCanLoader,
|
|
51
|
-
Collection,
|
|
52
|
-
CommandSubmitData,
|
|
53
|
-
CommandsPool,
|
|
54
|
-
CustomError,
|
|
55
|
-
Deserializer,
|
|
56
|
-
Devise,
|
|
57
|
-
ErrorLogger,
|
|
58
|
-
EventConnection,
|
|
59
|
-
EventCreated,
|
|
60
|
-
EventDestroyed,
|
|
61
|
-
EventEmitterListener,
|
|
62
|
-
EventListener,
|
|
63
|
-
EventModelClass,
|
|
64
|
-
EventUpdated,
|
|
65
|
-
instanceOfClassName,
|
|
66
|
-
KeyValueStore,
|
|
67
|
-
Logger,
|
|
68
|
-
ModelName,
|
|
69
|
-
ModelPropType,
|
|
70
|
-
ModelsResponseReader,
|
|
71
|
-
MoneyFormatter,
|
|
72
|
-
NotLoadedError,
|
|
73
|
-
Params,
|
|
74
|
-
Preloaded,
|
|
75
|
-
ResourceRoute,
|
|
76
|
-
ResourceRoutes,
|
|
77
|
-
Result,
|
|
78
|
-
Routes,
|
|
79
|
-
RoutesNative,
|
|
80
|
-
Serializer,
|
|
81
|
-
Services,
|
|
82
|
-
SessionStatusUpdater,
|
|
83
|
-
SourceMapsLoader,
|
|
84
|
-
UpdatedAttribute,
|
|
85
|
-
ValidationError,
|
|
86
|
-
ValidationErrors
|
|
87
|
-
}
|
|
1
|
+
export {}
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
]
|
|
17
17
|
},
|
|
18
18
|
"name": "@kaspernj/api-maker",
|
|
19
|
-
"version": "1.0.
|
|
19
|
+
"version": "1.0.149",
|
|
20
20
|
"description": "",
|
|
21
21
|
"main": "index.js",
|
|
22
22
|
"repository": {
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"prop-types": "^15.7.2",
|
|
57
57
|
"prop-types-exact": ">= 1.2.0",
|
|
58
58
|
"react": ">= 17.0.2",
|
|
59
|
-
"react-router-dom": ">= 5.2.0",
|
|
60
59
|
"react-test-renderer": "^17.0.1",
|
|
61
60
|
"source-map": "^0.7.3",
|
|
62
61
|
"stacktrace-parser": ">= 0.1.9"
|
package/src/base-model.cjs
CHANGED
|
@@ -329,7 +329,9 @@ class BaseModel {
|
|
|
329
329
|
static humanAttributeName (attributeName) {
|
|
330
330
|
const keyName = digg(this.modelClassData(), "i18nKey")
|
|
331
331
|
|
|
332
|
-
return shared.i18n.t(`activerecord.attributes.${keyName}.${BaseModel.snakeCase(attributeName)}`, {defaultValue: attributeName})
|
|
332
|
+
if (shared.i18n) return shared.i18n.t(`activerecord.attributes.${keyName}.${BaseModel.snakeCase(attributeName)}`, {defaultValue: attributeName})
|
|
333
|
+
|
|
334
|
+
return inflection.humanize(attributeName)
|
|
333
335
|
}
|
|
334
336
|
|
|
335
337
|
isAttributeChanged (attributeName) {
|
|
@@ -783,7 +785,10 @@ class BaseModel {
|
|
|
783
785
|
const relationshipClassData = relationships.find((relationship) => digg(relationship, "name") == relationshipName)
|
|
784
786
|
|
|
785
787
|
if (!relationshipClassData) {
|
|
786
|
-
|
|
788
|
+
const modelName = digg(this.modelClassData(), "name")
|
|
789
|
+
const relationshipsList = relationships.map((relationship) => relationship.name).join(", ")
|
|
790
|
+
|
|
791
|
+
throw new Error(`Could not find the relation ${relationshipName} on the ${modelName} model: ${relationshipsList}`)
|
|
787
792
|
}
|
|
788
793
|
|
|
789
794
|
const relationshipType = digg(relationshipClassData, "collectionName")
|
package/src/can-can.cjs
CHANGED
|
@@ -33,7 +33,7 @@ module.exports = class ApiMakerCanCan {
|
|
|
33
33
|
subjectLabel = digg(subject.modelClassData(), "name")
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
console.error(`Ability not loaded ${subjectLabel}#${abilityToUse}
|
|
36
|
+
console.error(`Ability not loaded ${subjectLabel}#${abilityToUse}`, {abilities: this.abilities, ability, subject})
|
|
37
37
|
|
|
38
38
|
return false
|
|
39
39
|
} else {
|
|
@@ -42,7 +42,26 @@ module.exports = class ApiMakerCanCan {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
findAbility (ability, subject) {
|
|
45
|
-
return this.abilities.find((abilityData) =>
|
|
45
|
+
return this.abilities.find((abilityData) => {
|
|
46
|
+
const abilityDataSubject = digg(abilityData, "subject")
|
|
47
|
+
const abilityDataAbility = digg(abilityData, "ability")
|
|
48
|
+
|
|
49
|
+
// If actually same class
|
|
50
|
+
if (abilityDataSubject == subject && abilityDataAbility == ability) return true
|
|
51
|
+
|
|
52
|
+
// Sometimes in dev when using linking it will actually be two different but identical resource classes
|
|
53
|
+
if (
|
|
54
|
+
typeof subject == "function" &&
|
|
55
|
+
subject.modelClassData &&
|
|
56
|
+
typeof abilityDataSubject == "function" &&
|
|
57
|
+
abilityDataSubject.modelClassData &&
|
|
58
|
+
digg(subject.modelClassData(), "name") == digg(abilityDataSubject.modelClassData(), "name")
|
|
59
|
+
) {
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false
|
|
64
|
+
})
|
|
46
65
|
}
|
|
47
66
|
|
|
48
67
|
isAbilityLoaded (ability, subject) {
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
const Collection = require("@kaspernj/api-maker/src/collection")
|
|
2
|
+
const {debounce} = require("debounce")
|
|
3
|
+
const {digg, digs} = require("diggerize")
|
|
4
|
+
const EventCreated = require("@kaspernj/api-maker/src/event-created").default
|
|
5
|
+
const EventDestroyed = require("@kaspernj/api-maker/src/event-destroyed").default
|
|
6
|
+
const EventUpdated = require("@kaspernj/api-maker/src/event-updated").default
|
|
7
|
+
const instanceOfClassName = require("@kaspernj/api-maker/src/instance-of-class-name")
|
|
8
|
+
const Params = require("@kaspernj/api-maker/src/params")
|
|
9
|
+
const PropTypes = require("prop-types")
|
|
10
|
+
const React = require("react")
|
|
11
|
+
|
|
12
|
+
import {LocationChanged} from "on-location-changed/src/location-changed-component"
|
|
13
|
+
|
|
14
|
+
export default class CollectionLoader extends React.PureComponent {
|
|
15
|
+
static defaultProps = {
|
|
16
|
+
destroyEnabled: true,
|
|
17
|
+
noRecordsAvailableContent: undefined,
|
|
18
|
+
noRecordsFoundContent: undefined,
|
|
19
|
+
preloads: [],
|
|
20
|
+
select: {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static propTypes = {
|
|
24
|
+
abilities: PropTypes.object,
|
|
25
|
+
appHistory: PropTypes.object,
|
|
26
|
+
className: PropTypes.string,
|
|
27
|
+
collection: PropTypes.oneOfType([
|
|
28
|
+
instanceOfClassName("ApiMakerCollection"),
|
|
29
|
+
PropTypes.instanceOf(Collection)
|
|
30
|
+
]),
|
|
31
|
+
component: PropTypes.object,
|
|
32
|
+
defaultParams: PropTypes.object,
|
|
33
|
+
groupBy: PropTypes.array,
|
|
34
|
+
modelClass: PropTypes.func.isRequired,
|
|
35
|
+
noRecordsAvailableContent: PropTypes.func,
|
|
36
|
+
noRecordsFoundContent: PropTypes.func,
|
|
37
|
+
onModelsLoaded: PropTypes.func,
|
|
38
|
+
paginateContent: PropTypes.func,
|
|
39
|
+
preloads: PropTypes.array.isRequired,
|
|
40
|
+
queryName: PropTypes.string,
|
|
41
|
+
select: PropTypes.object,
|
|
42
|
+
selectColumns: PropTypes.object
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
shape = digg(this, "props", "component", "shape")
|
|
46
|
+
|
|
47
|
+
constructor (props) {
|
|
48
|
+
super(props)
|
|
49
|
+
|
|
50
|
+
let queryName = props.queryName
|
|
51
|
+
|
|
52
|
+
if (!queryName) queryName = digg(props.modelClass.modelClassData(), "collectionKey")
|
|
53
|
+
|
|
54
|
+
this.shape.set({
|
|
55
|
+
models: undefined,
|
|
56
|
+
overallCount: undefined,
|
|
57
|
+
query: undefined,
|
|
58
|
+
queryName,
|
|
59
|
+
queryQName: `${queryName}_q`,
|
|
60
|
+
queryPageName: `${queryName}_page`,
|
|
61
|
+
qParams: undefined,
|
|
62
|
+
result: undefined,
|
|
63
|
+
showNoRecordsAvailableContent: false,
|
|
64
|
+
showNoRecordsFoundContent: false
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
componentDidMount () {
|
|
69
|
+
this.loadQParams()
|
|
70
|
+
this.loadModels()
|
|
71
|
+
|
|
72
|
+
const {noRecordsAvailableContent} = digs(this.props, "noRecordsAvailableContent")
|
|
73
|
+
|
|
74
|
+
if (noRecordsAvailableContent) this.loadOverallCount()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async loadOverallCount () {
|
|
78
|
+
const baseQuery = this.props.collection || this.props.modelClass.all()
|
|
79
|
+
const overallCount = await baseQuery.count()
|
|
80
|
+
|
|
81
|
+
this.shape.set({
|
|
82
|
+
overallCount,
|
|
83
|
+
showNoRecordsAvailableContent: this.showNoRecordsAvailableContent({overallCount}),
|
|
84
|
+
showNoRecordsFoundContent: this.showNoRecordsFoundContent({overallCount})
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
loadQParams () {
|
|
89
|
+
const {queryQName} = digs(this.shape, "queryQName")
|
|
90
|
+
const params = Params.parse()
|
|
91
|
+
const qParams = Object.assign({}, this.props.defaultParams, params[queryQName])
|
|
92
|
+
|
|
93
|
+
this.shape.set({qParams})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
loadModelsDebounce = debounce(this.loadModels)
|
|
97
|
+
|
|
98
|
+
loadModels = async () => {
|
|
99
|
+
const params = Params.parse()
|
|
100
|
+
const {abilities, collection, groupBy, modelClass, onModelsLoaded, preloads, select, selectColumns} = this.props
|
|
101
|
+
const {qParams, queryPageName, queryQName} = digs(this.shape, "qParams", "queryPageName", "queryQName")
|
|
102
|
+
|
|
103
|
+
let query = collection?.clone() || modelClass
|
|
104
|
+
|
|
105
|
+
if (groupBy) query = query.groupBy(groupBy)
|
|
106
|
+
|
|
107
|
+
query = query
|
|
108
|
+
.ransack(qParams)
|
|
109
|
+
.searchKey(queryQName)
|
|
110
|
+
.page(params[queryPageName])
|
|
111
|
+
.pageKey(queryPageName)
|
|
112
|
+
.preload(preloads)
|
|
113
|
+
.select(select)
|
|
114
|
+
|
|
115
|
+
if (abilities) query = query.abilities(abilities)
|
|
116
|
+
if (selectColumns) query = query.selectColumns(selectColumns)
|
|
117
|
+
|
|
118
|
+
const result = await query.result()
|
|
119
|
+
const models = result.models()
|
|
120
|
+
|
|
121
|
+
if (onModelsLoaded) {
|
|
122
|
+
onModelsLoaded({
|
|
123
|
+
models,
|
|
124
|
+
qParams,
|
|
125
|
+
query,
|
|
126
|
+
result
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.shape.set({
|
|
131
|
+
query,
|
|
132
|
+
result,
|
|
133
|
+
models: result.models(),
|
|
134
|
+
showNoRecordsAvailableContent: this.showNoRecordsAvailableContent({models}),
|
|
135
|
+
showNoRecordsFoundContent: this.showNoRecordsFoundContent({models})
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
onModelCreated = () => this.loadModels()
|
|
140
|
+
|
|
141
|
+
onModelDestroyed = (args) => {
|
|
142
|
+
const {models} = digs(this.shape, "models")
|
|
143
|
+
|
|
144
|
+
this.shape.set({
|
|
145
|
+
models: models.filter((model) => model.id() != args.model.id())
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
onModelUpdated = (args) => {
|
|
150
|
+
const {models} = digs(this.shape, "models")
|
|
151
|
+
const updatedModel = digg(args, "model")
|
|
152
|
+
const foundModel = models.find((model) => model.id() == updatedModel.id())
|
|
153
|
+
|
|
154
|
+
if (foundModel) this.loadModelsDebounce()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
onLocationChanged = () => {
|
|
158
|
+
const {queryQName} = digs(this.shape, "queryQName")
|
|
159
|
+
const params = Params.parse()
|
|
160
|
+
const qParams = Object.assign({}, this.props.defaultParams, params[queryQName])
|
|
161
|
+
|
|
162
|
+
this.shape.set({qParams})
|
|
163
|
+
this.loadModels()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
showNoRecordsAvailableContent (args) {
|
|
167
|
+
const {noRecordsAvailableContent} = digs(this.props, "noRecordsAvailableContent")
|
|
168
|
+
let models, overallCount
|
|
169
|
+
|
|
170
|
+
if (args.models !== undefined) {
|
|
171
|
+
models = args.models
|
|
172
|
+
} else if (this.shape.models !== undefined) {
|
|
173
|
+
models = this.shape.models
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (args.overallCount !== undefined) {
|
|
177
|
+
overallCount = args.overallCount
|
|
178
|
+
} else if (this.shape.overallCount !== undefined) {
|
|
179
|
+
overallCount = this.shape.overallCount
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (models === undefined || overallCount === undefined || noRecordsAvailableContent === undefined) return false
|
|
183
|
+
if (models.length === 0 && overallCount === 0 && noRecordsAvailableContent) return true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
showNoRecordsFoundContent (args) {
|
|
187
|
+
const {noRecordsAvailableContent, noRecordsFoundContent} = digs(this.props, "noRecordsAvailableContent", "noRecordsFoundContent")
|
|
188
|
+
let models, overallCount
|
|
189
|
+
|
|
190
|
+
if (args.models !== undefined) {
|
|
191
|
+
models = args.models
|
|
192
|
+
} else if (this.shape.models !== undefined) {
|
|
193
|
+
models = this.shape.models
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (args.overallCount !== undefined) {
|
|
197
|
+
overallCount = args.overallCount
|
|
198
|
+
} else if (this.shape.overallCount !== undefined) {
|
|
199
|
+
overallCount = this.shape.overallCount
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (models === undefined || noRecordsFoundContent === undefined) return false
|
|
203
|
+
|
|
204
|
+
// Dont show noRecordsAvailableContent together with noRecordsAvailableContent
|
|
205
|
+
if (models.length === 0 && overallCount === 0 && noRecordsAvailableContent) return false
|
|
206
|
+
if (models.length === 0 && noRecordsFoundContent) return true
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
render() {
|
|
210
|
+
const {modelClass} = digs(this.props, "modelClass")
|
|
211
|
+
const {models} = digs(this.shape, "models")
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<>
|
|
215
|
+
<EventCreated modelClass={modelClass} onCreated={digg(this, "onModelCreated")} />
|
|
216
|
+
<LocationChanged onChanged={digg(this, "onLocationChanged")} />
|
|
217
|
+
{models && models.map((model) =>
|
|
218
|
+
<React.Fragment key={model.id()}>
|
|
219
|
+
<EventDestroyed model={model} onDestroyed={digg(this, "onModelDestroyed")} />
|
|
220
|
+
<EventUpdated model={model} onUpdated={digg(this, "onModelUpdated")} />
|
|
221
|
+
</React.Fragment>
|
|
222
|
+
)}
|
|
223
|
+
</>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
}
|
package/src/config.js
CHANGED
|
@@ -14,6 +14,10 @@ class ApiMakerConfig {
|
|
|
14
14
|
setCurrenciesCollection(newCurrenciesCollection) {
|
|
15
15
|
this.global.currenciesCollection = newCurrenciesCollection
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
setHistory(history) {
|
|
19
|
+
this.global.history = history
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const apiMakerConfig = new ApiMakerConfig()
|
package/src/deserializer.cjs
CHANGED
|
@@ -8,9 +8,17 @@ module.exports = class ApiMakerDeserializer {
|
|
|
8
8
|
if (Array.isArray(object)) {
|
|
9
9
|
return object.map((value) => ApiMakerDeserializer.parse(value))
|
|
10
10
|
} else if (object && typeof object == "object") {
|
|
11
|
-
if (object.api_maker_type == "date"
|
|
11
|
+
if (object.api_maker_type == "date") {
|
|
12
12
|
const date = new Date(digg(object, "value"))
|
|
13
13
|
|
|
14
|
+
date.apiMakerType = "date"
|
|
15
|
+
|
|
16
|
+
return date
|
|
17
|
+
} else if (object.api_maker_type == "time") {
|
|
18
|
+
const date = new Date(digg(object, "value"))
|
|
19
|
+
|
|
20
|
+
date.apiMakerType = "time"
|
|
21
|
+
|
|
14
22
|
return date
|
|
15
23
|
} else if (object.api_maker_type == "collection") {
|
|
16
24
|
// Need to remove type to avoid circular error
|
package/src/link.jsx
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
export default class Link extends React.PureComponent {
|
|
4
|
+
render() {
|
|
5
|
+
const {to, ...restProps} = this.props
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<a href={to} {...restProps} onClick={this.onLinkClicked} />
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
onLinkClicked = (e) => {
|
|
13
|
+
e.preventDefault()
|
|
14
|
+
|
|
15
|
+
const history = global.apiMakerConfigGlobal?.history
|
|
16
|
+
|
|
17
|
+
if (!history) throw new Error("History hasn't been set in the API maker configuration")
|
|
18
|
+
|
|
19
|
+
history.push(this.props.to)
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/router.jsx
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import escapeStringRegexp from "escape-string-regexp"
|
|
2
|
+
import inflection from "inflection"
|
|
3
|
+
import PropTypes from "prop-types"
|
|
4
|
+
import React from "react"
|
|
5
|
+
import {shouldComponentUpdate} from "set-state-compare"
|
|
6
|
+
import {Suspense} from "react"
|
|
7
|
+
|
|
8
|
+
export default class ApiMakerRouter extends React.Component {
|
|
9
|
+
static propTypes = {
|
|
10
|
+
notFoundComponent: PropTypes.elementType,
|
|
11
|
+
path: PropTypes.string.isRequired,
|
|
12
|
+
requireComponent: PropTypes.func.isRequired,
|
|
13
|
+
routes: PropTypes.object,
|
|
14
|
+
routeDefinitions: PropTypes.object
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
parsedRouteDefinitions = this.parseRouteDefinitions()
|
|
18
|
+
|
|
19
|
+
shouldComponentUpdate(nextProps, nextState) {
|
|
20
|
+
return shouldComponentUpdate(this, nextProps, nextState)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
findRouteParams (routeDefinition) {
|
|
24
|
+
const result = []
|
|
25
|
+
const parts = routeDefinition.path.split("/")
|
|
26
|
+
|
|
27
|
+
for (const part of parts) {
|
|
28
|
+
if (part.match(/^:([a-z_]+)$/))
|
|
29
|
+
result.push(part)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return result
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
parseRouteDefinitions() {
|
|
36
|
+
const Locales = require("shared/locales").default
|
|
37
|
+
const {routeDefinitions, routes} = this.props
|
|
38
|
+
const regex = /:([A-z\d_]+)/
|
|
39
|
+
const parsedRouteDefinitions = []
|
|
40
|
+
|
|
41
|
+
for (const locale of Locales.availableLocales()) {
|
|
42
|
+
for (const routeDefinition of routeDefinitions.routes) {
|
|
43
|
+
const routePathName = `${inflection.camelize(routeDefinition.name, true)}Path`
|
|
44
|
+
const params = this.findRouteParams(routeDefinition)
|
|
45
|
+
|
|
46
|
+
params.push({locale})
|
|
47
|
+
|
|
48
|
+
if (!(routePathName in routes))
|
|
49
|
+
throw new Error(`${routePathName} not found in routes: ${Object.keys(routes, ", ")}`)
|
|
50
|
+
|
|
51
|
+
const routePath = routes[routePathName](...params).replace(/[\/]+$/, "")
|
|
52
|
+
const groups = []
|
|
53
|
+
|
|
54
|
+
let pathRegexString = '^'
|
|
55
|
+
|
|
56
|
+
pathRegexString += escapeStringRegexp(routePath)
|
|
57
|
+
|
|
58
|
+
while (true) {
|
|
59
|
+
const match = pathRegexString.match(regex)
|
|
60
|
+
|
|
61
|
+
if (!match) break
|
|
62
|
+
|
|
63
|
+
const variableName = match[1]
|
|
64
|
+
|
|
65
|
+
groups.push(variableName)
|
|
66
|
+
|
|
67
|
+
pathRegexString = pathRegexString.replace(match[0], `([^\/]+)`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
pathRegexString += '$'
|
|
71
|
+
|
|
72
|
+
const pathRegex = new RegExp(pathRegexString)
|
|
73
|
+
|
|
74
|
+
parsedRouteDefinitions.push({groups, pathRegex, routeDefinition})
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return parsedRouteDefinitions
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
findMatchingRoute() {
|
|
82
|
+
const path = this.props.path.replace(/[\/]+$/, "")
|
|
83
|
+
|
|
84
|
+
for (const parsedRouteDefinition of this.parsedRouteDefinitions) {
|
|
85
|
+
const match = path.match(parsedRouteDefinition.pathRegex)
|
|
86
|
+
let matched, params
|
|
87
|
+
|
|
88
|
+
if (match) {
|
|
89
|
+
matched = true
|
|
90
|
+
params = {}
|
|
91
|
+
|
|
92
|
+
for (const groupKey in parsedRouteDefinition.groups) {
|
|
93
|
+
const groupName = parsedRouteDefinition.groups[groupKey]
|
|
94
|
+
|
|
95
|
+
params[groupName] = match[Number(groupKey) + 1]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (path == "" && parsedRouteDefinition.routeDefinition.path == "/") matched = true
|
|
100
|
+
if (matched) {
|
|
101
|
+
return {params, parsedRouteDefinition}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
render() {
|
|
107
|
+
const matchingRoute = this.findMatchingRoute()
|
|
108
|
+
|
|
109
|
+
if (!matchingRoute) {
|
|
110
|
+
if (this.props.notFoundComponent) {
|
|
111
|
+
const NotFoundComponent = this.props.notFoundComponent
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<Suspense fallback={<div />}>
|
|
115
|
+
<NotFoundComponent />
|
|
116
|
+
</Suspense>
|
|
117
|
+
)
|
|
118
|
+
} else {
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const Component = this.props.requireComponent({routeDefinition: matchingRoute.parsedRouteDefinition.routeDefinition})
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<Suspense fallback={<div />}>
|
|
127
|
+
<Component match={{params: matchingRoute.params}} />
|
|
128
|
+
</Suspense>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
}
|
package/src/resource-route.cjs
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const {digg} = require("diggerize")
|
|
2
|
-
const inflection = require("inflection")
|
|
3
|
-
|
|
4
|
-
module.exports = class ApiMakerResourceRoute {
|
|
5
|
-
constructor ({jsRoutes, locales, requireComponent, routeDefinition}) {
|
|
6
|
-
this.jsRoutes = jsRoutes
|
|
7
|
-
this.locales = locales
|
|
8
|
-
this.requireComponent = requireComponent
|
|
9
|
-
this.routeDefinition = routeDefinition
|
|
10
|
-
|
|
11
|
-
if (!jsRoutes) {
|
|
12
|
-
throw new Error("No 'jsRoutes' given")
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
routesResult () {
|
|
17
|
-
if (digg(this, "locales")) {
|
|
18
|
-
return this.withLocale()
|
|
19
|
-
} else {
|
|
20
|
-
return this.withoutLocale()
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
findRouteParams () {
|
|
25
|
-
const result = []
|
|
26
|
-
const parts = digg(this, "routeDefinition", "path").split("/")
|
|
27
|
-
|
|
28
|
-
for (const part of parts) {
|
|
29
|
-
if (part.match(/^:([a-z_]+)$/))
|
|
30
|
-
result.push(part)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return result
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
requireComponentFromCaller () {
|
|
37
|
-
return this.requireComponent({
|
|
38
|
-
routeDefinition: digg(this, "routeDefinition")
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
withLocale () {
|
|
43
|
-
const component = this.requireComponentFromCaller()
|
|
44
|
-
const Locales = require("shared/locales").default
|
|
45
|
-
const routes = []
|
|
46
|
-
|
|
47
|
-
for (const locale of Locales.availableLocales()) {
|
|
48
|
-
const routePathName = `${inflection.camelize(digg(this, "routeDefinition", "name"), true)}Path`
|
|
49
|
-
const params = this.findRouteParams()
|
|
50
|
-
|
|
51
|
-
params.push({locale})
|
|
52
|
-
|
|
53
|
-
if (!(routePathName in this.jsRoutes)) {
|
|
54
|
-
throw new Error(`${routePathName} not found in routes: ${Object.keys(this.jsRoutes, ", ")}`)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const path = this.jsRoutes[routePathName](...params)
|
|
58
|
-
|
|
59
|
-
routes.push({path, component})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return routes
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
withoutLocale () {
|
|
66
|
-
const routePathName = inflection.camelize(digg(this, "routeDefinition", "name"), true)
|
|
67
|
-
const routePathMethod = this.jsRoutes[`${routePathName}Path`]
|
|
68
|
-
|
|
69
|
-
if (!routePathMethod)
|
|
70
|
-
throw new Error(`No such route could be found: ${routePathName}`)
|
|
71
|
-
|
|
72
|
-
const path = routePathMethod.apply(null, this.findRouteParams())
|
|
73
|
-
const component = this.requireComponentFromCaller()
|
|
74
|
-
|
|
75
|
-
return [
|
|
76
|
-
{
|
|
77
|
-
path,
|
|
78
|
-
component
|
|
79
|
-
}
|
|
80
|
-
]
|
|
81
|
-
}
|
|
82
|
-
}
|
package/src/resource-routes.jsx
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
const {digg} = require("diggerize")
|
|
2
|
-
const React = require("react")
|
|
3
|
-
const ResourceRoute = require("./resource-route.cjs")
|
|
4
|
-
const {Route} = require("react-router-dom")
|
|
5
|
-
|
|
6
|
-
export default class ApiMakerResourceRoutes {
|
|
7
|
-
static readRoutes ({jsRoutes, locales, requireComponent, routeDefinitions}) {
|
|
8
|
-
if (!routeDefinitions)
|
|
9
|
-
throw new Error("Please pass 'routeDefinitions' to this method")
|
|
10
|
-
|
|
11
|
-
const routes = []
|
|
12
|
-
|
|
13
|
-
for (const routeDefinition of routeDefinitions.routes) {
|
|
14
|
-
const resourceRoute = new ResourceRoute({jsRoutes, locales, requireComponent, routeDefinition})
|
|
15
|
-
|
|
16
|
-
for (const newRoute of resourceRoute.routesResult()) {
|
|
17
|
-
routes.push(
|
|
18
|
-
<Route
|
|
19
|
-
component={digg(newRoute, "component")}
|
|
20
|
-
exact
|
|
21
|
-
key={`route-${digg(newRoute, "path")}`}
|
|
22
|
-
path={digg(newRoute, "path")}
|
|
23
|
-
/>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return routes
|
|
29
|
-
}
|
|
30
|
-
}
|