Skip to content

Instantly share code, notes, and snippets.

@alexstrat
Last active November 16, 2018 15:43
Show Gist options
  • Save alexstrat/a1384d124c1d1c27f193054cd77cca9a to your computer and use it in GitHub Desktop.
Save alexstrat/a1384d124c1d1c27f193054cd77cca9a to your computer and use it in GitHub Desktop.
// StorageService.proto
service StorageService {
  rpc readKey(ReadRequest) returns (ReadReply) {}
}

message ReadRequest {
  string type;
  string key;
}

message ReadReply {
  string res;
}
// client.ts
import { ipcRenderer } from 'electron';
import { createClientMessageChannelInRenderer } from 'protobufjs-electron-ipc-message-channel';
import { createStub } from 'protobufjs-utils';

// StorageService.proto is compiled with protobuff to `StorageService.ts`
import StorageService from './StorageService'

// ipcChannel implememts ClientMessageChannel and use electron ipc as wire
// can use ipc multiplexing
const ipcChannel = createClientMessageChannelInRenderer(ipcRenderer);

// we create a stub from StorageService and use ipcChannel to wire requests
const storageStub = createStub(ipcChannel, StorageService);

const { res } = await storageService.readKey({type: 'sync', key: 'key-123'})
// server.ts
import { webContents } from 'electron';
import { createServerMessageChannelFromWebContents } from  'protobufjs-electron-ipc-message-channel';
import { ServiceImplCollection } from 'protobufjs-utils';

// StorageService.proto is compiled with protobuff to `StorageService.ts`
import StorageService from './StorageService'

// inspired from https://grpc.io/docs/tutorials/basic/c.html
class StorageServiceImpl implements ServiceImpl<StorageService, Context> {
  $service: StorageService;

  constructor(db) {
    this.db = db;
  }

  async readKey(readRequest, context: Context) {
    // context can contain elements relative to underlying communication channel: webContents incase of electron, etc.. 
    const val = await this.db.read(readRequest.key);
    return { res: val }
  }
}


const collection = new ServiceImplCollection();

const storageServiceImpl = new StorageServiceImpl(db);
collection.registerService(StorageServiceImpl);

// let's take a webContents that corresponds to the renderer used in client.ts
const wc = webContents.fromId(1);

// ipcChannel implememts ServerMessageChannel and use electron ipc as wire
const ipcChannel = createServerMessageChannelFromWebContents(wc);

ipcChannel.on('new-stream', (messageStream: Duplex<Message, Message>) => {
  collection.submit(messageStream, { channel: ipcChannel, webContens: wc }: Context);
})

Typings

import { Stream, Duplex } from 'stream';
import { EventEmitter } from 'events';
import { Message } from 'protobufjs'

interface AbstractMessageChannel extends EventEmitter {
  on(event: 'close', listener: () => void): this;
}

interface ClientMessageChannel extends AbstractMessageChannel {
  createStream(): Duplex<Message, Message>;
};

interface ServerMessageChannel extends AbstractMessageChannel {
  on(event: 'new-stream', listener: (messageStram: Duplex<Message, Message>, streamId: number) => void): this
};

interface DuplexMessageChannel extends ClientMessageChannel, ServerMessageChannel {
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment