diff --git a/web/lib/api/graphql/datasources/firebaseAPI.ts b/web/lib/api/graphql/datasources/firebaseAPI.ts new file mode 100644 index 00000000..a923f6a0 --- /dev/null +++ b/web/lib/api/graphql/datasources/firebaseAPI.ts @@ -0,0 +1,108 @@ +import type { Contract, BinaryContract } from 'common/contract' + +import { getOutcomeProbability, getProbability } from 'common/calculate' +import { listAllBets } from 'web/lib/firebase/bets' +import { + getContractFromId, + getContractFromSlug, + listAllContracts, +} from 'web/lib/firebase/contracts' +import { listAllComments } from 'web/lib/firebase/comments' +import { getUser } from 'web/lib/firebase/users' + +import { DataSource } from 'apollo-datasource' +import { InMemoryLRUCache, KeyValueCache } from 'apollo-server-caching' + +/* Simple wrapper around web/lib/firebase functions */ +export class FirebaseAPI extends DataSource { + cache: KeyValueCache + context?: any + + constructor() { + super() + this.cache = null as any + } + + initialize({ + context, + cache, + }: { context?: any; cache?: KeyValueCache } = {}) { + this.context = context + this.cache = cache || new InMemoryLRUCache() + } + + didEncounterError(error: any) { + throw error + } + + cacheKey(id: string | undefined, type: string) { + return `firebaseapi-${type}-${id}` + } + + async get( + id: string | undefined, + type: string, + func: () => Promise, + { ttlInSeconds = 200 }: { ttlInSeconds?: number } = {} + ): Promise { + const cacheDoc = await this.cache.get(this.cacheKey(id, type)) + if (cacheDoc) { + return JSON.parse(cacheDoc) + } + + const doc = await func() + + if (ttlInSeconds) { + this.cache.set(this.cacheKey(id, type), JSON.stringify(doc), { + ttl: ttlInSeconds, + }) + } + + return doc + } + + async listAllBets(id: string) { + return this.get(id, 'listAllBets', () => listAllBets(id)) + } + + async getContractFromSlug(id: string) { + return this.get(id, 'getContractFromSlug', () => getContractFromSlug(id)) + } + + async getContractFromID(id: string) { + return this.get(id, 'market', () => getContractFromId(id)) + } + + async getOutcomeProbability(contract: Contract, answerID: string) { + return this.get(answerID, 'getOutcomeProbability', () => + (async () => getOutcomeProbability(contract, answerID))() + ) + } + + async getProbability(contract: BinaryContract) { + return this.get(contract.id, 'getProbability', () => + (async () => getProbability(contract))() + ) + } + + async listAllCommentAnswers(commentId: string, contractID: string) { + return this.get(commentId, 'listAllCommentAnswers', async () => { + const allComments = await this.listAllComments(contractID) + return allComments.filter((c) => c.replyToCommentId === commentId) + }) + } + + async listAllComments(id: string) { + return this.get(id, 'listAllComments', () => listAllComments(id)) + } + + async listAllContracts(limit = 1000, before?: string) { + return this.get(before, 'listAllContracts', () => + listAllContracts(limit, before) + ) + } + + async getUser(id: string) { + return this.get(id, 'user', () => getUser(id)) + } +} diff --git a/web/lib/api/graphql/datasources/index.ts b/web/lib/api/graphql/datasources/index.ts new file mode 100644 index 00000000..dcfc198b --- /dev/null +++ b/web/lib/api/graphql/datasources/index.ts @@ -0,0 +1,10 @@ +import type { contextType } from '../types' + +import { FirebaseAPI } from './firebaseAPI' + +const dataSources = () => + ({ + firebaseAPI: new FirebaseAPI(), + } as contextType['dataSources']) + +export default dataSources diff --git a/web/lib/api/graphql/index.ts b/web/lib/api/graphql/index.ts index 53164e2d..54771fd3 100644 --- a/web/lib/api/graphql/index.ts +++ b/web/lib/api/graphql/index.ts @@ -1,6 +1,7 @@ import { ApolloServer } from 'apollo-server-micro' import { ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core' +import dataSources from './datasources' import resolvers from './resolvers' import typeDefs from 'web/generated/schema.graphql' @@ -9,6 +10,7 @@ export const apolloServer = new ApolloServer({ cache: 'bounded', plugins: [ApolloServerPluginLandingPageGraphQLPlayground()], + dataSources, resolvers, typeDefs, }) diff --git a/web/lib/api/graphql/types.ts b/web/lib/api/graphql/types.ts new file mode 100644 index 00000000..0ef7ef15 --- /dev/null +++ b/web/lib/api/graphql/types.ts @@ -0,0 +1,8 @@ +import type { FirebaseAPI } from './datasources/firebaseAPI' + +/* Context type */ +export type contextType = { + dataSources: { + firebaseAPI: FirebaseAPI + } +} diff --git a/web/package.json b/web/package.json index 2dd88de5..7a591a35 100644 --- a/web/package.json +++ b/web/package.json @@ -36,6 +36,7 @@ "@tiptap/react": "2.0.0-beta.114", "@tiptap/starter-kit": "2.0.0-beta.190", "algoliasearch": "4.13.0", + "apollo-server-caching": "3.3.0", "apollo-server-micro": "3.10.0", "clsx": "1.1.1", "cors": "2.8.5", diff --git a/yarn.lock b/yarn.lock index e2a137fa..785a8215 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4858,6 +4858,13 @@ apollo-reporting-protobuf@^3.3.1, apollo-reporting-protobuf@^3.3.2: dependencies: "@apollo/protobufjs" "1.2.4" +apollo-server-caching@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/apollo-server-caching/-/apollo-server-caching-3.3.0.tgz#f501cbeb820a4201d98c2b768c085f22848d9dc5" + integrity sha512-Wgcb0ArjZ5DjQ7ID+tvxUcZ7Yxdbk5l1MxZL8D8gkyjooOkhPNzjRVQ7ubPoXqO54PrOMOTm1ejVhsF+AfIirQ== + dependencies: + lru-cache "^6.0.0" + apollo-server-core@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-3.10.0.tgz#6680b4eb4699829ed50d8a592721ee5e5e11e041"