From 7e9ce41471c97b27e947e78fb68e62be4c0600fb Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 13 Oct 2023 10:30:03 +0100 Subject: [PATCH 1/2] feat: add config-type-generator script --- .env.template | 6 +- scripts/config-type-generators/domain.js | 164 ++++++++++++++++++ .../config-type-generators/oasis-borrow.js | 71 ++++++++ scripts/get-config-types.js | 74 +------- 4 files changed, 246 insertions(+), 69 deletions(-) create mode 100644 scripts/config-type-generators/domain.js create mode 100644 scripts/config-type-generators/oasis-borrow.js diff --git a/.env.template b/.env.template index 4c4b56e678..ff4ed24588 100644 --- a/.env.template +++ b/.env.template @@ -37,13 +37,13 @@ ANALYZE="false" NODE_ENV="development" NODE_OPTIONS="--max-old-space-size=6144" SHOW_BUILD_INFO=false - - DATABASE_URL="postgresql://user:pass@localhost:5432/db?schema=public" CHALLENGE_JWT_SECRET="ABC123" USER_JWT_SECRET="A123124124" PRODUCT_HUB_KEY="" ALLOWED_ORIGINS="" +CONFIG_URL="https://s3.amazonaws.com/summer-fi-configuration/oasis-borrow/oasis-borrow-development.json" +CONFIG_DOMAIN_URL="https://s3.amazonaws.com/summer-fi-configuration/domain/domain-development.json" # Subgraphs & Cache AJNA_SUBGRAPH_URL="" @@ -61,4 +61,4 @@ MAINNET_CACHE_URL="https://cache-mainnet-staging.staging.summer.fi/api/v1" NOTIFICATIONS_HOST="" NOTIFICATIONS_HOST_GOERLI="" -RPC_GATEWAY= \ No newline at end of file +RPC_GATEWAY= diff --git a/scripts/config-type-generators/domain.js b/scripts/config-type-generators/domain.js new file mode 100644 index 0000000000..fdead1d8a8 --- /dev/null +++ b/scripts/config-type-generators/domain.js @@ -0,0 +1,164 @@ +const dotenv = require('dotenv') +const { mkdir, readdir, writeFile } = require('fs/promises') +const fetch = require('node-fetch') +const { join } = require('path') + +dotenv.config({ + path: '.env', +}) +dotenv.config({ + path: '.env.local', +}) + +const getConfig = async () => { + const response = await fetch(process.env.CONFIG_URL) + return await response.json() +} + +// Helpers +const getTokenInterfaceName = (tokenName) => { + return `${tokenName.charAt(0).toUpperCase()}${tokenName.slice(1)}` +} + +/** + * Generates TypeScript domain types from a given configuration object. + * + * @param {Object} configObject - The configuration object to generate types from. + * @returns {string} The generated TypeScript types. + */ +const getDomainTypes = (configObject) => { + try { + const getRootInterface = () => { + return `export interface DomainConfigType { + tokens: Tokens; + tokensByNetwork: TokensByNetwork; + lendingProtocols: LendingProtocol; + networks: Networks; + }` + } + + const getTokenInterfaces = (tokens) => { + return Object.keys(tokens) + .map( + (token) => + `interface ${getTokenInterfaceName( + token, + )} {\n symbol: TokensEnum.${token};\n precision: number;\n}`, + ) + .join('\n\n') + } + + const getTokensEnum = (tokens) => { + return `export enum TokensEnum {\n${Object.keys(tokens) + .map((token) => ` ${token} = '${token}',`) + .join('\n')}\n}` + } + + const getTokensInterface = (tokens) => { + return `interface Tokens {\n ${Object.entries(tokens) + .map(([tokenName]) => `${tokenName}: ${getTokenInterfaceName(tokenName)};`) + .join('\n ')}\n}` + } + + const getTokensByNetworkInterface = (tokensByNetwork) => { + const replacements = Object.keys(tokensByNetwork).map((network) => { + const tokensForNetwork = tokensByNetwork[network] + const tokenNamesTypeUnion = tokensForNetwork + .map((tokenObj) => + Object.keys(tokenObj) + .map((token) => `{${token}: ${getTokenInterfaceName(token)}}`) + .join('; '), + ) + .join(' | ') + return `${network}: ${tokenNamesTypeUnion}[];` + }) + return `interface TokensByNetwork {\n ${replacements.join('\n ')}\n}` + } + + const getLendingProtocolsEnums = (lendingProtocols) => { + const protocolNames = Object.keys(lendingProtocols) + const protocolEnum = `export enum LendingProtocol {\n${protocolNames + .map((key) => ` ${key.charAt(0).toUpperCase() + key.slice(1)} = '${key}',`) + .join('\n')}\n}` + const labelEnum = `export enum LendingProtocolLabel {\n${protocolNames + .map((key) => ` ${key} = '${lendingProtocols[key]}',`) + .join('\n')}\n}` + return `${protocolEnum}\n\n${labelEnum}` + } + + const getNetworksEnum = (networks) => { + const enumEntries = Object.keys(networks) + .map((networkName) => ` ${networkName.toUpperCase()} = '${networkName.toLowerCase()}',`) + .join('\n') + return `export enum Networks {\n${enumEntries}\n}` + } + + const getDenominationSymbols = (denominationSymbols) => { + const enumEntries = Object.keys(denominationSymbols) + .map( + (denominationSymbol) => + ` ${denominationSymbol.toUpperCase()} = '${denominationSymbol.toUpperCase()}',`, + ) + .join('\n') + return `export enum DenominationSymbols {\n${enumEntries}\n}` + } + + // Construct the types for each section + const rootSection = getRootInterface() + const tokensSection = `${getTokensInterface(configObject.tokens)}\n\n${getTokensEnum( + configObject.tokens, + )}\n\n${getTokenInterfaces(configObject.tokens)}` + const tokensByNetworkSection = getTokensByNetworkInterface(configObject.tokensByNetwork) + const lendingProtocolsSection = getLendingProtocolsEnums(configObject.lendingProtocols) + const networksSection = getNetworksEnum(configObject.networks) + const denominationSymbolsSection = getDenominationSymbols(configObject.denominationSymbols) + + // Assemble the final types + const types = [ + rootSection, + tokensSection, + tokensByNetworkSection, + lendingProtocolsSection, + networksSection, + denominationSymbolsSection, + ].join('\n\n') + + return types + } catch (error) { + console.error(`Error generating config types: ${error}`) + return '' + } +} + +const main = async () => { + if (!process.env.CONFIG_URL) { + console.error('CONFIG_URL environment variable not set') + return + } + const config = await getConfig() + const types = getDomainTypes(config) + const configPath = join(__dirname, 'types') + const configPathExists = await readdir(configPath).catch(() => false) + + if (!configPathExists) { + await mkdir(configPath) + .catch(() => { + console.error('Error creating types/config directory') + }) + .then(() => { + console.info(`${configPath} directory created`) + }) + } + + if (types !== '') { + writeFile(join(configPath, 'index.ts'), types) + .then(() => { + console.info('Config types generated') + }) + .catch((error) => { + console.error(`Error generating config types: ${error}`) + }) + } +} + +module.export = { generateDomainConfigTypes: main } diff --git a/scripts/config-type-generators/oasis-borrow.js b/scripts/config-type-generators/oasis-borrow.js new file mode 100644 index 0000000000..7ec2988d4b --- /dev/null +++ b/scripts/config-type-generators/oasis-borrow.js @@ -0,0 +1,71 @@ +const dotenv = require('dotenv') +const { mkdir, readdir, writeFile } = require('fs/promises') +const JsonToTS = require('json-to-ts') +const fetch = require('node-fetch') +const { join } = require('path') + +dotenv.config({ + path: '.env', +}) +dotenv.config({ + path: '.env.local', +}) + +const getConfig = async () => { + const response = await fetch(process.env.CONFIG_URL) + return await response.json() +} + +const getInterfaces = (configObject = { features: {} }) => { + try { + const interfaces = JsonToTS(configObject) + .map((typeInterface) => { + if (typeInterface.includes('RootObject')) { + return typeInterface.replace('interface RootObject', 'export interface AppConfigType') + } + return typeInterface + }) + .join('\n\n') + const featuresList = Object.keys(configObject.features || {}) + const featuresEnum = `export enum FeaturesEnum { +${featuresList.map((feature) => ` ${feature} = '${feature}',`).join('\n')} +}` + return `${interfaces}\n${featuresEnum}` + } catch (error) { + console.error(`Error generating config types: ${error}`) + return '' + } +} + +const main = async () => { + if (!process.env.CONFIG_URL) { + console.error('CONFIG_URL environment variable not set') + return + } + const config = await getConfig() + const interfaces = getInterfaces(config) + const configPath = join(__dirname, '..', 'types', 'config') + const configPathExists = await readdir(configPath).catch(() => false) + + if (!configPathExists) { + await mkdir(configPath) + .catch(() => { + console.error('Error creating types/config directory') + }) + .then(() => { + console.info(`${configPath} directory created`) + }) + } + + if (interfaces !== '') { + writeFile(join(configPath, 'index.ts'), interfaces) + .then(() => { + console.info('Config types generated') + }) + .catch((error) => { + console.error(`Error generating config types: ${error}`) + }) + } +} + +module.export = { generateOasisBorrowConfigTypes: main } diff --git a/scripts/get-config-types.js b/scripts/get-config-types.js index 598fa704bb..12b003ab52 100644 --- a/scripts/get-config-types.js +++ b/scripts/get-config-types.js @@ -1,71 +1,13 @@ -const dotenv = require('dotenv') -const { mkdir, readdir, writeFile } = require('fs/promises') -const JsonToTS = require('json-to-ts') -const fetch = require('node-fetch') -const { join } = require('path') - -dotenv.config({ - path: '.env', -}) -dotenv.config({ - path: '.env.local', -}) - -const getConfig = async () => { - const response = await fetch(process.env.CONFIG_URL) - return await response.json() -} - -const getInterfaces = (configObject = { features: {} }) => { - try { - const interfaces = JsonToTS(configObject) - .map((typeInterface) => { - if (typeInterface.includes('RootObject')) { - return typeInterface.replace('interface RootObject', 'export interface AppConfigType') - } - return typeInterface - }) - .join('\n\n') - const featuresList = Object.keys(configObject.features || {}) - const featuresEnum = `export enum FeaturesEnum { -${featuresList.map((feature) => ` ${feature} = '${feature}',`).join('\n')} -}` - return `${interfaces}\n${featuresEnum}` - } catch (error) { - console.error(`Error generating config types: ${error}`) - return '' - } -} +const { generateOasisBorrowConfigTypes } = require('./config-type-generators/oasis-borrow') +const { generateDomainConfigTypes } = require('./config-type-generators/domain') const main = async () => { - if (!process.env.CONFIG_URL) { - console.error('CONFIG_URL environment variable not set') - return - } - const config = await getConfig() - const interfaces = getInterfaces(config) - const configPath = join(__dirname, '..', 'types', 'config') - const configPathExists = await readdir(configPath).catch(() => false) - - if (!configPathExists) { - await mkdir(configPath) - .catch(() => { - console.error('Error creating types/config directory') - }) - .then(() => { - console.info(`${configPath} directory created`) - }) - } - - if (interfaces !== '') { - writeFile(join(configPath, 'index.ts'), interfaces) - .then(() => { - console.info('Config types generated') - }) - .catch((error) => { - console.error(`Error generating config types: ${error}`) - }) - } + await generateOasisBorrowConfigTypes.catch((error) => { + console.error(`Error generating Oasis Borrow config types: ${error}`) + }) + await generateDomainConfigTypes.catch((error) => { + console.error(`Error generating Domain config types: ${error}`) + }) } void main() From ae9fe6ebe48884d73159b183fef03b8a5e1ccfe1 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 13 Oct 2023 10:45:53 +0100 Subject: [PATCH 2/2] chore: update .env.template --- .env.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.template b/.env.template index 21bcb8e55b..dc3b4cc613 100644 --- a/.env.template +++ b/.env.template @@ -42,8 +42,8 @@ CHALLENGE_JWT_SECRET="ABC123" USER_JWT_SECRET="A123124124" PRODUCT_HUB_KEY="" ALLOWED_ORIGINS="" -CONFIG_URL="https://s3.amazonaws.com/summer-fi-configuration/oasis-borrow/oasis-borrow-development.json" -CONFIG_DOMAIN_URL="https://s3.amazonaws.com/summer-fi-configuration/domain/domain-development.json" +CONFIG_URL="" +CONFIG_DOMAIN_URL="" # Subgraphs & Cache AJNA_SUBGRAPH_URL=""