Commit 56dcc70a authored by Benoit Lavenier's avatar Benoit Lavenier

[enh] Add CORS in express server

[enh] Add query movementsByPubkey(pubkey) to get amount easily
parent a124c54b
Pipeline #6197 failed with stages
......@@ -9,6 +9,7 @@
"test": "mocha tests/"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.16.4",
"express-graphql": "^0.7.1",
"graphql": "^14.0.2",
......
......@@ -4,9 +4,13 @@ import {Server} from "duniter/server";
import {makeExecutableSchema} from "graphql-tools";
import {FullIindexEntry, FullMindexEntry} from "duniter/app/lib/indexer";
import {hashf} from "duniter/app/lib/common";
import {DBTx} from "duniter/app/lib/db/DBTx";
import {txAmount, txAmountForPubkey, txToMovement} from "./tx";
const pkg = require('../package.json')
const logger_1 = require("duniter/app/lib/logger");
export function plugModule(server: Server, logger?: any) {
logger = logger || logger_1.NewLogger();
......@@ -74,6 +78,16 @@ export function plugModule(server: Server, logger?: any) {
return server.dal.txsDAL.getTX(hash)
},
movementsByPubkey: async (_, { pubkey }: { pubkey: string }) => {
const res = await Promise.all([
await server.dal.txsDAL.getLinkedWithIssuer(pubkey),
await server.dal.txsDAL.getPendingWithIssuer(pubkey),
await server.dal.txsDAL.getLinkedWithRecipient(pubkey),
await server.dal.txsDAL.getPendingWithRecipient(pubkey)]);
return res.reduce((res, item) => res.concat(item),[])
.map(tx => txToMovement(server, tx, pubkey));
},
transactionsOfIssuer: async (_, { issuer }: { issuer: string }) => {
return (await server.dal.txsDAL.getLinkedWithIssuer(issuer))
.concat(await server.dal.txsDAL.getPendingWithIssuer(issuer))
......@@ -119,6 +133,11 @@ export function plugModule(server: Server, logger?: any) {
},
Transaction: {
amountForPubkey: txAmountForPubkey,
amount: txAmount
},
Identity: {
certsIssued: async (identity: FullIindexEntry) => {
return server.dal.cindexDAL.getValidLinksFrom(identity.pub)
......@@ -167,7 +186,7 @@ export function plugModule(server: Server, logger?: any) {
},
async submitTransaction(_, { raw }: { raw: string }) {
return server.writeRawTransaction(raw)
},
}
}
}
})
......
......@@ -4,17 +4,22 @@ import graphqlHTTP = require("express-graphql");
import {Server} from "duniter/server";
import * as http from "http";
const logger_1 = require("duniter/app/lib/logger");
const cors = require('cors');
export function gvaHttpListen(server: Server, port: number, host = 'localhost'): http.Server {
const app = express()
const logger = logger_1.NewLogger('gva');
const app = express();
// Enable `cors` to set HTTP response header: Access-Control-Allow-Origin: *
app.use(cors());
app.use('/graphql', graphqlHTTP({
schema: plugModule(server, logger),
graphiql: true,
}))
return app.listen(port, host, () => {
logger && logger.info(`GVA server listening on http${port==443 ? 's' : ''}://${host}:${port}/graphql`);
})
}));
return app.listen(port, host,
() => {
logger && logger.info(`GVA server listening on http${port==443 ? 's' : ''}://${host}:${port}/graphql`);
});
}
#################################
# Inputs
#################################
input Paging {
pageNumber: Int! = 0
pageSize: Int! = 50
fromBlock: Int! = 0
# If toBlock is null, current block number is used
toBlock: Int
}
input BlockNumberAndHashInput {
# Block number
number: Int!
# Block hash
hash: String!
}
#################################
# Business entities
#################################
type Membership {
pub: String!
created_on: String!
......@@ -102,6 +126,34 @@ type PendingMembership {
block_number: Int
}
#################################
# Transactions types (tx is alias for transaction)
#################################
type Movement {
version: Int!
currency: String!
type: String!
comment: String!
pubkeys: [String!]!
amount: Int!
hash: String!
time: Int!
blockstamp: String!
block: String!
# Null if the document is not written in blockchain
#written_on: BlockRef!
}
type RfcTransaction {
blockRef: BlockRef!
# Null if the document is not written in blockchain
written_on: BlockRef!
}
type Transaction {
hash: String!
block_number: Int
......@@ -124,6 +176,10 @@ type Transaction {
output_amount: Int!
written_on: String
writtenOn: Int
# Get amount for one pubkey (compute diff between inputs and outputs)
amountForPubkey(pubkey: String!): Int!
amount: Int!
}
type BlockTransaction {
......@@ -212,13 +268,6 @@ type NodeSummary {
duniter: NodeSummaryDuniter
}
input Paging {
pageNumber: Int! = 0
pageSize: Int! = 50
fromBlock: Int! = 0
toBlock: Int
}
type Mutation {
submitIdentity(raw: String!): PendingIdentity!
submitCertification(raw: String!): PendingIdentity!
......@@ -237,6 +286,7 @@ type Query {
pendingIdentityByHash(hash: String!): PendingIdentity
pendingTransactions: [Transaction!]!
transactionByHash(hash: String!): Transaction
movementsByPubkey(pubkey: String!): [Movement!]!
transactionsOfIssuer(issuer: String!): [Transaction!]!
transactionsOfReceiver(receiver: String!): [Transaction!]!
sourcesOfPubkey(pub: String!): [Source!]!
......
import {DBTx} from "duniter/app/lib/db/DBTx";
import {Server} from "duniter/server";
import {uniqueWithExclusion} from "./utils";
export declare interface Movement {
type: 'T' | 'D';
version: number;
currency: string;
comment: string;
pubkeys: string[];
amount: number;
hash: string;
blockstamp: string;
time?: number | null;
block: string;
// Null if the document is not written in blockchain
//written_on?: BlockRef | null;
}
export function txInputOrOutputAmount(output: string) {
const parts = output.split(':');
return +parts[0] * Math.pow(10, +parts[1]);
}
export function txAmountForPubkey(tx: DBTx, pubkey:string){
const issuerIndex = tx.issuers.findIndex(i => i === pubkey);
const inputAmount = (issuerIndex === -1) ? 0 : tx.unlocks.reduce((sum, unlock, i) => {
return sum + (unlock.endsWith(`:SIG(${issuerIndex})`) && txInputOrOutputAmount(tx.inputs[i]) || 0);
}, 0);
const outputAmount = tx.outputs.reduce((sum, output) => {
return sum + (output.endsWith(`:SIG(${pubkey})`) && txInputOrOutputAmount(output) || 0);
}, 0);
return outputAmount - inputAmount;
}
export function txAmount(tx: DBTx){
return tx.issuers.reduce((sum, issuer) => {
return sum + txAmountForPubkey(tx, issuer);
}, 0);
}
/**
* Return pubkeys (issuers or recipients)
* @param server
* @param tx
* @param pubkey
*/
export function txToMovement(server: Server, tx: DBTx, pubkey: string): Movement {
const amount = txAmountForPubkey(tx, pubkey);
return {
version: tx.version,
currency: tx.currency,
type: 'T',
comment: tx.comment,
amount: amount,
pubkeys: amount > 0 ? uniqueWithExclusion(tx.issuers, pubkey) : uniqueWithExclusion(tx.recipients, pubkey),
blockstamp: tx.blockstamp,
time: tx.blockstampTime && (tx.blockstampTime /*medianTime*/ + server.conf.avgGenTime * server.conf.medianTimeBlocks) || null,
hash: tx.hash,
block: tx.blockstamp
//written_on: tx.written_on
}
}
export declare interface BlockRef {
number: number;
// Block hash
hash: string;
// Block time
time: number;
// Block median time
medianTime: number;
// = median time + (avgGenTime * medianTime / 2)
userTime: number;
}
/**
* Usage example:
* <code>
* var a = ['a', 1, 'a', 2, '1'];
* var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']
* </code>
* @param value
* @param index
* @param self
*/
export function onlyUnique<T>(value: T, index: number, self: T[]) {
return self.indexOf(value) === index;
}
/**
* Usage example:
* <code>
* var a = ['a', 1, 'a', 2, '1'];
* var unique = uniqueWithExclusion(a, 1); // returns ['a', 2, '1']
* </code>
* @param values
* @param valueToExclude
*/
export function uniqueWithExclusion<T>(values: T[], valueToExclude: T): T[]{
return (values||[])
.filter((value, index, self) => {
// Exclude given value
return value !== valueToExclude
// Avoid duplicated issuer
&& self.indexOf(value) === index;
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment