feat(js): Dynamic ActionCable URL (#35579)

* Failing test case

* feat: Dynamic Url Generation

Change createWebSocketURL to be a closure that allows url to be evaluated at the time the webSocket is established

* refactor: createWebSocketURL to Consumer, remove need for closure

Move initial call to createWebSocketURL in createConsumer

* docs: Add documentation for dynamic url and string args to createConsumer

Co-Authored-By: rmacklin <rmacklin@users.noreply.github.com>

[Ryan Castner, rmacklin]
This commit is contained in:
Ryan Castner 2019-03-31 13:41:12 -04:00 committed by Kasper Timm Hansen
parent ba4e74e1b7
commit 6d488a22d3
5 changed files with 79 additions and 32 deletions

View File

@ -28,6 +28,22 @@
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function() {
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, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var now = function now() {
return new Date().getTime();
};
@ -432,7 +448,7 @@
var Consumer = function() {
function Consumer(url) {
classCallCheck(this, Consumer);
this.url = url;
this._url = url;
this.subscriptions = new Subscriptions(this);
this.connection = new Connection(this);
}
@ -452,18 +468,14 @@
return this.connection.open();
}
};
createClass(Consumer, [ {
key: "url",
get: function get$$1() {
return createWebSocketURL(this._url);
}
} ]);
return Consumer;
}();
function createConsumer() {
var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getConfig("url") || INTERNAL.default_mount_path;
return new Consumer(createWebSocketURL(url));
}
function getConfig(name) {
var element = document.head.querySelector("meta[name='action-cable-" + name + "']");
if (element) {
return element.getAttribute("content");
}
}
function createWebSocketURL(url) {
var webSocketURL = typeof url === "function" ? url() : url;
if (webSocketURL && !/^wss?:/i.test(webSocketURL)) {
@ -476,6 +488,16 @@
return webSocketURL;
}
}
function createConsumer() {
var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getConfig("url") || INTERNAL.default_mount_path;
return new Consumer(url);
}
function getConfig(name) {
var element = document.head.querySelector("meta[name='action-cable-" + name + "']");
if (element) {
return element.getAttribute("content");
}
}
exports.Connection = Connection;
exports.ConnectionMonitor = ConnectionMonitor;
exports.Consumer = Consumer;
@ -484,9 +506,9 @@
exports.Subscriptions = Subscriptions;
exports.adapters = adapters;
exports.logger = logger;
exports.createWebSocketURL = createWebSocketURL;
exports.createConsumer = createConsumer;
exports.getConfig = getConfig;
exports.createWebSocketURL = createWebSocketURL;
Object.defineProperty(exports, "__esModule", {
value: true
});

View File

@ -29,11 +29,15 @@ import Subscriptions from "./subscriptions"
export default class Consumer {
constructor(url) {
this.url = url
this._url = url
this.subscriptions = new Subscriptions(this)
this.connection = new Connection(this)
}
get url() {
return createWebSocketURL(this._url)
}
send(data) {
return this.connection.send(data)
}
@ -52,3 +56,18 @@ export default class Consumer {
}
}
}
export function createWebSocketURL(url) {
const webSocketURL = typeof url === "function" ? url() : url
if (webSocketURL && !/^wss?:/i.test(webSocketURL)) {
const a = document.createElement("a")
a.href = webSocketURL
// Fix populating Location properties in IE. Otherwise, protocol will be blank.
a.href = a.href
a.protocol = a.protocol.replace("http", "ws")
return a.href
} else {
return webSocketURL
}
}

View File

@ -1,6 +1,6 @@
import Connection from "./connection"
import ConnectionMonitor from "./connection_monitor"
import Consumer from "./consumer"
import Consumer, { createWebSocketURL } from "./consumer"
import INTERNAL from "./internal"
import Subscription from "./subscription"
import Subscriptions from "./subscriptions"
@ -16,10 +16,11 @@ export {
Subscriptions,
adapters,
logger,
createWebSocketURL,
}
export function createConsumer(url = getConfig("url") || INTERNAL.default_mount_path) {
return new Consumer(createWebSocketURL(url))
return new Consumer(url)
}
export function getConfig(name) {
@ -28,18 +29,3 @@ export function getConfig(name) {
return element.getAttribute("content")
}
}
export function createWebSocketURL(url) {
const webSocketURL = typeof url === "function" ? url() : url
if (webSocketURL && !/^wss?:/i.test(webSocketURL)) {
const a = document.createElement("a")
a.href = webSocketURL
// Fix populating Location properties in IE. Otherwise, protocol will be blank.
a.href = a.href
a.protocol = a.protocol.replace("http", "ws")
return a.href
} else {
return webSocketURL
}
}

View File

@ -43,11 +43,14 @@ module("ActionCable", () => {
})
test("uses function to generate URL", assert => {
let dynamicURL = testURL
const generateURL = () => {
return testURL
return dynamicURL
}
dynamicURL = `${testURL}foo`
const consumer = ActionCable.createConsumer(generateURL)
assert.equal(consumer.url, testURL)
assert.equal(consumer.url, `${testURL}foo`)
})
})
})

View File

@ -190,6 +190,23 @@ This will ready a consumer that'll connect against `/cable` on your server by de
The connection won't be established until you've also specified at least one subscription
you're interested in having.
The consumer can optionally take an argument that specifies the url to connect to. This
can be a string, or a function that returns a string that will be called when the
WebSocket is opened.
```js
// Specify a different url to connect to
createConsumer('https://ws.example.com/cable')
// Use a function to dynamically generate the url
createConsumer(getWebSocketURL)
function getWebSocketURL {
const token = localStorage.get('auth-token')
return `https://ws.example.com/cable?token=${token}`
}
```
#### Subscriber
A consumer becomes a subscriber by creating a subscription to a given channel: