Skip to content

Commit

Permalink
works need ref and cache
Browse files Browse the repository at this point in the history
  • Loading branch information
Tommytrg committed May 11, 2022
1 parent 3457626 commit 2104729
Show file tree
Hide file tree
Showing 28 changed files with 259 additions and 78 deletions.
1 change: 1 addition & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"jest": "^27.0.1",
"migrate-mongo": "^8.2.3",
"mongodb": "^3.6.9",
"node-cache": "^5.1.2",
"web3": "^1.3.6",
"web3-utils": "^1.3.6"
}
Expand Down
38 changes: 38 additions & 0 deletions packages/api/src/fetchSvgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import axios from 'axios'

const DEFAULT_SVG = '<svg></svg>'

export async function fetchSvgs (
networksToFech: Array<string>
): Promise<{ [key: string]: string }> {
const BASE_URL =
'https://raw.githubusercontent.com/witnet/data-feeds-explorer/main/packages/ui/assets/svg/'

const logosUrls = networksToFech.map((networkToFech: string) => {
return `${BASE_URL}${networkToFech}.svg`
})

// Fech all logos from github
const promises = logosUrls.map(url => axios.get(url))
return new Promise(resolve => {
Promise.allSettled(promises).then(results => {
const svgs = results.map((result, index) => {
if (result.status === 'fulfilled') {
return result.value.data
} else {
console.log(`Error fetching logo from: ${logosUrls[index]}`)
return DEFAULT_SVG
}
})

const svgByName = networksToFech.reduce((acc, val, index) => {
return {
...acc,
[val]: svgs[index]
}
}, {})

resolve(svgByName)
})
})
}
27 changes: 23 additions & 4 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Web3Middleware } from './web3Middleware/index'
import { normalizeNetworkConfig } from './utils/index'
import axios from 'axios'
import { readDataFeeds } from './readDataFeeds'
import { SvgCache } from './svgCache'

async function getDataFeedsRouterConfig (): Promise<
RouterDataFeedsConfig | null
Expand All @@ -28,21 +29,39 @@ async function getDataFeedsRouterConfig (): Promise<
}

async function main () {
const svgCache = new SvgCache()
const mongoManager = new MongoManager()
const db = await mongoManager.start()
const dataFeedsRouterConfig: RouterDataFeedsConfig = await getDataFeedsRouterConfig()
const dataFeeds = await readDataFeeds(dataFeedsRouterConfig)
const networksConfig: Array<NetworksConfig> = normalizeNetworkConfig(
dataFeedsRouterConfig
const networksConfigPartial: Array<Omit<
NetworksConfig,
'logo'
>> = normalizeNetworkConfig(dataFeedsRouterConfig)

const logosToFech = networksConfigPartial.map(
(networksConfig: NetworksConfig) => {
return networksConfig.chain.toLowerCase()
}
)

// const networksLogos: Array<string> = await fetchSvgs(logosToFech)
const networksLogos: { [key: string]: string } = await svgCache.getMany(
logosToFech
)

const networksConfig = networksConfigPartial.map((networksConfig, index) => ({
...networksConfig,
logo: networksLogos[logosToFech[index]]
}))

const repositories: Repositories = {
feedRepository: new FeedRepository(dataFeeds),
resultRequestRepository: new ResultRequestRepository(db, dataFeeds)
}
const config = {
dataFeedsConfig: dataFeeds,
networksConfig: networksConfig
networksConfig
}

const web3Middleware = new Web3Middleware(
Expand All @@ -51,7 +70,7 @@ async function main () {
)
web3Middleware.listen()

const server = await createServer(repositories, config)
const server = await createServer(repositories, svgCache, config)

server
.listen({ host: '0.0.0.0', port: process.env.SERVER_PORT })
Expand Down
9 changes: 8 additions & 1 deletion packages/api/src/loaders/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import DataLoader from 'dataloader'
import { SvgCache } from '../svgCache'
import { Repositories, ResultRequestDbObjectNormalized } from '../types'

export class Loaders {
repositories: Repositories
svgCache: SvgCache

constructor (repositories: Repositories) {
constructor (repositories: Repositories, svgCache: SvgCache) {
this.repositories = repositories
this.svgCache = svgCache
}
// returns a loader that fetches data using the given function
private genericLoader<T> (load: (filter) => T) {
Expand All @@ -30,6 +33,7 @@ export class Loaders {
getLoaders (): {
lastResult: DataLoader<string, ResultRequestDbObjectNormalized, string>
requests: DataLoader<string, ResultRequestDbObjectNormalized, string>
logos: DataLoader<string, string, string>
} {
return {
lastResult: this.genericLoader<Promise<ResultRequestDbObjectNormalized>>(
Expand Down Expand Up @@ -72,6 +76,9 @@ export class Loaders {
filter.feedFullName,
timestamp
)
}),
logos: new DataLoader(async (logos: Array<string>) => {
return Object.values(await this.svgCache.getMany(logos))
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/repository/Feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export class FeedRepository {
}

return {
feeds,
total: feeds.length
feeds: feeds || [],
total: feeds ? feeds.length : 0
}
}

Expand Down
11 changes: 10 additions & 1 deletion packages/api/src/resolvers.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Context } from './types'

const resolvers = {
Query: {
feeds: async (_parent, args, { feedRepository }: Context) => {
return await feedRepository.getFeedsByNetwork(args.network)
},

networks: (_parent, _args, { config }): Context => {
networks: (_parent, _args, { config }: Context) => {
return config.networksConfig
},

Expand All @@ -21,7 +22,15 @@ const resolvers = {
return await feedRepository.get(args.feedFullName)
}
},
NetworksConfig: {
logo: async (parent, _args, { loaders }: Context) => {
return await loaders.logos.load(parent.chain.toLowerCase())
}
},
Feed: {
logo: async (parent, _args, { loaders }: Context) => {
return await loaders.logos.load(parent.name.split('/')[0])
},
requests: async (parent, args, { loaders }: Context) => {
return await loaders.requests.load({
feedFullName: parent.feedFullName,
Expand Down
4 changes: 3 additions & 1 deletion packages/api/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import {
Repositories,
NetworksConfig
} from './types'
import { SvgCache } from './svgCache'

export async function createServer (
repositories: Repositories,
svgCache: SvgCache,
config: {
dataFeedsConfig: Array<FeedInfo>
networksConfig: Array<NetworksConfig>
Expand All @@ -29,7 +31,7 @@ export async function createServer (
{}
)

const loaders = new Loaders(repositories)
const loaders = new Loaders(repositories, svgCache)
return {
...repositories,
config: {
Expand Down
43 changes: 43 additions & 0 deletions packages/api/src/svgCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import NodeCache from 'node-cache'
import { fetchSvgs } from './fetchSvgs'

export class SvgCache {
cache: NodeCache

constructor () {
this.cache = new NodeCache({ stdTTL: 1, checkperiod: 1 })
}

async get (svgName: string): Promise<string> {
const svg = this.cache.get(svgName)

if (svg) {
return svg as string
} else {
const fetchedSvg: { [key: string]: string } = await fetchSvgs([svgName])

const updatedSvg = fetchedSvg[svgName]
this.cache.set(svgName, updatedSvg)

return fetchedSvg[svgName]
}
}

async getMany (svgNames: Array<string>): Promise<{ [key: string]: string }> {
const missingSvgs = svgNames.filter(name => !this.cache.get(name))

const fetchedSvg = await fetchSvgs(missingSvgs)

// set missing svgs
Object.entries(fetchSvgs).forEach(([key, value]) => {
this.cache.set(key, value)
})

return svgNames.reduce((acc, name) => {
return {
...acc,
[name]: this.cache.get(name) || fetchedSvg[name]
}
}, {})
}
}
10 changes: 6 additions & 4 deletions packages/api/src/typeDefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const typeDefs = gql`
heartbeat: String!
finality: String!
requests(timestamp: Int!): [ResultRequest]!
logo: String!
}
type Total {
Expand All @@ -31,10 +32,11 @@ const typeDefs = gql`
total: Int!
}
type NetworksConfig @entity {
chain: String
label: String
key: String
type NetworksConfig {
chain: String!
label: String!
key: String!
logo: String!
}
type ResultRequest @entity {
Expand Down
10 changes: 6 additions & 4 deletions packages/api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type Context = {
resultRequestRepository: ResultRequestRepository
config: {
feedsConfig: ConfigByFullName
networksConfig: NetworksConfig
networksConfig: Array<NetworksConfig>
}
loaders: {
lastResult: DataLoader<string, ResultRequestDbObjectNormalized, string>
Expand All @@ -33,6 +33,7 @@ export type Context = {
timestamp: number
}
>
logos: DataLoader<string, string, string>
}
}

Expand Down Expand Up @@ -85,9 +86,10 @@ export type FeedInfoGeneric<ABI> = {
}

export type NetworksConfig = {
chain: String
label: String
key: String
chain: string
label: string
key: string
logo: string
}

export type FeedInfo = FeedInfoGeneric<Array<AbiItem>>
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,9 @@ function getNetworksListByChain (config: RouterDataFeedsConfig) {
}

// normalize config to fit network schema

export function normalizeNetworkConfig (
config: RouterDataFeedsConfig
): Array<NetworksConfig> {
): Array<Omit<NetworksConfig, 'logo'>> {
// Get a list of networks where every element of the array contains another array with networks that belong to a chain.
const networks = getNetworksListByChain(config)

Expand All @@ -74,6 +73,7 @@ export function normalizeNetworkConfig (
})
return networks
}, [])

return networkConfig
}

Expand Down
10 changes: 9 additions & 1 deletion packages/api/test/feeds.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import { readDataFeeds } from '../src/readDataFeeds'
import dataFeedsRouterConfig from '../../api/src/dataFeedsRouter.json'

const dataFeeds = readDataFeeds(dataFeedsRouterConfig)
const networksConfig = normalizeNetworkConfig(dataFeedsRouterConfig)
const networksConfig = normalizeNetworkConfig(dataFeedsRouterConfig).map(c => ({
...c,
logo: '<svg></svg>'
}))

const state: {
mongoManager: MongoManager
Expand All @@ -29,11 +32,16 @@ describe.skip('feeds', function () {
const mongoManager = new MongoManager()
const db = await mongoManager.start(process.env.CI ? ciUri : null)

const get = jest.fn(() => '<svg></svg>')
const getMany = jest.fn(arr => arr.map(_ => '<svg></svg>'))
const svgCache = jest.fn(() => ({ get, getMany }))

const server = await createServer(
{
feedRepository: new FeedRepository(dataFeeds),
resultRequestRepository: new ResultRequestRepository(db, dataFeeds)
},
svgCache as any,
{
dataFeedsConfig: dataFeeds,
networksConfig: networksConfig
Expand Down
Loading

0 comments on commit 2104729

Please sign in to comment.