qranswers 1.0.2 → 1.0.4
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/README.md +71 -0
- package/lib/Subscriptions.js +85 -0
- package/lib/Webhooks.js +3 -2
- package/lib/qranswers.js +10 -4
- package/package.json +2 -1
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# qranswers_module
|
|
2
|
+
QR-Answers API module
|
|
3
|
+
|
|
4
|
+
This module allows you to use nodejs to access the QR-Answers API.
|
|
5
|
+
|
|
6
|
+
## Webhooks
|
|
7
|
+
|
|
8
|
+
To use a webhook, create an endpoint (e.g. https://mydomain.com/qrhook)
|
|
9
|
+
In the app, go to Home -> Manage Plan -> Developer to retrieve your API key and Secret Key as well as input your endpoint.
|
|
10
|
+
For the webhook, you need to select which events you would like to receive from the Events to send section. Choose events from
|
|
11
|
+
the dropdown and you will see them appear in a list below. You may remove any events you no longer want to receive by
|
|
12
|
+
clicking the trash can next to the event and choosing Update Webhook.
|
|
13
|
+
|
|
14
|
+
To use this module:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
npm install qranswers
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Your webhook will need to collect the raw Body (a Buffer) of the request received. If you use express, you may add this to your file:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
npm install body-parser
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then, in your nodejs app:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
const bodyParser = require('body-parser')
|
|
30
|
+
|
|
31
|
+
app.use(
|
|
32
|
+
bodyParser.json({
|
|
33
|
+
verify: function(req, res, buf) {
|
|
34
|
+
req.rawBody = buf;
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Then when you receive the event via a post, you will need to retrieve the ```x-qr-signature``` header and verify the event with the ```qranswers.webhook.constructEvent()``` method.
|
|
41
|
+
The method will throw an error if the signature does not match or if the period between the request and the decoding is too long.
|
|
42
|
+
You should return a json response to the QR-Answers server like below.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
app.post('/qrhook', function(req, res) {
|
|
46
|
+
const sig = req.headers['x-qr-signature'];
|
|
47
|
+
let event
|
|
48
|
+
try {
|
|
49
|
+
event = qranswers.webhooks.constructEvent(req.rawBody, sig, endpointSecret)
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.log('Error', err);
|
|
52
|
+
res.json({error: `Webhook Error: ${err.message}`});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
switch (event.type) {
|
|
57
|
+
case 'vote.evResponseVote':
|
|
58
|
+
const data = event.data.object;
|
|
59
|
+
console.log('Received event:', event);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
res.json({success: 'post call succeed!', url: req.url, body: req.body})
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The QR-Answers server(s) will only try 3 times with backoff to deliver your event. The server determines whether to retry sending the event by examining the
|
|
68
|
+
{success: "xxx"} field in the returned JSON.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## API
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const {Amplify, API} = require('aws-amplify');
|
|
3
|
+
|
|
4
|
+
Amplify.configure({
|
|
5
|
+
'aws_appsync_graphqlEndpoint': "https://sy35eokcbzcrvjomxqrx3xgawy.appsync-api.us-east-1.amazonaws.com/graphql",
|
|
6
|
+
'aws_appsync_region': "us-east-1",
|
|
7
|
+
'aws_appsync_authenticationType': "AWS_LAMBDA",
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const onResponseByClientId = /* GraphQL */ `
|
|
11
|
+
subscription OnResponseByClientId($clientId: String!) {
|
|
12
|
+
onResponseByClientId(clientId: $clientId) {
|
|
13
|
+
clientId
|
|
14
|
+
projectId
|
|
15
|
+
baseId
|
|
16
|
+
answerId
|
|
17
|
+
count
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
const getClientIdFromApiKey = /* GraphQL */ `
|
|
22
|
+
query GetClientIdFromApiKey($apiKey: String!) {
|
|
23
|
+
getClientIdFromApiKey(apiKey: $apiKey) {
|
|
24
|
+
clientID
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const QRSubscription = {
|
|
30
|
+
setKey(key) {
|
|
31
|
+
this._apiKey = key;
|
|
32
|
+
},
|
|
33
|
+
async initialize() {
|
|
34
|
+
const apiKey = this._apiKey;
|
|
35
|
+
if (apiKey === undefined) {
|
|
36
|
+
throw new Error("You must provide your API key when establishing the module. const qranswers = require('qranswers')(apiKey); ");
|
|
37
|
+
}
|
|
38
|
+
if (this.clientId !== undefined) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
// We need to get our clientId from the server
|
|
42
|
+
try {
|
|
43
|
+
const qlResp = await API.graphql({
|
|
44
|
+
query: getClientIdFromApiKey,
|
|
45
|
+
variables: { apiKey: apiKey },
|
|
46
|
+
authToken: this._apiKey
|
|
47
|
+
});
|
|
48
|
+
this._clientId = qlResp.data.getClientIdFromApiKey.clientID;
|
|
49
|
+
return true;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.warn(err);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
subscribeToResponse(callBack, errCallBack) {
|
|
56
|
+
if (this._clientId === undefined) {
|
|
57
|
+
throw new Error('You must first call qranswers.subscriptions.initialize() before calling any subscription');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const subscription = API.graphql({
|
|
61
|
+
query: onResponseByClientId,
|
|
62
|
+
variables: { clientId: this._clientId },
|
|
63
|
+
authToken: this._apiKey
|
|
64
|
+
}
|
|
65
|
+
).subscribe({
|
|
66
|
+
next: ({ provider, value }) => {
|
|
67
|
+
const response = value.data.onResponseByClientId;
|
|
68
|
+
callBack(response);
|
|
69
|
+
},
|
|
70
|
+
error: (error) => {
|
|
71
|
+
console.warn(error)
|
|
72
|
+
if (errCallBack) {
|
|
73
|
+
errCallBack(error);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
return subscription;
|
|
78
|
+
},
|
|
79
|
+
unsubscribeToResponse(subscription) {
|
|
80
|
+
subscription.unsubscribe();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = QRSubscription;
|
|
85
|
+
//export default QRSubscription;
|
package/lib/Webhooks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const crypto = require('crypto');
|
|
2
|
+
//const crypto = require('crypto');
|
|
3
3
|
|
|
4
4
|
const Webhook = {
|
|
5
5
|
DEFAULT_TOLERANCE: 300000, // in ms
|
|
@@ -89,6 +89,7 @@ function parseHeader(header, scheme) {
|
|
|
89
89
|
signatures: [],
|
|
90
90
|
}
|
|
91
91
|
);
|
|
92
|
-
}
|
|
92
|
+
};
|
|
93
|
+
|
|
93
94
|
module.exports = Webhook;
|
|
94
95
|
|
package/lib/qranswers.js
CHANGED
|
@@ -24,10 +24,10 @@ function QRAnswers(key, config = {}) {
|
|
|
24
24
|
if (
|
|
25
25
|
props.protocol &&
|
|
26
26
|
props.protocol !== 'https' &&
|
|
27
|
-
(!props.host || /\.
|
|
27
|
+
(!props.host || /\.qr-answers\.com$/.test(props.host))
|
|
28
28
|
) {
|
|
29
29
|
throw new Error(
|
|
30
|
-
'The `https` protocol must be used when sending requests to `*.
|
|
30
|
+
'The `https` protocol must be used when sending requests to `*.qr-answers.com`'
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
const agent = props.httpAgent || null;
|
|
@@ -40,16 +40,22 @@ function QRAnswers(key, config = {}) {
|
|
|
40
40
|
version: props.apiVersion || DEFAULT_API_VERSION,
|
|
41
41
|
timeout: utils.validateInteger('timeout', props.timeout, DEFAULT_TIMEOUT),
|
|
42
42
|
};
|
|
43
|
+
this._setApiKey(key);
|
|
44
|
+
|
|
45
|
+
this.webhooks = require('./Webhooks');
|
|
46
|
+
this.subscriptions = require('./Subscriptions');
|
|
47
|
+
this.subscriptions.setKey(key);
|
|
43
48
|
|
|
44
49
|
}
|
|
45
50
|
QRAnswers.webhooks = require("./Webhooks");
|
|
51
|
+
QRAnswers.subscriptions = require("./Subscriptions");
|
|
46
52
|
QRAnswers.prototype = {
|
|
47
53
|
VERSION: null,
|
|
48
54
|
webhooks: null,
|
|
49
55
|
_api: null,
|
|
50
56
|
_setApiKey(key) {
|
|
51
57
|
if (key) {
|
|
52
|
-
this._setApiField('
|
|
58
|
+
this._setApiField('key', key);
|
|
53
59
|
}
|
|
54
60
|
},
|
|
55
61
|
_setApiField(key, value) {
|
|
@@ -91,7 +97,7 @@ QRAnswers.prototype = {
|
|
|
91
97
|
};
|
|
92
98
|
module.exports = QRAnswers;
|
|
93
99
|
// expose constructor as a named property to enable mocking with Sinon.JS
|
|
94
|
-
module.exports.
|
|
100
|
+
module.exports.QRAnswers = QRAnswers;
|
|
95
101
|
// Allow use with the TypeScript compiler without `esModuleInterop`.
|
|
96
102
|
// We may also want to add `Object.defineProperty(exports, "__esModule", {value: true});` in the future, so that Babel users will use the `default` version.
|
|
97
103
|
module.exports.default = QRAnswers;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qranswers",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Helper module to access QR-Answers API",
|
|
5
5
|
"main": "lib/qranswers.js",
|
|
6
6
|
"scripts": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/ggcespia/qranswers_module#readme",
|
|
25
25
|
"dependencies": {
|
|
26
|
+
"aws-amplify": "^5.3.11",
|
|
26
27
|
"crypto": "^1.0.1"
|
|
27
28
|
}
|
|
28
29
|
}
|