@kaspernj/api-maker 1.0.349 → 1.0.350

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/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  ]
17
17
  },
18
18
  "name": "@kaspernj/api-maker",
19
- "version": "1.0.349",
19
+ "version": "1.0.350",
20
20
  "type": "module",
21
21
  "description": "",
22
22
  "main": "index.js",
@@ -70,7 +70,7 @@
70
70
  "babel-jest": "^29.0.1",
71
71
  "eslint": "^8.2.0",
72
72
  "eslint-find-rules": "^4.0.0",
73
- "eslint-plugin-jest": "^28.2.0",
73
+ "eslint-plugin-jest": "^27.0.1",
74
74
  "eslint-plugin-react": "^7.23.2",
75
75
  "i18n-on-steroids": "^1.0.7",
76
76
  "jest": "^29.0.1",
package/src/api.mjs CHANGED
@@ -1,13 +1,7 @@
1
1
  import config from "./config.mjs"
2
2
  import CustomError from "./custom-error.mjs"
3
3
  import FormDataObjectizer from "form-data-objectizer"
4
- import Logger from "./logger.mjs"
5
4
  import qs from "qs"
6
- import SessionStatusUpdater from "./session-status-updater.mjs"
7
-
8
- const logger = new Logger({name: "ApiMaker / Api"})
9
-
10
- // logger.setDebug(true)
11
5
 
12
6
  export default class Api {
13
7
  static get(path, pathParams = null) {
@@ -68,14 +62,12 @@ export default class Api {
68
62
  })
69
63
  }
70
64
 
71
- static async requestLocal(args) {
65
+ static requestLocal(args) {
72
66
  if (!args.headers) {
73
67
  args.headers = {}
74
68
  }
75
69
 
76
- const token = await this._token()
77
-
78
- logger.debug(() => `Got token: ${token}`)
70
+ const token = this._token()
79
71
 
80
72
  if (token) {
81
73
  args.headers["X-CSRF-Token"] = token
@@ -90,14 +82,19 @@ export default class Api {
90
82
  args.data = args.rawData
91
83
  }
92
84
 
93
- return await this.request(args)
85
+ return this.request(args)
94
86
  }
95
87
 
96
88
  static put(path, data = {}) {
97
89
  return this.requestLocal({path, data, method: "PUT"})
98
90
  }
99
91
 
100
- static _token = async () => await SessionStatusUpdater.current().getCsrfToken()
92
+ static _token() {
93
+ const tokenElement = document.querySelector("meta[name='csrf-token']")
94
+
95
+ if (tokenElement)
96
+ return tokenElement.getAttribute("content")
97
+ }
101
98
 
102
99
  static _parseResponse(xhr) {
103
100
  const responseType = xhr.getResponseHeader("content-type")
@@ -107,6 +107,10 @@ export default class BaseModel {
107
107
  return new Collection({modelClass: this}, {ransack: query})
108
108
  }
109
109
 
110
+ static select(select) {
111
+ return this.ransack().select(select)
112
+ }
113
+
110
114
  static ransackableAssociations() {
111
115
  const relationships = digg(this.modelClassData(), "ransackable_associations")
112
116
  const reflections = []
@@ -14,7 +14,7 @@ import {ValidationErrors} from "./validation-errors.mjs"
14
14
  const shared = {}
15
15
 
16
16
  export default class ApiMakerCommandsPool {
17
- static addCommand(data, args = {}) {
17
+ static addCommand (data, args = {}) {
18
18
  let pool
19
19
 
20
20
  if (args.instant) {
@@ -34,17 +34,17 @@ export default class ApiMakerCommandsPool {
34
34
  return promiseResult
35
35
  }
36
36
 
37
- static current() {
37
+ static current () {
38
38
  if (!shared.currentApiMakerCommandsPool) shared.currentApiMakerCommandsPool = new ApiMakerCommandsPool()
39
39
 
40
40
  return shared.currentApiMakerCommandsPool
41
41
  }
42
42
 
43
- static flush() {
43
+ static flush () {
44
44
  ApiMakerCommandsPool.current().flush()
45
45
  }
46
46
 
47
- constructor() {
47
+ constructor () {
48
48
  this.flushCount = 0
49
49
  this.pool = {}
50
50
  this.poolData = {}
@@ -52,7 +52,7 @@ export default class ApiMakerCommandsPool {
52
52
  this.globalRequestData = {}
53
53
  }
54
54
 
55
- addCommand(data) {
55
+ addCommand (data) {
56
56
  return new Promise((resolve, reject) => {
57
57
  const id = this.currentId
58
58
  this.currentId += 1
@@ -87,11 +87,11 @@ export default class ApiMakerCommandsPool {
87
87
  })
88
88
  }
89
89
 
90
- commandsCount() {
90
+ commandsCount () {
91
91
  return Object.keys(this.pool)
92
92
  }
93
93
 
94
- async sendRequest({commandSubmitData, url}) {
94
+ async sendRequest ({commandSubmitData, url}) {
95
95
  let response
96
96
 
97
97
  for (let i = 0; i < 3; i++) {
@@ -113,7 +113,7 @@ export default class ApiMakerCommandsPool {
113
113
  throw new Error("Couldnt successfully execute request")
114
114
  }
115
115
 
116
- async flush() {
116
+ async flush () {
117
117
  if (this.commandsCount() == 0) {
118
118
  return
119
119
  }
@@ -166,7 +166,7 @@ export default class ApiMakerCommandsPool {
166
166
  }
167
167
  }
168
168
 
169
- handleFailedResponse(commandData, commandResponseData) {
169
+ handleFailedResponse (commandData, commandResponseData) {
170
170
  let error
171
171
 
172
172
  if (commandResponseData.error_type == "destroy_error") {
@@ -190,13 +190,13 @@ export default class ApiMakerCommandsPool {
190
190
  commandData.reject(error)
191
191
  }
192
192
 
193
- clearTimeout() {
193
+ clearTimeout () {
194
194
  if (this.flushTimeout) {
195
195
  clearTimeout(this.flushTimeout)
196
196
  }
197
197
  }
198
198
 
199
- isActive() {
199
+ isActive () {
200
200
  if (this.commandsCount() > 0) {
201
201
  return true
202
202
  }
@@ -208,7 +208,7 @@ export default class ApiMakerCommandsPool {
208
208
  return false
209
209
  }
210
210
 
211
- setFlushTimeout() {
211
+ setFlushTimeout () {
212
212
  this.clearTimeout()
213
213
  this.flushTimeout = setTimeout(() => this.flush(), 0)
214
214
  }
package/src/devise.mjs CHANGED
@@ -9,11 +9,11 @@ import Services from "./services.mjs"
9
9
  const shared = {}
10
10
 
11
11
  export default class ApiMakerDevise {
12
- static callSignOutEvent(args) {
12
+ static callSignOutEvent (args) {
13
13
  events.emit("onDeviseSignOut", {args})
14
14
  }
15
15
 
16
- static current() {
16
+ static current () {
17
17
  if (!shared.currentApiMakerDevise) {
18
18
  shared.currentApiMakerDevise = new ApiMakerDevise()
19
19
  }
@@ -21,11 +21,11 @@ export default class ApiMakerDevise {
21
21
  return shared.currentApiMakerDevise
22
22
  }
23
23
 
24
- static events() {
24
+ static events () {
25
25
  return events
26
26
  }
27
27
 
28
- static addUserScope(scope) {
28
+ static addUserScope (scope) {
29
29
  const currentMethodName = `current${inflection.camelize(scope)}`
30
30
 
31
31
  ApiMakerDevise[currentMethodName] = () => ApiMakerDevise.current().getCurrentScope(scope)
@@ -35,7 +35,7 @@ export default class ApiMakerDevise {
35
35
  ApiMakerDevise[isSignedInMethodName] = () => Boolean(ApiMakerDevise.current().getCurrentScope(scope))
36
36
  }
37
37
 
38
- static async signIn(username, password, args = {}) {
38
+ static async signIn (username, password, args = {}) {
39
39
  if (!args.scope) args.scope = "user"
40
40
 
41
41
  const postData = {username, password, args}
@@ -53,7 +53,7 @@ export default class ApiMakerDevise {
53
53
  return {model, response}
54
54
  }
55
55
 
56
- static updateSession(model, args = {}) {
56
+ static updateSession (model, args = {}) {
57
57
  if (!args.scope) {
58
58
  args.scope = digg(model.modelClassData(), "name")
59
59
  }
@@ -63,17 +63,11 @@ export default class ApiMakerDevise {
63
63
  ApiMakerDevise.current().currents[camelizedScopeName] = model
64
64
  }
65
65
 
66
- hasCurrentScope(scope) {
67
- const camelizedScopeName = inflection.camelize(scope, true)
68
-
69
- return camelizedScopeName in ApiMakerDevise.current().currents
70
- }
71
-
72
- static setSignedOut(args) {
66
+ static setSignedOut (args) {
73
67
  ApiMakerDevise.current().currents[inflection.camelize(args.scope, true)] = null
74
68
  }
75
69
 
76
- static async signOut(args = {}) {
70
+ static async signOut (args = {}) {
77
71
  if (!args.scope) {
78
72
  args.scope = "user"
79
73
  }
@@ -93,11 +87,11 @@ export default class ApiMakerDevise {
93
87
  return response
94
88
  }
95
89
 
96
- constructor() {
90
+ constructor () {
97
91
  this.currents = {}
98
92
  }
99
93
 
100
- getCurrentScope(scope) {
94
+ getCurrentScope (scope) {
101
95
  if (!(scope in this.currents)) {
102
96
  this.currents[scope] = this.loadCurrentScope(scope)
103
97
  }
@@ -105,7 +99,7 @@ export default class ApiMakerDevise {
105
99
  return this.currents[scope]
106
100
  }
107
101
 
108
- hasGlobalCurrentScope(scope) {
102
+ hasCurrentScope(scope) {
109
103
  if (globalThis.apiMakerDeviseCurrent && scope in globalThis.apiMakerDeviseCurrent) {
110
104
  return true
111
105
  }
@@ -113,8 +107,8 @@ export default class ApiMakerDevise {
113
107
  return false
114
108
  }
115
109
 
116
- loadCurrentScope(scope) {
117
- if (!this.hasGlobalCurrentScope(scope)) {
110
+ loadCurrentScope (scope) {
111
+ if (!this.hasCurrentScope(scope)) {
118
112
  return null
119
113
  }
120
114
 
package/src/logger.mjs CHANGED
@@ -12,13 +12,7 @@ export default class ApiMakerLogger {
12
12
  }
13
13
 
14
14
  debug(message) {
15
- if (this.getDebug()) {
16
- this.log(message)
17
- }
18
- }
19
-
20
- error(message) {
21
- console.error(message)
15
+ if (this.getDebug()) this.log(message)
22
16
  }
23
17
 
24
18
  log(message) {
@@ -1,82 +1,38 @@
1
- import config from "./config.mjs"
2
1
  import Devise from "./devise.mjs"
3
2
  import * as inflection from "inflection"
4
3
  import Logger from "./logger.mjs"
5
4
  import wakeEvent from "wake-event"
6
5
 
7
6
  const logger = new Logger({name: "ApiMaker / SessionStatusUpdater"})
8
- const shared = {}
9
-
10
- // logger.setDebug(true)
11
7
 
12
8
  export default class ApiMakerSessionStatusUpdater {
13
- static current(args) {
14
- if (!shared.apiMakerSessionStatusUpdater) {
15
- shared.apiMakerSessionStatusUpdater = new ApiMakerSessionStatusUpdater(args)
16
- }
9
+ static current () {
10
+ if (!globalThis.apiMakerSessionStatusUpdater)
11
+ globalThis.apiMakerSessionStatusUpdater = new ApiMakerSessionStatusUpdater()
17
12
 
18
- return shared.apiMakerSessionStatusUpdater
13
+ return globalThis.apiMakerSessionStatusUpdater
19
14
  }
20
15
 
21
- constructor(args = {}) {
16
+ constructor (args = {}) {
22
17
  this.events = {}
23
18
  this.timeout = args.timeout || 600000
24
- this.useMetaElement = ("useMetaElement" in args) ? args.useMetaElement : true
25
19
 
26
20
  this.connectOnlineEvent()
27
21
  this.connectWakeEvent()
28
22
  }
29
23
 
30
- connectOnlineEvent() {
31
- window.addEventListener("online", this.updateSessionStatus, false)
24
+ connectOnlineEvent () {
25
+ window.addEventListener("online", () => this.updateSessionStatus(), false)
32
26
  }
33
27
 
34
- connectWakeEvent() {
35
- wakeEvent(this.updateSessionStatus)
28
+ connectWakeEvent () {
29
+ wakeEvent(() => this.updateSessionStatus())
36
30
  }
37
31
 
38
- async getCsrfToken() {
39
- if (this.csrfToken) {
40
- logger.debug(`Get CSRF token from set variable: ${this.csrfToken}`)
41
-
42
- return this.csrfToken
43
- }
44
-
45
- if (this.useMetaElement) {
46
- const csrfTokenElement = document.querySelector("meta[name='csrf-token']")
47
-
48
- if (csrfTokenElement) {
49
- logger.debug(() => `Get CSRF token from meta element: ${csrfTokenElement.getAttribute("content")}`)
50
-
51
- this.csrfToken = csrfTokenElement.getAttribute("content")
52
-
53
- return this.csrfToken
54
- }
55
- }
56
-
57
- logger.debug("Updating session status because no CSRF token set yet")
58
- await this.updateSessionStatus()
59
-
60
- if (this.csrfToken) {
61
- logger.debug(() => `Returning CSRF token after updating session status: ${this.csrfToken}`)
62
-
63
- return this.csrfToken
64
- }
65
-
66
- throw new Error("CSRF token hasn't been set")
67
- }
68
-
69
- sessionStatus() {
32
+ async sessionStatus () {
70
33
  return new Promise((resolve) => {
71
- const host = config.getHost()
72
- let requestPath = ""
73
-
74
- if (host) requestPath += host
75
-
76
- requestPath += "/api_maker/session_statuses"
77
-
78
34
  const xhr = new XMLHttpRequest()
79
- xhr.open("POST", requestPath, true)
35
+ xhr.open("POST", "/api_maker/session_statuses", true)
80
36
  xhr.onload = () => {
81
37
  const response = JSON.parse(xhr.responseText)
82
38
  resolve(response)
@@ -85,11 +41,11 @@ export default class ApiMakerSessionStatusUpdater {
85
41
  })
86
42
  }
87
43
 
88
- onSignedOut(callback) {
44
+ onSignedOut (callback) {
89
45
  this.addEvent("onSignedOut", callback)
90
46
  }
91
47
 
92
- startTimeout() {
48
+ startTimeout () {
93
49
  logger.debug("startTimeout")
94
50
 
95
51
  if (this.updateTimeout)
@@ -104,12 +60,12 @@ export default class ApiMakerSessionStatusUpdater {
104
60
  )
105
61
  }
106
62
 
107
- stopTimeout() {
63
+ stopTimeout () {
108
64
  if (this.updateTimeout)
109
65
  clearTimeout(this.updateTimeout)
110
66
  }
111
67
 
112
- updateSessionStatus = async () => {
68
+ async updateSessionStatus () {
113
69
  logger.debug("updateSessionStatus")
114
70
 
115
71
  const result = await this.sessionStatus()
@@ -119,30 +75,25 @@ export default class ApiMakerSessionStatusUpdater {
119
75
  this.updateUserSessionsFromResult(result)
120
76
  }
121
77
 
122
- updateMetaElementsFromResult(result) {
78
+ updateMetaElementsFromResult (result) {
123
79
  logger.debug("updateMetaElementsFromResult")
80
+ const csrfTokenElement = document.querySelector("meta[name='csrf-token']")
124
81
 
125
- this.csrfToken = result.csrf_token
126
-
127
- if (this.useMetaElement) {
128
- const csrfTokenElement = document.querySelector("meta[name='csrf-token']")
129
-
130
- if (csrfTokenElement) {
131
- logger.debug(() => `Changing token from "${csrfTokenElement.getAttribute("content")}" to "${result.csrf_token}"`)
132
- csrfTokenElement.setAttribute("content", result.csrf_token)
133
- } else {
134
- logger.debug("csrf token element couldn't be found")
135
- }
82
+ if (csrfTokenElement) {
83
+ logger.debug(() => `Changing token from "${csrfTokenElement.getAttribute("content")}" to "${result.csrf_token}"`)
84
+ csrfTokenElement.setAttribute("content", result.csrf_token)
85
+ } else {
86
+ logger.debug("csrf token element couldn't be found")
136
87
  }
137
88
  }
138
89
 
139
- updateUserSessionsFromResult(result) {
90
+ updateUserSessionsFromResult (result) {
140
91
  for (const scopeName in result.scopes) {
141
92
  this.updateUserSessionScopeFromResult(scopeName, result.scopes[scopeName])
142
93
  }
143
94
  }
144
95
 
145
- updateUserSessionScopeFromResult(scopeName, scope) {
96
+ updateUserSessionScopeFromResult (scopeName, scope) {
146
97
  const deviseIsSignedInMethodName = `is${inflection.camelize(scopeName)}SignedIn`
147
98
 
148
99
  if (!(deviseIsSignedInMethodName in Devise)) {
@@ -3,39 +3,43 @@ import {digg} from "diggerize"
3
3
  import * as inflection from "inflection"
4
4
  import Params from "../params"
5
5
  import PropTypes from "prop-types"
6
- import {memo, useCallback} from "react"
6
+ import {memo, useCallback, useMemo} from "react"
7
7
  import Table from "../table/table"
8
8
  import useCurrentUser from "../use-current-user"
9
+ import useShape from "set-state-compare/src/use-shape.js"
9
10
 
10
- const ApiMakerSuperAdminModelClassTable = ({modelClass, ...restProps}) => {
11
+ const ApiMakerSuperAdminModelClassTable = (props) => {
12
+ const s = useShape(props)
13
+ const {modelClass, ...restProps} = props
11
14
  const currentUser = useCurrentUser()
12
-
13
- const columns = useCallback(() => {
14
- const configReader = ConfigReader.forModel(modelClass)
15
-
16
- return configReader.tableColumns()
17
- }, [modelClass])
15
+ const configReader = useMemo(() => ConfigReader.forModel(modelClass), [modelClass])
16
+ const columns = useMemo(() => configReader.tableColumns(), [modelClass])
17
+ const tableConfig = configReader.modelConfig?.table
18
18
 
19
19
  const editModelPath = useCallback((args) => {
20
- const argName = inflection.camelize(digg(modelClass.modelClassData(), "name"), true)
20
+ const argName = inflection.camelize(digg(s.p.modelClass.modelClassData(), "name"), true)
21
21
  const model = digg(args, argName)
22
22
 
23
23
  return Params.withParams({
24
- model: modelClass.modelClassData().name,
24
+ model: s.p.modelClass.modelClassData().name,
25
25
  model_id: model.primaryKey(),
26
26
  mode: "edit"
27
27
  })
28
- })
28
+ }, [])
29
29
 
30
30
  const viewModelPath = useCallback((args) => {
31
- const argName = inflection.camelize(digg(modelClass.modelClassData(), "name"), true)
31
+ const argName = inflection.camelize(digg(s.p.modelClass.modelClassData(), "name"), true)
32
32
  const model = digg(args, argName)
33
33
 
34
34
  return Params.withParams({
35
- model: modelClass.modelClassData().name,
35
+ model: s.p.modelClass.modelClassData().name,
36
36
  model_id: model.primaryKey()
37
37
  })
38
- }, [modelClass])
38
+ }, [])
39
+
40
+ const tableProps = {}
41
+
42
+ if (tableConfig?.query) tableProps.collection = tableConfig.query
39
43
 
40
44
  return (
41
45
  <Table
@@ -44,6 +48,7 @@ const ApiMakerSuperAdminModelClassTable = ({modelClass, ...restProps}) => {
44
48
  editModelPath={editModelPath}
45
49
  modelClass={modelClass}
46
50
  viewModelPath={viewModelPath}
51
+ {...tableProps}
47
52
  {...restProps}
48
53
  />
49
54
  )
@@ -1,15 +1,10 @@
1
1
  import {useCallback, useEffect, useMemo} from "react"
2
2
  import {camelize} from "inflection"
3
3
  import Devise from "./devise.mjs"
4
- import Logger from "./logger.mjs"
5
4
  import Services from "./services.mjs"
6
5
  import useEventEmitter from "./use-event-emitter.mjs"
7
6
  import useShape from "set-state-compare/src/use-shape.js"
8
7
 
9
- const logger = new Logger({name: "ApiMaker / useCurrentUser"})
10
-
11
- // logger.setDebug(true)
12
-
13
8
  const useCurrentUser = (args) => {
14
9
  const s = useShape(args || {})
15
10
  const scope = args?.scope || "user"
@@ -18,10 +13,20 @@ const useCurrentUser = (args) => {
18
13
  s.meta.scope = scope
19
14
  s.meta.scopeName = scopeName
20
15
 
16
+ const debugs = useCallback((debugCallback) => {
17
+ if (s.props.debug) {
18
+ let debugArgs = debugCallback()
19
+
20
+ if (!Array.isArray(debugArgs)) debugArgs = [debugArgs]
21
+
22
+ console.log("useCurrentUser", ...debugArgs)
23
+ }
24
+ })
25
+
21
26
  const loadCurrentUserFromRequest = useCallback(async () => {
22
27
  const {scope, scopeName} = s.m
23
28
 
24
- logger.debug(() => `Loading ${scope} with request`)
29
+ debugs(() => `Loading ${scope} with request`)
25
30
 
26
31
  const result = await Services.current().sendRequest("Devise::Current", {scope})
27
32
  const current = digg(result, "current")
@@ -36,28 +41,21 @@ const useCurrentUser = (args) => {
36
41
 
37
42
  const defaultCurrentUser = useCallback(() => {
38
43
  const {scope, scopeName} = s.m
39
- let current
40
44
 
41
- if (Devise.current().hasCurrentScope(s.m.scope)) {
42
- current = Devise.current().getCurrentScope(scope)
45
+ if (Devise.current().hasCurrentScope(scope)) {
46
+ const current = Devise[scopeName]()
43
47
 
44
- logger.debug(() => `Setting ${scope} from current scope: ${current?.id()}`)
45
- } else if (Devise.current().hasGlobalCurrentScope(scope)) {
46
- current = Devise[scopeName]()
48
+ debugs(() => `Setting ${scope} from current scope: ${current?.id()}`)
47
49
 
48
- logger.debug(() => `Setting ${scope} from global current scope: ${current?.id()}`)
49
- }
50
+ if (s.props.onCurrentUserLoaded) setTimeout(() => s.props.onCurrentUserLoaded(current), 0)
50
51
 
51
- if (current && s.props.onCurrentUserLoaded) {
52
- setTimeout(() => s.props.onCurrentUserLoaded(current), 0)
52
+ return current
53
53
  }
54
-
55
- return current
56
54
  }, [])
57
55
 
58
56
  const useStatesArgument = {}
59
57
 
60
- useStatesArgument[scopeName] = () => defaultCurrentUser()
58
+ useStatesArgument[scopeName] = defaultCurrentUser()
61
59
 
62
60
  s.useStates(useStatesArgument)
63
61
 
@@ -70,8 +68,7 @@ const useCurrentUser = (args) => {
70
68
  }, [])
71
69
 
72
70
  useEffect(() => {
73
- if (!Devise.current().hasGlobalCurrentScope(s.m.scope) && !Devise.current().hasCurrentScope(s.m.scope)) {
74
- logger.debug(() => `Devise hasn't got current scope ${s.m.scope} so loading from request`)
71
+ if (!Devise.current().hasCurrentScope(s.m.scope)) {
75
72
  loadCurrentUserFromRequest()
76
73
  }
77
74
  }, [])