Skip to content

Commit

Permalink
react-api: stats request uses POST for big queries/queryParameters (#656
Browse files Browse the repository at this point in the history
)
  • Loading branch information
zbigg authored Apr 26, 2023
1 parent 5767fa6 commit 25471cb
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Not released

- react-api: getStats request uses POST for big queries/queryParameters [#656](https://github.com/CartoDB/carto-react/pull/656)
- New DS core component: Table [#657](https://github.com/CartoDB/carto-react/pull/657)
- Improve upgrade guide documentation [#651](https://github.com/CartoDB/carto-react/pull/651)
- Fix storybook publication with Node 18 [#654](https://github.com/CartoDB/carto-react/pull/654)
Expand Down
76 changes: 76 additions & 0 deletions packages/react-api/__tests__/api/stats.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,82 @@ describe('stats', () => {
});
});

test('big query source - should return stats using POST', async () => {
const hugeQuery = Array.from(Array(5000)).join(',');
const QUERY_TEST = {
input: {
source: { ...QUERY_SOURCE, data: hugeQuery },
column: 'injuries'
},
url: `https://gcp-us-east1.api.carto.com/v3/stats/carto-ps-bq-developers/injuries`,
output: TABLE_AND_QUERY_STATS
};

const fetchMock = (global.fetch = jest.fn().mockImplementation(async () => {
return {
ok: true,
json: async () => QUERY_TEST.output
};
}));

const abortController = new AbortController();

const res = await getStats({ ...QUERY_TEST.input, opts: { abortController } });

expect(res).toEqual(QUERY_TEST.output);

expect(fetchMock).toBeCalledWith(QUERY_TEST.url, {
method: 'POST',
headers: {
Authorization: `Bearer ${QUERY_TEST.input.source.credentials.accessToken}`,
'Content-Type': 'application/json'
},
signal: abortController.signal,
body: JSON.stringify({ q: hugeQuery })
});
});

test('query source - big queryParameters, should return stats using POST', async () => {
const huugeQueryParameters = Array.from(Array(5000)).reduce((r, _, i) => {
r[`param${i}`] = i;
return r;
}, {});
const QUERY_TEST = {
input: {
source: { ...QUERY_SOURCE, queryParameters: huugeQueryParameters },
column: 'injuries'
},
url: `https://gcp-us-east1.api.carto.com/v3/stats/carto-ps-bq-developers/injuries`,
output: TABLE_AND_QUERY_STATS
};

const fetchMock = (global.fetch = jest.fn().mockImplementation(async () => {
return {
ok: true,
json: async () => QUERY_TEST.output
};
}));

const abortController = new AbortController();

const res = await getStats({ ...QUERY_TEST.input, opts: { abortController } });

expect(res).toEqual(QUERY_TEST.output);

expect(fetchMock).toBeCalledWith(QUERY_TEST.url, {
method: 'POST',
headers: {
Authorization: `Bearer ${QUERY_TEST.input.source.credentials.accessToken}`,
'Content-Type': 'application/json'
},
signal: abortController.signal,
body: JSON.stringify({
q: QUERY_SOURCE.data,
queryParameters: huugeQueryParameters
})
});
});

test('tileset source - should return stats', async () => {
const TILESET_TEST = {
input: {
Expand Down
48 changes: 29 additions & 19 deletions packages/react-api/src/api/stats.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { checkCredentials, makeCall } from './common';
import { MAP_TYPES, API_VERSIONS } from '@deck.gl/carto';
import { getTileJson } from './tilejson';
import { InvalidColumnError, _assert as assert } from '@carto/react-core/';
import {
InvalidColumnError,
REQUEST_GET_MAX_URL_LENGTH,
_assert as assert
} from '@carto/react-core/';

/**
* Execute a stats service request.
Expand Down Expand Up @@ -34,35 +38,41 @@ export async function getStats(props) {
}

return columnStats;
} else {
const url = buildUrl(source, column);

return makeCall({
url,
credentials: source.credentials,
opts,
queryParameters: source.queryParameters
});
}
}

// Aux
function buildUrl(source, column) {
const isQuery = source.type === MAP_TYPES.QUERY;

const url = new URL(
const baseUrl = new URL(
`${source.credentials.apiBaseUrl}/v3/stats/${source.connection}/${
isQuery ? column : `${source.data}/${column}`
}`
);

const queryParams = {};
if (isQuery) {
url.searchParams.set('q', source.data);

queryParams.q = source.data;
if (source.queryParameters) {
url.searchParams.set('queryParameters', JSON.stringify(source.queryParameters));
queryParams.queryParameters = JSON.stringify(source.queryParameters);
}
}

return url;
const queryParamsFormatted = new URLSearchParams(queryParams).toString();
const getUrl = `${baseUrl}${queryParamsFormatted ? '?' + queryParamsFormatted : ''}`;
if (getUrl.length <= REQUEST_GET_MAX_URL_LENGTH) {
return makeCall({
url: getUrl,
credentials: source.credentials,
opts
});
} else {
queryParams.queryParameters = source.queryParameters;
return makeCall({
url: baseUrl,
credentials: source.credentials,
opts: {
...opts,
method: 'POST',
body: JSON.stringify(queryParams)
}
});
}
}

0 comments on commit 25471cb

Please sign in to comment.