api_maker 0.0.1
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +476 -0
- data/Rakefile +27 -0
- data/app/channels/api_maker/subscriptions_channel.rb +80 -0
- data/app/controllers/api_maker/base_controller.rb +32 -0
- data/app/controllers/api_maker/commands_controller.rb +26 -0
- data/app/controllers/api_maker/devise_controller.rb +60 -0
- data/app/controllers/api_maker/session_statuses_controller.rb +33 -0
- data/app/services/api_maker/application_service.rb +7 -0
- data/app/services/api_maker/collection_command_service.rb +24 -0
- data/app/services/api_maker/command_response.rb +67 -0
- data/app/services/api_maker/command_service.rb +31 -0
- data/app/services/api_maker/create_command.rb +62 -0
- data/app/services/api_maker/create_command_service.rb +18 -0
- data/app/services/api_maker/destroy_command.rb +39 -0
- data/app/services/api_maker/destroy_command_service.rb +22 -0
- data/app/services/api_maker/generate_react_native_api_service.rb +61 -0
- data/app/services/api_maker/index_command.rb +96 -0
- data/app/services/api_maker/index_command_service.rb +22 -0
- data/app/services/api_maker/js_method_namer_service.rb +11 -0
- data/app/services/api_maker/member_command_service.rb +25 -0
- data/app/services/api_maker/model_content_generator_service.rb +108 -0
- data/app/services/api_maker/models_finder_service.rb +22 -0
- data/app/services/api_maker/models_generator_service.rb +104 -0
- data/app/services/api_maker/update_command.rb +43 -0
- data/app/services/api_maker/update_command_service.rb +21 -0
- data/app/services/api_maker/valid_command.rb +35 -0
- data/app/services/api_maker/valid_command_service.rb +21 -0
- data/app/views/api_maker/_data.html.erb +15 -0
- data/config/rails_best_practices.yml +55 -0
- data/config/routes.rb +7 -0
- data/lib/api_maker.rb +36 -0
- data/lib/api_maker/ability.rb +39 -0
- data/lib/api_maker/ability_loader.rb +21 -0
- data/lib/api_maker/action_controller_base_extensions.rb +5 -0
- data/lib/api_maker/base_command.rb +81 -0
- data/lib/api_maker/base_resource.rb +78 -0
- data/lib/api_maker/collection_serializer.rb +69 -0
- data/lib/api_maker/command_spec_helper.rb +57 -0
- data/lib/api_maker/configuration.rb +34 -0
- data/lib/api_maker/engine.rb +5 -0
- data/lib/api_maker/individual_command.rb +37 -0
- data/lib/api_maker/javascript/api.js +92 -0
- data/lib/api_maker/javascript/base-model.js +543 -0
- data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +16 -0
- data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +47 -0
- data/lib/api_maker/javascript/bootstrap/card.jsx +79 -0
- data/lib/api_maker/javascript/bootstrap/checkbox.jsx +127 -0
- data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +105 -0
- data/lib/api_maker/javascript/bootstrap/live-table.jsx +168 -0
- data/lib/api_maker/javascript/bootstrap/money-input.jsx +136 -0
- data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +80 -0
- data/lib/api_maker/javascript/bootstrap/select.jsx +168 -0
- data/lib/api_maker/javascript/bootstrap/string-input.jsx +203 -0
- data/lib/api_maker/javascript/cable-connection-pool.js +169 -0
- data/lib/api_maker/javascript/cable-subscription-pool.js +111 -0
- data/lib/api_maker/javascript/cable-subscription.js +33 -0
- data/lib/api_maker/javascript/collection.js +186 -0
- data/lib/api_maker/javascript/commands-pool.js +123 -0
- data/lib/api_maker/javascript/custom-error.js +14 -0
- data/lib/api_maker/javascript/deserializer.js +35 -0
- data/lib/api_maker/javascript/devise.js.erb +113 -0
- data/lib/api_maker/javascript/error-logger.js +119 -0
- data/lib/api_maker/javascript/event-connection.jsx +24 -0
- data/lib/api_maker/javascript/event-created.jsx +26 -0
- data/lib/api_maker/javascript/event-destroyed.jsx +26 -0
- data/lib/api_maker/javascript/event-emitter-listener.jsx +32 -0
- data/lib/api_maker/javascript/event-listener.jsx +41 -0
- data/lib/api_maker/javascript/event-updated.jsx +26 -0
- data/lib/api_maker/javascript/form-data-to-object.js +70 -0
- data/lib/api_maker/javascript/included.js +39 -0
- data/lib/api_maker/javascript/key-value-store.js +47 -0
- data/lib/api_maker/javascript/logger.js +23 -0
- data/lib/api_maker/javascript/model-name.js +21 -0
- data/lib/api_maker/javascript/model-template.js.erb +110 -0
- data/lib/api_maker/javascript/models-response-reader.js +43 -0
- data/lib/api_maker/javascript/paginate.jsx +128 -0
- data/lib/api_maker/javascript/params.js +68 -0
- data/lib/api_maker/javascript/resource-route.jsx +75 -0
- data/lib/api_maker/javascript/resource-routes.jsx +36 -0
- data/lib/api_maker/javascript/result.js +25 -0
- data/lib/api_maker/javascript/session-status-updater.js +113 -0
- data/lib/api_maker/javascript/sort-link.jsx +88 -0
- data/lib/api_maker/javascript/updated-attribute.jsx +60 -0
- data/lib/api_maker/loader.rb +14 -0
- data/lib/api_maker/memory_storage.rb +65 -0
- data/lib/api_maker/model_extensions.rb +96 -0
- data/lib/api_maker/permitted_params_argument.rb +12 -0
- data/lib/api_maker/preloader.rb +91 -0
- data/lib/api_maker/preloader_belongs_to.rb +58 -0
- data/lib/api_maker/preloader_has_many.rb +69 -0
- data/lib/api_maker/preloader_has_one.rb +70 -0
- data/lib/api_maker/preloader_through.rb +101 -0
- data/lib/api_maker/railtie.rb +14 -0
- data/lib/api_maker/relationship_includer.rb +42 -0
- data/lib/api_maker/resource_routing.rb +8 -0
- data/lib/api_maker/result_parser.rb +50 -0
- data/lib/api_maker/serializer.rb +86 -0
- data/lib/api_maker/spec_helper.rb +100 -0
- data/lib/api_maker/version.rb +3 -0
- data/lib/tasks/api_maker_tasks.rake +5 -0
- metadata +581 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Included from "./included"
|
|
2
|
+
|
|
3
|
+
const inflection = require("inflection")
|
|
4
|
+
|
|
5
|
+
export default class ModelsResponseReader {
|
|
6
|
+
static first(response) {
|
|
7
|
+
return ModelsResponseReader.collection(response)[0]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static collection(response) {
|
|
11
|
+
var reader = new ModelsResponseReader({response: response})
|
|
12
|
+
return reader.models()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
constructor(args) {
|
|
16
|
+
this.response = args.response
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
models() {
|
|
20
|
+
var included = new Included(this.response)
|
|
21
|
+
var models = []
|
|
22
|
+
|
|
23
|
+
for(var modelType in this.response.data) {
|
|
24
|
+
var modelClassName = inflection.singularize(modelType)
|
|
25
|
+
var modelClass = require(`api-maker/models/${modelClassName}`).default
|
|
26
|
+
var collectionName = inflection.dasherize(modelClass.modelClassData().collectionName)
|
|
27
|
+
|
|
28
|
+
for(var modelId of this.response.data[modelType]) {
|
|
29
|
+
var modelData = this.response.included[collectionName][modelId]
|
|
30
|
+
|
|
31
|
+
if (!modelData)
|
|
32
|
+
throw new Error(`Couldn't find model data for ${collectionName}(${modelId})`)
|
|
33
|
+
|
|
34
|
+
var model = new modelClass({data: modelData, isNewRecord: false, response: this.response})
|
|
35
|
+
|
|
36
|
+
model._readIncludedRelationships(included)
|
|
37
|
+
models.push(model)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return models
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Link } from "react-router-dom"
|
|
2
|
+
import qs from "qs"
|
|
3
|
+
import React from "react"
|
|
4
|
+
|
|
5
|
+
export default class extends React.Component {
|
|
6
|
+
isPageActiveClass(pageNumber) {
|
|
7
|
+
if (this.props.result.currentPage() == pageNumber)
|
|
8
|
+
return "active"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
pages() {
|
|
12
|
+
var currentPage = this.props.result.currentPage()
|
|
13
|
+
var pages = []
|
|
14
|
+
var totalPages = this.props.result.totalPages()
|
|
15
|
+
var pagesFrom = currentPage - 5
|
|
16
|
+
var pagesTo = currentPage + 5
|
|
17
|
+
|
|
18
|
+
if (pagesFrom < 1)
|
|
19
|
+
pagesFrom = 1
|
|
20
|
+
|
|
21
|
+
if (pagesTo > totalPages)
|
|
22
|
+
pagesTo = totalPages
|
|
23
|
+
|
|
24
|
+
for(var i = pagesFrom; i <= pagesTo; i++) {
|
|
25
|
+
pages.push(i)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return pages
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pagePath(pageNumber) {
|
|
32
|
+
var pageKey = this.props.result.data.collection.queryArgs.pageKey
|
|
33
|
+
if (!pageKey)
|
|
34
|
+
pageKey = "page"
|
|
35
|
+
|
|
36
|
+
var currentParams = qs.parse(window.location.search.substr(1))
|
|
37
|
+
currentParams[pageKey] = pageNumber
|
|
38
|
+
var newParams = qs.stringify(currentParams)
|
|
39
|
+
var newPath = `${location.pathname}?${newParams}`
|
|
40
|
+
|
|
41
|
+
return newPath
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
previousPagePath() {
|
|
45
|
+
var previousPage
|
|
46
|
+
|
|
47
|
+
if (this.props.result.currentPage() > 1) {
|
|
48
|
+
previousPage = this.props.result.currentPage() - 1
|
|
49
|
+
} else {
|
|
50
|
+
previousPage = this.props.result.currentPage()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return this.pagePath(previousPage)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
nextPagePath() {
|
|
57
|
+
var nextPage
|
|
58
|
+
|
|
59
|
+
if (this.props.result.currentPage() < this.props.result.totalPages()) {
|
|
60
|
+
nextPage = this.props.result.currentPage() + 1
|
|
61
|
+
} else {
|
|
62
|
+
nextPage = this.props.result.currentPage()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return this.pagePath(nextPage)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
showBackwardsDots() {
|
|
69
|
+
var currentPage = this.props.result.currentPage()
|
|
70
|
+
|
|
71
|
+
return (currentPage - 5 > 1)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
showForwardsDots() {
|
|
75
|
+
var currentPage = this.props.result.currentPage()
|
|
76
|
+
var totalPages = this.props.result.totalPages()
|
|
77
|
+
|
|
78
|
+
return (currentPage + 5 < totalPages)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
render() {
|
|
82
|
+
return (
|
|
83
|
+
<ul className="pagination">
|
|
84
|
+
<li className={`page-item ${this.props.result.currentPage() <= 1 ? "disabled" : ""}`} key="page-first">
|
|
85
|
+
<Link className="page-link" to={this.pagePath(1)}>
|
|
86
|
+
⇤
|
|
87
|
+
</Link>
|
|
88
|
+
</li>
|
|
89
|
+
<li className={`page-item ${this.props.result.currentPage() <= 1 ? "disabled" : ""}`} key="page-previous">
|
|
90
|
+
<Link className="page-link" to={this.previousPagePath()}>
|
|
91
|
+
←
|
|
92
|
+
</Link>
|
|
93
|
+
</li>
|
|
94
|
+
{this.showBackwardsDots() &&
|
|
95
|
+
<li className="page-item">
|
|
96
|
+
<a className="page-link disabled" href="#">
|
|
97
|
+
…
|
|
98
|
+
</a>
|
|
99
|
+
</li>
|
|
100
|
+
}
|
|
101
|
+
{this.pages().map(page =>
|
|
102
|
+
<li className={`page-item ${this.isPageActiveClass(page)}`} key={`page-${page}`}>
|
|
103
|
+
<Link className="page-link" to={this.pagePath(page)}>
|
|
104
|
+
{page}
|
|
105
|
+
</Link>
|
|
106
|
+
</li>
|
|
107
|
+
)}
|
|
108
|
+
{this.showForwardsDots() &&
|
|
109
|
+
<li className="page-item">
|
|
110
|
+
<a className="page-link disabled" href="#">
|
|
111
|
+
…
|
|
112
|
+
</a>
|
|
113
|
+
</li>
|
|
114
|
+
}
|
|
115
|
+
<li className={`page-item ${this.props.result.currentPage() >= this.props.result.totalPages() ? "disabled" : ""}`} key="page-next">
|
|
116
|
+
<Link className="page-link" to={this.nextPagePath()}>
|
|
117
|
+
→
|
|
118
|
+
</Link>
|
|
119
|
+
</li>
|
|
120
|
+
<li className={`page-item ${this.props.result.currentPage() >= this.props.result.totalPages() ? "disabled" : ""}`} key="page-last">
|
|
121
|
+
<Link className="page-link" to={this.pagePath(this.props.result.totalPages())}>
|
|
122
|
+
⇥
|
|
123
|
+
</Link>
|
|
124
|
+
</li>
|
|
125
|
+
</ul>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import formSerialize from "form-serialize"
|
|
2
|
+
import KeyValueStore from "api-maker/key-value-store"
|
|
3
|
+
import merge from "merge"
|
|
4
|
+
import qs from "qs"
|
|
5
|
+
|
|
6
|
+
export default class Params {
|
|
7
|
+
static parse() {
|
|
8
|
+
return qs.parse(window.location.search.substr(1))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static change(given) {
|
|
12
|
+
return merge.recursive(true, Params.parse(), given)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static changeParams(given) {
|
|
16
|
+
var params = Params.change(given)
|
|
17
|
+
var newParams = qs.stringify(params)
|
|
18
|
+
var newPath = `${location.pathname}?${newParams}`
|
|
19
|
+
|
|
20
|
+
AppHistory.push(newPath)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static async getCachedParams(paramName, args = {}) {
|
|
24
|
+
var oldQuery = await KeyValueStore.get(paramName)
|
|
25
|
+
var params = Params.parse()
|
|
26
|
+
|
|
27
|
+
if (params && paramName in params) {
|
|
28
|
+
return params[paramName]
|
|
29
|
+
} else if (oldQuery) {
|
|
30
|
+
return oldQuery
|
|
31
|
+
} else {
|
|
32
|
+
return args.default || {}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static async setCachedParams(paramName, qParams) {
|
|
37
|
+
return await KeyValueStore.set(paramName, qParams)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static serializeForm(form) {
|
|
41
|
+
var hash = formSerialize(form, {empty: true, hash: true})
|
|
42
|
+
return Params.setUndefined(hash)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// This is used to set all empty values to 'undefined' which makes qs removed those elements from the query string
|
|
46
|
+
static setUndefined(given) {
|
|
47
|
+
if (Array.isArray(given)) {
|
|
48
|
+
if (given.length == 0)
|
|
49
|
+
return undefined
|
|
50
|
+
|
|
51
|
+
return given.map(givenI => Params.setUndefined(givenI))
|
|
52
|
+
} else if (typeof given === "object") {
|
|
53
|
+
if (Object.keys(given).length == 0)
|
|
54
|
+
return undefined
|
|
55
|
+
|
|
56
|
+
var newGiven = {}
|
|
57
|
+
for(var key in given) {
|
|
58
|
+
newGiven[key] = Params.setUndefined(given[key])
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return newGiven
|
|
62
|
+
} else if (given === "") {
|
|
63
|
+
return undefined
|
|
64
|
+
} else {
|
|
65
|
+
return given
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const inflection = require("inflection")
|
|
2
|
+
|
|
3
|
+
export default class ResourceRoute {
|
|
4
|
+
constructor(args) {
|
|
5
|
+
this.args = args.args
|
|
6
|
+
this.parsedContext = args.parsedContext
|
|
7
|
+
this.route = args.route
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
routes() {
|
|
11
|
+
if (this.args.locales) {
|
|
12
|
+
return this.withLocale()
|
|
13
|
+
} else {
|
|
14
|
+
return this.withoutLocale()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
findRouteParams(route) {
|
|
19
|
+
var result = []
|
|
20
|
+
var parts = route.path.split("/")
|
|
21
|
+
|
|
22
|
+
for(var part of parts) {
|
|
23
|
+
if (part.match(/^:([a-z_]+)$/))
|
|
24
|
+
result.push(part)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
requireComponent() {
|
|
31
|
+
var requireResult = this.parsedContext[`components/${this.route.component}.jsx`]
|
|
32
|
+
|
|
33
|
+
if (!requireResult)
|
|
34
|
+
var requireResult = this.parsedContext[`components/${this.route.component}/index.jsx`]
|
|
35
|
+
|
|
36
|
+
if (!requireResult)
|
|
37
|
+
throw new Error(`No such require: ${this.route.component}`)
|
|
38
|
+
|
|
39
|
+
return requireResult.default
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
withLocale() {
|
|
43
|
+
var component = this.requireComponent()
|
|
44
|
+
var Locales = require("shared/locales").default
|
|
45
|
+
var Path = require("shared/path").default
|
|
46
|
+
var routes = []
|
|
47
|
+
|
|
48
|
+
for(var locale of Locales.availableLocales()) {
|
|
49
|
+
var path = Path.localized(inflection.camelize(this.route.name, true), this.findRouteParams(this.route), {locale: locale})
|
|
50
|
+
|
|
51
|
+
routes.push({
|
|
52
|
+
path: path,
|
|
53
|
+
component: component
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return routes
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
withoutLocale() {
|
|
61
|
+
var routePathName = inflection.camelize(this.route.name, true)
|
|
62
|
+
var routePathMethod = Routes[`${routePathName}Path`]
|
|
63
|
+
|
|
64
|
+
if (!routePathMethod)
|
|
65
|
+
throw new Error(`No such route could be found: ${routePathName}`)
|
|
66
|
+
|
|
67
|
+
var path = routePathMethod.apply(null, this.findRouteParams(this.route))
|
|
68
|
+
var component = this.requireComponent()
|
|
69
|
+
|
|
70
|
+
return [{
|
|
71
|
+
path: path,
|
|
72
|
+
component: component
|
|
73
|
+
}]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react" // This fixes an issue with the Baristo project where it needed it to be loaded
|
|
2
|
+
import ResourceRoute from "./resource-route"
|
|
3
|
+
import { Route } from "react-router-dom"
|
|
4
|
+
|
|
5
|
+
export default class ApiMakerResourceRoutes {
|
|
6
|
+
static readRoutes(args = {}) {
|
|
7
|
+
if (!args.routes)
|
|
8
|
+
throw new Error("Please pass 'routes' to this method")
|
|
9
|
+
|
|
10
|
+
var parsedContext = ApiMakerResourceRoutes.parseContext(args)
|
|
11
|
+
var routesJson = args.routes
|
|
12
|
+
var routes = []
|
|
13
|
+
|
|
14
|
+
for(var route of routesJson.routes) {
|
|
15
|
+
var resourceRoute = new ResourceRoute({args, parsedContext, route})
|
|
16
|
+
|
|
17
|
+
for(var newRoute of resourceRoute.routes()) {
|
|
18
|
+
routes.push(
|
|
19
|
+
<Route exact key={`route-${newRoute.path}`} path={newRoute.path} component={newRoute.component} />
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return routes
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static parseContext(args) {
|
|
28
|
+
var result = {}
|
|
29
|
+
args.context.keys().forEach(key => {
|
|
30
|
+
var newKey = `${args.path}/${key.substring(2, key.length)}`
|
|
31
|
+
result[newKey] = args.context(key)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return result
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export default class Result {
|
|
2
|
+
constructor(data) {
|
|
3
|
+
this.data = data
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
currentPage() {
|
|
7
|
+
return this.data.response.meta.currentPage
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
models() {
|
|
11
|
+
return this.data.models
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
modelClass() {
|
|
15
|
+
return this.data.collection.modelClass()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
totalCount() {
|
|
19
|
+
return this.data.response.meta.totalCount
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
totalPages() {
|
|
23
|
+
return this.data.response.meta.totalPages
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import Devise from "./devise"
|
|
2
|
+
|
|
3
|
+
var inflection = require("inflection")
|
|
4
|
+
var wakeEvent = require("wake-event")
|
|
5
|
+
|
|
6
|
+
export default class ApiMakerSessionStatusUpdater {
|
|
7
|
+
static current() {
|
|
8
|
+
if (!window.apiMakerSessionStatusUpdater)
|
|
9
|
+
window.apiMakerSessionStatusUpdater = new ApiMakerSessionStatusUpdater()
|
|
10
|
+
|
|
11
|
+
return window.apiMakerSessionStatusUpdater
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
constructor(args = {}) {
|
|
15
|
+
this.debugging = args.debug || false
|
|
16
|
+
this.events = {}
|
|
17
|
+
this.timeout = args.timeout || 600000
|
|
18
|
+
|
|
19
|
+
this.connectOnlineEvent()
|
|
20
|
+
this.connectWakeEvent()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
connectOnlineEvent() {
|
|
24
|
+
window.addEventListener("online", () => { this.updateSessionStatus() }, false)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
connectWakeEvent() {
|
|
28
|
+
wakeEvent(() => { this.updateSessionStatus() })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
debug(message) {
|
|
32
|
+
if (this.debugging)
|
|
33
|
+
console.log(`ApiMakerSessionStatusUpdater: ${message}`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
sessionStatus() {
|
|
37
|
+
return new Promise(resolve => {
|
|
38
|
+
var xhr = new XMLHttpRequest()
|
|
39
|
+
xhr.open("POST", "/api_maker/session_statuses", true)
|
|
40
|
+
xhr.onload = () => {
|
|
41
|
+
var response = JSON.parse(xhr.responseText)
|
|
42
|
+
resolve(response)
|
|
43
|
+
}
|
|
44
|
+
xhr.send()
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onSignedOut(callback) {
|
|
49
|
+
this.addEvent("onSignedOut", callback)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
startTimeout() {
|
|
53
|
+
this.debug("startTimeout")
|
|
54
|
+
|
|
55
|
+
if (this.updateTimeout)
|
|
56
|
+
clearTimeout(this.updateTimeout)
|
|
57
|
+
|
|
58
|
+
this.updateTimeout = setTimeout(
|
|
59
|
+
() => {
|
|
60
|
+
this.startTimeout()
|
|
61
|
+
this.updateSessionStatus()
|
|
62
|
+
},
|
|
63
|
+
this.timeout
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
stopTimeout() {
|
|
68
|
+
if (this.updateTimeout)
|
|
69
|
+
clearTimeout(this.updateTimeout)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
updateSessionStatus() {
|
|
73
|
+
this.debug("updateSessionStatus")
|
|
74
|
+
|
|
75
|
+
this.sessionStatus().then(result => {
|
|
76
|
+
this.debug(`Result: ${JSON.stringify(result, null, 2)}`)
|
|
77
|
+
this.updateMetaElementsFromResult(result)
|
|
78
|
+
this.updateUserSessionsFromResult(result)
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
updateMetaElementsFromResult(result) {
|
|
83
|
+
this.debug("updateMetaElementsFromResult")
|
|
84
|
+
var csrfTokenElement = document.querySelector("meta[name='csrf-token']")
|
|
85
|
+
|
|
86
|
+
if (csrfTokenElement) {
|
|
87
|
+
this.debug(`Changing token from "${csrfTokenElement.getAttribute("content")}" to "${result.csrf_token}"`)
|
|
88
|
+
csrfTokenElement.setAttribute("content", result.csrf_token)
|
|
89
|
+
} else {
|
|
90
|
+
this.debug("csrf token element couldn't be found")
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
updateUserSessionsFromResult(result) {
|
|
95
|
+
for(var scopeName in result.scopes) {
|
|
96
|
+
this.updateUserSessionScopeFromResult(scopeName, result.scopes[scopeName])
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
updateUserSessionScopeFromResult(scopeName, scope) {
|
|
101
|
+
var deviseMethodName = `current${inflection.camelize(scopeName)}`
|
|
102
|
+
var deviseIsSignedInMethodName = `is${inflection.camelize(scopeName)}SignedIn`
|
|
103
|
+
var currentlySignedIn = Devise[deviseIsSignedInMethodName]()
|
|
104
|
+
var signedInOnBackend = scope.signed_in
|
|
105
|
+
|
|
106
|
+
if (currentlySignedIn && !signedInOnBackend) {
|
|
107
|
+
this.debug(`${inflection.camelize(scopeName)} signed in on frontend but not in backend!`)
|
|
108
|
+
|
|
109
|
+
Devise.setSignedOut({scope: scopeName})
|
|
110
|
+
Devise.callSignOutEvent({scope: scopeName})
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|