anear-js-api 0.4.3 → 0.4.5

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.
@@ -4,6 +4,10 @@ const ApiService = require('./ApiService')
4
4
 
5
5
  class AnearApi extends ApiService {
6
6
 
7
+ constructor(apiKey, apiVersion) {
8
+ super(apiKey, apiVersion)
9
+ }
10
+
7
11
  async getAccount() {
8
12
  logger.debug("API: GET /accounts")
9
13
 
@@ -42,14 +46,10 @@ class AnearApi extends ApiService {
42
46
  logger.debug(`API: POST transition event ${eventName}`)
43
47
 
44
48
  const relationships = {event: eventId}
45
- try {
46
- const json = await this.post("transitions", {event_name: eventName}, relationships)
47
- const attrs = json.data.attributes
48
- logger.info(`API: newState is ${attrs.state}`)
49
- return attrs
50
- } catch(err) {
51
- logger.error(err)
52
- }
49
+ const json = await this.post("transitions", {event_name: eventName}, relationships)
50
+ const attrs = json.data.attributes
51
+ logger.debug(`API: newState is ${attrs.state}`)
52
+ return attrs
53
53
  }
54
54
 
55
55
  async getEventParticipantJson(participantId, geoLocation) {
@@ -1,18 +1,18 @@
1
1
  "use strict"
2
+
2
3
  const ErrorResponse = require('./ErrorResponse')
3
4
  const qs = require('qs')
4
5
  const fetch = require('cross-fetch')
5
6
 
6
- const DeveloperApiURL = "https://api.anearapp.com/developer/v1"
7
+ const DeveloperApiURL = 'https://api.anearapp.com/developer/'
7
8
 
8
9
  class ApiService {
9
-
10
- constructor() {
11
- this.api_base_url = DeveloperApiURL
10
+ constructor(apiKey, apiVersion) {
11
+ this.api_base_url = DeveloperApiURL + apiVersion
12
12
  this.defaultHeaderObject = {
13
13
  'Accept': 'application/json',
14
14
  'Content-Type': 'application/json',
15
- 'X-Api-Key': process.env.ANEARAPP_API_KEY
15
+ 'X-Api-Key': apiKey
16
16
  }
17
17
  this.default_headers = new fetch.Headers(this.defaultHeaderObject)
18
18
  }
@@ -39,12 +39,12 @@ class ApiService {
39
39
  )
40
40
  }
41
41
 
42
- async get(resource, params={}) {
42
+ get(resource, params={}) {
43
43
  const request = this.prepareGetRequest(resource, params)
44
- return await this.issueRequest(request)
44
+ return this.issueRequest(request)
45
45
  }
46
46
 
47
- async post(resource, attributes, relationships={}) {
47
+ post(resource, attributes, relationships={}) {
48
48
  const payload = this.formatPayload(resource, attributes, relationships)
49
49
  const request = new fetch.Request(
50
50
  `${this.api_base_url}/${resource}`, {
@@ -53,10 +53,10 @@ class ApiService {
53
53
  body: JSON.stringify(payload)
54
54
  }
55
55
  )
56
- return await this.issueRequest(request)
56
+ return this.issueRequest(request)
57
57
  }
58
58
 
59
- async put(resource, id, attributes, relationships={}) {
59
+ put(resource, id, attributes, relationships={}) {
60
60
  const payload = this.formatPayload(resource, attributes, relationships)
61
61
  const urlString = `${this.api_base_url}/${resource}/${id}`
62
62
  const request = new fetch.Request(
@@ -66,10 +66,10 @@ class ApiService {
66
66
  body: JSON.stringify(payload)
67
67
  }
68
68
  )
69
- return await this.issueRequest(request)
69
+ return this.issueRequest(request)
70
70
  }
71
71
 
72
- async httpDelete(resource, id=null) {
72
+ httpDelete(resource, id=null) {
73
73
  const idStr = (id ? `/${id}` : '')
74
74
  const urlString = `${this.api_base_url}/${resource}${idStr}`
75
75
  const request = new fetch.Request(
@@ -78,12 +78,12 @@ class ApiService {
78
78
  headers: this.default_headers
79
79
  }
80
80
  )
81
- return await this.issueRequest(request)
81
+ return this.issueRequest(request)
82
82
  }
83
83
 
84
84
  async issueRequest(request) {
85
85
  const resp = await fetch(request)
86
- return await this.checkStatus(resp)
86
+ return this.checkStatus(resp)
87
87
  }
88
88
 
89
89
  formatPayload(resource, attributes, relationships) {
@@ -19,15 +19,21 @@ const ALREADY_PRESENT = 'present'
19
19
  const ChannelParams = {params: {rewind: "5s"}}
20
20
 
21
21
 
22
- const AblyLogLevel = process.env.ANEARAPP_ABLY_LOG_LEVEL || 0 // 0 - no logging, 4 - verbose logging
22
+ // 0 - no logging, 4 - verbose logging
23
+ const AblyLogLevel = process.env.ANEARAPP_ABLY_LOG_LEVEL || 0
24
+
23
25
  const AnearCreateEventChannelName = `anear:${AppId}:e`
24
26
 
25
27
  class AnearMessaging {
26
28
  constructor(AnearEventClass, AnearParticipantClass) {
27
- this.api = new AnearApi()
29
+ this.api = new AnearApi(
30
+ process.env.ANEARAPP_API_KEY,
31
+ process.env.ANEARAPP_API_VERSION
32
+ )
28
33
  this.AnearEventClass = AnearEventClass
29
34
  this.AnearParticipantClass = AnearParticipantClass
30
35
  this.anearEvents = {}
36
+ this.eventChannels = {}
31
37
 
32
38
  const baseUrl = this.api.api_base_url
33
39
  const authUrl = `${baseUrl}/messaging_auth`
@@ -49,8 +55,6 @@ class AnearMessaging {
49
55
  }
50
56
  }
51
57
 
52
- this.eventChannels = {}
53
-
54
58
  this.initRealtime(clientOptions)
55
59
  }
56
60
 
@@ -59,34 +63,35 @@ class AnearMessaging {
59
63
 
60
64
  this.realtime = new Ably.Realtime(clientOptions)
61
65
 
62
- this.realtime.connection.on(
63
- "connected",
64
- async () => {
65
- await this.getAppInfo(AppId)
66
- logger.info("Ably connected!")
67
- await this.setupCreateEventChannel()
68
- await this.reloadAnyEventsInProgress(AppId)
69
- }
70
- )
66
+ const connectedCallback = async () => {
67
+ await this.getAppInfo(AppId)
68
+ logger.debug("Ably connected!")
69
+ await this.setupCreateEventChannel()
70
+ await this.reloadAnyEventsInProgress(AppId)
71
+ }
72
+
73
+ this.realtime.connection.on("connected", connectedCallback)
71
74
  }
72
75
 
73
76
  async getAppInfo(appId) {
77
+ // fetch the App info from the Anear API
74
78
  const anearApp = await this.api.getApp(appId)
75
- logger.info("================")
76
- logger.info(`STARTING APP ${anearApp.data.attributes['short-name']}`)
77
- logger.info("================")
79
+
80
+ logger.debug("================")
81
+ logger.debug(`STARTING APP ${anearApp.data.attributes['short-name']}`)
82
+ logger.debug("================")
78
83
  }
79
84
 
80
85
  getChannel(channelName, channelParams = ChannelParams) {
81
86
  return this.realtime.channels.get(channelName, channelParams)
82
87
  }
83
88
 
84
- async getAnearEventFromStorage(eventId) {
85
- return await this.AnearEventClass.getFromStorage(eventId, this)
89
+ getAnearEventFromStorage(eventId) {
90
+ return this.AnearEventClass.getFromStorage(eventId, this)
86
91
  }
87
92
 
88
- async getAnearParticipantFromStorage(participantId) {
89
- return await this.AnearParticipantClass.getFromStorage(participantId, this)
93
+ getAnearParticipantFromStorage(participantId) {
94
+ return this.AnearParticipantClass.getFromStorage(participantId, this)
90
95
  }
91
96
 
92
97
  async initEventRealtimeMessaging(anearEvent) {
@@ -98,14 +103,10 @@ class AnearMessaging {
98
103
 
99
104
  this.eventChannels[anearEvent.id] = {privates: {}}
100
105
 
101
- try {
102
- await this.setupSpectatorsChannel(anearEvent)
103
- this.setupEventsChannel(anearEvent)
104
- await this.setupParticipantsChannel(anearEvent)
105
- await this.setupActionsChannel(anearEvent)
106
- } catch(error) {
107
- logger.error(`initEventRealtimeMessaging(${anearEvent.id}): `, error)
108
- }
106
+ await this.setupSpectatorsChannel(anearEvent)
107
+ this.setupEventsChannel(anearEvent)
108
+ await this.setupParticipantsChannel(anearEvent)
109
+ await this.setupActionsChannel(anearEvent)
109
110
 
110
111
  logger.debug(`initEventRealtimeMessaging(${anearEvent.id}) is complete`)
111
112
 
@@ -117,76 +118,75 @@ class AnearMessaging {
117
118
  // create the AnearEvent subclass, persist it in storage, and
118
119
  // initialize its realtime messaging
119
120
  //
120
- logger.info(`createEventMessagingCallback(${message.name})`)
121
-
122
- try {
123
- const eventJson = JSON.parse(message.data)
124
- const anearEvent = new this.AnearEventClass(eventJson, this)
125
-
126
- //
127
- // if we are getting this event create message from history after a quick restart,
128
- // we just return if the event already exists
129
- //
130
- const eventExists = await anearEvent.exists()
131
-
132
- logger.info(`Event ${anearEvent.id} ${eventExists ? "already exists" : "does not exist"} in Storage`)
133
-
134
- let loadedEvent = anearEvent
135
-
136
- if (!eventExists) {
137
- await anearEvent.runExclusive(`createEventCallback ${anearEvent.id}`, async () => {
138
- await anearEvent.createdEventCallback()
139
- await anearEvent.persist()
140
- // start the state machine before initialiing Realtime Messaging
141
- // as REFRESH events come in and the state machine should be ready
142
- // to handle those XState events
143
- anearEvent.startStateMachine()
144
- await this.initEventRealtimeMessaging(anearEvent)
145
- })
146
- } else {
147
- loadedEvent = await this.getAnearEventFromStorage(anearEvent.id)
148
- await this.initEventRealtimeMessaging(loadedEvent)
149
- loadedEvent.startStateMachine()
150
- }
121
+ logger.debug(`createEventMessagingCallback(${message.name})`)
122
+
123
+ const eventJson = JSON.parse(message.data)
124
+ const anearEvent = new this.AnearEventClass(eventJson, this)
125
+
126
+ //
127
+ // if we are getting this event create message from history after a quick restart,
128
+ // we just return if the event already exists
129
+ //
130
+ const eventExists = await anearEvent.exists()
151
131
 
152
- logger.info(`New ${loadedEvent.constructor.name} Event: `, loadedEvent.toJSON())
132
+ logger.debug(
133
+ `Event ${anearEvent.id} ${eventExists ? "already exists" : "does not exist"} in Storage`
134
+ )
153
135
 
154
- this.anearEvents[loadedEvent.id] = loadedEvent
136
+ let loadedEvent = anearEvent
137
+
138
+ if (!eventExists) {
139
+ const messagingInitializedCallback = async () => {
140
+ await anearEvent.createdEventCallback()
141
+ await anearEvent.persist()
142
+ // start the state machine before initializing Realtime Messaging
143
+ // as REFRESH events come in and the state machine should be ready
144
+ // to handle those XState events
145
+ anearEvent.startStateMachine()
146
+ await this.initEventRealtimeMessaging(anearEvent)
147
+ }
155
148
 
156
- } catch(err) {
157
- logger.error(err)
149
+ await anearEvent.runExclusive(
150
+ `createEventCallback ${anearEvent.id}`,
151
+ messagingInitializedCallback
152
+ )
153
+ } else {
154
+ loadedEvent = await this.getAnearEventFromStorage(anearEvent.id)
155
+ await this.initEventRealtimeMessaging(loadedEvent)
156
+ loadedEvent.startStateMachine()
158
157
  }
158
+
159
+ logger.debug(`New ${loadedEvent.constructor.name} Event: `, loadedEvent.toJSON())
160
+
161
+ this.anearEvents[loadedEvent.id] = loadedEvent
159
162
  }
160
163
 
161
164
  async reloadAnyEventsInProgress(appId) {
162
- try {
163
- const anearApp = await this.api.getAppZones(appId)
164
- for (const zone of anearApp.data.relationships.zones.data) {
165
- const zoneEvents = await this.api.getZoneEvents(zone.id)
166
- const events = zoneEvents.data.relationships.events.data
167
-
168
- for (const eventData of events) {
169
- const eventJson = await this.api.getEvent(eventData.id)
170
- const anearEvent = new this.AnearEventClass(eventJson, this)
171
- await this.initEventRealtimeMessaging(anearEvent)
172
-
173
- const attachedParticipants = this.getPresentParticipants(anearEvent)
174
- // TBD: might want to change the attach and presence logic on
175
- // the actions channel. The Ably docs show subscribing to the
176
- // presence events on the actions channel, and instead of using History,
177
- // it does a get() to fetch all of the current members. This behavior
178
- // is useful for both event start, and event restart within this function
179
- // anearEvent.startStateMachine()
180
- //
181
- }
165
+ const anearApp = await this.api.getAppZones(appId)
166
+
167
+ for (const zone of anearApp.data.relationships.zones.data) {
168
+ const zoneEvents = await this.api.getZoneEvents(zone.id)
169
+ const events = zoneEvents.data.relationships.events.data
170
+
171
+ for (const eventData of events) {
172
+ const eventJson = await this.api.getEvent(eventData.id)
173
+ const anearEvent = new this.AnearEventClass(eventJson, this)
174
+ await this.initEventRealtimeMessaging(anearEvent)
175
+
176
+ const attachedParticipants = await this.getPresentParticipants(anearEvent)
177
+ // TBD: might want to change the attach and presence logic on
178
+ // the actions channel. The Ably docs show subscribing to the
179
+ // presence events on the actions channel, and instead of using History,
180
+ // it does a get() to fetch all of the current members. This behavior
181
+ // is useful for both event start, and event restart within this function
182
+ // anearEvent.startStateMachine()
183
+ //
182
184
  }
183
- } catch (err) {
184
- logger.error(err)
185
185
  }
186
186
  }
187
187
 
188
188
  async setupCreateEventChannel() {
189
- logger.info(`attaching to channel ${AnearCreateEventChannelName}`)
189
+ logger.debug(`attaching to channel ${AnearCreateEventChannelName}`)
190
190
 
191
191
  const createEventsChannel = this.getChannel(AnearCreateEventChannelName)
192
192
 
@@ -232,11 +232,11 @@ class AnearMessaging {
232
232
  return members.length
233
233
  }
234
234
 
235
- async getPresentParticipants(anearEvent) {
235
+ getPresentParticipants(anearEvent) {
236
236
  // returns the participant presence data for each member who is present on
237
237
  // the event's actions channel
238
238
  const channel = this.eventChannels[anearEvent.id].actions
239
- return await channel.presence.get()
239
+ return channel.presence.get() // returns Promise
240
240
  }
241
241
 
242
242
  async setupActionsChannel(anearEvent) {
@@ -333,29 +333,22 @@ class AnearMessaging {
333
333
  // check if the participant is already in storage, and if so, instantiate, else
334
334
  // instantiate from API response
335
335
  //
336
- try {
337
- const participantJson = await this.api.getEventParticipantJson(participantId)
338
- const participant = new this.AnearParticipantClass(participantJson, anearEvent)
336
+ const participantJson = await this.api.getEventParticipantJson(participantId)
337
+ const participant = new this.AnearParticipantClass(participantJson, anearEvent)
339
338
 
340
- const persistedAnearParticipant = await this.AnearParticipantClass.getFromStorage(participantId, anearEvent)
339
+ const persistedAnearParticipant = await this.AnearParticipantClass.getFromStorage(participantId, anearEvent)
341
340
 
342
- if (persistedAnearParticipant) {
343
- participant.context = persistedAnearParticipant.context
344
- }
341
+ if (persistedAnearParticipant) {
342
+ participant.context = persistedAnearParticipant.context
343
+ }
345
344
 
346
- await anearEvent.runExclusive(`participantEnterCallback ${participant.id}`, async () => {
347
- participant.geoLocation = geoLocation
345
+ await anearEvent.runExclusive(`participantEnterCallback ${participant.id}`, async () => {
346
+ participant.geoLocation = geoLocation
348
347
 
349
- await this.setupPrivatePublishingChannel(participant)
350
- await anearEvent.participantEnter(participant)
351
- await anearEvent.update()
352
- })
353
- } catch(error) {
354
- // participant not found or is not currently marked active at the API service
355
- // don't allow participation. FIX: we need to publish to the private channel
356
- // with an error message type.
357
- logger.error(`processParticipanEnter(${anearEvent.id}, ${participantId}) error: `, error)
358
- }
348
+ await this.setupPrivatePublishingChannel(participant)
349
+ await anearEvent.participantEnter(participant)
350
+ await anearEvent.update()
351
+ })
359
352
  }
360
353
 
361
354
  async spectatorEnterMessagingCallback(anearEvent, message) {
@@ -458,11 +451,7 @@ class AnearMessaging {
458
451
  }
459
452
 
460
453
  async attachChannel(channel) {
461
- try {
462
- return await channel.attach()
463
- } catch(err) {
464
- this.logErrorInfo(err)
465
- }
454
+ return await channel.attach()
466
455
  }
467
456
 
468
457
  subscribeEventMessages(channel, messageType, callback) {
@@ -475,22 +464,18 @@ class AnearMessaging {
475
464
  }
476
465
 
477
466
  async subscribePresenceEventWithHistory(channel, action, callback) {
478
- logger.info(`subscribePresenceEvents(${action}) for channel ${channel.name}`)
467
+ logger.debug(`subscribePresenceEvents(${action}) for channel ${channel.name}`)
479
468
 
480
469
  this.subscribePresenceEvent(channel, action, callback)
481
470
 
482
- try {
483
- const history = await channel.presence.history({limit: 25})
471
+ const history = await channel.presence.history({limit: 25})
484
472
 
485
- history.items.filter(message => message.action === action).forEach(
486
- async message => {
487
- logger.info(`presence history ${action} event received`, message)
488
- await callback(message)
489
- }
490
- )
491
- } catch(err) {
492
- logger.error('Unable to get presence history; err = ' + err.message)
493
- }
473
+ history.items.filter(message => message.action === action).forEach(
474
+ async message => {
475
+ logger.debug(`presence history ${action} event received`, message)
476
+ await callback(message)
477
+ }
478
+ )
494
479
  }
495
480
 
496
481
  async publishEventSpectatorsMessage(anearEvent, css, message, messageType = PublicDisplayMessageType) {
@@ -596,14 +581,10 @@ class AnearMessaging {
596
581
  }
597
582
 
598
583
  async publishChannelMessage(channel, messageType, payload) {
599
- try {
600
- await channel.publish(
601
- messageType,
602
- payload
603
- )
604
- } catch(err) {
605
- this.logErrorInfo(err)
606
- }
584
+ await channel.publish(
585
+ messageType,
586
+ payload
587
+ )
607
588
  }
608
589
 
609
590
  async detachAll(eventId) {
@@ -625,17 +606,13 @@ class AnearMessaging {
625
606
  async detachChannel (channel) {
626
607
  if (!channel || channel.state !== 'attached') return
627
608
 
628
- try {
629
- await channel.detach(this.logErrorInfo)
630
- logger.info(`channel ${channel.name} detached`)
631
- } catch(err) {
632
- this.logErrorInfo(err)
633
- }
609
+ await channel.detach(this.logErrorInfo)
610
+ logger.debug(`channel ${channel.name} detached`)
634
611
  }
635
612
 
636
613
  logErrorInfo(errInfo) {
637
614
  if (errInfo) {
638
- logger.error(`Ably ERROR (${errInfo.code}): ${errInfo.message}`)
615
+ logger.error("Ably ERROR:", errInfo)
639
616
  }
640
617
  }
641
618
  }
@@ -11,6 +11,8 @@ const logger = require('../utils/Logger')
11
11
 
12
12
  const PrivateDisplayMessageType = 'private_display'
13
13
 
14
+ const PlayableStates = ['closing', 'closed', 'canceled']
15
+
14
16
  class AnearEvent extends JsonApiResource {
15
17
 
16
18
  constructor(json, messaging) {
@@ -64,7 +66,7 @@ class AnearEvent extends JsonApiResource {
64
66
  }
65
67
 
66
68
  async getClonedEvent() {
67
- // if the current event was a clone of previous event, try to fetch if from
69
+ // if the current event was a clone of previous event, fetch if from
68
70
  // Peristence and return
69
71
  const clonedEventData = this.relationships["cloned-event"].data
70
72
  if (!clonedEventData) return null
@@ -221,13 +223,13 @@ class AnearEvent extends JsonApiResource {
221
223
  if (this.participants.exists(participant)) {
222
224
  logger.info(`AnearEvent: participant ${participant.id} exists. Refreshing...`)
223
225
 
224
- this.participants.add(participant) // update the participants entry
226
+ this.participants.add(participant) // update the participants record
225
227
 
226
228
  this.anearStateMachine.sendRefreshEvent({ participant })
227
229
 
228
230
  await participant.update()
229
231
  } else {
230
- this.participants.add(participant) // add the participants entry
232
+ this.participants.add(participant) // add the participants record
231
233
 
232
234
  this.anearStateMachine.sendJoinEvent({ participant })
233
235
 
@@ -271,18 +273,14 @@ class AnearEvent extends JsonApiResource {
271
273
  //
272
274
  logger.debug(`AnearEvent: transitionEvent(${eventName})`)
273
275
 
274
- try {
275
- const responseAttributes = await this.messaging.api.transitionEvent(this.id, eventName)
276
- const newState = responseAttributes.state
277
- this.attributes.state = newState
278
- await this.messaging.publishEventTransitionMessage(this, newState)
279
- } catch(err) {
280
- logger.error(`AnearEvent: transitionEvent error: ${err}`)
281
- }
276
+ const responseAttributes = await this.messaging.api.transitionEvent(this.id, eventName)
277
+ const newState = responseAttributes.state
278
+ this.attributes.state = newState
279
+ await this.messaging.publishEventTransitionMessage(this, newState)
282
280
  }
283
281
 
284
282
  isPlayable() {
285
- return !['closing', 'closed', 'canceled'].includes(this.eventState)
283
+ return !PlayableStates.includes(this.eventState)
286
284
  }
287
285
 
288
286
  async transitionToAnnounce() {
@@ -11,7 +11,12 @@ const TimeoutEvent = 'TIMEOUT'
11
11
 
12
12
  class AnearXstate {
13
13
  constructor(machineConfig, machineOptions, previousState, anearEventContext) {
14
- this.machine = createMachine(machineConfig, machineOptions).withContext(anearEventContext)
14
+ const config = {predictableActionArguments: true, ...machineConfig}
15
+
16
+ this.machine = createMachine(
17
+ config,
18
+ machineOptions
19
+ ).withContext(anearEventContext)
15
20
 
16
21
  this._currentState = previousState ? State.create(previousState) : this.machine.initialState
17
22
  this._currentContext = anearEventContext
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anear-js-api",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Javascript Developer API for Anear Apps",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {