Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: using SQL to handle the wallet list; #260

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions __tests__/integration/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ const sinon = require("sinon");
const chai = require("chai");
const server = require("../../server/app");
chai.use(require('chai-uuid'));
const Zaven = require("../mock-data/Zaven.json");
const walletA = require("../mock-data/walletA.json");
const testUtils = require("./testUtils");

describe('Authentication', () => {
let registeredUser;

beforeEach(async () => {
await testUtils.clear();
registeredUser = await testUtils.register(Zaven);
registeredUser = await testUtils.register(walletA);
})

// Authorization path
it(`[POST /auth] login with ${Zaven.name}`, (done) => {
it(`[POST /auth] login with ${walletA.name}`, (done) => {
request(server)
.post('/auth')
.set('treetracker-api-key', registeredUser.apiKey)
Expand All @@ -36,7 +36,7 @@ describe('Authentication', () => {
});


it(`[POST /auth] login with using wallet id of ${Zaven.name}`, (done) => {
it(`[POST /auth] login with using wallet id of ${walletA.name}`, (done) => {
request(server)
.post('/auth')
.set('treetracker-api-key', registeredUser.apiKey)
Expand Down
36 changes: 18 additions & 18 deletions __tests__/integration/bundle-transfer-decline.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,43 @@ const log = require('loglevel');
const chai = require("chai");
const server = require("../../server/app");
chai.use(require('chai-uuid'));
const Zaven = require("../mock-data/Zaven.json");
const Meisze = require("../mock-data/Meisze.json");
const walletA = require("../mock-data/walletA.json");
const walletB = require("../mock-data/walletB.json");
const testUtils = require("./testUtils");
const Transfer = require("../../server/models/Transfer");
const TokenA = require("../mock-data/TokenA");

describe('Zaven request to send 1 token to Meisze', () => {
let registeredZaven;
let registeredMeisze;
describe('walletA request to send 1 token to walletB', () => {
let registeredWalletA;
let registeredWalletB;
let transfer;

beforeEach(async () => {
await testUtils.clear();
registeredZaven = await testUtils.registerAndLogin(Zaven);
await testUtils.addToken(registeredZaven, TokenA);
expect(registeredZaven).property("id").a("string");
registeredMeisze = await testUtils.registerAndLogin(Meisze);
transfer = await testUtils.sendAndPend(registeredZaven,registeredMeisze, 1);
registeredWalletA = await testUtils.registerAndLogin(walletA);
await testUtils.addToken(registeredWalletA, TokenA);
expect(registeredWalletA).property("id").a("string");
registeredWalletB = await testUtils.registerAndLogin(walletB);
transfer = await testUtils.sendAndPend(registeredWalletA,registeredWalletB, 1);
})

describe("Meisze decline the request", () => {
describe("walletB decline the request", () => {

beforeEach(async () => {
await request(server)
.post(`/transfers/${transfer.id}/decline`)
.set('Content-Type', "application/json")
.set('treetracker-api-key', registeredMeisze.apiKey)
.set('Authorization', `Bearer ${registeredMeisze.token}`)
.set('treetracker-api-key', registeredWalletB.apiKey)
.set('Authorization', `Bearer ${registeredWalletB.token}`)
.expect(200);
});

it("The transfer status should be cancelled", async () => {

await request(server)
.get(`/transfers?limit=1000`)
.set('treetracker-api-key', registeredMeisze.apiKey)
.set('Authorization', `Bearer ${registeredMeisze.token}`)
.set('treetracker-api-key', registeredWalletB.apiKey)
.set('Authorization', `Bearer ${registeredWalletB.token}`)
.expect(200)
.then(res => {
expect(res.body.transfers).lengthOf(1);
Expand All @@ -50,12 +50,12 @@ describe('Zaven request to send 1 token to Meisze', () => {

});

it("Zaven should still have 1 token", async () =>{
it("walletA should still have 1 token", async () =>{

await request(server)
.get(`/tokens?limit=10`)
.set('treetracker-api-key', registeredZaven.apiKey)
.set('Authorization', `Bearer ${registeredZaven.token}`)
.set('treetracker-api-key', registeredWalletA.apiKey)
.set('Authorization', `Bearer ${registeredWalletA.token}`)
.expect(200)
.then(res => {
expect(res.body.tokens).lengthOf(1);
Expand Down
35 changes: 35 additions & 0 deletions __tests__/integration/wallet.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require('dotenv').config()
const request = require('supertest');
const { expect } = require('chai');
const log = require('loglevel');
const chai = require("chai");
const server = require("../../server/app");
chai.use(require('chai-uuid'));
const walletA = require("../mock-data/walletA.json");
const testUtils = require("./testUtils");
const Transfer = require("../../server/models/Transfer");
const TokenA = require("../mock-data/TokenA");

describe('walletA login', () => {
let registeredWalletA;
let transfer;

beforeEach(async () => {
await testUtils.clear();
registeredWalletA = await testUtils.registerAndLogin(walletA);
await testUtils.addToken(registeredWalletA, TokenA);
})

it("walletA list his wallet list", async () => {
const res = await request(server)
.get(`/wallets?limit=1`)
.set('Content-Type', "application/json")
.set('treetracker-api-key', registeredWalletA.apiKey)
.set('Authorization', `Bearer ${registeredWalletA.token}`)
.expect(200);
expect(res.body.wallets).lengthOf(1);
expect(res.body.wallets[0].tokens_in_wallet).eq('1');
expect(res.body.wallets[0].name).eq('walletA');
});

});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"id": "082450d6-278c-4887-bf6c-66a63ef177e7",
"name": "zaven",
"name": "walletA",
"password": "test1234"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"id": "24f44064-df75-4168-8fdd-cc5478146142",
"name": "meisze",
"name": "walletB",
"password": "test1234"
}
22 changes: 2 additions & 20 deletions server/routes/walletRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,13 @@ walletRouter.get(
req.query,
Joi.object({
limit: Joi.number().required(),
offset: Joi.number().min(1).integer(),
offset: Joi.number().min(0).integer(),
}),
);
const { limit, offset } = req.query;
const session = new Session();
const walletService = new WalletService(session);
const loggedInWallet = await walletService.getById(res.locals.wallet_id);
const subWallets = await loggedInWallet.getSubWallets();
// at logged in wallets to list of wallets
subWallets.push(loggedInWallet);

let walletsJson = [];

const tokenService = new TokenService(session);
for (const wallet of subWallets) {
const json = await wallet.toJSON();
json.tokens_in_wallet = await tokenService.countTokenByWallet(wallet);
walletsJson.push(json);
}

const numStart = parseInt(offset);
const numLimit = parseInt(limit);
const numBegin = numStart ? numStart - 1 : 0;
const numEnd = numBegin + numLimit;
walletsJson = walletsJson.slice(numBegin, numEnd);
const walletsJson = await walletService.getSubWalletList(res.locals.wallet_id, parseInt(offset || 0), parseInt(limit))

res.status(200).json({
wallets: walletsJson.map((wallet) =>
Expand Down
39 changes: 8 additions & 31 deletions server/routes/walletRouter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe("walletRouter", ()=> {
beforeEach(() => {
sinon.stub(ApiKeyService.prototype, "check");
sinon.stub(JWTService.prototype, "verify").returns({
id: authenticatedWallet.id,
id: authenticatedWallet.getId(),
});
app = express();
app.use(bodyParser.urlencoded({ extended: false })); // parse application/x-www-form-urlencoded
Expand All @@ -54,15 +54,15 @@ describe("walletRouter", ()=> {
});

it("successfully", async () => {
sinon.stub(WalletService.prototype, "getById").resolves(mockWallet);
sinon.stub(TrustService.prototype, "convertToResponse").resolves(mockTrust);
sinon.stub(TokenService.prototype, "countTokenByWallet").resolves(10);
const fn = sinon.stub(Wallet.prototype, "getSubWallets").resolves([ mockWallet2 ]);
const w1 = await mockWallet.toJSON();
const w2 = await mockWallet2.toJSON();
const f1 = sinon.stub(WalletService.prototype, "getSubWalletList").resolves([{...w1, tokens_in_wallet:1}, {...w2, tokens_in_wallet:2}]);
const res = await request(app)
.get('/?limit=2');
expect(res).property("statusCode").eq(200);
expect(res.body.wallets).lengthOf(2);
expect(res.body.wallets[0]).property("tokens_in_wallet").eq(10);
expect(res.body.wallets[0]).property("tokens_in_wallet").eq(1);
expect(f1).calledWith(authenticatedWallet.getId(), 0, 2);
});

it("should omit private fields", async () => {
Expand All @@ -74,10 +74,8 @@ describe("walletRouter", ()=> {
"salt": "private field",
"tokens_in_wallet": 10
})
sinon.stub(WalletService.prototype, "getById").resolves(wallet);
sinon.stub(TrustService.prototype, "convertToResponse").resolves(mockTrust);
sinon.stub(TokenService.prototype, "countTokenByWallet").resolves(10);
sinon.stub(Wallet.prototype, "getSubWallets").resolves( []);
const walletJson = await wallet.toJSON();
const f1 = sinon.stub(WalletService.prototype, "getSubWalletList").resolves([{...walletJson, tokens_in_wallet:1}]);
const res = await request(app)
.get('/?limit=2');
expect(res).property("statusCode").eq(200);
Expand All @@ -86,27 +84,6 @@ describe("walletRouter", ()=> {
expect(resWallet).not.to.contain.keys(['password', 'salt', 'type'])
});

it("limit and offet working successfully", async () => {
sinon.stub(WalletService.prototype, "getById").resolves(mockWallet);
console.log(mockWallet.getId())
console.log(mockWallet2.getId())
console.log(mockWallet3.getId())
console.log(mockWallet4.getId())
sinon.stub(TrustService.prototype, "convertToResponse").resolves(mockTrust);
sinon.stub(TokenService.prototype, "countTokenByWallet").resolves(10);
const fn = sinon.stub(Wallet.prototype, "getSubWallets").resolves([ mockWallet2, mockWallet3, mockWallet4]);
const res = await request(app)
.get('/?limit=3&offset=2');
expect(res).property("statusCode").eq(200);
expect(res.body.wallets).lengthOf(3);
console.log(authenticatedWallet.getId());
console.log(res.body)
expect(res.body.wallets[0]).property("tokens_in_wallet").eq(10);
expect(res.body.wallets[0]).property("id").eq(mockWallet3.getId());
expect(res.body.wallets[1]).property("id").eq(mockWallet4.getId());
expect(res.body.wallets[2]).property("id").eq(mockWallet.getId());
})

})

describe("get /wallets/:wallet_id/trust_relationships", () => {
Expand Down
48 changes: 48 additions & 0 deletions server/services/WalletService.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,54 @@ class WalletService {
const wallet = new Wallet(walletObject.id, this._session);
return wallet;
}

/*
* A faster way to get sub wallet list directly, from DB, and count
* the token in these wallet
*/
async getSubWalletList(walletId, offset, limit){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

const result = await this._session.getDB().raw(`
SELECT
w.*,
CASE WHEN tokens_in_wallet IS NULL THEN 0 ELSE tokens_in_wallet END
FROM
wallet w
JOIN
(
/* including the wallet himself */
SELECT '${walletId}' AS sub_wallet_id
UNION
/* manage */
SELECT
wt.target_wallet_id AS sub_wallet_id
FROM
wallet_trust wt
WHERE
wt."state" = 'trusted'
AND actor_wallet_id = '${walletId}'
AND wt.request_type = 'manage'
UNION
/* yield */
SELECT
wt.actor_wallet_id AS sub_wallet_id
FROM
wallet_trust wt
WHERE
wt."state" = 'trusted'
AND target_wallet_id = '${walletId}'
AND wt.request_type = 'yield'
) sub_wallet_ids
ON w.id = sub_wallet_ids.sub_wallet_id
LEFT JOIN (
SELECT wallet_id, count(wallet_id) tokens_in_wallet FROM "token" GROUP BY wallet_id
) token_stat
ON w.id = token_stat.wallet_id
ORDER BY name
OFFSET ${offset}
LIMIT ${limit}
`);
return result.rows;
}
}

module.exports = WalletService;