Advanced Tutorial: Using the user meta field
In this tutorial, we will create a plugin that allows an admin to check the number of trades a user has made. Once checked, the plugin will store the timestamp of the last trade completed by the user in meta. The next time the plugin checks the user's trade amount, it will only count trades made after the user's stored trade timestamp.
We will focus on the script section for this plugin.
The user meta field is a JSON object in the User DB model that we can use to add additional fields for users.

Script

1
'use strict';
2
3
const {
4
app,
5
loggerPlugin,
6
toolsLib
7
} = this.pluginLibraries;
8
const { query, validationResult } = require('express-validator');
9
const moment = require('moment');
10
11
const USER_META_FIELD_NAME = 'trade_count_plugin-latest_trade';
12
13
const init = async () => {
14
loggerPlugin.info(
15
'PLUGIN TRADE COUNT initializing...'
16
);
17
18
if (!toolsLib.getKitConfig().user_meta[USER_META_FIELD_NAME]) {
19
loggerPlugin.verbose(
20
'PLUGIN TRADE COUNT',
21
`User meta field ${USER_META_FIELD_NAME} not found. Creating field.`
22
);
23
24
await toolsLib.addKitUserMeta(
25
USER_META_FIELD_NAME,
26
'date-time',
27
'Last trade timestamp used for trade-count plugin',
28
false
29
);
30
}
31
32
loggerPlugin.info(
33
'PLUGIN TRADE COUNT initialized'
34
);
35
};
36
37
init()
38
.then(() => {
39
app.get(
40
'/plugins/trade-count/check',
41
[
42
toolsLib.security.verifyBearerTokenExpressMiddleware(['admin']),
43
query('user_id').isInt({ min: 1 }).toInt().optional()
44
],
45
async (req, res) => {
46
const errors = validationResult(req);
47
if (!errors.isEmpty()) {
48
return res.status(400).json({ errors: errors.array() });
49
}
50
51
const { user_id } = req.query;
52
53
loggerPlugin.verbose(
54
req.uuid,
55
'GET /plugins/trade-count/check auth',
56
req.auth.sub,
57
'user_id:',
58
user_id
59
);
60
61
try {
62
const user = await toolsLib.user.getUserByKitId(user_id, false);
63
64
if (!user) {
65
throw new Error('User not found');
66
}
67
68
const lastTradeTimestamp = user.meta[USER_META_FIELD_NAME];
69
const queryStartDate = toolsLib.isDatetime(lastTradeTimestamp)
70
? moment(lastTradeTimestamp).add(1, 'ms').toISOString()
71
: null;
72
73
loggerPlugin.verbose(
74
req.uuid,
75
'GET /plugins/trade-count/check',
76
'user last trade timestamp',
77
lastTradeTimestamp,
78
'trades query start date',
79
queryStartDate
80
);
81
82
const trades = await toolsLib.order.getAllUserTradesByKitId(
83
user.id,
84
null,
85
1,
86
1,
87
'timestamp',
88
'desc',
89
queryStartDate
90
);
91
92
loggerPlugin.verbose(
93
req.uuid,
94
'GET /plugins/trade-count/check',
95
'trade count',
96
trades.count
97
);
98
99
if (trades.count === 0) {
100
return res.json({
101
count: 0
102
});
103
}
104
105
const lastTrade = trades.data[0];
106
107
loggerPlugin.verbose(
108
req.uuid,
109
'GET /plugins/trade-count/check',
110
'last trade timestamp',
111
lastTrade.timestamp
112
);
113
114
await user.update({
115
meta: {
116
...user.meta,
117
[USER_META_FIELD_NAME]: lastTrade.timestamp
118
}
119
});
120
121
return res.json({
122
count: trades.count
123
});
124
125
} catch (err) {
126
loggerPlugin.error(
127
req.uuid,
128
'GET /plugins/trade-count/check err',
129
err.message
130
);
131
132
return res.status(400).json({ message: err.message });
133
}
134
}
135
);
136
})
137
.catch((err) => {
138
loggerPlugin.error(
139
'PLUGIN TRADE COUNT err during initialization',
140
err.message
141
);
142
});
143
Copied!

Breakdown

Import Requirements

1
const {
2
app,
3
loggerPlugin,
4
toolsLib
5
} = this.pluginLibraries;
6
const { query, validationResult } = require('express-validator');
7
const moment = require('moment');
Copied!
First, import all the libraries that are required for this plugin.

Set meta field name

1
const USER_META_FIELD_NAME = 'trade_count_plugin-latest_trade';
Copied!
All user meta fields require a name. Here, we are calling the meta field trade_count_plugin-latest_trade. We recommend formatting meta field names for plugins as <PLUGIN_NAME>-<FIELD_NAME>

Check if meta field exists in init

1
const init = async () => {
2
loggerPlugin.info(
3
'PLUGIN TRADE COUNT initializing...'
4
);
5
6
if (!toolsLib.getKitConfig().user_meta[USER_META_FIELD_NAME]) {
7
loggerPlugin.verbose(
8
'PLUGIN TRADE COUNT',
9
`User meta field ${USER_META_FIELD_NAME} not found. Creating field.`
10
);
11
12
await toolsLib.addKitUserMeta(
13
USER_META_FIELD_NAME,
14
'date-time',
15
'Last trade timestamp used for trade-count plugin',
16
false
17
);
18
}
19
20
loggerPlugin.info(
21
'PLUGIN TRADE COUNT initialized'
22
);
23
};
24
25
init()
26
.then(() => {...})
27
.catch((err) => {
28
loggerPlugin.error(
29
'PLUGIN TRADE COUNT err during initialization',
30
err.message
31
);
32
});
Copied!
Next, we create an init function that creates the new user meta field if it doesn't already exist. All user meta fields can be found in the Kit config object user_meta. Each field in user_meta has a type, required and description value.
1
// KIT CONFIG
2
3
{
4
...,
5
"user_meta": {
6
"trade_count_plugin-latest_trade": {
7
"type": "date-time",
8
"required": false,
9
"description": "Last trade timestamp used for trade-count plugin"
10
}
11
},
12
...
13
}
Copied!
We are first checking if the user meta field exists in the Kit config returned from toolsLib.getKitConfig. If not, we are creating the new field using the tools library function toolsLib.addKitUserMeta. Please refer to the tools library documentation for more information regarding the tools library. Once initialized, the main script will be executed.

Create an endpoint for counting user trades

1
app.get(
2
'/plugins/trade-count/check',
3
[
4
toolsLib.security.verifyBearerTokenExpressMiddleware(['admin']),
5
query('user_id').isInt({ min: 1 }).toInt().optional()
6
],
7
async (req, res) => {
8
const errors = validationResult(req);
9
if (!errors.isEmpty()) {
10
return res.status(400).json({ errors: errors.array() });
11
}
12
13
const { user_id } = req.query;
14
15
loggerPlugin.verbose(
16
req.uuid,
17
'GET /plugins/trade-count/check auth',
18
req.auth.sub,
19
'user_id:',
20
user_id
21
);
Copied!
For this plugin, we need an endpoint GET /plugins/trade-count/check that returns the number of trades a user has made.
1
try {
2
const user = await toolsLib.user.getUserByKitId(user_id, false);
3
4
if (!user) {
5
throw new Error('User not found');
6
}
7
8
const lastTradeTimestamp = user.meta[USER_META_FIELD_NAME];
9
const queryStartDate = toolsLib.isDatetime(lastTradeTimestamp)
10
? moment(lastTradeTimestamp).add(1, 'ms').toISOString()
11
: null;
12
13
loggerPlugin.verbose(
14
req.uuid,
15
'GET /plugins/trade-count/check',
16
'user last trade timestamp',
17
lastTradeTimestamp,
18
'trades query start date',
19
queryStartDate
20
);
21
22
const trades = await toolsLib.order.getAllUserTradesByKitId(
23
user.id,
24
null,
25
1,
26
1,
27
'timestamp',
28
'desc',
29
queryStartDate
30
);
31
32
loggerPlugin.verbose(
33
req.uuid,
34
'GET /plugins/trade-count/check',
35
'trade count',
36
trades.count
37
);
38
39
if (trades.count === 0) {
40
return res.json({
41
count: 0
42
});
43
}
44
45
const lastTrade = trades.data[0];
46
47
loggerPlugin.verbose(
48
req.uuid,
49
'GET /plugins/trade-count/check',
50
'last trade timestamp',
51
lastTrade.timestamp
52
);
53
54
await user.update({
55
meta: {
56
...user.meta,
57
[USER_META_FIELD_NAME]: lastTrade.timestamp
58
}
59
});
60
61
return res.json({
62
count: trades.count
63
});
64
65
} catch (err) {
66
loggerPlugin.error(
67
req.uuid,
68
'GET /plugins/trade-count/check err',
69
err.message
70
);
71
72
return res.status(400).json({ message: err.message });
73
}
Copied!
We are first checking to see if a user with the given user_id exists. If so, we check to see if the user's meta.trade_count_plugin-latest_trade value exists. If it doesn't that means we haven't checked the user's trade count using this plugin before. If it does, that means we have previously checked the user's trade count.
Using the found trade_count_plugin-latest_trade value, we can determine which date to query the user's trades from. If trade_count_plugin-latest_trade was null, then we will query all of the user's trades.
Once we get the trades response, we can read the number of trades made and the timestamp of the last trade completed. If the count is 0, then we can just return 0 without updating the user's trade_count_plugin-latest_trade value. Otherwise, we will get the latest trade's timestamp, update the user's trade_count_plugin-latest_trade value to that timestamp, and return the count. The next time we check the user's trade count, all trades starting from the updated trade_count_plugin-latest_trade will be counted.