evernode-js-client 0.5.10 → 0.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +14 -0
- package/LICENSE +21 -0
- package/README.md +25 -3
- package/clean-pkg.sh +4 -0
- package/npm-readme.md +4 -0
- package/package.json +10 -1
- package/remove-versions.sh +10 -0
- package/src/clients/base-evernode-client.js +609 -0
- package/src/clients/host-client.js +560 -0
- package/src/clients/registry-client.js +54 -0
- package/src/clients/tenant-client.js +276 -0
- package/src/defaults.js +21 -0
- package/src/eccrypto.js +258 -0
- package/src/encryption-helper.js +96 -0
- package/src/event-emitter.js +45 -0
- package/src/evernode-common.js +113 -0
- package/src/evernode-helpers.js +45 -0
- package/src/firestore/firestore-handler.js +309 -0
- package/src/index.js +37 -0
- package/src/state-helpers.js +396 -0
- package/src/transaction-helper.js +62 -0
- package/src/util-helpers.js +50 -0
- package/src/xfl-helpers.js +130 -0
- package/src/xrpl-account.js +515 -0
- package/src/xrpl-api.js +301 -0
- package/src/xrpl-common.js +17 -0
- package/test/package-lock.json +884 -0
- package/test/package.json +9 -0
- package/test/test.js +409 -0
- package/index.js +0 -15876
@@ -0,0 +1,45 @@
|
|
1
|
+
class EventEmitter {
|
2
|
+
constructor() {
|
3
|
+
this.handlers = {};
|
4
|
+
}
|
5
|
+
|
6
|
+
on(event, handler) {
|
7
|
+
if (!this.handlers[event])
|
8
|
+
this.handlers[event] = [];
|
9
|
+
this.handlers[event].push({
|
10
|
+
once: false,
|
11
|
+
func: handler
|
12
|
+
});
|
13
|
+
}
|
14
|
+
|
15
|
+
once(event, handler) {
|
16
|
+
if (!this.handlers[event])
|
17
|
+
this.handlers[event] = [];
|
18
|
+
this.handlers[event].push({
|
19
|
+
once: true,
|
20
|
+
func: handler
|
21
|
+
});
|
22
|
+
}
|
23
|
+
|
24
|
+
off(event, handler = null) {
|
25
|
+
if (this.handlers[event]) {
|
26
|
+
if (handler)
|
27
|
+
this.handlers[event] = this.handlers[event].filter(h => h !== handler);
|
28
|
+
else
|
29
|
+
delete this.handlers[event];
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
emit(event, value, error = null) {
|
34
|
+
if (this.handlers[event]) {
|
35
|
+
this.handlers[event].forEach(handler => handler.func(value, error));
|
36
|
+
|
37
|
+
// Rmove all handlers marked as 'once'.
|
38
|
+
this.handlers[event] = this.handlers[event].filter(h => !h.once);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
module.exports = {
|
44
|
+
EventEmitter
|
45
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
const EvernodeConstants = {
|
2
|
+
EVR: 'EVR',
|
3
|
+
NFT_PREFIX_HEX: '657672686F7374', // evrhost
|
4
|
+
LEASE_NFT_PREFIX_HEX: '6576726C65617365', // evrlease
|
5
|
+
HOOK_NAMESPACE: '01EAF09326B4911554384121FF56FA8FECC215FDDE2EC35D9E59F2C53EC665A0',
|
6
|
+
NOW_IN_EVRS: "0.00000001"
|
7
|
+
}
|
8
|
+
|
9
|
+
const MemoTypes = {
|
10
|
+
ACQUIRE_LEASE: 'evnAcquireLease',
|
11
|
+
ACQUIRE_SUCCESS: 'evnAcquireSuccess',
|
12
|
+
ACQUIRE_ERROR: 'evnAcquireError',
|
13
|
+
ACQUIRE_REF: 'evnAcquireRef',
|
14
|
+
HOST_REG: 'evnHostReg',
|
15
|
+
HOST_DEREG: 'evnHostDereg',
|
16
|
+
HOST_UPDATE_INFO: 'evnHostUpdateReg',
|
17
|
+
HEARTBEAT: 'evnHeartbeat',
|
18
|
+
HOST_POST_DEREG: 'evnHostPostDereg',
|
19
|
+
HOST_TRANSFER: 'evnTransfer',
|
20
|
+
EXTEND_LEASE: 'evnExtendLease',
|
21
|
+
EXTEND_SUCCESS: 'evnExtendSuccess',
|
22
|
+
EXTEND_ERROR: 'evnExtendError',
|
23
|
+
EXTEND_REF: 'evnExtendRef',
|
24
|
+
REGISTRY_INIT: 'evnInitialize',
|
25
|
+
REFUND: 'evnRefund',
|
26
|
+
REFUND_REF: 'evnRefundRef',
|
27
|
+
DEAD_HOST_PRUNE: 'evnDeadHostPrune',
|
28
|
+
HOST_REBATE: 'evnHostRebate',
|
29
|
+
HOST_REGISTRY_REF: 'evnHostRegistryRef'
|
30
|
+
}
|
31
|
+
|
32
|
+
const MemoFormats = {
|
33
|
+
TEXT: 'text/plain',
|
34
|
+
JSON: 'text/json',
|
35
|
+
BASE64: 'base64',
|
36
|
+
HEX: 'hex'
|
37
|
+
}
|
38
|
+
|
39
|
+
const ErrorCodes = {
|
40
|
+
ACQUIRE_ERR: 'ACQUIRE_ERR',
|
41
|
+
EXTEND_ERR: 'EXTEND_ERR'
|
42
|
+
}
|
43
|
+
|
44
|
+
const ErrorReasons = {
|
45
|
+
TRANSACTION_FAILURE: 'TRANSACTION_FAILURE',
|
46
|
+
NO_OFFER: 'NO_OFFER',
|
47
|
+
NO_NFT: 'NO_NFT',
|
48
|
+
INTERNAL_ERR: 'INTERNAL_ERR',
|
49
|
+
TIMEOUT: 'TIMEOUT',
|
50
|
+
HOST_INVALID: 'HOST_INVALID',
|
51
|
+
HOST_INACTIVE: 'HOST_INACTIVE',
|
52
|
+
NO_STATE_KEY: 'NO_STATE_KEY'
|
53
|
+
}
|
54
|
+
|
55
|
+
// All keys are prefixed with 'EVR' (0x455652)
|
56
|
+
// Config keys sub-prefix: 0x01
|
57
|
+
const HookStateKeys = {
|
58
|
+
// Configuration.
|
59
|
+
EVR_ISSUER_ADDR: "4556520100000000000000000000000000000000000000000000000000000001",
|
60
|
+
FOUNDATION_ADDR: "4556520100000000000000000000000000000000000000000000000000000002",
|
61
|
+
MOMENT_SIZE: "4556520100000000000000000000000000000000000000000000000000000003",
|
62
|
+
MINT_LIMIT: "4556520100000000000000000000000000000000000000000000000000000004",
|
63
|
+
FIXED_REG_FEE: "4556520100000000000000000000000000000000000000000000000000000005",
|
64
|
+
HOST_HEARTBEAT_FREQ: "4556520100000000000000000000000000000000000000000000000000000006",
|
65
|
+
PURCHASER_TARGET_PRICE: "4556520100000000000000000000000000000000000000000000000000000007",
|
66
|
+
LEASE_ACQUIRE_WINDOW: "4556520100000000000000000000000000000000000000000000000000000008",
|
67
|
+
REWARD_CONFIGURATION: "4556520100000000000000000000000000000000000000000000000000000009",
|
68
|
+
MAX_TOLERABLE_DOWNTIME: "455652010000000000000000000000000000000000000000000000000000000A",
|
69
|
+
MOMENT_TRANSIT_INFO: "455652010000000000000000000000000000000000000000000000000000000B",
|
70
|
+
MAX_TRX_EMISSION_FEE: "455652010000000000000000000000000000000000000000000000000000000C",
|
71
|
+
|
72
|
+
|
73
|
+
// Singleton
|
74
|
+
HOST_COUNT: "4556523200000000000000000000000000000000000000000000000000000000",
|
75
|
+
MOMENT_BASE_INFO: "4556523300000000000000000000000000000000000000000000000000000000",
|
76
|
+
HOST_REG_FEE: "4556523400000000000000000000000000000000000000000000000000000000",
|
77
|
+
MAX_REG: "4556523500000000000000000000000000000000000000000000000000000000",
|
78
|
+
REWARD_INFO: "4556523600000000000000000000000000000000000000000000000000000000",
|
79
|
+
|
80
|
+
// Prefixes
|
81
|
+
PREFIX_HOST_TOKENID: "45565202",
|
82
|
+
PREFIX_HOST_ADDR: "45565203",
|
83
|
+
PREFIX_TRANSFEREE_ADDR: "45565204",
|
84
|
+
}
|
85
|
+
|
86
|
+
const EvernodeEvents = {
|
87
|
+
HostRegistered: "HostRegistered",
|
88
|
+
HostDeregistered: "HostDeregistered",
|
89
|
+
HostPostDeregistered: "HostPostDeregistered",
|
90
|
+
HostTransfer: "HostTransfer",
|
91
|
+
AcquireLease: "AcquireLease",
|
92
|
+
AcquireSuccess: "AcquireSuccess",
|
93
|
+
AcquireError: "AcquireError",
|
94
|
+
Heartbeat: "Heartbeat",
|
95
|
+
ExtendLease: "ExtendLease",
|
96
|
+
ExtendSuccess: "ExtendSuccess",
|
97
|
+
ExtendError: "ExtendError",
|
98
|
+
HostRegUpdated: "HostRegUpdated",
|
99
|
+
HostReRegistered: "HostReRegistered",
|
100
|
+
RegistryInitialized: "RegistryInitialized",
|
101
|
+
DeadHostPrune: "DeadHostPrune",
|
102
|
+
HostRebate: "HostRebate"
|
103
|
+
}
|
104
|
+
|
105
|
+
module.exports = {
|
106
|
+
EvernodeConstants,
|
107
|
+
MemoTypes,
|
108
|
+
MemoFormats,
|
109
|
+
ErrorCodes,
|
110
|
+
ErrorReasons,
|
111
|
+
HookStateKeys,
|
112
|
+
EvernodeEvents
|
113
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
const { EvernodeConstants } = require('./evernode-common');
|
2
|
+
const NFT_PAGE_LEDGER_ENTRY_TYPE_HEX = '0050';
|
3
|
+
|
4
|
+
class EvernodeHelpers {
|
5
|
+
static async getLeaseOffers(xrplAcc) {
|
6
|
+
const hostNfts = (await xrplAcc.getNfts()).filter(nft => nft.URI.startsWith(EvernodeConstants.LEASE_NFT_PREFIX_HEX));
|
7
|
+
const hostTokenIDs = hostNfts.map(nft => nft.NFTokenID);
|
8
|
+
const nftOffers = (await xrplAcc.getNftOffers())?.filter(offer => (offer.Flags == 1 && hostTokenIDs.includes(offer.NFTokenID))); // Filter only sell offers
|
9
|
+
return nftOffers;
|
10
|
+
}
|
11
|
+
|
12
|
+
static async getNFTPageAndLocation(nfTokenId, xrplAcc, xrplApi, buffer = true) {
|
13
|
+
|
14
|
+
const nftPageApprxKeylet = xrplAcc.generateKeylet('nftPage', { nfTokenId: nfTokenId });
|
15
|
+
const nftPageMaxKeylet = xrplAcc.generateKeylet('nftPageMax');
|
16
|
+
// Index is the last 32 bytes of the Keylet (Last 64 HEX characters).
|
17
|
+
let page = await xrplApi.getLedgerEntry(nftPageMaxKeylet.substring(4, 68));
|
18
|
+
while (page?.PreviousPageMin) {
|
19
|
+
// Compare the low 96 bits. (Last 24 HEX characters).
|
20
|
+
if (Number('0x' + page.index.substring(40, 64)) >= Number('0x' + nftPageApprxKeylet.substring(40, 64))) {
|
21
|
+
// Check the existence of the NFToken
|
22
|
+
let token = page.NFTokens.find(n => n.NFToken.NFTokenID == nfTokenId);
|
23
|
+
if (!token) {
|
24
|
+
page = await xrplApi.getLedgerEntry(page.PreviousPageMin);
|
25
|
+
}
|
26
|
+
else
|
27
|
+
break;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
const nftPageInfo = page.NFTokens.map((n, loc) => { return { NFTPage: NFT_PAGE_LEDGER_ENTRY_TYPE_HEX + page.index, NFTokenID: n.NFToken.NFTokenID, location: loc } }).find(n => n.NFTokenID == nfTokenId);
|
32
|
+
if (buffer) {
|
33
|
+
let locBuf = Buffer.allocUnsafe(2);
|
34
|
+
locBuf.writeUInt16BE(nftPageInfo.location);
|
35
|
+
// <NFT_PAGE_KEYLET(34 bytes)><LOCATION(2 bytes)>
|
36
|
+
return Buffer.concat([Buffer.from(nftPageInfo.NFTPage, "hex"), locBuf]);
|
37
|
+
}
|
38
|
+
|
39
|
+
return nftPageInfo;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
module.exports = {
|
44
|
+
EvernodeHelpers
|
45
|
+
}
|
@@ -0,0 +1,309 @@
|
|
1
|
+
const https = require('https');
|
2
|
+
const { DefaultValues } = require('../defaults');
|
3
|
+
|
4
|
+
const FirestoreOperations = {
|
5
|
+
EQUAL: 'EQUAL',
|
6
|
+
AND: 'AND'
|
7
|
+
}
|
8
|
+
|
9
|
+
class FirestoreHandler {
|
10
|
+
#projectId = null;
|
11
|
+
#collectionPrefix = null;
|
12
|
+
|
13
|
+
constructor(options = {}) {
|
14
|
+
this.#projectId = options.stateIndexId || DefaultValues.stateIndexId;
|
15
|
+
this.#collectionPrefix = options.collectionPrefix || DefaultValues.registryAddress;
|
16
|
+
}
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Convert given document value object to real format and snake_case key to camelCase.
|
20
|
+
* @param key Name of the property.
|
21
|
+
* @param value Value to be parsed.
|
22
|
+
* @returns Parsed key and value.
|
23
|
+
*/
|
24
|
+
#parseValue(key, value) {
|
25
|
+
// Convert snake_case to camelCase.
|
26
|
+
const ccKey = key.replace(/_([a-z])/g, function (g) { return g[1].toUpperCase(); });
|
27
|
+
const type = Object.keys(value)[0];
|
28
|
+
let parsed;
|
29
|
+
switch (type) {
|
30
|
+
case 'integerValue':
|
31
|
+
parsed = parseInt(value[type]);
|
32
|
+
break;
|
33
|
+
case 'floatValue':
|
34
|
+
parsed = parseFloat(value[type]);
|
35
|
+
break;
|
36
|
+
case 'mapValue':
|
37
|
+
parsed = {};
|
38
|
+
for (const [subKey, subValue] of Object.entries(value[type].fields)) {
|
39
|
+
const field = this.#parseValue(subKey, subValue);
|
40
|
+
parsed[field.key] = field.value;
|
41
|
+
}
|
42
|
+
break;
|
43
|
+
default:
|
44
|
+
parsed = value[type];
|
45
|
+
break;
|
46
|
+
}
|
47
|
+
return { key: ccKey, value: parsed };
|
48
|
+
}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Get values filtered according to the given body.
|
52
|
+
* @param body Body to parsed in to the query reqest.
|
53
|
+
* @returns Result set.
|
54
|
+
*/
|
55
|
+
async #runQuery(body) {
|
56
|
+
const url = this.buildApiPath(null, null, true);
|
57
|
+
return await this.sendRequest('POST', url, null, body);
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Get documents from a collection.
|
62
|
+
* @param collectionId Name of the collection.
|
63
|
+
* @param pageSize Optianal page size if result set needed to be paginated
|
64
|
+
* @param nextPageToken Next page token of a paginated result set.
|
65
|
+
* @returns Result set.
|
66
|
+
*/
|
67
|
+
async #read(collectionId, pageSize = null, nextPageToken = null) {
|
68
|
+
if (!collectionId)
|
69
|
+
throw { type: 'Validation Error', message: 'collectionId is required' };
|
70
|
+
|
71
|
+
const url = this.buildApiPath(collectionId);
|
72
|
+
let params = (pageSize || nextPageToken) ? {} : null;
|
73
|
+
if (pageSize)
|
74
|
+
params = { pageSize: pageSize };
|
75
|
+
if (nextPageToken)
|
76
|
+
params = { pageToken: nextPageToken, ...params };
|
77
|
+
|
78
|
+
return await this.sendRequest('GET', url, params);
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Get documents from a collection with filtering support.
|
83
|
+
* @param collectionId Name of the collection.
|
84
|
+
* @param filter Optional filters to filter documents.
|
85
|
+
* @param pageSize Optianal page size if result set needed to be paginated
|
86
|
+
* @param nextPageToken Next page token of a paginated result set.
|
87
|
+
* @returns Parsed readable result set.
|
88
|
+
*/
|
89
|
+
async #getDocuments(collectionId, filters = null, pageSize = null, nextPageToken = null) {
|
90
|
+
if (filters && (pageSize || nextPageToken))
|
91
|
+
throw { type: 'Validation Error', message: 'Pagination isn\'t supported with filter.' };
|
92
|
+
|
93
|
+
let data;
|
94
|
+
// If there are filters send requst in query mode.
|
95
|
+
if (filters) {
|
96
|
+
// Prepare the query body from given filters.
|
97
|
+
let where = {
|
98
|
+
compositeFilter: {
|
99
|
+
filters: Object.entries(filters).map(([key, value]) => {
|
100
|
+
const field = this.convertValue(key, value);
|
101
|
+
return {
|
102
|
+
fieldFilter: {
|
103
|
+
field: { fieldPath: field.key },
|
104
|
+
op: FirestoreOperations.EQUAL,
|
105
|
+
value: field.value
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}),
|
109
|
+
op: filters.operator ? filters.operator : FirestoreOperations.AND
|
110
|
+
}
|
111
|
+
};
|
112
|
+
for (const [key, value] of Object.entries(filters)) {
|
113
|
+
const field = this.convertValue(key, value);
|
114
|
+
let fieldFilter = {
|
115
|
+
field: { fieldPath: field.key },
|
116
|
+
op: FirestoreOperations.EQUAL,
|
117
|
+
value: field.value
|
118
|
+
}
|
119
|
+
where.compositeFilter.filters.push({ fieldFilter: fieldFilter });
|
120
|
+
}
|
121
|
+
|
122
|
+
let body = {
|
123
|
+
structuredQuery: {
|
124
|
+
where: where,
|
125
|
+
from: [{ collectionId: collectionId }]
|
126
|
+
}
|
127
|
+
};
|
128
|
+
data = await this.#runQuery(body);
|
129
|
+
data = data ? JSON.parse(data) : [];
|
130
|
+
if (data && data.length && data[0].document) {
|
131
|
+
return data.map(d => {
|
132
|
+
return this.parseDocument(d.document);
|
133
|
+
});
|
134
|
+
}
|
135
|
+
}
|
136
|
+
else {
|
137
|
+
data = await this.#read(collectionId, pageSize, nextPageToken);
|
138
|
+
data = data ? JSON.parse(data) : {};
|
139
|
+
if (data.documents && data.documents.length) {
|
140
|
+
const list = data.documents.map(d => {
|
141
|
+
return this.parseDocument(d);
|
142
|
+
});
|
143
|
+
return data.nextPageToken ? {
|
144
|
+
data: list,
|
145
|
+
nextPageToken: data.nextPageToken
|
146
|
+
} : list;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
return [];
|
151
|
+
}
|
152
|
+
|
153
|
+
getCollectionId(collection) {
|
154
|
+
// Document if is generated with given prefix.
|
155
|
+
return `${this.#collectionPrefix}_${collection}`;
|
156
|
+
}
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Send http requst.
|
160
|
+
* @param httpMethod GET/POST/PATCH/DELETE
|
161
|
+
* @param url Url for the request.
|
162
|
+
* @param params Optional query params.
|
163
|
+
* @param data Optional request body.
|
164
|
+
* @param options Optional options.
|
165
|
+
* @returns Result set.
|
166
|
+
*/
|
167
|
+
async sendRequest(httpMethod, url, params = null, data = null, options = null) {
|
168
|
+
const urlObj = new URL(url);
|
169
|
+
// Populate uri params to the URL object.
|
170
|
+
if (params) {
|
171
|
+
for (const [key, value] of Object.entries(params)) {
|
172
|
+
if (value) {
|
173
|
+
// If value is a array, populate it with same name.
|
174
|
+
if (typeof value === 'object') {
|
175
|
+
for (const val of value) {
|
176
|
+
if (val)
|
177
|
+
urlObj.searchParams.append(key, val);
|
178
|
+
}
|
179
|
+
}
|
180
|
+
else
|
181
|
+
urlObj.searchParams.set(key, value);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
let reqOptions = {
|
187
|
+
method: httpMethod,
|
188
|
+
protocol: urlObj.protocol,
|
189
|
+
hostname: urlObj.hostname,
|
190
|
+
port: urlObj.port,
|
191
|
+
path: `${urlObj.pathname}?${urlObj.searchParams.toString()}`,
|
192
|
+
...(options ? options : {})
|
193
|
+
};
|
194
|
+
|
195
|
+
return new Promise(async (resolve, reject) => {
|
196
|
+
// Send request and collect data chuncks to a buffer. Then return the result set on end event.
|
197
|
+
// Resolve only if the status is 200.
|
198
|
+
const req = https.request(reqOptions, (res) => {
|
199
|
+
let resData = '';
|
200
|
+
res.on('data', (d) => {
|
201
|
+
resData += d;
|
202
|
+
});
|
203
|
+
res.on('end', async () => {
|
204
|
+
if (res.statusCode === 200) {
|
205
|
+
resolve(resData);
|
206
|
+
return;
|
207
|
+
}
|
208
|
+
reject({ status: res.statusCode, data: resData });
|
209
|
+
return;
|
210
|
+
});
|
211
|
+
}).on('error', async (e) => {
|
212
|
+
reject(e);
|
213
|
+
return;
|
214
|
+
});
|
215
|
+
|
216
|
+
if (data)
|
217
|
+
req.write(typeof data === 'object' ? JSON.stringify(data) : data);
|
218
|
+
|
219
|
+
req.end();
|
220
|
+
});
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* Generate url for firestore.
|
225
|
+
* @param collectionId Optional collection name.
|
226
|
+
* @param documentId Optional document name.
|
227
|
+
* @param isQuery Whether a query request or not.
|
228
|
+
* @returns The generated url.
|
229
|
+
*/
|
230
|
+
buildApiPath(collectionId = null, documentId = null, isQuery = false) {
|
231
|
+
let path = `https://firestore.googleapis.com/v1/projects/${this.#projectId}/databases/(default)/documents`;
|
232
|
+
if (collectionId)
|
233
|
+
path += `/${collectionId.toString()}`;
|
234
|
+
if (documentId)
|
235
|
+
path += `/${documentId.toString()}`;
|
236
|
+
if (isQuery)
|
237
|
+
path += ':runQuery';
|
238
|
+
return path;
|
239
|
+
}
|
240
|
+
|
241
|
+
/**
|
242
|
+
* Generate firestore document value object from given key and value, Convert camelCase key to snake_case.
|
243
|
+
* @param key Name of the value.
|
244
|
+
* @param value Value to be parsed.
|
245
|
+
* @returns Generated firestore document object.
|
246
|
+
*/
|
247
|
+
convertValue(key, value) {
|
248
|
+
// Convert camelCase to snake_case.
|
249
|
+
const uKey = key.replace(/([A-Z])/g, function (g) { return `_${g[0].toLocaleLowerCase()}`; });
|
250
|
+
let val = {};
|
251
|
+
let type
|
252
|
+
switch (typeof value) {
|
253
|
+
case 'number':
|
254
|
+
type = (value % 1 > 0 ? 'float' : 'integer');
|
255
|
+
val = value;
|
256
|
+
break;
|
257
|
+
case 'object':
|
258
|
+
type = 'map';
|
259
|
+
val = {
|
260
|
+
fields: {}
|
261
|
+
}
|
262
|
+
// Prepare the firestore write body with the given data object.
|
263
|
+
for (const [subKey, subValue] of Object.entries(value)) {
|
264
|
+
const field = this.convertValue(subKey, subValue);
|
265
|
+
val.fields[field.key] = field.value;
|
266
|
+
}
|
267
|
+
break;
|
268
|
+
default:
|
269
|
+
type = 'string';
|
270
|
+
val = value;
|
271
|
+
break;
|
272
|
+
}
|
273
|
+
type = `${type}Value`;
|
274
|
+
let obj = {};
|
275
|
+
obj[type] = val;
|
276
|
+
return { key: uKey, value: obj };
|
277
|
+
}
|
278
|
+
|
279
|
+
/**
|
280
|
+
* Convert the firestore document to human readable simplified json object.
|
281
|
+
* @param document Firestore document.
|
282
|
+
* @returns Simplified json object for the document.
|
283
|
+
*/
|
284
|
+
parseDocument(document) {
|
285
|
+
let item = {
|
286
|
+
id: document.name.split('/').pop(),
|
287
|
+
createTime: new Date(document.createTime),
|
288
|
+
updateTime: new Date(document.updateTime)
|
289
|
+
};
|
290
|
+
for (const [key, value] of Object.entries(document.fields)) {
|
291
|
+
const field = this.#parseValue(key, value);
|
292
|
+
item[field.key] = field.value;
|
293
|
+
}
|
294
|
+
return item;
|
295
|
+
}
|
296
|
+
|
297
|
+
async getHosts(filters = null, pageSize = null, nextPageToken = null) {
|
298
|
+
return await this.#getDocuments(this.getCollectionId('hosts'), filters, pageSize, nextPageToken);
|
299
|
+
}
|
300
|
+
|
301
|
+
async getConfigs(filters = null, pageSize = null, nextPageToken = null) {
|
302
|
+
return await this.#getDocuments(this.getCollectionId('configs'), filters, pageSize, nextPageToken);
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
module.exports = {
|
307
|
+
FirestoreHandler,
|
308
|
+
FirestoreOperations
|
309
|
+
}
|
package/src/index.js
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
const { Defaults } = require('./defaults');
|
2
|
+
const { RegistryClient, RegistryEvents } = require("./clients/registry-client");
|
3
|
+
const { TenantClient, TenantEvents } = require("./clients/tenant-client");
|
4
|
+
const { HostClient, HostEvents } = require("./clients/host-client");
|
5
|
+
const { XrplApi } = require('./xrpl-api');
|
6
|
+
const { XrplApiEvents, XrplConstants } = require('./xrpl-common');
|
7
|
+
const { XrplAccount } = require('./xrpl-account');
|
8
|
+
const { EvernodeConstants, HookStateKeys, MemoTypes } = require('./evernode-common');
|
9
|
+
const { XflHelpers } = require('./xfl-helpers');
|
10
|
+
const { FirestoreHandler } = require('./firestore/firestore-handler');
|
11
|
+
const { StateHelpers } = require('./state-helpers');
|
12
|
+
const { UtilHelpers } = require('./util-helpers');
|
13
|
+
const { TransactionHelper } = require('./transaction-helper');
|
14
|
+
const { EncryptionHelper } = require('./encryption-helper');
|
15
|
+
|
16
|
+
module.exports = {
|
17
|
+
RegistryClient,
|
18
|
+
RegistryEvents,
|
19
|
+
TenantClient,
|
20
|
+
TenantEvents,
|
21
|
+
HostClient,
|
22
|
+
HostEvents,
|
23
|
+
XrplApi,
|
24
|
+
XrplApiEvents,
|
25
|
+
XrplConstants,
|
26
|
+
XrplAccount,
|
27
|
+
EvernodeConstants,
|
28
|
+
Defaults,
|
29
|
+
XflHelpers,
|
30
|
+
StateHelpers,
|
31
|
+
FirestoreHandler,
|
32
|
+
UtilHelpers,
|
33
|
+
TransactionHelper,
|
34
|
+
EncryptionHelper,
|
35
|
+
HookStateKeys,
|
36
|
+
MemoTypes
|
37
|
+
}
|