Simplify searchTx

This commit is contained in:
Simon Warta 2023-05-04 18:25:11 +02:00
parent d0071697bb
commit eb52e93fd4
6 changed files with 49 additions and 249 deletions

View File

@ -196,12 +196,12 @@ describe("CosmWasmClient.getTx and .searchTx", () => {
});
});
describe("with SearchByHeightQuery", () => {
describe("searchTx", () => {
it("can search successful tx by height", async () => {
pendingWithoutWasmd();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await CosmWasmClient.connect(wasmd.endpoint);
const result = await client.searchTx({ height: sendSuccessful.height });
const result = await client.searchTx(`tx.height=${sendSuccessful.height}`);
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result).toContain(
jasmine.objectContaining({
@ -218,7 +218,7 @@ describe("CosmWasmClient.getTx and .searchTx", () => {
pendingWithoutWasmd();
assert(sendUnsuccessful, "value must be set in beforeAll()");
const client = await CosmWasmClient.connect(wasmd.endpoint);
const result = await client.searchTx({ height: sendUnsuccessful.height });
const result = await client.searchTx(`tx.height=${sendUnsuccessful.height}`);
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result).toContain(
jasmine.objectContaining({
@ -230,14 +230,14 @@ describe("CosmWasmClient.getTx and .searchTx", () => {
}),
);
});
});
describe("with SearchBySentFromOrToQuery", () => {
it("can search by sender", async () => {
pendingWithoutWasmd();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await CosmWasmClient.connect(wasmd.endpoint);
const results = await client.searchTx({ sentFromOrTo: sendSuccessful.sender });
const results = await client.searchTx(
`message.module='bank' AND transfer.sender='${sendSuccessful.sender}'`,
);
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
@ -266,7 +266,9 @@ describe("CosmWasmClient.getTx and .searchTx", () => {
pendingWithoutWasmd();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await CosmWasmClient.connect(wasmd.endpoint);
const results = await client.searchTx({ sentFromOrTo: sendSuccessful.recipient });
const results = await client.searchTx(
`message.module='bank' AND transfer.recipient='${sendSuccessful.recipient}'`,
);
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
@ -290,69 +292,11 @@ describe("CosmWasmClient.getTx and .searchTx", () => {
);
});
it("can search by recipient and filter by minHeight", async () => {
pendingWithoutWasmd();
assert(sendSuccessful);
const client = await CosmWasmClient.connect(wasmd.endpoint);
const query = { sentFromOrTo: sendSuccessful.recipient };
{
const result = await client.searchTx(query, { minHeight: 0 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height - 1 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height + 1 });
expect(result.length).toEqual(0);
}
});
it("can search by recipient and filter by maxHeight", async () => {
pendingWithoutWasmd();
assert(sendSuccessful);
const client = await CosmWasmClient.connect(wasmd.endpoint);
const query = { sentFromOrTo: sendSuccessful.recipient };
{
const result = await client.searchTx(query, { maxHeight: 9999999999999 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height + 1 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height - 1 });
expect(result.length).toEqual(0);
}
});
});
describe("with SearchByTagsQuery", () => {
it("can search by transfer.recipient", async () => {
it("works with tags", async () => {
pendingWithoutWasmd();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await CosmWasmClient.connect(wasmd.endpoint);
const results = await client.searchTx({
tags: [{ key: "transfer.recipient", value: sendSuccessful.recipient }],
});
const results = await client.searchTx([{ key: "transfer.recipient", value: sendSuccessful.recipient }]);
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results

View File

@ -12,11 +12,7 @@ import {
DeliverTxResponse,
fromTendermintEvent,
IndexedTx,
isSearchByHeightQuery,
isSearchBySentFromOrToQuery,
isSearchByTagsQuery,
QueryClient,
SearchTxFilter,
SearchTxQuery,
SequenceResponse,
setupAuthExtension,
@ -221,42 +217,16 @@ export class CosmWasmClient {
return results[0] ?? null;
}
public async searchTx(query: SearchTxQuery, filter: SearchTxFilter = {}): Promise<readonly IndexedTx[]> {
const minHeight = filter.minHeight || 0;
const maxHeight = filter.maxHeight || Number.MAX_SAFE_INTEGER;
if (maxHeight < minHeight) return []; // optional optimization
function withFilters(originalQuery: string): string {
return `${originalQuery} AND tx.height>=${minHeight} AND tx.height<=${maxHeight}`;
}
let txs: readonly IndexedTx[];
if (isSearchByHeightQuery(query)) {
txs =
query.height >= minHeight && query.height <= maxHeight
? await this.txsQuery(`tx.height=${query.height}`)
: [];
} else if (isSearchBySentFromOrToQuery(query)) {
const sentQuery = withFilters(`message.module='bank' AND transfer.sender='${query.sentFromOrTo}'`);
const receivedQuery = withFilters(
`message.module='bank' AND transfer.recipient='${query.sentFromOrTo}'`,
);
const [sent, received] = await Promise.all(
[sentQuery, receivedQuery].map((rawQuery) => this.txsQuery(rawQuery)),
);
const sentHashes = sent.map((t) => t.hash);
txs = [...sent, ...received.filter((t) => !sentHashes.includes(t.hash))];
} else if (isSearchByTagsQuery(query)) {
const rawQuery = withFilters(query.tags.map((t) => `${t.key}='${t.value}'`).join(" AND "));
txs = await this.txsQuery(rawQuery);
public async searchTx(query: SearchTxQuery): Promise<IndexedTx[]> {
let rawQuery: string;
if (typeof query === "string") {
rawQuery = query;
} else if (Array.isArray(query)) {
rawQuery = query.map((t) => `${t.key}='${t.value}'`).join(" AND ");
} else {
throw new Error("Unknown query type");
throw new Error("Got unsupported query type. See CosmJS 0.31 CHANGELOG for API breaking changes here.");
}
const filtered = txs.filter((tx) => tx.height >= minHeight && tx.height <= maxHeight);
return filtered;
return this.txsQuery(rawQuery);
}
public disconnect(): void {
@ -477,7 +447,7 @@ export class CosmWasmClient {
}
}
private async txsQuery(query: string): Promise<readonly IndexedTx[]> {
private async txsQuery(query: string): Promise<IndexedTx[]> {
const results = await this.forceGetTmClient().txSearchAll({ query: query });
return results.txs.map((tx) => {
return {

View File

@ -112,16 +112,7 @@ export {
QueryClient,
QueryStoreResponse,
} from "./queryclient";
export {
isSearchByHeightQuery,
isSearchBySentFromOrToQuery,
isSearchByTagsQuery,
SearchByHeightQuery,
SearchBySentFromOrToQuery,
SearchByTagsQuery,
SearchTxFilter,
SearchTxQuery,
} from "./search";
export { SearchByHeightQuery, SearchBySentFromOrToQuery, SearchTxQuery } from "./search";
export {
createDefaultAminoConverters,
defaultRegistryTypes,

View File

@ -7,28 +7,11 @@ export interface SearchBySentFromOrToQuery {
}
/**
* This query type allows you to pass arbitrary key/value pairs to the backend. It is
* more powerful and slightly lower level than the other search options.
* This query type allows you to pass arbitrary key/value pairs to the backend.
*/
export interface SearchByTagsQuery {
readonly tags: ReadonlyArray<{ readonly key: string; readonly value: string }>;
}
export type SearchTxQuery = SearchByHeightQuery | SearchBySentFromOrToQuery | SearchByTagsQuery;
export function isSearchByHeightQuery(query: SearchTxQuery): query is SearchByHeightQuery {
return (query as SearchByHeightQuery).height !== undefined;
}
export function isSearchBySentFromOrToQuery(query: SearchTxQuery): query is SearchBySentFromOrToQuery {
return (query as SearchBySentFromOrToQuery).sentFromOrTo !== undefined;
}
export function isSearchByTagsQuery(query: SearchTxQuery): query is SearchByTagsQuery {
return (query as SearchByTagsQuery).tags !== undefined;
}
export interface SearchTxFilter {
readonly minHeight?: number;
readonly maxHeight?: number;
}
export type SearchTxQuery =
| string
| ReadonlyArray<{
readonly key: string;
readonly value: string;
}>;

View File

@ -190,12 +190,12 @@ describe("StargateClient.getTx and .searchTx", () => {
});
});
describe("with SearchByHeightQuery", () => {
describe("searchTx", () => {
it("can search successful tx by height", async () => {
pendingWithoutSimapp();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await StargateClient.connect(simapp.tendermintUrl);
const result = await client.searchTx({ height: sendSuccessful.height });
const result = await client.searchTx(`tx.height=${sendSuccessful.height}`);
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result).toContain(
jasmine.objectContaining({
@ -211,7 +211,7 @@ describe("StargateClient.getTx and .searchTx", () => {
pendingWithoutSimapp();
assert(sendUnsuccessful, "value must be set in beforeAll()");
const client = await StargateClient.connect(simapp.tendermintUrl);
const result = await client.searchTx({ height: sendUnsuccessful.height });
const result = await client.searchTx(`tx.height=${sendUnsuccessful.height}`);
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result).toContain(
jasmine.objectContaining({
@ -222,14 +222,14 @@ describe("StargateClient.getTx and .searchTx", () => {
}),
);
});
});
describe("with SearchBySentFromOrToQuery", () => {
it("can search by sender", async () => {
pendingWithoutSimapp();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await StargateClient.connect(simapp.tendermintUrl);
const results = await client.searchTx({ sentFromOrTo: sendSuccessful.sender });
const results = await client.searchTx(
`message.module='bank' AND transfer.sender='${sendSuccessful.sender}'`,
);
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
@ -257,7 +257,9 @@ describe("StargateClient.getTx and .searchTx", () => {
pendingWithoutSimapp();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await StargateClient.connect(simapp.tendermintUrl);
const results = await client.searchTx({ sentFromOrTo: sendSuccessful.recipient });
const results = await client.searchTx(
`message.module='bank' AND transfer.recipient='${sendSuccessful.recipient}'`,
);
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
@ -281,69 +283,11 @@ describe("StargateClient.getTx and .searchTx", () => {
);
});
it("can search by recipient and filter by minHeight", async () => {
pendingWithoutSimapp();
assert(sendSuccessful);
const client = await StargateClient.connect(simapp.tendermintUrl);
const query = { sentFromOrTo: sendSuccessful.recipient };
{
const result = await client.searchTx(query, { minHeight: 0 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height - 1 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height + 1 });
expect(result.length).toEqual(0);
}
});
it("can search by recipient and filter by maxHeight", async () => {
pendingWithoutSimapp();
assert(sendSuccessful);
const client = await StargateClient.connect(simapp.tendermintUrl);
const query = { sentFromOrTo: sendSuccessful.recipient };
{
const result = await client.searchTx(query, { maxHeight: 9999999999999 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height + 1 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height - 1 });
expect(result.length).toEqual(0);
}
});
});
describe("with SearchByTagsQuery", () => {
it("can search by transfer.recipient", async () => {
it("works with tags", async () => {
pendingWithoutSimapp();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await StargateClient.connect(simapp.tendermintUrl);
const results = await client.searchTx({
tags: [{ key: "transfer.recipient", value: sendSuccessful.recipient }],
});
const results = await client.searchTx([{ key: "transfer.recipient", value: sendSuccessful.recipient }]);
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results

View File

@ -28,13 +28,7 @@ import {
TxExtension,
} from "./modules";
import { QueryClient } from "./queryclient";
import {
isSearchByHeightQuery,
isSearchBySentFromOrToQuery,
isSearchByTagsQuery,
SearchTxFilter,
SearchTxQuery,
} from "./search";
import { SearchTxQuery } from "./search";
export class TimeoutError extends Error {
public readonly txId: string;
@ -394,42 +388,16 @@ export class StargateClient {
return results[0] ?? null;
}
public async searchTx(query: SearchTxQuery, filter: SearchTxFilter = {}): Promise<readonly IndexedTx[]> {
const minHeight = filter.minHeight || 0;
const maxHeight = filter.maxHeight || Number.MAX_SAFE_INTEGER;
if (maxHeight < minHeight) return []; // optional optimization
function withFilters(originalQuery: string): string {
return `${originalQuery} AND tx.height>=${minHeight} AND tx.height<=${maxHeight}`;
}
let txs: readonly IndexedTx[];
if (isSearchByHeightQuery(query)) {
txs =
query.height >= minHeight && query.height <= maxHeight
? await this.txsQuery(`tx.height=${query.height}`)
: [];
} else if (isSearchBySentFromOrToQuery(query)) {
const sentQuery = withFilters(`message.module='bank' AND transfer.sender='${query.sentFromOrTo}'`);
const receivedQuery = withFilters(
`message.module='bank' AND transfer.recipient='${query.sentFromOrTo}'`,
);
const [sent, received] = await Promise.all(
[sentQuery, receivedQuery].map((rawQuery) => this.txsQuery(rawQuery)),
);
const sentHashes = sent.map((t) => t.hash);
txs = [...sent, ...received.filter((t) => !sentHashes.includes(t.hash))];
} else if (isSearchByTagsQuery(query)) {
const rawQuery = withFilters(query.tags.map((t) => `${t.key}='${t.value}'`).join(" AND "));
txs = await this.txsQuery(rawQuery);
public async searchTx(query: SearchTxQuery): Promise<IndexedTx[]> {
let rawQuery: string;
if (typeof query === "string") {
rawQuery = query;
} else if (Array.isArray(query)) {
rawQuery = query.map((t) => `${t.key}='${t.value}'`).join(" AND ");
} else {
throw new Error("Unknown query type");
throw new Error("Got unsupported query type. See CosmJS 0.31 CHANGELOG for API breaking changes here.");
}
const filtered = txs.filter((tx) => tx.height >= minHeight && tx.height <= maxHeight);
return filtered;
return this.txsQuery(rawQuery);
}
public disconnect(): void {
@ -503,7 +471,7 @@ export class StargateClient {
);
}
private async txsQuery(query: string): Promise<readonly IndexedTx[]> {
private async txsQuery(query: string): Promise<IndexedTx[]> {
const results = await this.forceGetTmClient().txSearchAll({ query: query });
return results.txs.map((tx) => {
return {