From 6f7746c564778dd6c60e2a97aafbb4ade274ae1b Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 12 Sep 2024 16:23:20 +0100 Subject: [PATCH 01/15] refactor: don't use parameter in DB query --- lib/plugins.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins.js b/lib/plugins.js index fe6b5b79..5eb83f51 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -239,12 +239,12 @@ function plugins (settings, deviceId) { function fetchCurrentConfigVersion () { const sql = `select id from user_config - where type=$1 + where type='config' and valid order by id desc limit 1` - return db.one(sql, ['config']) + return db.one(sql) .then(row => row.id) } From 083a7764d2407410c90f2227072940fe0aa12d07 Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 12 Sep 2024 16:24:41 +0100 Subject: [PATCH 02/15] chore: query formatting --- lib/plugins.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/plugins.js b/lib/plugins.js index 5eb83f51..bd7a1a92 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -238,14 +238,12 @@ function plugins (settings, deviceId) { } function fetchCurrentConfigVersion () { - const sql = `select id from user_config - where type='config' - and valid - order by id desc - limit 1` - - return db.one(sql) - .then(row => row.id) + const sql = `SELECT id FROM user_config + WHERE type = 'config' + AND valid + ORDER BY id DESC + LIMIT 1` + return db.one(sql).then(row => row.id) } function mapCoinSettings (coinParams) { From 446ac9f8dbe957be7568f239ea39a5b0b28e759b Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 12 Sep 2024 16:38:37 +0100 Subject: [PATCH 03/15] chore: SQL formatting --- lib/new-settings-loader.js | 73 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index 58a490cd..ec830209 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -56,9 +56,10 @@ const addTermsHash = configs => { )(terms) } -const accountsSql = `update user_config set data = $2, valid = $3, schema_version = $4 where type = $1; -insert into user_config (type, data, valid, schema_version) -select $1, $2, $3, $4 where $1 not in (select type from user_config)` +const accountsSql = `UPDATE user_config SET data = $2, valid = $3, schema_version = $4 WHERE type = $1; +INSERT INTO user_config (type, data, valid, schema_version) +SELECT $1, $2, $3, $4 WHERE $1 NOT IN (SELECT type FROM user_config)` + function saveAccounts (accounts) { return Promise.all([loadAccounts(), getOperatorId('middleware')]) .then(([currentAccounts, operatorId]) => { @@ -92,13 +93,13 @@ function resetAccounts (schemaVersion) { } function loadAccounts (schemaVersion) { - const sql = `select data - from user_config - where type=$1 - and schema_version=$2 - and valid - order by id desc - limit 1` + const sql = `SELECT data + FROM user_config + WHERE type = $1 + AND schema_version = $2 + AND valid + ORDER BY id DESC + LIMIT 1` return db.oneOrNone(sql, ['accounts', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(_.compose(_.defaultTo({}), _.get('data.accounts'))) @@ -116,7 +117,7 @@ function showAccounts (schemaVersion) { }) } -const configSql = 'insert into user_config (type, data, valid, schema_version) values ($1, $2, $3, $4)' +const configSql = 'INSERT INTO user_config (type, data, valid, schema_version) VALUES ($1, $2, $3, $4)' function saveConfig (config) { return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')]) .then(([currentConfig, operatorId]) => { @@ -170,13 +171,13 @@ function loadLatest (schemaVersion) { } function loadLatestConfig () { - const sql = `select data - from user_config - where type=$1 - and schema_version=$2 - and valid - order by id desc - limit 1` + const sql = `SELECT data + FROM user_config + WHERE type = $1 + AND schema_version = $2 + AND valid + ORDER BY id DESC + LIMIT 1` return db.one(sql, ['config', NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row ? row.data.config : {}) @@ -186,36 +187,36 @@ function loadLatestConfig () { } function loadLatestConfigOrNoneReturningVersion (schemaVersion) { - const sql = `select data, id - from user_config - where type=$1 - and schema_version=$2 - order by id desc - limit 1` + const sql = `SELECT data, id + FROM user_config + WHERE type = $1 + AND schema_version = $2 + ORDER BY id DESC + LIMIT 1` return db.oneOrNone(sql, ['config', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row ? { config: row.data.config, version: row.id } : {}) } function loadLatestConfigOrNone (schemaVersion) { - const sql = `select data - from user_config - where type=$1 - and schema_version=$2 - order by id desc - limit 1` + const sql = `SELECT data + FROM user_config + WHERE type = $1 + AND schema_version = $2 + ORDER BY id DESC + LIMIT 1` return db.oneOrNone(sql, ['config', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row ? row.data.config : {}) } function loadConfig (versionId) { - const sql = `select data - from user_config - where id=$1 - and type=$2 - and schema_version=$3 - and valid` + const sql = `SELECT data + FROM user_config + WHERE id = $1 + AND type = $2 + AND schema_version = $3 + AND valid` return db.one(sql, [versionId, 'config', NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row.data.config) From 19c7d73444e58618af9b9b45b36ae4bc08505d91 Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 12 Sep 2024 17:07:31 +0100 Subject: [PATCH 04/15] chore: remove dead code --- lib/config-migration.js | 6 +- .../graphql/resolvers/settings.resolver.js | 3 - lib/new-admin/graphql/types/settings.type.js | 3 - lib/new-settings-loader.js | 23 ------ .../src/pages/ConfigMigration.js | 82 ------------------- new-lamassu-admin/src/routing/routes.js | 1 - 6 files changed, 1 insertion(+), 117 deletions(-) delete mode 100644 new-lamassu-admin/src/pages/ConfigMigration.js diff --git a/lib/config-migration.js b/lib/config-migration.js index b6f16e10..493bc4ea 100644 --- a/lib/config-migration.js +++ b/lib/config-migration.js @@ -474,8 +474,4 @@ function migrate (config, accounts) { } } -module.exports = { - migrateConfig, - migrateAccounts, - migrate -} +module.exports = { migrate } diff --git a/lib/new-admin/graphql/resolvers/settings.resolver.js b/lib/new-admin/graphql/resolvers/settings.resolver.js index 383f9309..e0453511 100644 --- a/lib/new-admin/graphql/resolvers/settings.resolver.js +++ b/lib/new-admin/graphql/resolvers/settings.resolver.js @@ -7,10 +7,7 @@ const resolvers = { }, Mutation: { saveAccounts: (...[, { accounts }]) => settingsLoader.saveAccounts(accounts), - // resetAccounts: (...[, { schemaVersion }]) => settingsLoader.resetAccounts(schemaVersion), saveConfig: (...[, { config }]) => settingsLoader.saveConfig(config), - // resetConfig: (...[, { schemaVersion }]) => settingsLoader.resetConfig(schemaVersion), - // migrateConfigAndAccounts: () => settingsLoader.migrate() } } diff --git a/lib/new-admin/graphql/types/settings.type.js b/lib/new-admin/graphql/types/settings.type.js index 3385a585..8079b18a 100644 --- a/lib/new-admin/graphql/types/settings.type.js +++ b/lib/new-admin/graphql/types/settings.type.js @@ -8,10 +8,7 @@ const typeDef = gql` type Mutation { saveAccounts(accounts: JSONObject): JSONObject @auth - # resetAccounts(schemaVersion: Int): JSONObject @auth saveConfig(config: JSONObject): JSONObject @auth - # resetConfig(schemaVersion: Int): JSONObject @auth - # migrateConfigAndAccounts: JSONObject @auth } ` diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index ec830209..eb9def63 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -80,17 +80,6 @@ function saveAccounts (accounts) { }).catch(console.error) }) } -function resetAccounts (schemaVersion) { - return db.none( - accountsSql, - [ - 'accounts', - { accounts: NEW_SETTINGS_LOADER_SCHEMA_VERSION ? {} : [] }, - true, - schemaVersion - ] - ) -} function loadAccounts (schemaVersion) { const sql = `SELECT data @@ -149,18 +138,6 @@ function migrationSaveConfig (config) { }) } -function resetConfig (schemaVersion) { - return db.none( - configSql, - [ - 'config', - { config: schemaVersion === NEW_SETTINGS_LOADER_SCHEMA_VERSION ? {} : [] }, - true, - schemaVersion - ] - ) -} - function loadLatest (schemaVersion) { return Promise.all([loadLatestConfigOrNoneReturningVersion(schemaVersion), loadAccounts(schemaVersion)]) .then(([configObj, accounts]) => ({ diff --git a/new-lamassu-admin/src/pages/ConfigMigration.js b/new-lamassu-admin/src/pages/ConfigMigration.js deleted file mode 100644 index 6da89560..00000000 --- a/new-lamassu-admin/src/pages/ConfigMigration.js +++ /dev/null @@ -1,82 +0,0 @@ -import { useMutation } from '@apollo/react-hooks' -import { Box } from '@material-ui/core' -import { makeStyles } from '@material-ui/core/styles' -import gql from 'graphql-tag' -import React, { useState } from 'react' - -import Title from 'src/components/Title' -import { Button } from 'src/components/buttons' - -const styles = { - button: { - marginBottom: 10 - } -} -const useStyles = makeStyles(styles) - -const RESET = gql` - mutation Reset($schemaVersion: Int) { - resetConfig(schemaVersion: $schemaVersion) - resetAccounts(schemaVersion: $schemaVersion) - } -` - -const MIGRATE = gql` - mutation Migrate { - migrateConfigAndAccounts - } -` - -const OLD_SCHEMA_VERSION = 1 -const NEW_SCHEMA_VERSION = 2 - -const ConfigMigration = () => { - const [loading, setLoading] = useState(false) - const [reset] = useMutation(RESET, { - onCompleted: () => setLoading(false) - }) - - const [migrate] = useMutation(MIGRATE, { - onCompleted: () => setLoading(false) - }) - - const classes = useStyles() - - const innerReset = schemaVersion => { - setLoading(true) - reset({ variables: { schemaVersion } }) - } - - const innerMigrate = () => { - setLoading(true) - migrate() - } - - return ( - <> - Config Migration - - - - - - - ) -} - -export default ConfigMigration diff --git a/new-lamassu-admin/src/routing/routes.js b/new-lamassu-admin/src/routing/routes.js index 18876686..78f5edcf 100644 --- a/new-lamassu-admin/src/routing/routes.js +++ b/new-lamassu-admin/src/routing/routes.js @@ -143,7 +143,6 @@ const Routes = () => { - {/* */} From ccca6c345ccbb28980bb823d2ed27e0f8aaf9835 Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 12 Sep 2024 17:08:06 +0100 Subject: [PATCH 05/15] chore: remove dead code --- lib/new-settings-loader.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index eb9def63..b9a5cefb 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -216,29 +216,15 @@ function load (versionId) { })) } -function migrate () { - return loadLatest(OLD_SETTINGS_LOADER_SCHEMA_VERSION) - .then(res => { - const migrated = migration.migrate(res.config, res.accounts) - saveConfig(migrated.config) - saveAccounts(migrated.accounts) - - return migrated - }) -} - module.exports = { saveConfig, migrationSaveConfig, - resetConfig, saveAccounts, - resetAccounts, loadAccounts, showAccounts, loadLatest, loadLatestConfig, loadLatestConfigOrNone, load, - migrate, removeFromConfig } From 47692f445680ff5ebb20f93637810a8c24dea672 Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 12 Sep 2024 17:08:15 +0100 Subject: [PATCH 06/15] chore: drop unused require --- migrations/1621430588944-notify-cashbox-removal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/1621430588944-notify-cashbox-removal.js b/migrations/1621430588944-notify-cashbox-removal.js index c8a7a21b..9f3d34e0 100644 --- a/migrations/1621430588944-notify-cashbox-removal.js +++ b/migrations/1621430588944-notify-cashbox-removal.js @@ -1,5 +1,5 @@ const db = require('./db') -const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader') +const { migrationSaveConfig } = require('../lib/new-settings-loader') exports.up = function (next) { const sql = [ From 6cf170303a6a35956e12f5f748cfdc2f21d2a1f7 Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 12 Sep 2024 17:23:56 +0100 Subject: [PATCH 07/15] refactor: extract duplicate code into its own function --- lib/new-settings-loader.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index b9a5cefb..9d4d5d50 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -106,15 +106,20 @@ function showAccounts (schemaVersion) { }) } -const configSql = 'INSERT INTO user_config (type, data, valid, schema_version) VALUES ($1, $2, $3, $4)' +const insertConfigRow = (dbOrTx, data) => + dbOrTx.none( + "INSERT INTO user_config (type, data, valid, schema_version) VALUES ('config', $1, TRUE, $2)", + [data, NEW_SETTINGS_LOADER_SCHEMA_VERSION] + ) + function saveConfig (config) { return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')]) .then(([currentConfig, operatorId]) => { const newConfig = addTermsHash(_.assign(currentConfig, config)) - return db.tx(t => { - return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.tx(t => + insertConfigRow(t, { config: newConfig }) .then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })])) - }).catch(console.error) + ).catch(console.error) }) } @@ -122,10 +127,10 @@ function removeFromConfig (fields) { return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')]) .then(([currentConfig, operatorId]) => { const newConfig = _.omit(fields, currentConfig) - return db.tx(t => { - return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.tx(t => + insertConfigRow(t, { config: newConfig }) .then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })])) - }).catch(console.error) + ).catch(console.error) }) } @@ -133,7 +138,7 @@ function migrationSaveConfig (config) { return loadLatestConfigOrNone() .then(currentConfig => { const newConfig = _.assign(currentConfig, config) - return db.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return insertConfigRow(db, { config: newConfig }) .catch(console.error) }) } From 3a548fea6f7e82be63196892d0e475e732b47b07 Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 12 Sep 2024 17:24:24 +0100 Subject: [PATCH 08/15] refactor: extract duplicate code into its own function --- lib/new-settings-loader.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index 9d4d5d50..199c2481 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -56,6 +56,12 @@ const addTermsHash = configs => { )(terms) } +const notifyReload = (dbOrTx, operatorId) => + dbOrTx.none( + 'NOTIFY $1:name, $2', + ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })] + ) + const accountsSql = `UPDATE user_config SET data = $2, valid = $3, schema_version = $4 WHERE type = $1; INSERT INTO user_config (type, data, valid, schema_version) SELECT $1, $2, $3, $4 WHERE $1 NOT IN (SELECT type FROM user_config)` @@ -76,7 +82,7 @@ function saveAccounts (accounts) { return db.tx(t => { return t.none(accountsSql, ['accounts', { accounts: newAccounts }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) - .then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })])) + .then(() => notifyReload(t, operatorId)) }).catch(console.error) }) } @@ -118,7 +124,7 @@ function saveConfig (config) { const newConfig = addTermsHash(_.assign(currentConfig, config)) return db.tx(t => insertConfigRow(t, { config: newConfig }) - .then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })])) + .then(() => notifyReload(t, operatorId)) ).catch(console.error) }) } @@ -129,7 +135,7 @@ function removeFromConfig (fields) { const newConfig = _.omit(fields, currentConfig) return db.tx(t => insertConfigRow(t, { config: newConfig }) - .then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })])) + .then(() => notifyReload(t, operatorId)) ).catch(console.error) }) } From 437a1d3505259b5e6e314ba91bebcc869b9550ab Mon Sep 17 00:00:00 2001 From: siiky Date: Tue, 17 Sep 2024 14:57:30 +0100 Subject: [PATCH 09/15] refactor: remove unnecessary query parameters --- lib/new-settings-loader.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index 199c2481..229ed2dd 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -161,13 +161,13 @@ function loadLatest (schemaVersion) { function loadLatestConfig () { const sql = `SELECT data FROM user_config - WHERE type = $1 - AND schema_version = $2 + WHERE type = 'config' + AND schema_version = $1 AND valid ORDER BY id DESC LIMIT 1` - return db.one(sql, ['config', NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.one(sql, [NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row ? row.data.config : {}) .catch(err => { throw err @@ -177,24 +177,25 @@ function loadLatestConfig () { function loadLatestConfigOrNoneReturningVersion (schemaVersion) { const sql = `SELECT data, id FROM user_config - WHERE type = $1 - AND schema_version = $2 + WHERE type = 'config' + AND schema_version = $1 + AND valid ORDER BY id DESC LIMIT 1` - return db.oneOrNone(sql, ['config', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.oneOrNone(sql, [schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row ? { config: row.data.config, version: row.id } : {}) } function loadLatestConfigOrNone (schemaVersion) { const sql = `SELECT data FROM user_config - WHERE type = $1 - AND schema_version = $2 + WHERE type = 'config' + AND schema_version = $1 ORDER BY id DESC LIMIT 1` - return db.oneOrNone(sql, ['config', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.oneOrNone(sql, [schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row ? row.data.config : {}) } @@ -202,11 +203,11 @@ function loadConfig (versionId) { const sql = `SELECT data FROM user_config WHERE id = $1 - AND type = $2 - AND schema_version = $3 + AND type = 'config' + AND schema_version = $2 AND valid` - return db.one(sql, [versionId, 'config', NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.one(sql, [versionId, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row.data.config) .catch(err => { if (err.name === 'QueryResultError') { From 5342e9a8beb2afc6e29930132538efa247e9ebde Mon Sep 17 00:00:00 2001 From: siiky Date: Tue, 17 Sep 2024 14:58:58 +0100 Subject: [PATCH 10/15] fix: use more specific function `loadLatest()` returns a `Promise`, therefore accessing the `config` property is not correct. --- lib/machine-loader.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/machine-loader.js b/lib/machine-loader.js index fe08f15e..dcaa78b6 100644 --- a/lib/machine-loader.js +++ b/lib/machine-loader.js @@ -11,7 +11,6 @@ const pairing = require('./pairing') const { checkPings, checkStuckScreen } = require('./notifier') const dbm = require('./postgresql_interface') const configManager = require('./new-config-manager') -const settingsLoader = require('./new-settings-loader') const notifierUtils = require('./notifier/utils') const notifierQueries = require('./notifier/queries') const { ApolloError } = require('apollo-server-errors'); @@ -94,9 +93,7 @@ function getUnpairedMachines () { } function getConfig (defaultConfig) { - if (defaultConfig) return Promise.resolve(defaultConfig) - - return settingsLoader.loadLatest().config + return defaultConfig ? Promise.resolve(defaultConfig) : loadLatestConfig() } const getStatus = (ping, stuck) => { @@ -529,7 +526,6 @@ module.exports = { updateNetworkHeartbeat, getNetworkPerformance, getNetworkHeartbeat, - getConfig, getMachineIds, emptyMachineUnits, refillMachineUnits, From cac88fda45779e4b356a8dec16aa44ffc0578a64 Mon Sep 17 00:00:00 2001 From: siiky Date: Tue, 17 Sep 2024 15:38:35 +0100 Subject: [PATCH 11/15] refactor: use `loadLatestConfig()` in place of `loadLatest()` where applicable --- lib/admin/settings-loader.js | 1 + lib/cash-in/cash-in-tx.js | 4 ++-- lib/notifier/index.js | 10 +++++----- .../1617742522808-zeroConfLimit-migrate.js | 4 ++-- migrations/1618507684019-rename-0-conf.js | 2 +- ...683-fiat-balance-notification-to-percent.js | 6 +++--- migrations/1620319260238-timezones.js | 4 ++-- ...1623975493095-add-crypto-units-to-config.js | 8 ++++---- .../1630432869178-add-more-cassette-support.js | 12 ++++++------ ...45010873828-add-advanced-wallet-settings.js | 18 ++++++++---------- migrations/1645459054117-default-timezone.js | 9 ++++----- .../1655807727853-default_timezone_fix.js | 9 ++++----- ...0716689-remove-coin-specific-cryptounits.js | 8 ++++---- .../1661125970289-eth-zero-conf-value.js | 9 ++++----- migrations/migrate-tools.js | 4 ++-- 15 files changed, 52 insertions(+), 56 deletions(-) diff --git a/lib/admin/settings-loader.js b/lib/admin/settings-loader.js index eccd677f..ef5b2f2c 100644 --- a/lib/admin/settings-loader.js +++ b/lib/admin/settings-loader.js @@ -240,6 +240,7 @@ module.exports = { loadRecentConfig, load, loadLatest, + loadLatestConfig, save, loadFixture, mergeValues, diff --git a/lib/cash-in/cash-in-tx.js b/lib/cash-in/cash-in-tx.js index 57ea8ccf..8f7703cb 100644 --- a/lib/cash-in/cash-in-tx.js +++ b/lib/cash-in/cash-in-tx.js @@ -36,7 +36,7 @@ function post (machineTx, pi) { let addressReuse = false let walletScore = {} - const promises = [settingsLoader.loadLatest()] + const promises = [settingsLoader.loadLatestConfig()] const isFirstPost = !r.tx.fiat || r.tx.fiat.isZero() if (isFirstPost) { @@ -44,7 +44,7 @@ function post (machineTx, pi) { } return Promise.all(promises) - .then(([{ config }, blacklistItems = false, isReusedAddress = false, fetchedWalletScore = null]) => { + .then(([config, blacklistItems = false, isReusedAddress = false, fetchedWalletScore = null]) => { const rejectAddressReuse = configManager.getCompliance(config).rejectAddressReuse walletScore = fetchedWalletScore diff --git a/lib/notifier/index.js b/lib/notifier/index.js index 8bad9a51..8e61f162 100644 --- a/lib/notifier/index.js +++ b/lib/notifier/index.js @@ -130,8 +130,8 @@ function checkStuckScreen (deviceEvents, machine) { } function transactionNotify (tx, rec) { - return settingsLoader.loadLatest().then(settings => { - const notifSettings = configManager.getGlobalNotifications(settings.config) + return settingsLoader.loadLatestConfig().then(config => { + const notifSettings = configManager.getGlobalNotifications(config) const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity) const isCashOut = tx.direction === 'cashOut' @@ -147,7 +147,7 @@ function transactionNotify (tx, rec) { } // alert through sms or email any transaction or high value transaction, if SMS || email alerts are enabled - const walletSettings = configManager.getWalletSettings(tx.cryptoCode, settings.config) + const walletSettings = configManager.getWalletSettings(tx.cryptoCode, config) const zeroConfLimit = walletSettings.zeroConfLimit || 0 const zeroConf = isCashOut && tx.fiat.lte(zeroConfLimit) const notificationsEnabled = notifSettings.sms.transactions || notifSettings.email.transactions @@ -308,8 +308,8 @@ function cashboxNotify (deviceId) { // for notification center, check if type of notification is active before calling the respective notify function const notifyIfActive = (type, fnName, ...args) => { - return settingsLoader.loadLatest().then(settings => { - const notificationSettings = configManager.getGlobalNotifications(settings.config).notificationCenter + return settingsLoader.loadLatestConfig().then(config => { + const notificationSettings = configManager.getGlobalNotifications(config).notificationCenter if (!notificationCenter[fnName]) return Promise.reject(new Error(`Notification function ${fnName} for type ${type} does not exist`)) if (!(notificationSettings.active && notificationSettings[type])) return Promise.resolve() return notificationCenter[fnName](...args) diff --git a/migrations/1617742522808-zeroConfLimit-migrate.js b/migrations/1617742522808-zeroConfLimit-migrate.js index 789c0656..4aa746a9 100644 --- a/migrations/1617742522808-zeroConfLimit-migrate.js +++ b/migrations/1617742522808-zeroConfLimit-migrate.js @@ -5,9 +5,9 @@ const configManager = require('../lib/new-config-manager') exports.up = function (next) { return db.tx(async t => { - const settingsPromise = settingsLoader.loadLatest() + const settingsPromise = settingsLoader.loadLatestConfig() const machinesPromise = t.any('SELECT device_id FROM devices') - const [{ config }, machines] = await Promise.all([settingsPromise, machinesPromise]) + const [config, machines] = await Promise.all([settingsPromise, machinesPromise]) const cryptoCodes = configManager.getCryptosFromWalletNamespace(config) const deviceIds = _.map(_.get('device_id'))(machines) diff --git a/migrations/1618507684019-rename-0-conf.js b/migrations/1618507684019-rename-0-conf.js index 7a416239..79d7e437 100644 --- a/migrations/1618507684019-rename-0-conf.js +++ b/migrations/1618507684019-rename-0-conf.js @@ -3,7 +3,7 @@ const settingsLoader = require('../lib/new-settings-loader') const configManager = require('../lib/new-config-manager') exports.up = async function (next) { - const { config } = await settingsLoader.loadLatest() + const config = await settingsLoader.loadLatestConfig() const cryptoCodes = configManager.getCryptosFromWalletNamespace(config) _.forEach(cryptoCode => { const key = `wallets_${cryptoCode}_zeroConf` diff --git a/migrations/1619968992683-fiat-balance-notification-to-percent.js b/migrations/1619968992683-fiat-balance-notification-to-percent.js index a3d6b500..ed0f6628 100644 --- a/migrations/1619968992683-fiat-balance-notification-to-percent.js +++ b/migrations/1619968992683-fiat-balance-notification-to-percent.js @@ -1,10 +1,10 @@ const _ = require('lodash/fp') -const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader') +const { migrationSaveConfig, loadLatestConfig } = require('../lib/new-settings-loader') const CASSETTE_MAX_CAPACITY = 500 exports.up = function (next) { - return loadLatest() - .then(({ config }) => { + return loadLatestConfig() + .then(config => { const fiatBalance1 = config.notifications_fiatBalanceCassette1 const fiatBalance2 = config.notifications_fiatBalanceCassette2 const fiatBalance3 = config.notifications_fiatBalanceCassette3 diff --git a/migrations/1620319260238-timezones.js b/migrations/1620319260238-timezones.js index c2d08484..789655b8 100644 --- a/migrations/1620319260238-timezones.js +++ b/migrations/1620319260238-timezones.js @@ -2,8 +2,8 @@ const _ = require('lodash/fp') const settingsLoader = require('../lib/new-settings-loader') exports.up = function (next) { - settingsLoader.loadLatest() - .then(({ config }) => { + settingsLoader.loadLatestConfig() + .then(config => { if (!_.isEmpty(config)) config.locale_timezone = '0:0' return settingsLoader.migrationSaveConfig(config) diff --git a/migrations/1623975493095-add-crypto-units-to-config.js b/migrations/1623975493095-add-crypto-units-to-config.js index 95d87c07..c251b2b1 100644 --- a/migrations/1623975493095-add-crypto-units-to-config.js +++ b/migrations/1623975493095-add-crypto-units-to-config.js @@ -1,13 +1,13 @@ -const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader') +const { migrationSaveConfig, loadLatestConfig } = require('../lib/new-settings-loader') const { getCryptosFromWalletNamespace } = require('../lib/new-config-manager.js') const { utils: coinUtils } = require('@lamassu/coins') const _ = require('lodash/fp') exports.up = function (next) { - loadLatest() - .then(settings => { + loadLatestConfig() + .then(config => { const newSettings = {} - const activeCryptos = getCryptosFromWalletNamespace(settings.config) + const activeCryptos = getCryptosFromWalletNamespace(config) if (!activeCryptos.length) return Promise.resolve() _.map(crypto => { const defaultUnit = _.head(_.keys(coinUtils.getCryptoCurrency(crypto).units)) diff --git a/migrations/1630432869178-add-more-cassette-support.js b/migrations/1630432869178-add-more-cassette-support.js index 70c906fe..5c73a17b 100644 --- a/migrations/1630432869178-add-more-cassette-support.js +++ b/migrations/1630432869178-add-more-cassette-support.js @@ -1,6 +1,6 @@ var db = require('./db') const _ = require('lodash/fp') -const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader') +const { migrationSaveConfig, loadLatestConfig } = require('../lib/new-settings-loader') const { getMachineIds } = require('../lib/machine-loader') exports.up = function (next) { @@ -25,16 +25,16 @@ exports.up = function (next) { ADD COLUMN denomination_4 INTEGER` ] - return Promise.all([loadLatest(), getMachineIds()]) + return Promise.all([loadLatestConfig(), getMachineIds()]) .then(([config, machineIds]) => { const newConfig = _.reduce((acc, value) => { const deviceId = value.device_id - if (_.includes(`cashOut_${deviceId}_top`, _.keys(config.config))) { - acc[`cashOut_${deviceId}_cassette1`] = config.config[`cashOut_${deviceId}_top`] + if (_.includes(`cashOut_${deviceId}_top`, _.keys(config))) { + acc[`cashOut_${deviceId}_cassette1`] = config[`cashOut_${deviceId}_top`] } - if (_.includes(`cashOut_${deviceId}_bottom`, _.keys(config.config))) { - acc[`cashOut_${deviceId}_cassette2`] = config.config[`cashOut_${deviceId}_bottom`] + if (_.includes(`cashOut_${deviceId}_bottom`, _.keys(config))) { + acc[`cashOut_${deviceId}_cassette2`] = config[`cashOut_${deviceId}_bottom`] } return acc diff --git a/migrations/1645010873828-add-advanced-wallet-settings.js b/migrations/1645010873828-add-advanced-wallet-settings.js index 34252cc6..1b2fad5d 100644 --- a/migrations/1645010873828-add-advanced-wallet-settings.js +++ b/migrations/1645010873828-add-advanced-wallet-settings.js @@ -1,16 +1,14 @@ const uuid = require('uuid') -const { saveConfig, loadLatest } = require('../lib/new-settings-loader') +const { saveConfig } = require('../lib/new-settings-loader') exports.up = function (next) { - const newConfig = {} - return loadLatest() - .then(config => { - newConfig[`wallets_advanced_feeMultiplier`] = '1' - newConfig[`wallets_advanced_cryptoUnits`] = 'full' - newConfig[`wallets_advanced_allowTransactionBatching`] = false - newConfig[`wallets_advanced_id`] = uuid.v4() - return saveConfig(newConfig) - }) + const newConfig = { + wallets_advanced_feeMultiplier: '1', + wallets_advanced_cryptoUnits: 'full', + wallets_advanced_allowTransactionBatching: false, + wallets_advanced_id: uuid.v4(), + } + return saveConfig(newConfig) .then(next) .catch(err => { return next(err) diff --git a/migrations/1645459054117-default-timezone.js b/migrations/1645459054117-default-timezone.js index d2e64fb0..de8ef590 100644 --- a/migrations/1645459054117-default-timezone.js +++ b/migrations/1645459054117-default-timezone.js @@ -1,12 +1,11 @@ const _ = require('lodash/fp') -const { saveConfig, loadLatest } = require('../lib/new-settings-loader') +const { saveConfig, loadLatestConfig } = require('../lib/new-settings-loader') exports.up = function (next) { - const newConfig = {} - return loadLatest() + return loadLatestConfig() .then(config => { - if (!_.isNil(config.config.locale_timezone)) return - newConfig[`locale_timezone`] = 'GMT' + if (!_.isNil(config.locale_timezone)) return + const newConfig = { locale_timezone: 'GMT' } return saveConfig(newConfig) }) .then(next) diff --git a/migrations/1655807727853-default_timezone_fix.js b/migrations/1655807727853-default_timezone_fix.js index fba11a26..6d2cc4a7 100644 --- a/migrations/1655807727853-default_timezone_fix.js +++ b/migrations/1655807727853-default_timezone_fix.js @@ -1,11 +1,10 @@ -const { saveConfig, loadLatest } = require('../lib/new-settings-loader') +const { saveConfig, loadLatestConfig } = require('../lib/new-settings-loader') exports.up = function (next) { - const newConfig = {} - return loadLatest() + return loadLatestConfig() .then(config => { - if (config.config.locale_timezone === "0:0") { - newConfig[`locale_timezone`] = 'GMT' + if (config.locale_timezone === "0:0") { + const newConfig = { locale_timezone: 'GMT' } return saveConfig(newConfig) } }) diff --git a/migrations/1658940716689-remove-coin-specific-cryptounits.js b/migrations/1658940716689-remove-coin-specific-cryptounits.js index e91bca7c..d9bada35 100644 --- a/migrations/1658940716689-remove-coin-specific-cryptounits.js +++ b/migrations/1658940716689-remove-coin-specific-cryptounits.js @@ -1,11 +1,11 @@ -const { removeFromConfig, loadLatest } = require('../lib/new-settings-loader') +const { removeFromConfig, loadLatestConfig } = require('../lib/new-settings-loader') const { getCryptosFromWalletNamespace } = require('../lib/new-config-manager.js') const _ = require('lodash/fp') exports.up = function (next) { - loadLatest() - .then(settings => { - const configuredCryptos = getCryptosFromWalletNamespace(settings.config) + loadLatestConfig() + .then(config => { + const configuredCryptos = getCryptosFromWalletNamespace(config) if (!configuredCryptos.length) return Promise.resolve() return removeFromConfig(_.map(it => `wallets_${it}_cryptoUnits`, configuredCryptos)) diff --git a/migrations/1661125970289-eth-zero-conf-value.js b/migrations/1661125970289-eth-zero-conf-value.js index 4376d67e..1dda9e4e 100644 --- a/migrations/1661125970289-eth-zero-conf-value.js +++ b/migrations/1661125970289-eth-zero-conf-value.js @@ -1,13 +1,12 @@ const _ = require('lodash/fp') -const { saveConfig, loadLatest } = require('../lib/new-settings-loader') +const { saveConfig, loadLatestConfig } = require('../lib/new-settings-loader') exports.up = function (next) { - const newConfig = {} - return loadLatest() + return loadLatestConfig() .then(config => { - if (!_.isNil(config.config.wallets_ETH_zeroConfLimit) && config.config.wallets_ETH_zeroConfLimit !== 0) { - newConfig[`wallets_ETH_zeroConfLimit`] = 0 + if (!_.isNil(config.wallets_ETH_zeroConfLimit) && config.wallets_ETH_zeroConfLimit !== 0) { + const newConfig = { wallets_ETH_zeroConfLimit: 0 } return saveConfig(newConfig) } }) diff --git a/migrations/migrate-tools.js b/migrations/migrate-tools.js index 06f5c463..0aa1f62c 100644 --- a/migrations/migrate-tools.js +++ b/migrations/migrate-tools.js @@ -9,8 +9,8 @@ module.exports = {migrateNames} function migrateNames () { const cs = new pgp.helpers.ColumnSet(['?device_id', 'name'], {table: 'devices'}) - return settingsLoader.loadLatest(false) - .then(r => machineLoader.getMachineNames(r.config)) + return settingsLoader.loadLatestConfig(false) + .then(config => machineLoader.getMachineNames(config)) .then(_.map(r => ({device_id: r.deviceId, name: r.name}))) .then(data => pgp.helpers.update(data, cs) + ' WHERE t.device_id=v.device_id') } From d3d4042d97d641aeaf801a42bccfb69c0a3f1730 Mon Sep 17 00:00:00 2001 From: siiky Date: Tue, 17 Sep 2024 16:48:27 +0100 Subject: [PATCH 12/15] refactor: inline query parameters --- lib/new-settings-loader.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index 229ed2dd..70768037 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -62,11 +62,11 @@ const notifyReload = (dbOrTx, operatorId) => ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })] ) -const accountsSql = `UPDATE user_config SET data = $2, valid = $3, schema_version = $4 WHERE type = $1; -INSERT INTO user_config (type, data, valid, schema_version) -SELECT $1, $2, $3, $4 WHERE $1 NOT IN (SELECT type FROM user_config)` - function saveAccounts (accounts) { + const accountsSql = `UPDATE user_config SET data = $1, valid = TRUE, schema_version = $2 WHERE type = 'accounts'; + INSERT INTO user_config (type, data, valid, schema_version) + SELECT 'accounts', $1, TRUE, $2 WHERE 'accounts' NOT IN (SELECT type FROM user_config)` + return Promise.all([loadAccounts(), getOperatorId('middleware')]) .then(([currentAccounts, operatorId]) => { const newAccounts = _.merge(currentAccounts, accounts) @@ -80,10 +80,10 @@ function saveAccounts (accounts) { newAccounts.elliptic.enabled = false } - return db.tx(t => { - return t.none(accountsSql, ['accounts', { accounts: newAccounts }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.tx(t => + t.none(accountsSql, [{ accounts: newAccounts }, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(() => notifyReload(t, operatorId)) - }).catch(console.error) + ).catch(console.error) }) } From bb440eedd6cb367c53f766921b79b20ed9f78b40 Mon Sep 17 00:00:00 2001 From: siiky Date: Tue, 17 Sep 2024 18:16:10 +0100 Subject: [PATCH 13/15] refactor: move `fetchCurrentConfigVersion()` to settings loader --- lib/graphql/resolvers.js | 4 ++-- lib/new-settings-loader.js | 12 +++++++++++- lib/plugins.js | 13 ++----------- lib/routes/termsAndConditionsRoutes.js | 15 +++++---------- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/lib/graphql/resolvers.js b/lib/graphql/resolvers.js index 9b158ccf..cf4f4da0 100644 --- a/lib/graphql/resolvers.js +++ b/lib/graphql/resolvers.js @@ -3,6 +3,7 @@ const nmd = require('nano-markdown') const plugins = require('../plugins') const configManager = require('../new-config-manager') +const settingsLoader = require('../new-settings-loader') const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests') const state = require('../middlewares/state') const { getMachine } = require('../machine-loader') @@ -323,8 +324,7 @@ const terms = (parent, { currentConfigVersion, currentHash }, { deviceId, settin const isHashNew = hash !== currentHash const text = isHashNew ? latestTerms.text : null - return plugins(settings, deviceId) - .fetchCurrentConfigVersion() + return settingsLoader.fetchCurrentConfigVersion() .catch(() => null) .then(configVersion => isHashNew || _.isNil(currentConfigVersion) || currentConfigVersion < configVersion) .then(isVersionNew => isVersionNew ? _.omit(['text'], latestTerms) : null) diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index 70768037..09cb3e6a 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -228,6 +228,15 @@ function load (versionId) { })) } +const fetchCurrentConfigVersion = () => { + const sql = `SELECT id FROM user_config + WHERE type = 'config' + AND valid + ORDER BY id DESC + LIMIT 1` + return db.one(sql).then(row => row.id) +} + module.exports = { saveConfig, migrationSaveConfig, @@ -238,5 +247,6 @@ module.exports = { loadLatestConfig, loadLatestConfigOrNone, load, - removeFromConfig + removeFromConfig, + fetchCurrentConfigVersion, } diff --git a/lib/plugins.js b/lib/plugins.js index bd7a1a92..1abb0489 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -11,6 +11,7 @@ const logger = require('./logger') const logs = require('./logs') const T = require('./time') const configManager = require('./new-config-manager') +const settingsLoader = require('./new-settings-loader') const ticker = require('./ticker') const wallet = require('./wallet') const walletScoring = require('./wallet-scoring') @@ -237,15 +238,6 @@ function plugins (settings, deviceId) { .then(([cassettes, recyclers]) => ({ cassettes: cassettes.cassettes, recyclers: recyclers.recyclers })) } - function fetchCurrentConfigVersion () { - const sql = `SELECT id FROM user_config - WHERE type = 'config' - AND valid - ORDER BY id DESC - LIMIT 1` - return db.one(sql).then(row => row.id) - } - function mapCoinSettings (coinParams) { const [ cryptoCode, cryptoNetwork ] = coinParams const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config) @@ -287,7 +279,7 @@ function plugins (settings, deviceId) { return Promise.all([ buildAvailableCassettes(), buildAvailableRecyclers(), - fetchCurrentConfigVersion(), + settingsLoader.fetchCurrentConfigVersion(), millisecondsToMinutes(getTimezoneOffset(localeConfig.timezone)), loyalty.getNumberOfAvailablePromoCodes(), Promise.all(supportsBatchingPromise), @@ -1030,7 +1022,6 @@ function plugins (settings, deviceId) { sell, getNotificationConfig, notifyOperator, - fetchCurrentConfigVersion, pruneMachinesHeartbeat, rateAddress, isWalletScoringEnabled, diff --git a/lib/routes/termsAndConditionsRoutes.js b/lib/routes/termsAndConditionsRoutes.js index 5becd362..fef9f52d 100644 --- a/lib/routes/termsAndConditionsRoutes.js +++ b/lib/routes/termsAndConditionsRoutes.js @@ -4,7 +4,7 @@ const nmd = require('nano-markdown') const router = express.Router() const configManager = require('../new-config-manager') -const plugins = require('../plugins') +const settingsLoader = require('../new-settings-loader') const createTerms = terms => (terms.active && terms.text) ? ({ delay: terms.delay, @@ -18,15 +18,10 @@ const createTerms = terms => (terms.active && terms.text) ? ({ function getTermsConditions (req, res, next) { const deviceId = req.deviceId - const settings = req.settings - - const terms = configManager.getTermsConditions(settings.config) - - const pi = plugins(settings, deviceId) - - return pi.fetchCurrentConfigVersion().then(version => { - return res.json({ terms: createTerms(terms), version }) - }) + const { config } = req.settings + const terms = configManager.getTermsConditions(config) + return settingsLoader.fetchCurrentConfigVersion() + .then(version => res.json({ terms: createTerms(terms), version })) .catch(next) } From ea9a373676830f8931ef6d30144f9066efb8d796 Mon Sep 17 00:00:00 2001 From: siiky Date: Thu, 19 Sep 2024 16:10:18 +0100 Subject: [PATCH 14/15] fix: `No data returned from the query` errors on migration --- lib/admin/settings-loader.js | 2 +- lib/new-settings-loader.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/admin/settings-loader.js b/lib/admin/settings-loader.js index ef5b2f2c..cb4bacf7 100644 --- a/lib/admin/settings-loader.js +++ b/lib/admin/settings-loader.js @@ -92,7 +92,7 @@ function loadLatestConfig (filterSchemaVersion = true) { order by id desc limit 1` - return db.one(sql, ['config', configValidate.SETTINGS_LOADER_SCHEMA_VERSION]) + return db.oneOrNone(sql, ['config', configValidate.SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row.data.config) .then(configValidate.validate) .catch(err => { diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index 09cb3e6a..76cd120c 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -167,7 +167,7 @@ function loadLatestConfig () { ORDER BY id DESC LIMIT 1` - return db.one(sql, [NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.oneOrNone(sql, [NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row ? row.data.config : {}) .catch(err => { throw err From 1f8975d68611c322ca8b5565998a8476e2cade29 Mon Sep 17 00:00:00 2001 From: siiky Date: Fri, 27 Sep 2024 11:08:16 +0100 Subject: [PATCH 15/15] chore: deprecate `deviceId` from `MachineInfo` --- lib/graphql/types.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/graphql/types.js b/lib/graphql/types.js index 26622c37..e2d8595e 100644 --- a/lib/graphql/types.js +++ b/lib/graphql/types.js @@ -29,7 +29,7 @@ type OperatorInfo { } type MachineInfo { - deviceId: String! + deviceId: String! @deprecated(reason: "unused by the machine") deviceName: String numberOfCassettes: Int numberOfRecyclers: Int