From 830a5565b91194cad4b6b4a03677b26a73aea70e Mon Sep 17 00:00:00 2001 From: Jamie Hewitt Date: Thu, 18 Aug 2022 16:04:49 +0300 Subject: [PATCH] Implementing API for getEnsTextRecords --- package-lock.json | 105 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + pages/api/_types.ts | 0 pages/api/_utils.ts | 52 +++++++++++++++++++++- pages/api/name.ts | 19 +------- pages/api/text.ts | 53 ++++++++++++++++++++++ 6 files changed, 212 insertions(+), 19 deletions(-) create mode 100644 pages/api/_types.ts create mode 100644 pages/api/text.ts diff --git a/package-lock.json b/package-lock.json index 5a3a7a0..54d6152 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,10 @@ "dependencies": { "@ensdomains/ensjs": "^2.1.0", "@oceanprotocol/lib": "^1.1.8", + "@urql/exchange-refocus": "^0.2.5", "cors": "^2.8.5", "next": "^12.2.5", + "urql": "^2.2.3", "web3": "^1.7.5" }, "devDependencies": { @@ -1317,6 +1319,14 @@ "@ethersproject/strings": "^5.6.1" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", @@ -2117,6 +2127,30 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, + "node_modules/@urql/core": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-2.6.1.tgz", + "integrity": "sha512-gYrEHy3tViJhwIhauK6MIf2Qp09QTsgNHZRd0n71rS+hF6gdwjspf1oKljl4m25+272cJF7fPjBUGmjaiEr7Kg==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "wonka": "^4.0.14" + }, + "peerDependencies": { + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/@urql/exchange-refocus": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@urql/exchange-refocus/-/exchange-refocus-0.2.5.tgz", + "integrity": "sha512-JlrEXlG37mK/WgyxqVrHkLZtBMVC7x3GtgGJHiiXK41AQdU2hVy3IAQ208ENMIrkFw9SHQf8d8h7Wb2EY/1zdw==", + "dependencies": { + "@urql/core": ">=2.3.6", + "wonka": "^4.0.14" + }, + "peerDependencies": { + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4805,6 +4839,15 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphql": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", + "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -8846,6 +8889,19 @@ "node": ">= 4" } }, + "node_modules/urql": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/urql/-/urql-2.2.3.tgz", + "integrity": "sha512-XMkSYJKW9s4ZlbSuxcUz3fTBIykOn0sGileRXQeyZpaRBXJPVz5saSY05k7jdefNxShZtTI+/nr7PYUWQertfg==", + "dependencies": { + "@urql/core": "^2.6.1", + "wonka": "^4.0.14" + }, + "peerDependencies": { + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "react": ">= 16.8.0" + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -9422,6 +9478,11 @@ "node": ">= 0.10.0" } }, + "node_modules/wonka": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz", + "integrity": "sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==" + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -10487,6 +10548,12 @@ "@ethersproject/strings": "^5.6.1" } }, + "@graphql-typed-document-node/core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "requires": {} + }, "@humanwhocodes/config-array": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", @@ -11037,6 +11104,24 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, + "@urql/core": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-2.6.1.tgz", + "integrity": "sha512-gYrEHy3tViJhwIhauK6MIf2Qp09QTsgNHZRd0n71rS+hF6gdwjspf1oKljl4m25+272cJF7fPjBUGmjaiEr7Kg==", + "requires": { + "@graphql-typed-document-node/core": "^3.1.1", + "wonka": "^4.0.14" + } + }, + "@urql/exchange-refocus": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@urql/exchange-refocus/-/exchange-refocus-0.2.5.tgz", + "integrity": "sha512-JlrEXlG37mK/WgyxqVrHkLZtBMVC7x3GtgGJHiiXK41AQdU2hVy3IAQ208ENMIrkFw9SHQf8d8h7Wb2EY/1zdw==", + "requires": { + "@urql/core": ">=2.3.6", + "wonka": "^4.0.14" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -13137,6 +13222,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "graphql": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", + "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", + "peer": true + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -16066,6 +16157,15 @@ "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==" }, + "urql": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/urql/-/urql-2.2.3.tgz", + "integrity": "sha512-XMkSYJKW9s4ZlbSuxcUz3fTBIykOn0sGileRXQeyZpaRBXJPVz5saSY05k7jdefNxShZtTI+/nr7PYUWQertfg==", + "requires": { + "@urql/core": "^2.6.1", + "wonka": "^4.0.14" + } + }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -16543,6 +16643,11 @@ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", "integrity": "sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==" }, + "wonka": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz", + "integrity": "sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index b03aca4..045ef97 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,10 @@ "dependencies": { "@ensdomains/ensjs": "^2.1.0", "@oceanprotocol/lib": "^1.1.8", + "@urql/exchange-refocus": "^0.2.5", "cors": "^2.8.5", "next": "^12.2.5", + "urql": "^2.2.3", "web3": "^1.7.5" }, "devDependencies": { diff --git a/pages/api/_types.ts b/pages/api/_types.ts new file mode 100644 index 0000000..e69de29 diff --git a/pages/api/_utils.ts b/pages/api/_utils.ts index 568ef8f..8a1cbde 100644 --- a/pages/api/_utils.ts +++ b/pages/api/_utils.ts @@ -1,5 +1,16 @@ -import { ConfigHelper, Config } from '@oceanprotocol/lib' +import { ConfigHelper, Config, LoggerInstance } from '@oceanprotocol/lib' import Web3 from 'web3' +import ENS, { getEnsAddress as getEnsAddressVendor } from '@ensdomains/ensjs' +import { + createClient, + dedupExchange, + TypedDocumentNode, + OperationContext, + fetchExchange +} from 'urql' +import { refocusExchange } from '@urql/exchange-refocus' + +let ens: any function getOceanConfig(network: string | number): Config { const config = new ConfigHelper().getConfig( @@ -17,7 +28,44 @@ function getOceanConfig(network: string | number): Config { return config as Config } -export async function getDummyWeb3(chainId: number): Promise { +async function getDummyWeb3(chainId: number): Promise { const config = getOceanConfig(chainId) return new Web3(config.nodeUri) } + +async function createUrqlClient() { + const config = getOceanConfig(1) + const client = createClient({ + url: `${config.subgraphUri}/subgraphs/name/oceanprotocol/ocean-subgraph`, + exchanges: [dedupExchange, refocusExchange(), fetchExchange] + }) + return client +} + +export async function getEns(): Promise { + const _ens = + ens || + new ENS({ + provider: (await getDummyWeb3(1)).currentProvider, + ensAddress: getEnsAddressVendor(1) + }) + ens = _ens + + return _ens +} + +export async function fetchData( + query: TypedDocumentNode, + variables: any, + context: OperationContext +): Promise { + try { + const client = await createUrqlClient() + + const response = await client.query(query, variables, context).toPromise() + return response + } catch (error) { + LoggerInstance.error('Error fetchData: ', error.message) + } + return null +} diff --git a/pages/api/name.ts b/pages/api/name.ts index 9783040..c35adf7 100644 --- a/pages/api/name.ts +++ b/pages/api/name.ts @@ -1,20 +1,5 @@ import { NextApiRequest, NextApiResponse } from 'next' -import ENS, { getEnsAddress as getEnsAddressVendor } from '@ensdomains/ensjs' -import { getDummyWeb3 } from './_utils' - -let ens: any - -async function getEns(): Promise { - const _ens = - ens || - new ENS({ - provider: (await getDummyWeb3(1)).currentProvider, - ensAddress: getEnsAddressVendor(1) - }) - ens = _ens - - return _ens -} +import { getEns } from './_utils' export default async function getEnsName( request: NextApiRequest, @@ -33,6 +18,6 @@ export default async function getEnsName( response.setHeader('Cache-Control', 's-maxage=86400') response.status(200).send(name) } catch (error) { - response.send(`${error}`) + response.status(500).send(`${error}`) } } diff --git a/pages/api/text.ts b/pages/api/text.ts new file mode 100644 index 0000000..c5bde71 --- /dev/null +++ b/pages/api/text.ts @@ -0,0 +1,53 @@ +import { NextApiRequest, NextApiResponse } from 'next' +import { gql, OperationResult } from 'urql' +import { fetchData } from './_utils' + +const ProfileTextRecordsQuery = gql<{ + domains: [{ resolver: { texts: string[] } }] +}>` + query ProfileTextRecords($name: String!) { + domains(where: { name: $name }) { + resolver { + texts + } + } + } +` + +export default async function getEnsTextRecords( + request: NextApiRequest, + response: NextApiResponse +): Promise<{ key: string; value: string }[]> { + try { + const ensName = String(request.query.name) + // 1. Check which text records are set for the domain with ENS subgraph, + // to prevent unnecessary contract calls. + const result: OperationResult<{ + domains: [{ resolver: { texts: string[] } }] + }> = await fetchData( + ProfileTextRecordsQuery, + { name: ensName }, + { + url: `https://api.thegraph.com/subgraphs/name/ensdomains/ens`, + requestPolicy: 'cache-and-network' + } + ) + if (!result?.data?.domains[0]?.resolver) return + + // 2. Retrieve the text records. + const { texts } = result.data.domains[0].resolver + const records = [] + let ens: any + + for (let index = 0; index < texts?.length; index++) { + const key = texts[index] + const value = await ens.name(ensName).getText(key) + records.push({ key, value }) + } + + response.setHeader('Cache-Control', 's-maxage=86400') + response.status(200).send(records) + } catch (error) { + response.status(500).send(`${error}`) + } +}