anear-js-api 0.4.21 → 0.4.23

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.
@@ -1,9 +1,5 @@
1
1
  "use strict"
2
2
 
3
- const Persist = require('../utils/Persist')
4
-
5
- const Storage = new Persist()
6
-
7
3
  class JsonApiResource {
8
4
 
9
5
  constructor(json) {
@@ -25,10 +21,6 @@ class JsonApiResource {
25
21
  return null // override in subclass if desired
26
22
  }
27
23
 
28
- async onLoad() {
29
- return // called each time the event is loaded from Storage
30
- }
31
-
32
24
  get attributes() {
33
25
  return this.data.attributes
34
26
  }
@@ -52,47 +44,6 @@ class JsonApiResource {
52
44
  return inc.type === data.type && inc.id === data.id
53
45
  })
54
46
  }
55
-
56
- static async getFromStorage(modelId, ...args) {
57
- const json = await this.getJsonFor(modelId)
58
- if (!json) return null
59
- const model = new this(json, ...args)
60
- await model.onLoad()
61
- return model
62
- }
63
-
64
- async persist() {
65
- return await Storage.create(this)
66
- }
67
-
68
- async update() {
69
- return await Storage.update(this)
70
- }
71
-
72
- async exists() {
73
- return await Storage.exists(this)
74
- }
75
-
76
- async remove() {
77
- return await Storage.remove(this)
78
- }
79
-
80
- static async close() {
81
- await Storage.close()
82
- }
83
-
84
- static persistKey(id) {
85
- return `${this.className()}:${id}`
86
- }
87
-
88
- get key() {
89
- return this.constructor.persistKey(this.id)
90
- }
91
-
92
- static async getJsonFor(id) {
93
- const key = this.persistKey(id)
94
- return await Storage.fetch(key)
95
- }
96
47
  }
97
48
 
98
49
  module.exports = JsonApiResource
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anear-js-api",
3
- "version": "0.4.21",
3
+ "version": "0.4.23",
4
4
  "description": "Javascript Developer API for Anear Apps",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -18,9 +18,6 @@
18
18
  "homepage": "https://github.com/machvee/anear-js-api#readme",
19
19
  "dependencies": {
20
20
  "ably": "^2.7.0",
21
- "archiver": "^6.0.1",
22
- "async-mutex": "^0.3.2",
23
- "async-redis": "^2.0.0",
24
21
  "clean-css": "^5.3.3",
25
22
  "cross-fetch": "^3.1.5",
26
23
  "pug": "^3.0.2",
@@ -31,8 +28,7 @@
31
28
  },
32
29
  "devDependencies": {
33
30
  "dotenv": "^16.0.3",
34
- "fakeredis": "^2.0.0",
35
- "jest": "^26.6.3",
31
+ "jest": "^29.7.0",
36
32
  "node-notifier": "^10.0.1"
37
33
  }
38
34
  }
@@ -1,181 +0,0 @@
1
- "use strict"
2
-
3
- const ActiveState = "active"
4
- const IdleState = "idle"
5
-
6
- const MINUTES = (60 * 1000)
7
- const HOURS = (60 * MINUTES)
8
- const DefaultIdleMsecs = (30 * MINUTES)
9
- const DefaultPurgeMsecs = (2 * HOURS) // after Idle
10
-
11
- class Participants {
12
-
13
- constructor(anearEvent, {idleMsecs = DefaultIdleMsecs, purgeMsecs = DefaultPurgeMsecs, ids = []} = {}) {
14
- this.anearEvent = anearEvent
15
- this.idleMsecs = idleMsecs
16
- this.purgeMsecs = purgeMsecs
17
- this._participants = {}
18
- for (const id of ids) {
19
- // app restart logic ...
20
- // seeds from ids with empty objects awaiting full
21
- // anearParticipant rehydration from redis
22
- this._participants[id] = {}
23
- }
24
- this._host = null
25
- }
26
-
27
- toJSON() {
28
- return {
29
- ids: this.all.map(p => p.id),
30
- idleMsecs: this.idleMsecs,
31
- purgeMsecs: this.purgeMsecs
32
- }
33
- }
34
-
35
- get ids() {
36
- return Object.keys(this._participants)
37
- }
38
-
39
- indexedById() {
40
- // returns an object that has AnearParticipant.id as key
41
- return this._participants
42
- }
43
-
44
- getById(participantId) {
45
- return this._participants[participantId]
46
- }
47
-
48
- exists({id}) {
49
- const emptyObject = (obj) => {
50
- return Object.keys(obj).length === 0 && Object.getPrototypeOf(obj) === Object.prototype
51
- }
52
-
53
- const participant = this.getById(id)
54
-
55
- if (!participant) { return false } // id not found
56
-
57
- return !emptyObject(participant)
58
- }
59
-
60
- get({id}) {
61
- return this.getById(id)
62
- }
63
-
64
- get host() {
65
- return this._host
66
- }
67
-
68
- set host(h) {
69
- this._host = h
70
- }
71
-
72
- get all() {
73
- return Object.values(this._participants).
74
- sort((ca, cb) => ca.timestamp - cb.timestamp)
75
- }
76
-
77
- get active() {
78
- return Object.values(this._participants).filter(c => c.state === ActiveState)
79
- }
80
-
81
- get idle() {
82
- return Object.values(this._participants).filter(c => c.state === IdleState)
83
- }
84
-
85
- get count() {
86
- return this.ids.length
87
- }
88
-
89
- get numActive() {
90
- return this.active.length
91
- }
92
-
93
- get numIdle() {
94
- return this.idle.length
95
- }
96
-
97
- isIdle(c, currentTimestamp) {
98
- if (!this.idleMsecs) return false
99
- return (currentTimestamp - c.timestamp) >= this.idleMsecs
100
- }
101
-
102
- isActive(c, currentTimestamp) {
103
- if (!this.idleMsecs) return true
104
- return (currentTimestamp - c.timestamp) < this.idleMsecs
105
- }
106
-
107
- isPurge(c, currentTimestamp) {
108
- if (!this.purgeMsecs) return false
109
- return (currentTimestamp - c.timestamp) >= this.purgeMsecs
110
- }
111
-
112
- updateState(currentTimestamp) {
113
- const sweeper = participant => {
114
- if (participant.state === ActiveState) {
115
- if (this.isIdle(participant, currentTimestamp)) {
116
- participant.state = IdleState
117
- }
118
- } else if (participant.state === IdleState) {
119
- if (this.isPurge(participant, currentTimestamp)) {
120
- this.purge(participant)
121
- }
122
- }
123
- }
124
-
125
- this.ids.forEach(
126
- k => {
127
- const c = this._participants[k]
128
- sweeper(c)
129
- }
130
- )
131
- }
132
-
133
- add(anearParticipant, withTimestamp = this.currentTimestamp) {
134
- if (anearParticipant.isHost() && this.anearEvent.hosted) {
135
- // the host is not an eligible participant and isn't active nor idle
136
- this.host = anearParticipant
137
- } else {
138
- this._participants[anearParticipant.id] = anearParticipant
139
- this.markActive(anearParticipant, withTimestamp)
140
- }
141
- return anearParticipant
142
- }
143
-
144
- markActive(anearParticipant, withTimestamp = this.currentTimestamp) {
145
- anearParticipant.timestamp = withTimestamp
146
- anearParticipant.state = ActiveState
147
- }
148
-
149
- purge(participant) {
150
- if (!participant) return
151
-
152
- const {id} = participant
153
-
154
- if (this.host && (id === this.host.id)) {
155
- this.host = null
156
- } else {
157
- if (this._participants[id]) {
158
- delete this._participants[id]
159
- }
160
- }
161
- }
162
-
163
- get currentTimestamp() {
164
- return new Date().getTime()
165
- }
166
-
167
- load(anearParticipants) {
168
- // used for tests only
169
- anearParticipants.forEach(
170
- p => {
171
- if (p.isHost() && this.anearEvent.hosted) {
172
- this.host = p
173
- } else {
174
- this._participants[p.id] = p
175
- }
176
- }
177
- )
178
- }
179
- }
180
-
181
- module.exports = Participants
@@ -1,52 +0,0 @@
1
- "use strict"
2
-
3
- const redis = require('async-redis')
4
-
5
- class Persist {
6
-
7
- constructor(options={}) {
8
- this.init(options)
9
- }
10
-
11
- init(options={}) {
12
- this.client = redis.createClient() // TODO: createClient redis options passed via options.redis_config
13
- }
14
-
15
- async close() {
16
- await this.client.quit()
17
- }
18
-
19
- async store(model) {
20
- const jsonString = JSON.stringify(model.toJSON())
21
- return await this.client.set(model.key, jsonString)
22
- }
23
-
24
- async fetch(key) {
25
- const model = await this.client.get(key)
26
- return JSON.parse(model)
27
- }
28
-
29
- async exists(model) {
30
- const result = await this.client.exists(model.key)
31
- return result == 1
32
- }
33
-
34
- async create(model) {
35
- const alreadyPersisted = await this.exists(model)
36
-
37
- if (alreadyPersisted) {
38
- throw new Error(`model ${model.id} already exists`)
39
- }
40
- return await this.store(model)
41
- }
42
-
43
- async update(model) {
44
- return await this.store(model)
45
- }
46
-
47
- async remove(model) {
48
- return await this.client.del(model.key)
49
- }
50
- }
51
-
52
- module.exports = Persist
@@ -1,234 +0,0 @@
1
- "use strict"
2
-
3
- const Participants = require('../lib/utils/Participants')
4
- const { ParticipantsFixture: participantsJSON,
5
- AnearParticipantFixture2: visitor2JSON,
6
- AnearHostFixture: hostJSON } = require('./fixtures')
7
- const AnearParticipant = require('../lib/models/AnearParticipant')
8
- const AnearParticipantJSONBuilder = require('./utils/AnearParticipantJSONBuilder')
9
-
10
- const user1Id = "e053977c-dcb6-40e0-b7b8-e3dbd70ec8fd"
11
- const idleId = "f1056e6c-c393-4617-8a06-67ba9d2f4b8a"
12
- const activeId = user1Id
13
- const hours24 = (24 * 60 * 60 * 1000)
14
- const GeoLocation = {lat: 25.8348343, lng: -80.38438434}
15
-
16
- const MockHostedEvent = {hosted: true, anearParticipantClass: AnearParticipant}
17
- const MockNonHostedEvent = {hosted: false, anearParticipantClass: AnearParticipant}
18
-
19
- const newActiveParticipants = (timestamp, args = {}) => {
20
- const copyParticipantsFixture = JSON.parse(JSON.stringify(participantsJSON))
21
- const activeParticipants = Object.values(copyParticipantsFixture).filter(p => !p.isHost)
22
-
23
- const participants = new Participants(MockHostedEvent, args)
24
-
25
- activeParticipants.forEach(
26
- (attrs, i) => {
27
- const participant = new AnearParticipant(AnearParticipantJSONBuilder(attrs), MockHostedEvent)
28
- participant.host = attrs.isHost
29
- participants.add(participant, timestamp - (i*2000))
30
- }
31
- )
32
- return participants
33
- }
34
-
35
- const newCurrentParticipants = (timestamp, anearEvent = MockHostedEvent) => {
36
- const copyParticipantsFixture = JSON.parse(JSON.stringify(participantsJSON))
37
- const currentParticipants= Object.values(copyParticipantsFixture).filter(p => !p.isHost)
38
-
39
- const participants = new Participants(anearEvent)
40
-
41
- const anearParticipants = currentParticipants.map(
42
- (attrs, i) => {
43
- const participant = new AnearParticipant(AnearParticipantJSONBuilder(attrs), anearEvent)
44
-
45
- let withTimestamp
46
- if (attrs.state === 'active') {
47
- withTimestamp = timestamp - (i*2000)
48
- } else {
49
- withTimestamp = timestamp - participants.idleMsecs
50
- }
51
-
52
- participant.state = attrs.state
53
- participant.timestamp = withTimestamp
54
- participant.host = attrs.isHost
55
- return participant
56
- }
57
- )
58
-
59
- participants.load(anearParticipants)
60
-
61
- return participants
62
- }
63
-
64
- const now = new Date().getTime()
65
-
66
- test('constructor with JSON provided', () => {
67
- const ids = ['123', '345', '456']
68
- const args = {idleMsecs: 23400, purgeMsecs: 678900, ids}
69
- const p = new Participants(MockHostedEvent, args)
70
- expect(p).toBeDefined()
71
- expect(p.idleMsecs).toBe(args.idleMsecs)
72
- expect(p.purgeMsecs).toBe(args.purgeMsecs)
73
- expect(p.ids).toStrictEqual(ids)
74
- })
75
-
76
- test('constructor with NO JSON provided has default idle and purge Msecs', () => {
77
- const p = new Participants(MockHostedEvent)
78
- expect(p).toBeDefined()
79
- expect(p.idleMsecs).toBe(1800000)
80
- expect(p.purgeMsecs).toBe(7200000)
81
- expect(p.all).toHaveLength(0)
82
- })
83
-
84
- test('constructor with null idle and purge msecs avoids idle purge', () => {
85
- const p = newActiveParticipants(now, {idleMsecs: null, purgeMsecs: null})
86
- expect(p.idleMsecs).toBe(null)
87
- expect(p.purgeMsecs).toBe(null)
88
- expect(p.idle).toHaveLength(0)
89
- expect(p.active).toHaveLength(10)
90
-
91
- const c = p.getById(idleId)
92
- expect(p.isIdle(c, now)).toBeFalsy()
93
- c.timestamp = c.timestamp - hours24
94
- expect(p.isPurge(c, now)).toBeFalsy()
95
- })
96
-
97
- test('getById', () => {
98
- const p = newActiveParticipants(now)
99
- expect(p.getById(user1Id)).toHaveProperty("name", "user1")
100
- })
101
-
102
- test('exists success', () => {
103
- const p = newActiveParticipants(now)
104
- expect(p.exists({id: user1Id})).toBeTruthy()
105
- })
106
-
107
- test('exists fail', () => {
108
- const p = newActiveParticipants(now)
109
- expect(p.exists({id: "abcd"})).toBeFalsy()
110
- })
111
-
112
- test('get success', () => {
113
- const p = newActiveParticipants(now)
114
- expect(p.get({id: user1Id})).toHaveProperty("name", "user1")
115
- })
116
-
117
- test('getParticipant fail', () => {
118
- const p = newActiveParticipants(now)
119
- expect(p.get({id: "abcd"})).toBeUndefined()
120
- })
121
-
122
- test('getParticipant success', () => {
123
- const p = newActiveParticipants(now)
124
- expect(p.get({id: user1Id}).name).toBe("user1")
125
- })
126
-
127
- test('add() participant user', async () => {
128
- const p = newCurrentParticipants(now, MockNonHostedEvent)
129
- const participant = new AnearParticipant(visitor2JSON, MockNonHostedEvent)
130
- participant.geoLocation = GeoLocation
131
-
132
- p.add(participant)
133
- const part = p.get(participant)
134
- expect(part.name).toBe("bbondfl93")
135
- expect(part.avatarUrl).toBe("https://s3.amazonaws.com/anearassets/barbara_bond.png")
136
- expect(part.userId).toBe("d280da7c-1baf-4607-a286-4b5faa03eaa7")
137
-
138
- await AnearParticipant.close()
139
- })
140
-
141
- test('add() host user', async () => {
142
- const p = newCurrentParticipants(now)
143
- const host = new AnearParticipant(hostJSON, MockNonHostedEvent)
144
- host.geoLocation = GeoLocation
145
-
146
- p.add(host)
147
- expect(p.get(host)).toBeUndefined()
148
- expect(p.host.name).toBe('foxhole_host')
149
- expect(p.host.avatarUrl).toBe("https://s3.amazonaws.com/anearassets/foxhole.png")
150
-
151
- await AnearParticipant.close()
152
- })
153
-
154
- test('active', () => {
155
- const p = newActiveParticipants(now)
156
- expect(p.active).toHaveLength(10)
157
- })
158
-
159
- test('idle', () => {
160
- const p = newCurrentParticipants(now)
161
- expect(p.idle).toHaveLength(2)
162
- })
163
-
164
- test('toJSON', () => {
165
- const p = newActiveParticipants(now)
166
- const j = p.toJSON()
167
- expect(j).toHaveProperty("ids")
168
- expect(j).toHaveProperty("idleMsecs")
169
- expect(j).toHaveProperty("purgeMsecs")
170
- expect(j.ids.sort()).toEqual(p.ids.sort())
171
- expect(j.ids.length).toBe(10)
172
- expect(j.idleMsecs).toBe(1800000)
173
- expect(j.purgeMsecs).toBe(7200000)
174
- })
175
-
176
- test('isIdle', () => {
177
- const p = newCurrentParticipants(now)
178
- const c = p.getById(idleId)
179
- expect(p.isIdle(c, now)).toBeTruthy()
180
- })
181
-
182
- test('isActive', () => {
183
- const p = newCurrentParticipants(now)
184
- const c = p.getById(activeId)
185
- expect(p.isActive(c, now)).toBeTruthy()
186
- })
187
-
188
- test('isPurge', () => {
189
- const p = newCurrentParticipants(now)
190
- const c = p.getById(idleId)
191
- c.timestamp = c.timestamp - hours24
192
- expect(p.isPurge(c, now)).toBeTruthy()
193
- })
194
-
195
- test('purgeParticipant participant user-type', () => {
196
- const p = newCurrentParticipants(now)
197
- const c = p.getById(idleId)
198
- p.purge(c)
199
- expect(p.exists(c)).toBeFalsy()
200
- })
201
-
202
- test('purge host user-type', () => {
203
- const p = newActiveParticipants(now)
204
- const c = p.host
205
- p.purge(c)
206
- expect(p.host).toStrictEqual(null)
207
- })
208
-
209
- test('updateState will leave state unchanged when timeout criteria not met', () => {
210
- const p = newCurrentParticipants(now)
211
- p.updateState(now)
212
- expect(p.active).toHaveLength(8)
213
- expect(p.idle).toHaveLength(2)
214
- expect(p.numActive).toBe(8)
215
- expect(p.numIdle).toBe(2)
216
- })
217
-
218
- test('updateState will mark active to idle when timeout reached', () => {
219
- const current = now
220
- const p = newCurrentParticipants(current)
221
- expect(p.active).toHaveLength(8)
222
- p.updateState(current + p.idleMsecs + 1000)
223
- expect(Object.values(p.all).
224
- filter(p => p.state === 'active')).toHaveLength(0)
225
- })
226
-
227
- test('updateState will purge idle participants', () => {
228
- const current = now
229
- const p = newCurrentParticipants(current)
230
- const idlers = p.idle
231
- expect(idlers).toHaveLength(2)
232
- p.updateState(current + p.purgeMsecs + 1000)
233
- idlers.forEach(c => expect(p.getById(c.id)).toBeUndefined())
234
- })
@@ -1,102 +0,0 @@
1
- const Persist = require('../lib/utils/Persist')
2
-
3
- const p = new Persist()
4
- const key = "myModel"
5
-
6
- afterAll(
7
- async () => {
8
- await p.close()
9
- }
10
- )
11
-
12
- const barb = "Barbie"
13
- const dave = "Dave"
14
-
15
- class TestModel {
16
- constructor(keyArg, nameArg) {
17
- this.id = 1
18
- this.key = keyArg
19
- this.name = nameArg
20
- }
21
-
22
- toJSON() {
23
- return {name: this.name, age: 27, state: 'FL'}
24
- }
25
- }
26
-
27
- const testModel = new TestModel(key, barb)
28
- const daveModel = new TestModel(key, dave)
29
-
30
- test('constructor', () => {
31
- expect(p).not.toBe(null)
32
- })
33
-
34
- test('store a model', async () => {
35
- try {
36
- const result = await p.store(testModel)
37
-
38
- expect(testModel.key).toBe(key)
39
- expect(result).toBe('OK')
40
- } catch(error) {
41
- console.error(error)
42
- }
43
- })
44
-
45
- test('fetch a model', async () => {
46
- try {
47
- const model = await p.fetch(key)
48
- expect(model.name).toBe(barb)
49
- } catch(error) {
50
- console.error(error)
51
- }
52
- })
53
-
54
- test('remove a model', async () => {
55
- try {
56
- const result = await p.remove(testModel)
57
- } catch(error) {
58
- console.error(error)
59
- }
60
- })
61
-
62
- test('create a model only once, and exists only when it does', async () => {
63
- try {
64
- const result = await p.create(testModel)
65
- expect(result).toBe('OK')
66
-
67
- let isThere = await p.exists(testModel)
68
- expect(isThere).toBe(true)
69
-
70
- await expect(p.create(testModel))
71
- .rejects
72
- .toThrow('model 1 already exists');
73
-
74
- await p.remove(testModel)
75
-
76
- isThere = await p.exists(testModel)
77
- expect(isThere).toBe(false)
78
-
79
- } catch(error) {
80
- console.error(error)
81
- }
82
- })
83
-
84
- test('update a model', async () => {
85
- try {
86
- const result = await p.create(testModel)
87
- expect(result).toBe('OK')
88
-
89
- let model = await p.fetch(key)
90
- expect(model.name).toBe(barb)
91
-
92
- const ures = await p.update(daveModel)
93
- expect(ures).toBe('OK')
94
-
95
- model = await p.fetch(key)
96
- expect(model.name).toBe(dave)
97
-
98
- await p.remove(testModel)
99
- } catch(error) {
100
- console.error(error)
101
- }
102
- })