Skip to content

Commit f77c6c8

Browse files
authored
Sessions (#19)
* geenrate the session on the client via crypto.randomUUID * update readme * set the session before dispatching an event to ensure Hellotext.session returns the same value * update form_controller * add jest config files * add dedicated session model and move initialization code there * update code * add jsdoc for Response class * add jsdoc for Response class * update dist * 1.8.3
1 parent 99ad68a commit f77c6c8

21 files changed

+177
-192
lines changed

README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,18 @@ Short links redirections attaches a session identifier to the destination url as
8585

8686
### Get session
8787

88-
It is possible to obtain the current session by simply calling `Hellotext.session`.
88+
It is possible to obtain the current session by simply calling `Hellotext.session`. When the session is present in the cookies,
89+
the value stored in the cookies is returned. Otherwise, a new session is generated via [crypto.randomUUID()](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID).
90+
The session is kept in the client and not sent to Hellotext's servers until an event is tracked.
91+
92+
An event is tracked in the following cases
93+
94+
- Explicitly calling `Hellotext.track` method.
95+
- When a form is submitted and the form data is sent to Hellotext.
8996

9097
```javascript
91-
await Hellotext.session
92-
// Returns bBJn9vR15yPaYkWmR2QK0jopMeNxrA6l
98+
Hellotext.session
99+
// Returns da834c54-97fa-44ef-bafd-2bd4fec60636
93100
```
94101

95102
If the session has not been set yet, the result returned will be `undefined`.

__tests__/api/sessions_test.js

-12
This file was deleted.

__tests__/hellotext_test.js

-17
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ afterEach(() => {
2020
});
2121

2222
describe("when trying to call methods before initializing the class", () => {
23-
it("raises an error when Hellotext.session is called", () => {
24-
expect(() => Hellotext.session).toThrowError()
25-
});
26-
2723
it("raises an error when Hellotext.track is called", () => {
2824
expect(Hellotext.track("page.viewed")).rejects.toThrowError()
2925
});
@@ -196,23 +192,10 @@ describe("when hello_preview query parameter is present", () => {
196192
Hellotext.initialize(123)
197193
})
198194

199-
describe(".initialize", () => {
200-
it("does not attempt to set the session", () => {
201-
expect(getCookieValue("hello_session")).toEqual(undefined)
202-
});
203-
})
204-
205195
describe(".track", () => {
206196
it("returns a success response without interacting with the API", async () => {
207197
const response = await Hellotext.track("page.viewed")
208198
expect(response.succeeded).toEqual(true)
209199
});
210200
})
211201
})
212-
213-
describe('setSession', () => {
214-
it('sets the session', () => {
215-
Hellotext.setSession("12345")
216-
expect(Hellotext.session).toEqual("12345")
217-
})
218-
})

dist/hellotext.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jest.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
setupFiles: ['<rootDir>/jest.setup.js'], // Setup file to run before tests
3+
};

jest.setup.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
global.crypto.randomUUID = () => {
2+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
3+
const r = (Math.random() * 16) | 0;
4+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
5+
return v.toString(16);
6+
});
7+
};

lib/api/index.js

-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ Object.defineProperty(exports, "Response", {
1010
}
1111
});
1212
exports.default = void 0;
13-
var _sessions = _interopRequireDefault(require("./sessions"));
1413
var _businesses = _interopRequireDefault(require("./businesses"));
1514
var _events = _interopRequireDefault(require("./events"));
1615
var _forms = _interopRequireDefault(require("./forms"));
@@ -26,11 +25,6 @@ var API = /*#__PURE__*/function () {
2625
_classCallCheck(this, API);
2726
}
2827
_createClass(API, null, [{
29-
key: "sessions",
30-
value: function sessions(businessId) {
31-
return new _sessions.default(businessId);
32-
}
33-
}, {
3428
key: "businesses",
3529
get: function get() {
3630
return _businesses.default;

lib/api/response.js

+26
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototy
1515
var id = 0;
1616
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
1717
var _success = /*#__PURE__*/_classPrivateFieldLooseKey("success");
18+
/**
19+
* Response class
20+
* @class
21+
* @classdesc Represents a response from the API
22+
* @property {Boolean} succeeded
23+
* @property {Object} data
24+
*/
1825
var Response = /*#__PURE__*/function () {
1926
function Response(success, response) {
2027
_classCallCheck(this, Response);
@@ -25,11 +32,21 @@ var Response = /*#__PURE__*/function () {
2532
this.response = response;
2633
_classPrivateFieldLooseBase(this, _success)[_success] = success;
2734
}
35+
36+
/**
37+
* Get the response data
38+
* @returns {*}
39+
*/
2840
_createClass(Response, [{
2941
key: "data",
3042
get: function get() {
3143
return this.response;
3244
}
45+
46+
/**
47+
* Parse the response as JSON
48+
* @returns {Promise<*>}
49+
*/
3350
}, {
3451
key: "json",
3552
value: function () {
@@ -41,11 +58,20 @@ var Response = /*#__PURE__*/function () {
4158
}
4259
return json;
4360
}()
61+
/**
62+
* Has the request failed?
63+
* @returns {boolean}
64+
*/
4465
}, {
4566
key: "failed",
4667
get: function get() {
4768
return _classPrivateFieldLooseBase(this, _success)[_success] === false;
4869
}
70+
71+
/**
72+
* Has the request succeeded?
73+
* @returns {boolean}
74+
*/
4975
}, {
5076
key: "succeeded",
5177
get: function get() {

lib/api/sessions.js

-46
This file was deleted.

lib/controllers/form_controller.js

-3
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,6 @@ var _default = /*#__PURE__*/function (_Controller) {
7979
}
8080
this.buttonTarget.style.display = 'none';
8181
this.element.querySelectorAll('input').forEach(input => input.disabled = true);
82-
if (!data.identified) {
83-
_hellotext.default.setSession(data.session);
84-
}
8582
this.completed();
8683
});
8784
function submit(_x) {

lib/hellotext.js

+6-38
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ Object.defineProperty(exports, "__esModule", {
55
});
66
exports.default = void 0;
77
var _core = require("./core");
8-
var _api = _interopRequireDefault(require("./api"));
8+
var _api = _interopRequireWildcard(require("./api"));
99
var _models = require("./models");
1010
var _errors = require("./errors");
11-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
12+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
1213
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
1314
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
1415
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -24,7 +25,6 @@ var id = 0;
2425
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
2526
var _session = /*#__PURE__*/_classPrivateFieldLooseKey("session");
2627
var _query = /*#__PURE__*/_classPrivateFieldLooseKey("query");
27-
var _mintAnonymousSession = /*#__PURE__*/_classPrivateFieldLooseKey("mintAnonymousSession");
2828
var Hellotext = /*#__PURE__*/function () {
2929
function Hellotext() {
3030
_classCallCheck(this, Hellotext);
@@ -39,24 +39,10 @@ var Hellotext = /*#__PURE__*/function () {
3939
*/
4040
function initialize(business, config) {
4141
_core.Configuration.assign(config);
42+
_models.Session.initialize();
4243
_classPrivateFieldLooseBase(this, _query)[_query] = new _models.Query();
4344
this.business = new _models.Business(business);
4445
this.forms = new _models.FormCollection();
45-
if (_classPrivateFieldLooseBase(this, _query)[_query].inPreviewMode) return;
46-
if (_core.Configuration.session) {
47-
_classPrivateFieldLooseBase(this, _session)[_session] = _core.Configuration.session;
48-
} else if (_classPrivateFieldLooseBase(this, _query)[_query].session) {
49-
_classPrivateFieldLooseBase(this, _session)[_session] = _models.Cookies.set('hello_session', _classPrivateFieldLooseBase(this, _query)[_query].session);
50-
} else if (_core.Configuration.autoGenerateSession) {
51-
_classPrivateFieldLooseBase(this, _mintAnonymousSession)[_mintAnonymousSession]().then(response => {
52-
_classPrivateFieldLooseBase(this, _session)[_session] = _models.Cookies.set('hello_session', response.id);
53-
});
54-
}
55-
}
56-
}, {
57-
key: "setSession",
58-
value: function setSession(value) {
59-
_classPrivateFieldLooseBase(this, _session)[_session] = _models.Cookies.set('hello_session', value);
6046
}
6147

6248
/**
@@ -121,10 +107,7 @@ var Hellotext = /*#__PURE__*/function () {
121107
}, {
122108
key: "session",
123109
get: function get() {
124-
if (this.notInitialized) {
125-
throw new _errors.NotInitializedError();
126-
}
127-
return _classPrivateFieldLooseBase(this, _session)[_session];
110+
return _models.Session.session;
128111
}
129112

130113
/**
@@ -134,7 +117,7 @@ var Hellotext = /*#__PURE__*/function () {
134117
}, {
135118
key: "isInitialized",
136119
get: function get() {
137-
return _classPrivateFieldLooseBase(this, _session)[_session] !== undefined;
120+
return _models.Session.session !== undefined;
138121
}
139122

140123
// private
@@ -158,21 +141,6 @@ var Hellotext = /*#__PURE__*/function () {
158141
}]);
159142
return Hellotext;
160143
}();
161-
function _mintAnonymousSession2() {
162-
return _mintAnonymousSession3.apply(this, arguments);
163-
}
164-
function _mintAnonymousSession3() {
165-
_mintAnonymousSession3 = _asyncToGenerator(function* () {
166-
if (this.notInitialized) {
167-
throw new _errors.NotInitializedError();
168-
}
169-
return _api.default.sessions(this.business.id).create();
170-
});
171-
return _mintAnonymousSession3.apply(this, arguments);
172-
}
173-
Object.defineProperty(Hellotext, _mintAnonymousSession, {
174-
value: _mintAnonymousSession2
175-
});
176144
Object.defineProperty(Hellotext, _session, {
177145
writable: true,
178146
value: void 0

lib/models/index.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,15 @@ Object.defineProperty(exports, "Query", {
3333
return _query.Query;
3434
}
3535
});
36+
Object.defineProperty(exports, "Session", {
37+
enumerable: true,
38+
get: function get() {
39+
return _session.Session;
40+
}
41+
});
3642
var _business = require("./business");
3743
var _form = require("./form");
3844
var _query = require("./query");
3945
var _cookies = require("./cookies");
40-
var _form_collection = require("./form_collection");
46+
var _form_collection = require("./form_collection");
47+
var _session = require("./session");

lib/models/session.js

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"use strict";
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
exports.Session = void 0;
7+
var _core = require("../core");
8+
var _index = require("./index");
9+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
10+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
11+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
12+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
13+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
14+
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
15+
var id = 0;
16+
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
17+
var _session = /*#__PURE__*/_classPrivateFieldLooseKey("session");
18+
var _query = /*#__PURE__*/_classPrivateFieldLooseKey("query");
19+
var Session = /*#__PURE__*/function () {
20+
function Session() {
21+
_classCallCheck(this, Session);
22+
}
23+
_createClass(Session, null, [{
24+
key: "session",
25+
get: function get() {
26+
return _classPrivateFieldLooseBase(this, _session)[_session];
27+
},
28+
set: function set(value) {
29+
_classPrivateFieldLooseBase(this, _session)[_session] = value;
30+
_index.Cookies.set('hello_session', value);
31+
}
32+
}, {
33+
key: "initialize",
34+
value: function initialize() {
35+
_classPrivateFieldLooseBase(this, _query)[_query] = new _index.Query();
36+
if (_core.Configuration.session) {
37+
this.session = _core.Configuration.session;
38+
} else if (_classPrivateFieldLooseBase(this, _query)[_query].session) {
39+
this.session = _classPrivateFieldLooseBase(this, _query)[_query].session;
40+
} else if (_core.Configuration.autoGenerateSession) {
41+
this.session = crypto.randomUUID();
42+
}
43+
}
44+
}]);
45+
return Session;
46+
}();
47+
exports.Session = Session;
48+
Object.defineProperty(Session, _session, {
49+
writable: true,
50+
value: void 0
51+
});
52+
Object.defineProperty(Session, _query, {
53+
writable: true,
54+
value: void 0
55+
});

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hellotext/hellotext",
3-
"version": "1.8.2",
3+
"version": "1.8.3",
44
"description": "Hellotext JavaScript Client",
55
"source": "src/index.js",
66
"main": "lib/index.js",

0 commit comments

Comments
 (0)