Skip to content

Commit

Permalink
feat: query now accepts array of urls
Browse files Browse the repository at this point in the history
  • Loading branch information
jackmellis committed Jul 17, 2024
1 parent 3e2bc20 commit c6ad594
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 104 deletions.
138 changes: 77 additions & 61 deletions packages/query/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class QueryError extends Error {
}

type Args = Omit<RequestInit, 'headers' | 'method' | 'body'> & {
url: string;
url: string | string[];
data?: Record<string, any> | string;
headers?: Record<string, string>;
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | string;
Expand All @@ -23,7 +23,7 @@ type Args = Omit<RequestInit, 'headers' | 'method' | 'body'> & {

const query = async <T>(args: Args): Promise<T> => {
const {
url,
url: baseUrl,
data: sourceData,
headers = {},
method = 'GET',
Expand All @@ -40,82 +40,98 @@ const query = async <T>(args: Args): Promise<T> => {
'Could not find fetch api. You may need to import a polyfill'
);
}
if (!url) {
const urls = Array.isArray(baseUrl) ? baseUrl : [baseUrl];

if (!urls.some(Boolean)) {
throw new Error('No URL provided');
}

const uri = new URL(url);

if (method === 'GET') {
if (typeof sourceData === 'string' && sourceData) {
if (sourceData.startsWith('?')) {
uri.search = sourceData;
} else {
uri.search = '?' + sourceData;
while (urls.length) {
try {
const url = urls.shift();
if (!url) {
continue;
}
} else if (sourceData) {
Object.entries(sourceData).forEach(([key, value]) => {
if (value == null) {
return;
}
if (Array.isArray(value)) {
value.forEach((v) => {
if (v == null) {
const uri = new URL(url);

if (method === 'GET') {
if (typeof sourceData === 'string' && sourceData) {
if (sourceData.startsWith('?')) {
uri.search = sourceData;
} else {
uri.search = '?' + sourceData;
}
} else if (sourceData) {
Object.entries(sourceData).forEach(([key, value]) => {
if (value == null) {
return;
}
uri.searchParams.append(key, v);
if (Array.isArray(value)) {
value.forEach((v) => {
if (v == null) {
return;
}
uri.searchParams.append(key, v);
});
} else {
uri.searchParams.set(key, value);
}
});
} else {
uri.searchParams.set(key, value);
}
});
}
}
}

const body =
method === 'GET'
? undefined
: typeof sourceData === 'string'
? sourceData
: stringify(sourceData);
const body =
method === 'GET'
? undefined
: typeof sourceData === 'string'
? sourceData
: stringify(sourceData);

if (method !== 'GET' && typeof sourceData === 'object') {
headers['Content-Type'] = 'application/json';
}
if (method !== 'GET' && typeof sourceData === 'object') {
headers['Content-Type'] = 'application/json';
}

const response = await fetch(uri.toString(), {
method,
body,
headers,
...requestInit,
});
const response = await fetch(uri.toString(), {
method,
body,
headers,
...requestInit,
});

if (
response.status >= 500 &&
response.status <= 599 &&
attempt < maxAttempts
) {
await new Promise((res) => setTimeout(res, 1000));
return query({ ...args, attempt: attempt + 1 });
}
const contentType = response.headers.get('content-type');
if (
response.status >= 500 &&
response.status <= 599 &&
attempt < maxAttempts
) {
await new Promise((res) => setTimeout(res, 1000));
return query({ ...args, attempt: attempt + 1 });
}
const contentType = response.headers.get('content-type');

if (!response.ok) {
if (contentType?.includes('application/json')) {
const json = await response.json();
throw new QueryError(response, json, `Error fetching ${url}`);
} else {
const text = await response.text();
throw new QueryError(response, {}, text);
}
}

if (!response.ok) {
if (contentType?.includes('application/json')) {
const json = await response.json();
throw new QueryError(response, json, `Error fetching ${url}`);
} else {
const text = await response.text();
throw new QueryError(response, {}, text);
if (contentType?.includes('application/json')) {
return parse(text);
} else {
return text as T;
}
} catch (e) {
if (!urls.length) {
throw e;
}
}
}

const text = await response.text();
if (contentType?.includes('application/json')) {
return parse(text);
} else {
return text as T;
}
throw new Error();
};

export default query;
46 changes: 4 additions & 42 deletions packages/query/src/queryGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { normalizeIfAddress } from './utils';
type Query = typeof sendQuery;
const defaultSendQuery = sendQuery;

type SendQueryArgs = Omit<Parameters<Query>[0], 'url' | 'data'>;
type SendQueryArgs = Omit<Parameters<Query>[0], 'data'>;

const interpolateQuery = (query: string, variables: Record<string, any>) => {
return Object.entries(variables).reduce((query, [key, value]) => {
Expand Down Expand Up @@ -51,7 +51,7 @@ const sendGraphQuery = async ({
sendQuery,
headers = {},
...rest
}: SendQueryArgs & { url: string; query: string; sendQuery: Query }) => {
}: SendQueryArgs & { query: string; sendQuery: Query }) => {
const { data, errors } = await sendQuery<{
errors: { message: string }[] & { message: string };
data: any;
Expand All @@ -69,44 +69,6 @@ const sendGraphQuery = async ({
return data;
};

const queryUrls = async ({
baseUrl,
query,
...rest
}: Omit<SendQueryArgs, 'url'> & {
baseUrl: string | string[];
query: string;
sendQuery: Query;
}) => {
// We can be passed a single url or an array of urls
// If we have an array, we'll try them in order until we get a successful response
const urls = [baseUrl].flat();

while (urls.length) {
try {
const url = urls.shift();
// Ignore empty urls (baseUrl could be undefined, or an array could've been built with missing content)
if (url == null) {
continue;
}

const data = await sendGraphQuery({
query,
url,
...rest,
});

return data;
} catch (e) {
// If there's been an error, we'll try the next url
// if we've exhausted all urls, throw the most recent error
if (!urls.length) {
throw e;
}
}
}
};

async function queryGraph<Q extends QueryBase<any, any>>(
args: SendQueryArgs & {
url: string | string[];
Expand Down Expand Up @@ -140,8 +102,8 @@ async function queryGraph({
);
}

return queryUrls({
baseUrl: url,
return sendGraphQuery({
url,
query: formatQuery({ query, variables }),
fetch,
sendQuery,
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/web2/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { query as sendQuery } from '@nftx/query';
type Fetch = typeof fetch;

type Args = {
url: string;
url: string | string[];
query?: Record<string, any>;
headers?: Record<string, string>;
cache?: RequestCache;
Expand Down

0 comments on commit c6ad594

Please sign in to comment.