Cycle.js - Драйвер - PhoenixJS (веб-сокеты)
В настоящее время у нас есть VueJS
приложение, и я смотрю на его перенос в Cycle.js (первый крупный проект).
Я понимаю, что в Cycle.JS у нас есть SI и SO для драйверов (используя adapt()); Естественно, реализация WebSocket подходит для этого, поскольку имеет эффекты как чтения, так и записи.
Мы используем Phoenix (Elixir) в качестве нашего бэкенда, используя каналы для мягкой коммуникации в реальном времени. Наша клиентская библиотека WS - это Phoenix здесь https://www.npmjs.com/package/phoenix.
Пример на Cycle.js.org идеально подходит, если вы знаете, как подключиться.
В нашем случае мы аутентифицируемся, используя конечную точку REST, которая возвращает токен (JWT), который используется для инициализации WebSocket (параметр токена). Этот токен нельзя просто передать в драйвер, так как драйвер инициализируется при запуске приложения Cycle.js.
Пример (не фактический код) того, что мы имеем сейчас (в нашем приложении VueJS):
// Code ommited for brevity
socketHandler = new vueInstance.$phoenix.Socket(FQDN, {
_token: token
});
socketHandler.onOpen(() => VueBus.$emit('SOCKET_OPEN'));
//...... Vue component (example)
VueBus.$on('SOCKET_OPEN', function () {
let chan = VueStore.socketHandler.channel('PRIV_CHANNEL', {
_token: token
});
chan.join()
.receive('ok', () => {
//... code
})
})
Выше приведен пример, у нас есть Vuex
хранить глобальное состояние (сокет и т. д.), централизованную шину сообщений (приложение Vue) для обмена данными между компонентами и настройками канала, которые поступают из экземпляра Phoenix Socket.
Наша настройка канала основана на аутентифицированном соединении Socket, которое требует самой аутентификации для подключения к этому конкретному каналу.
Вопрос в том, возможно ли это с Cycle.js?
- Инициализируйте соединение WebSocket с параметрами токена из вызова REST (ответ токена JWT) - мы реализовали это частично
- Создавать каналы на основе этого сокета и токена (канал транслируется из драйвера?)
- Доступ к нескольким канальным потокам (я предполагаю, что это может работать как sources.HTTP.select (CATEGORY))
У нас есть зависимость 1: N, которая, я не уверен, возможна с драйверами.
Заранее спасибо,
Обновление @ 17/12/2018
По сути, я пытаюсь подражать следующему (из Cycle.js.org):
Драйвер принимает приемник для выполнения эффектов записи (отправка сообщений по определенным каналам), но также может возвращать источник; это означает, что есть два потока, которые являются асинхронными? Это означает, что создание сокета во время выполнения может привести к тому, что один поток получит доступ к "сокету" до его создания; пожалуйста, смотрите комментарии во фрагменте ниже.
import {adapt} from '@cycle/run/lib/adapt';
function makeSockDriver(peerId) {
// This socket may be created at an unknown period
//let socket = new Sock(peerId);
let socket = undefined;
// Sending is perfect
function sockDriver(sink$) {
sink$.addListener({
next: listener => {
sink$.addListener({
next: ({ channel, data }) => {
if(channel === 'OPEN_SOCKET' && socket === null) {
token = data;
// Initialising the socket
socket = new phoenix.Socket(FQDN, { token });
socketHandler.onOpen(() => listener.next({
channel: 'SOCKET_OPEN'
}));
} else {
if(channels[channel] === undefined) {
channels[channel] = new Channel(channel, { token });
}
channels[channel].join()
.receive('ok', () => {
sendData(data);
});
}
}
});
},
error: () => {},
complete: () => {},
});
const source$ = xs.create({
start: listener => {
sock.onReceive(function (msg) {
// There is no guarantee that "socket" is defined here, as this may fire before the socket is actually created
socket.on('some_event'); // undefined
// This works however because a call has been placed back onto the browser stack which probably gives the other blocking thread chance to write to the local stack variable "socket". But this is far from ideal
setTimeout(() => socket.on('some_event'));
});
},
stop: () => {},
});
return adapt(source$);
}
return sockDriver;
}
Ян ван Брюгге, предоставленный вами солутон идеален (спасибо), за исключением того, что у меня возникли проблемы с ответной частью. Пожалуйста, смотрите выше пример.
Например, что я пытаюсь достичь, это что-то вроде этого:
// login component
return {
DOM: ...
WS: xs.of({
channel: "OPEN_CHANNEL",
data: {
_token: 'Bearer 123'
}
})
}
//////////////////////////////////////
// Some authenticated component
// Intent
const intent$ = sources.WS.select(CHANNEL_NAME).startWith(null)
// Model
const model$ = intent$.map(resp => {
if (resp.some_response !== undefined) {
return {...}; // some model
}
return resp;
})
return {
DOM: model$.map(resp => {
// Use response from websocket to create UI of some sort
})
}
1 ответ
Во -первых, да, это возможно с драйвером, и мое предложение приведет к тому, что драйвер будет похож на драйвер HTTP.
Прежде всего, чтобы иметь некоторый грубый псевдокод, где я могу объяснить все, я мог бы неправильно понять части вашего вопроса, так что это может быть неправильно.
interface WebsocketMessage {
channel: string;
data: any;
}
function makeWebSocketDriver() {
let socket = null;
let token = null;
let channels = {}
return function websocketDriver(sink$: Stream<WebsocketMessage> {
return xs.create({
start: listener => {
sink$.addListener({
next: ({ channel, data }) => {
if(channel === 'OPEN_SOCKET' && socket === null) {
token = data;
socket = new phoenix.Socket(FQDN, { token });
socketHandler.onOpen(() => listener.next({
channel: 'SOCKET_OPEN'
}));
} else {
if(channels[channel] === undefined) {
channels[channel] = new Channel(channel, { token });
}
channels[channel].join()
.receive('ok', () => {
sendData(data);
});
}
}
});
}
});
};
}
Это была бы грубая структура такого водителя. Вы видите, что он ждет сообщения с токеном, а затем открывает сокет. Он также отслеживает открытые каналы и отправляет / получает те, которые основаны на категории сообщения. Этот метод просто требует, чтобы все каналы имели уникальные имена, я не уверен, как работает протокол вашего канала в этом отношении или что вы хотите в частности.
Я надеюсь, что этого достаточно, чтобы вы начали, если вы уточнить API канала отправки / получения и сокета, я мог бы помочь больше. Вы также всегда можете задать вопросы в нашем канале Gitter