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 2 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
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 Zaven = require("../mock-data/Zaven.json");
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use a generic title here rather than my name?

const testUtils = require("./testUtils");
const Transfer = require("../../server/models/Transfer");
const TokenA = require("../mock-data/TokenA");

describe('Zaven login', () => {
let registeredZaven;
let transfer;

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

it("Zaven list his wallet list", async () => {
const res = await request(server)
.get(`/wallets?limit=1`)
.set('Content-Type', "application/json")
.set('treetracker-api-key', registeredZaven.apiKey)
.set('Authorization', `Bearer ${registeredZaven.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('zaven');
});

});
20 changes: 1 addition & 19 deletions server/routes/walletRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,7 @@ walletRouter.get(
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 || 1) - 1, parseInt(limit))
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's fix this so that the offset send in the request starts with 0 as per normal practice, that than '1'


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;