Skip to content
This repository has been archived by the owner on Aug 31, 2021. It is now read-only.

Commit

Permalink
Merge pull request #11 from 8thlight/add_postgraphile_subscription
Browse files Browse the repository at this point in the history
Add Postgraphile subscription exposure for price feed
  • Loading branch information
TakaGoto authored Sep 17, 2018
2 parents b332dbf + adbdd28 commit 4c16dc1
Show file tree
Hide file tree
Showing 29 changed files with 5,336 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea
.vscode
test_data_dir/
contracts/*
environments/*.toml
Expand All @@ -7,3 +8,6 @@ vagrant*.sh
.vagrant
test_scripts/
vulcanizedb
postgraphile/build/
postgraphile/node_modules/
postgraphile/package-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TRIGGER notify_pricefeeds ON maker.price_feeds;
13 changes: 13 additions & 0 deletions db/migrations/20180911215603_add_price_feed_trigger.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CREATE OR REPLACE FUNCTION notify_pricefeed() RETURNS trigger AS $$
BEGIN
PERFORM pg_notify(
CAST('postgraphile:price_feed' AS text),
row_to_json(NEW)::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER notify_pricefeeds
AFTER INSERT ON maker.price_feeds
FOR EACH ROW
EXECUTE PROCEDURE notify_pricefeed();
23 changes: 23 additions & 0 deletions db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';


--
-- Name: notify_pricefeed(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.notify_pricefeed() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
PERFORM pg_notify(
CAST('postgraphile:price_feed' AS text),
row_to_json(NEW)::text);
RETURN NEW;
END;
$$;


SET default_tablespace = '';

SET default_with_oids = false;
Expand Down Expand Up @@ -1482,6 +1498,13 @@ CREATE INDEX tx_from_index ON public.transactions USING btree (tx_from);
CREATE INDEX tx_to_index ON public.transactions USING btree (tx_to);


--
-- Name: price_feeds notify_pricefeeds; Type: TRIGGER; Schema: maker; Owner: -
--

CREATE TRIGGER notify_pricefeeds AFTER INSERT ON maker.price_feeds FOR EACH ROW EXECUTE PROCEDURE public.notify_pricefeed();


--
-- Name: bite bite_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: -
--
Expand Down
19 changes: 19 additions & 0 deletions postgraphile/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Vulcanize GraphQL API

This application utilizes Postgraphile to expose GraphQL endpoints for exposure of the varied data that VulcanizeDB tracks.

## Building

*This application assumes the use of the [Yarn package manager](https://yarnpkg.com/en/). The use of npm may produce unexpected results.*

Install dependencies with `yarn` and execute `yarn build`. The bundle produced by Webpack will be present in `build/dist/`.

This application currently uses the Postgraphile supporter plugin. This plugin is present in the `vendor/` directory and is copied to `node_modules/` after installation of packages. It is a fresh checkout of the plugin as of August 31st, 2018.

## Running

Provide the built bundle to node as a runnable script: `node ./build/dist/vulcanize-postgraphile-server.js`

## Testing

Tests are executed via Jasmine with a console reporter via the `yarn test` task.
56 changes: 56 additions & 0 deletions postgraphile/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "vulcanizedb",
"version": "1.0.0",
"description": "[![Join the chat at https://gitter.im/vulcanizeio/VulcanizeDB](https://badges.gitter.im/vulcanizeio/VulcanizeDB.svg)](https://gitter.im/vulcanizeio/VulcanizeDB?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)",
"scripts": {
"build": "rm -rf ./build/dist && webpack --config=./webpack.config.js",
"lint": "tslint --project ./tsconfig.json --config ./tslint.json",
"postinstall": "rm -rf node_modules/@graphile && mkdir node_modules/@graphile && cp -R ./vendor/postgraphile-supporter/ ./node_modules/@graphile/plugin-supporter/",
"start": "npm run build && node ./build/dist/vulcanize-postgraphile-server.js",
"test": "rm -rf ./build/spec && tsc --build ./tsconfig.test.json && jasmine --config=./spec/support/jasmine.json",
"test:ci": "npm run lint && npm run test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vulcanize/vulcanizedb.git"
},
"author": "Vulcanize, Inc.",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/vulcanize/vulcanizedb/issues"
},
"homepage": "https://github.com/vulcanize/vulcanizedb",
"dependencies": {
"express-session": "1.15.6",
"lodash": "4.17.10",
"passport": "0.4.0",
"pg-native": "3.0.0",
"postgraphile": "4.0.0-rc.4",
"graphql-subscriptions": "0.5.8",
"subscriptions-transport-ws": "0.9.14",
"toml": "2.3.3",
"pg": "6.4.2"
},
"devDependencies": {
"@types/graphql": "^0.13.4",
"@types/express": "4.16.0",
"@types/express-session": "1.15.10",
"@types/jasmine": "2.8.8",
"@types/lodash": "4.14.116",
"@types/node": "10.9.3",
"@types/passport": "0.4.6",
"awesome-typescript-loader": "5.2.0",
"jasmine": "3.2.0",
"jasmine-ts-console-reporter": "3.1.1",
"source-map-loader": "0.2.4",
"tslint": "5.11.0",
"tslint-eslint-rules": "5.4.0",
"typescript": "3.0.1",
"webpack": "4.17.1",
"webpack-cli": "3.1.0",
"webpack-dev-server": "3.1.6"
},
"resolutions": {
"pg": "6.4.2"
}
}
78 changes: 78 additions & 0 deletions postgraphile/spec/config/parse.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { parseConfig } from '../../src/config/parse';

describe('parseConfig', () => {
let configPath: string;
let tomlContents: string;
let parsedToml: object;
let readCallback: jasmine.Spy;
let tomlParseCallback: jasmine.Spy;

beforeEach(() => {
configPath = '/example/config/path.toml';
tomlContents = `[database]\nname = 'example_database'\n `
+ `hostname = 'example.com'\nport = 1234`;

parsedToml = {
database: {
hostname: 'example.com',
name: 'example_database',
port: '1234',
user: 'user',
password: 'password'
}
};

readCallback = jasmine.createSpy('readCallback');
readCallback.and.returnValue(tomlContents);

tomlParseCallback = jasmine.createSpy('tomlParseCallback');
tomlParseCallback.and.returnValue(parsedToml);
});

it('provides the database host', () => {
const databaseConfig = parseConfig(
readCallback, tomlParseCallback, configPath);

expect(databaseConfig.host).toEqual('postgres://user:[email protected]:1234');
});

it('provides the database name', () => {
const databaseConfig = parseConfig(
readCallback, tomlParseCallback, configPath);

expect(databaseConfig.database).toEqual('example_database');
});

it('handles a missing config path', () => {
const failingCall = () =>
parseConfig(readCallback, tomlParseCallback, '');

tomlParseCallback.and.returnValue({
database: { hostname: 'example.com', name: 'example_database' }
});

expect(failingCall).toThrow();
});

it('handles a missing database host', () => {
const failingCall = () =>
parseConfig(readCallback, tomlParseCallback, configPath);

tomlParseCallback.and.returnValue({
database: { hostname: '', name: 'example_database' }
});

expect(failingCall).toThrow();
});

it('handles a missing database name', () => {
const failingCall = () =>
parseConfig(readCallback, tomlParseCallback, configPath);

tomlParseCallback.and.returnValue({
database: { hostname: 'example.com', name: '', port: '1234' }
});

expect(failingCall).toThrow();
});
});
7 changes: 7 additions & 0 deletions postgraphile/spec/helpers/ts_console.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// NOTE ([email protected]) This file helps Jasmine
// comprehend TS sourcemaps by installing a reporter
// specifically for TypeScript
const TSConsoleReporter = require('jasmine-ts-console-reporter');

jasmine.getEnv().clearReporters();
jasmine.getEnv().addReporter(new TSConsoleReporter());
105 changes: 105 additions & 0 deletions postgraphile/spec/server/config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { PassportStatic } from 'passport';

import { PostgraphileMiddleware } from '../../src/adapters/postgraphile';
import { buildServerConfig } from '../../src/server/config';

import {
DatabaseConfig,
ServerConfig,
ServerUtilities
} from '../../src/server/interface';

describe('buildServerConfig', () => {
let configParser: jasmine.Spy;
let postgraphileMiddleware: PostgraphileMiddleware;
let expressSessionHandler: jasmine.Spy;
let passportInitializer: jasmine.Spy;
let passportSessionHandler: jasmine.Spy;
let serverConfig: ServerConfig;

let serverUtilities: ServerUtilities;
let databaseConfig: DatabaseConfig;

beforeEach(() => {
databaseConfig = { host: 'example.com', database: 'example_database' };

postgraphileMiddleware = jasmine
.createSpyObj<PostgraphileMiddleware>(['call']),

serverUtilities = {
pluginHook: jasmine.createSpy('pluginHook'),
enableSubscriptions: jasmine.createSpy('enableSubscriptions'),
express: jasmine.createSpy('express'),
expressSession: jasmine.createSpy('expressSession'),
httpServerFactory: jasmine.createSpy('httpServerFactory'),
passport: jasmine.createSpyObj<PassportStatic>(['initialize', 'session']),
postgraphile: jasmine.createSpy('postgraphile')
};

const rawConfig: object = { exampleOption: 'example value' };

configParser = jasmine.createSpy('configParser');
configParser.and.returnValue(rawConfig);

expressSessionHandler = jasmine.createSpy('expressSessionHandler');
passportInitializer = jasmine.createSpy('passportInitializer');
passportSessionHandler = jasmine.createSpy('passportSessionHandler');

(serverUtilities.postgraphile as jasmine.Spy)
.and.returnValue(postgraphileMiddleware);
(serverUtilities.expressSession as jasmine.Spy)
.and.returnValue(expressSessionHandler);
(serverUtilities.passport.initialize as jasmine.Spy)
.and.returnValue(passportInitializer);
(serverUtilities.passport.session as jasmine.Spy)
.and.returnValue(passportSessionHandler);

serverConfig = buildServerConfig(
serverUtilities, databaseConfig, undefined);
});

it('provides the Postgraphile options', () => {
expect(serverConfig.options).not.toBeNull();
});

it('enables simple subscriptions', () => {
expect(serverConfig.options.simpleSubscriptions).toBe(true);
});

it('it adds the express session handler as the first middleware', () => {
expect(serverConfig.options.webSocketMiddlewares[0])
.toBe(expressSessionHandler);
});

it('it adds the passport initializer as the second middleware', () => {
expect(serverConfig.options.webSocketMiddlewares[1])
.toBe(passportInitializer);
});

it('it adds the passport session handler as the third middleware', () => {
expect(serverConfig.options.webSocketMiddlewares[2])
.toBe(passportSessionHandler);
});

it('provides the database config to Postgraphile', () => {
expect(serverUtilities.postgraphile).toHaveBeenCalledWith(
`${databaseConfig.host}/${databaseConfig.database}`,
["public", "maker"],
jasmine.any(Object));
});

it('provides the Postgraphile middleware', () => {
expect(serverConfig.middleware).toBe(postgraphileMiddleware);
});

it('sets the default server port', () => {
expect(serverConfig.port).toEqual(3000);
});

it('sets an explicit server port', () => {
const serverConfigWithPort = buildServerConfig(
serverUtilities, databaseConfig, '1234');

expect(serverConfigWithPort.port).toEqual(1234);
});
});
Loading

0 comments on commit 4c16dc1

Please sign in to comment.