Add rates GQL query to return rates from payment rates config

This commit is contained in:
Nabarun 2023-08-02 15:46:11 +05:30
parent 47d4b667f4
commit 78cd09002f
5 changed files with 179 additions and 33 deletions

View File

@ -5,16 +5,19 @@
"license": "AGPL-3.0",
"dependencies": {
"@apollo/utils.keyvaluecache": "^1.0.1",
"@cerc-io/nitro-client": "^0.1.5",
"@cerc-io/nitro-client": "^0.1.6",
"@cerc-io/solidity-mapper": "^0.2.50",
"@cerc-io/ts-channel": "1.0.3-ts-nitro-0.1.1",
"@ethersproject/providers": "^5.4.4",
"@graphql-tools/graphql-file-loader": "^8.0.0",
"@graphql-tools/load": "^8.0.0",
"@graphql-tools/schema": "^9.0.10",
"@graphql-tools/utils": "^9.1.1",
"@ipld/dag-cbor": "^6.0.12",
"apollo-server-core": "^3.11.1",
"apollo-server-express": "^3.11.1",
"apollo-server-plugin-response-cache": "^3.8.1",
"apollo-type-bigint": "^0.1.3",
"debug": "^4.3.1",
"decimal.js": "^10.3.1",
"ethers": "^5.4.4",

View File

@ -0,0 +1,16 @@
scalar BigInt
enum RateType {
QUERY
MUTATION
}
type RateInfo {
type: RateType!
name: String!
amount: BigInt!
}
type Query {
_rates_: [RateInfo!]!
}

View File

@ -3,18 +3,22 @@ import { LRUCache } from 'lru-cache';
import { FieldNode } from 'graphql';
import { ApolloServerPlugin, GraphQLResponse, GraphQLRequestContext } from 'apollo-server-plugin-base';
import { Response as HTTPResponse } from 'apollo-server-env';
import ApolloBigInt from 'apollo-type-bigint';
import Channel from '@cerc-io/ts-channel';
import type { ReadWriteChannel } from '@cerc-io/ts-channel';
import type { Client, Voucher } from '@cerc-io/nitro-client';
import { utils as nitroUtils, ChannelStatus } from '@cerc-io/nitro-client';
import { IResolvers } from '@graphql-tools/utils';
import { BaseRatesConfig, PaymentsConfig } from './config';
import { gqlQueryCount, gqlTotalQueryCount } from './gql-metrics';
const log = debug('laconic:payments');
const IntrospectionQuery = 'IntrospectionQuery';
const IntrospectionQuerySelection = '__schema';
const INTROSPECTION_QUERY = 'IntrospectionQuery';
const INTROSPECTION_QUERY_SELECTION = '__schema';
const RATES_QUERY_SELECTION = '_rates_';
const PAYMENT_HEADER_KEY = 'x-payment';
const PAYMENT_HEADER_REGEX = /vhash:(.*),vsig:(.*)/;
@ -49,6 +53,17 @@ interface Payment {
amount: bigint;
}
enum RateType {
Query = 'QUERY',
Mutation = 'MUTATION'
}
interface RateInfo {
type: RateType;
name: string;
amount: bigint;
}
export class PaymentsManager {
clientAddress?: string;
@ -83,7 +98,7 @@ export class PaymentsManager {
}
get freeQueriesList (): string[] {
return this.ratesConfig.freeQueriesList ?? DEFAULT_FREE_QUERIES_LIST;
return [RATES_QUERY_SELECTION, ...(this.ratesConfig.freeQueriesList ?? DEFAULT_FREE_QUERIES_LIST)];
}
get queryRates (): { [key: string]: string } {
@ -94,6 +109,35 @@ export class PaymentsManager {
return this.ratesConfig.mutations ?? {};
}
getResolvers (): IResolvers {
return {
BigInt: new ApolloBigInt('bigInt'),
Query: {
_rates_: async (): Promise<RateInfo[]> => {
log('_rates_');
gqlTotalQueryCount.inc(1);
gqlQueryCount.labels('_rates_').inc(1);
const queryRates = this.queryRates;
const rateInfos = Object.entries(queryRates).map(([name, amount]) => ({
type: RateType.Query,
name,
amount: BigInt(amount)
}));
const mutationRates = this.mutationRates;
Object.entries(mutationRates).forEach(([name, amount]) => rateInfos.push({
type: RateType.Mutation,
name,
amount: BigInt(amount)
}));
return rateInfos;
}
}
};
}
async subscribeToVouchers (client: Client): Promise<void> {
this.clientAddress = client.address;
@ -291,15 +335,16 @@ export const paymentsPlugin = (paymentsManager?: PaymentsManager): ApolloServerP
// Continue if it's an introspection query for schema
// (made by ApolloServer playground / default landing page)
if (
requestContext.operationName === IntrospectionQuery &&
requestContext.operationName === INTROSPECTION_QUERY &&
querySelections && querySelections.length === 1 &&
querySelections[0] === IntrospectionQuerySelection
querySelections[0] === INTROSPECTION_QUERY_SELECTION
) {
return null;
}
const paymentHeader = requestContext.request.http?.headers.get(PAYMENT_HEADER_KEY);
if (paymentHeader == null) {
// TODO: Make payment header optional and check only for rate configured queries in loop below
return {
errors: [{ message: ERR_HEADER_MISSING }],
http: new HTTPResponse(undefined, {

View File

@ -1,3 +1,4 @@
import path from 'path';
import { Application } from 'express';
import { ApolloServer } from 'apollo-server-express';
import { createServer } from 'http';
@ -6,11 +7,13 @@ import { useServer } from 'graphql-ws/lib/use/ws';
import { ApolloServerPluginDrainHttpServer, ApolloServerPluginLandingPageLocalDefault } from 'apollo-server-core';
import debug from 'debug';
import responseCachePlugin from 'apollo-server-plugin-response-cache';
import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache';
import queue from 'express-queue';
import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache';
import { TypeSource } from '@graphql-tools/utils';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { makeExecutableSchema, addResolversToSchema, mergeSchemas } from '@graphql-tools/schema';
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
import { loadSchema } from '@graphql-tools/load';
import { DEFAULT_MAX_GQL_CACHE_SIZE } from './constants';
import { ServerConfig } from './config';
@ -33,7 +36,19 @@ export const createAndStartServer = async (
const httpServer = createServer(app);
// Create the schema
const schema = makeExecutableSchema({ typeDefs, resolvers });
let schema = makeExecutableSchema({ typeDefs, resolvers });
if (paymentsManager) {
let paymentsSchema = await loadSchema(path.join(__dirname, 'payments-schema.gql'), {
loaders: [new GraphQLFileLoader()]
});
const resolvers = paymentsManager.getResolvers();
paymentsSchema = addResolversToSchema({ schema: paymentsSchema, resolvers });
schema = mergeSchemas({
schemas: [schema, paymentsSchema]
});
}
// Create our WebSocket server using the HTTP server we just set up.
const wsServer = new WebSocketServer({

115
yarn.lock
View File

@ -350,13 +350,13 @@
wherearewe "^2.0.0"
xsalsa20 "^1.1.0"
"@cerc-io/nitro-client@^0.1.5":
version "0.1.5"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-client/-/0.1.5/nitro-client-0.1.5.tgz#43152a8482b53431c35018064cc70031b759895b"
integrity sha512-px/7IgOv1m+DWskJPQ4DUyX84MZHOYMEPN3iNK9uPf+TjyQQm0w2eTPcdQEQN20xzfFFx3k+Dzys3Ko06pXPDQ==
"@cerc-io/nitro-client@^0.1.6":
version "0.1.6"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-client/-/0.1.6/nitro-client-0.1.6.tgz#1da8a9f9055e79861a055686f86d1291e6d0bcc1"
integrity sha512-71yDfi+E4/xFyd77GZMM8t8OaxSJJl2DbaVgqGMObfZa9/cJkWD0nWi4mfW4O63qHhTraVkTyYBbDOGF7fSlsQ==
dependencies:
"@cerc-io/libp2p" "0.42.2-laconic-0.1.3"
"@cerc-io/nitro-util" "^0.1.5"
"@cerc-io/nitro-util" "^0.1.6"
"@cerc-io/peer" "^0.2.49"
"@cerc-io/ts-channel" "1.0.3-ts-nitro-0.1.1"
"@libp2p/crypto" "^1.0.4"
@ -373,10 +373,10 @@
promjs "^0.4.2"
uint8arrays "^4.0.3"
"@cerc-io/nitro-util@^0.1.5":
version "0.1.5"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-util/-/0.1.5/nitro-util-0.1.5.tgz#829d7cb56c436fcf29184d1d87ed47ac57a2a32f"
integrity sha512-1nNXfoHVOV2QSnnQSiW/dgTuMGJYEcN2M12e5rMXAb4KrJIScT1lWTp+P5dja/F6jN8ZuNlu5REQTdOjdhMNwQ==
"@cerc-io/nitro-util@^0.1.6":
version "0.1.6"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-util/-/0.1.6/nitro-util-0.1.6.tgz#7cdf6ee37c21c7863eea3332e84f9450b7534bf3"
integrity sha512-moalyL8uG4dvUT1NleHqPXnOSqeto8VxFnyW+nRoTkkjmyrn8cb5XF0nlEDxf2gR/pBzNeGv1NdYwhVYv5rkTA==
dependencies:
"@statechannels/nitro-protocol" "^2.0.0-alpha.4"
assert "^2.0.0"
@ -1433,6 +1433,26 @@
dependencies:
assemblyscript "0.19.10"
"@graphql-tools/graphql-file-loader@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.0.tgz#a2026405bce86d974000455647511bf65df4f211"
integrity sha512-wRXj9Z1IFL3+zJG1HWEY0S4TXal7+s1vVhbZva96MSp0kbb/3JBF7j0cnJ44Eq0ClccMgGCDFqPFXty4JlpaPg==
dependencies:
"@graphql-tools/import" "7.0.0"
"@graphql-tools/utils" "^10.0.0"
globby "^11.0.3"
tslib "^2.4.0"
unixify "^1.0.0"
"@graphql-tools/import@7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.0.0.tgz#a6a91a90a707d5f46bad0fd3fde2f407b548b2be"
integrity sha512-NVZiTO8o1GZs6OXzNfjB+5CtQtqsZZpQOq+Uu0w57kdUkT4RlQKlwhT8T81arEsbV55KpzkpFsOZP7J1wdmhBw==
dependencies:
"@graphql-tools/utils" "^10.0.0"
resolve-from "5.0.0"
tslib "^2.4.0"
"@graphql-tools/load-files@^6.5.2":
version "6.6.1"
resolved "https://registry.yarnpkg.com/@graphql-tools/load-files/-/load-files-6.6.1.tgz#91ce18d910baf8678459486d8cccd474767bec0a"
@ -1442,6 +1462,16 @@
tslib "^2.4.0"
unixify "1.0.0"
"@graphql-tools/load@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.0.0.tgz#62e00f48c39b4085167a096f66ba6c21fb3fc796"
integrity sha512-Cy874bQJH0FP2Az7ELPM49iDzOljQmK1PPH6IuxsWzLSTxwTqd8dXA09dcVZrI7/LsN26heTY2R8q2aiiv0GxQ==
dependencies:
"@graphql-tools/schema" "^10.0.0"
"@graphql-tools/utils" "^10.0.0"
p-limit "3.1.0"
tslib "^2.4.0"
"@graphql-tools/merge@8.3.1":
version "8.3.1"
resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.1.tgz#06121942ad28982a14635dbc87b5d488a041d722"
@ -1458,6 +1488,14 @@
"@graphql-tools/utils" "9.2.1"
tslib "^2.4.0"
"@graphql-tools/merge@^9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.0.0.tgz#b0a3636c82716454bff88e9bb40108b0471db281"
integrity sha512-J7/xqjkGTTwOJmaJQJ2C+VDBDOWJL3lKrHJN4yMaRLAJH3PosB7GiPRaSDZdErs0+F77sH2MKs2haMMkywzx7Q==
dependencies:
"@graphql-tools/utils" "^10.0.0"
tslib "^2.4.0"
"@graphql-tools/mock@^8.1.2":
version "8.7.19"
resolved "https://registry.yarnpkg.com/@graphql-tools/mock/-/mock-8.7.19.tgz#b6c01ecc44074a01d6f472213de5f56fe0a3380c"
@ -1478,6 +1516,16 @@
tslib "^2.4.0"
value-or-promise "1.0.12"
"@graphql-tools/schema@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.0.tgz#7b5f6b6a59f51c927de8c9069bde4ebbfefc64b3"
integrity sha512-kf3qOXMFcMs2f/S8Y3A8fm/2w+GaHAkfr3Gnhh2LOug/JgpY/ywgFVxO3jOeSpSEdoYcDKLcXVjMigNbY4AdQg==
dependencies:
"@graphql-tools/merge" "^9.0.0"
"@graphql-tools/utils" "^10.0.0"
tslib "^2.4.0"
value-or-promise "^1.0.12"
"@graphql-tools/schema@^8.0.0":
version "8.5.1"
resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.5.1.tgz#c2f2ff1448380919a330312399c9471db2580b58"
@ -1503,6 +1551,15 @@
"@graphql-typed-document-node/core" "^3.1.1"
tslib "^2.4.0"
"@graphql-tools/utils@^10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.0.4.tgz#3104bea54752ae54f1f4a67833d7e3b734400dbe"
integrity sha512-MF+nZgGROSnFgyOYWhrl2PuJMlIBvaCH48vtnlnDQKSeDc2fUfOzUVloBAQvnYmK9JBmHHks4Pxv25Ybg3r45Q==
dependencies:
"@graphql-typed-document-node/core" "^3.1.1"
dset "^3.1.2"
tslib "^2.4.0"
"@graphql-typed-document-node/core@^3.1.1":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
@ -4612,6 +4669,11 @@ apollo-server-types@^3.8.0:
apollo-reporting-protobuf "^3.4.0"
apollo-server-env "^4.2.1"
apollo-type-bigint@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/apollo-type-bigint/-/apollo-type-bigint-0.1.3.tgz#9242115ca909b9467ba5c4bc6493a56a06984c0b"
integrity sha512-nyfwEWRZ+kon3Nnot20DufGm2EHZrkJoryYzw3soD+USdxhkcW434w1c/n+mjMLQDl86Z6EvlkvMX5Lordf2Wg==
app-root-path@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz"
@ -7172,6 +7234,11 @@ dreamopt@~0.6.0:
dependencies:
wordwrap ">=0.0.2"
dset@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a"
integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==
dtrace-provider@~0.8:
version "0.8.8"
resolved "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz"
@ -9048,7 +9115,7 @@ globalthis@^1.0.3:
dependencies:
define-properties "^1.1.3"
globby@11.1.0, globby@^11.1.0:
globby@11.1.0, globby@^11.0.3, globby@^11.1.0:
version "11.1.0"
resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
@ -12801,6 +12868,13 @@ p-finally@^1.0.0:
resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
p-limit@3.1.0, p-limit@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
dependencies:
yocto-queue "^0.1.0"
p-limit@^1.1.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
@ -12815,13 +12889,6 @@ p-limit@^2.2.0:
dependencies:
p-try "^2.0.0"
p-limit@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
dependencies:
yocto-queue "^0.1.0"
p-limit@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644"
@ -14189,16 +14256,16 @@ resolve-cwd@^3.0.0:
dependencies:
resolve-from "^5.0.0"
resolve-from@5.0.0, resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz"
@ -15937,7 +16004,7 @@ universalify@^2.0.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unixify@1.0.0:
unixify@1.0.0, unixify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090"
integrity sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==
@ -16137,7 +16204,7 @@ value-or-promise@1.0.11:
resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140"
integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==
value-or-promise@1.0.12:
value-or-promise@1.0.12, value-or-promise@^1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c"
integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==