From 06b446c2682eab01cc56fe80cc10def6d39b93a2 Mon Sep 17 00:00:00 2001 From: Iveta Date: Fri, 29 Mar 2024 10:26:07 -0400 Subject: [PATCH 01/24] All Assets --- .../explore-endpoints/[[...pages]]/page.tsx | 2 +- src/components/FormElements/LimitPicker.tsx | 1 - src/components/FormElements/TextPicker.tsx | 37 +++++++++++++++++++ src/components/formComponentTemplate.tsx | 31 ++++++++++++++++ src/constants/exploreEndpointsPages.ts | 12 +++++- 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/components/FormElements/TextPicker.tsx diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index 4bbc8046..ff7850cb 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -179,7 +179,7 @@ export default function ExploreEndpoints() { return pathParams.map((pp) => params[pp] ?? pp).join("/"); }; - const endpointPath = `/accounts${pageData?.endpointPathParams ? `/${mapPathParamToValue(pageData.endpointPathParams.split(","))}` : ""}`; + const endpointPath = `${pageData?.endpointPath}${pageData?.endpointPathParams ? `/${mapPathParamToValue(pageData.endpointPathParams.split(","))}` : ""}`; const endpointParams = pageData?.endpointParams; const baseUrl = `${endpointNetwork.horizonUrl}${endpointPath}`; diff --git a/src/components/FormElements/LimitPicker.tsx b/src/components/FormElements/LimitPicker.tsx index 8c73ff64..be572ff4 100644 --- a/src/components/FormElements/LimitPicker.tsx +++ b/src/components/FormElements/LimitPicker.tsx @@ -5,7 +5,6 @@ interface LimitPickerProps extends Omit { id: string; fieldSize?: "sm" | "md" | "lg"; labelSuffix?: string | React.ReactNode; - placeholder?: string; value: string; error: string | undefined; // eslint-disable-next-line no-unused-vars diff --git a/src/components/FormElements/TextPicker.tsx b/src/components/FormElements/TextPicker.tsx new file mode 100644 index 00000000..7362c7ad --- /dev/null +++ b/src/components/FormElements/TextPicker.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { Input, InputProps } from "@stellar/design-system"; + +interface TextPickerProps extends Omit { + id: string; + fieldSize?: "sm" | "md" | "lg"; + labelSuffix?: string | React.ReactNode; + label: string; + value: string; + error: string | undefined; + // eslint-disable-next-line no-unused-vars + onChange: (e: React.ChangeEvent) => void; +} + +export const TextPicker = ({ + id, + fieldSize = "md", + labelSuffix, + label, + value, + error, + onChange, + ...props +}: TextPickerProps) => { + return ( + + ); +}; diff --git a/src/components/formComponentTemplate.tsx b/src/components/formComponentTemplate.tsx index 2ef7355e..64039a80 100644 --- a/src/components/formComponentTemplate.tsx +++ b/src/components/formComponentTemplate.tsx @@ -9,6 +9,7 @@ import { LimitPicker } from "@/components/FormElements/LimitPicker"; import { parseJsonString } from "@/helpers/parseJsonString"; import { validate } from "@/validate"; import { AnyObject, AssetObjectValue } from "@/types/types"; +import { TextPicker } from "./FormElements/TextPicker"; type TemplateRenderProps = { value: string | undefined; @@ -77,6 +78,36 @@ export const formComponentTemplate = ( ), validate: validate.asset, }; + case "asset_code": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: validate.assetCode, + }; + case "asset_issuer": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: validate.publicKey, + }; case "cursor": return { render: (templ: TemplateRenderProps) => ( diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index 600493ea..c6e19ee9 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -79,7 +79,17 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { { route: Routes.EXPLORE_ENDPOINTS_ASSETS, label: "All Assets", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/list-all-assets", + docsLabel: "assets", + requestMethod: "GET", + endpointPath: "/assets", + endpointPathParams: "", + endpointParams: "asset_code,asset_issuer,cursor,order,limit", + requiredParams: "", + isStreaming: true, + }, }, ], }, From 09c862401d3d6d0d55cb70a4079e8dc9bc41eed9 Mon Sep 17 00:00:00 2001 From: Iveta Date: Fri, 29 Mar 2024 10:42:06 -0400 Subject: [PATCH 02/24] Claimable Balances --- .../explore-endpoints/[[...pages]]/page.tsx | 1 + src/components/formComponentTemplate.tsx | 30 +++++++++++++++++++ src/constants/exploreEndpointsPages.ts | 24 +++++++++++++-- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index ff7850cb..442dea2c 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -219,6 +219,7 @@ export default function ExploreEndpoints() { }, [ endpointNetwork.horizonUrl, pageData?.endpointParams, + pageData?.endpointPath, pageData?.endpointPathParams, params, ]); diff --git a/src/components/formComponentTemplate.tsx b/src/components/formComponentTemplate.tsx index 64039a80..6bafb9f4 100644 --- a/src/components/formComponentTemplate.tsx +++ b/src/components/formComponentTemplate.tsx @@ -108,6 +108,36 @@ export const formComponentTemplate = ( ), validate: validate.publicKey, }; + case "claimable_balance_id": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: null, + }; + case "claimant": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: validate.publicKey, + }; case "cursor": return { render: (templ: TemplateRenderProps) => ( diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index c6e19ee9..be7a11aa 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -100,12 +100,32 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { { route: Routes.EXPLORE_ENDPOINTS_CLAIMABLE_BALANCES, label: "All Claimable Balances", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/list-all-claimable-balances", + docsLabel: "claimable balances", + requestMethod: "GET", + endpointPath: "/claimable_balances", + endpointPathParams: "", + endpointParams: "sponsor,asset,claimant,cursor,limit,order", + requiredParams: "", + isStreaming: false, + }, }, { route: Routes.EXPLORE_ENDPOINTS_CLAIMABLE_BALANCES_SINGLE, label: "Single Claimable Balance", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-a-claimable-balance", + docsLabel: "claimable balances", + requestMethod: "GET", + endpointPath: "/claimable_balances", + endpointPathParams: "claimable_balance_id", + endpointParams: "", + requiredParams: "claimable_balance_id", + isStreaming: false, + }, }, ], }, From c14e892e649af806150f3fd59af1be45276403d1 Mon Sep 17 00:00:00 2001 From: Iveta Date: Fri, 29 Mar 2024 15:24:25 -0400 Subject: [PATCH 03/24] Effects + refactor endpoint URL template --- .../explore-endpoints/[[...pages]]/page.tsx | 93 ++++++++++++++---- src/components/FormElements/LimitPicker.tsx | 1 + .../FormElements/PositiveIntPicker.tsx | 38 ++++++++ src/components/FormElements/TextPicker.tsx | 1 + src/components/formComponentTemplate.tsx | 65 +++++++++++++ src/constants/exploreEndpointsPages.ts | 94 ++++++++++++++----- src/validate/index.ts | 2 + src/validate/methods/transactionHash.ts | 11 +++ 8 files changed, 264 insertions(+), 41 deletions(-) create mode 100644 src/components/FormElements/PositiveIntPicker.tsx create mode 100644 src/validate/methods/transactionHash.ts diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index 442dea2c..c24a4d5b 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -55,8 +55,56 @@ export default function ExploreEndpoints() { resetParams, } = exploreEndpoints; + const REGEX_TEMPLATE_SEARCH_PARAMS = /\{\?.+?\}/; + const REGEX_TEMPLATE_SEARCH_PARAMS_VALUE = /(?<=\{\?).+?(?=\})/; + const REGEX_TEMPLATE_PATH_PARAM_VALUE = /(?<=\{).+?(?=\})/; + + // Parse page URL from the template to get path and search params + const parseTemplate = useCallback((templateString: string | undefined) => { + let template = templateString; + let templateParams = ""; + + if (template) { + const matchSearchParams = template.match( + REGEX_TEMPLATE_SEARCH_PARAMS, + )?.[0]; + + if (matchSearchParams) { + template = template.replace(matchSearchParams, ""); + templateParams = + matchSearchParams.match(REGEX_TEMPLATE_SEARCH_PARAMS_VALUE)?.[0] ?? + ""; + } + } + + // Getting path params + if (template) { + const urlPathParamArr: string[] = []; + + template.split("/").forEach((p) => { + const param = p.match(REGEX_TEMPLATE_PATH_PARAM_VALUE)?.[0]; + + if (param) { + return urlPathParamArr.push(param); + } + }); + + setUrlPathparams(urlPathParamArr.join(",")); + } + + return { + templatePath: template ?? "", + templateParams: templateParams ?? "", + }; + // Not including RegEx const + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const [formError, setFormError] = useState({}); const [requestUrl, setRequestUrl] = useState(""); + const [urlPath, setUrlPath] = useState(""); + const [urlPathParams, setUrlPathparams] = useState(""); + const [urlParams, setUrlParams] = useState(""); const queryClient = useQueryClient(); const { @@ -156,6 +204,14 @@ export default function ExploreEndpoints() { } }, [currentEndpoint, currentPage, resetStates, updateCurrentEndpoint]); + useEffect(() => { + const { templatePath, templateParams } = parseTemplate( + pageData?.endpointUrlTemplate, + ); + setUrlPath(templatePath); + setUrlParams(templateParams); + }, [pageData?.endpointUrlTemplate, parseTemplate]); + useEffect(() => { // Save network for endpoints if we don't have it yet. if (network.id && !endpointNetwork.id) { @@ -175,16 +231,25 @@ export default function ExploreEndpoints() { }, [isSuccess, isError]); const buildUrl = useCallback(() => { - const mapPathParamToValue = (pathParams: string[]) => { - return pathParams.map((pp) => params[pp] ?? pp).join("/"); - }; + const parseUrlPath = (path: string) => { + const pathArr: string[] = []; + + path.split("/").forEach((p) => { + const param = p.match(REGEX_TEMPLATE_PATH_PARAM_VALUE)?.[0]; - const endpointPath = `${pageData?.endpointPath}${pageData?.endpointPathParams ? `/${mapPathParamToValue(pageData.endpointPathParams.split(","))}` : ""}`; - const endpointParams = pageData?.endpointParams; + if (param) { + return pathArr.push(params[param] ?? ""); + } + + return pathArr.push(p); + }); + + return pathArr.join("/"); + }; - const baseUrl = `${endpointNetwork.horizonUrl}${endpointPath}`; + const baseUrl = `${endpointNetwork.horizonUrl}${parseUrlPath(urlPath)}`; const searchParams = new URLSearchParams(); - const templateParams = endpointParams?.split(","); + const templateParams = urlParams?.split(","); const getParamRequestValue = (param: string) => { const value = parseJsonString(params[param]); @@ -216,13 +281,9 @@ export default function ExploreEndpoints() { const searchParamString = searchParams.toString(); return `${baseUrl}${searchParamString ? `?${searchParamString}` : ""}`; - }, [ - endpointNetwork.horizonUrl, - pageData?.endpointParams, - pageData?.endpointPath, - pageData?.endpointPathParams, - params, - ]); + // Not including RegEx const + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [endpointNetwork.horizonUrl, params, urlParams, urlPath]); useEffect(() => { setRequestUrl(buildUrl()); @@ -285,8 +346,8 @@ export default function ExploreEndpoints() { } const allFields = sanitizeArray([ - ...pageData.endpointPathParams.split(","), - ...pageData.endpointParams.split(","), + ...urlPathParams.split(","), + ...urlParams.split(","), ]); return ( diff --git a/src/components/FormElements/LimitPicker.tsx b/src/components/FormElements/LimitPicker.tsx index be572ff4..f157feb5 100644 --- a/src/components/FormElements/LimitPicker.tsx +++ b/src/components/FormElements/LimitPicker.tsx @@ -6,6 +6,7 @@ interface LimitPickerProps extends Omit { fieldSize?: "sm" | "md" | "lg"; labelSuffix?: string | React.ReactNode; value: string; + placeholder?: string; error: string | undefined; // eslint-disable-next-line no-unused-vars onChange: (e: React.ChangeEvent) => void; diff --git a/src/components/FormElements/PositiveIntPicker.tsx b/src/components/FormElements/PositiveIntPicker.tsx new file mode 100644 index 00000000..a5404493 --- /dev/null +++ b/src/components/FormElements/PositiveIntPicker.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { Input, InputProps } from "@stellar/design-system"; + +interface PositiveIntPickerProps extends Omit { + id: string; + fieldSize?: "sm" | "md" | "lg"; + labelSuffix?: string | React.ReactNode; + label: string; + value: string; + placeholder?: string; + error: string | undefined; + // eslint-disable-next-line no-unused-vars + onChange: (e: React.ChangeEvent) => void; +} + +export const PositiveIntPicker = ({ + id, + fieldSize = "md", + labelSuffix, + label, + value, + error, + onChange, + ...props +}: PositiveIntPickerProps) => { + return ( + + ); +}; diff --git a/src/components/FormElements/TextPicker.tsx b/src/components/FormElements/TextPicker.tsx index 7362c7ad..93ac7063 100644 --- a/src/components/FormElements/TextPicker.tsx +++ b/src/components/FormElements/TextPicker.tsx @@ -7,6 +7,7 @@ interface TextPickerProps extends Omit { labelSuffix?: string | React.ReactNode; label: string; value: string; + placeholder?: string; error: string | undefined; // eslint-disable-next-line no-unused-vars onChange: (e: React.ChangeEvent) => void; diff --git a/src/components/formComponentTemplate.tsx b/src/components/formComponentTemplate.tsx index 6bafb9f4..aa86d753 100644 --- a/src/components/formComponentTemplate.tsx +++ b/src/components/formComponentTemplate.tsx @@ -10,6 +10,7 @@ import { parseJsonString } from "@/helpers/parseJsonString"; import { validate } from "@/validate"; import { AnyObject, AssetObjectValue } from "@/types/types"; import { TextPicker } from "./FormElements/TextPicker"; +import { PositiveIntPicker } from "./FormElements/PositiveIntPicker"; type TemplateRenderProps = { value: string | undefined; @@ -152,6 +153,22 @@ export const formComponentTemplate = ( ), validate: null, }; + case "ledger": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: validate.positiveInt, + }; case "limit": return { render: (templ: TemplateRenderProps) => ( @@ -166,6 +183,38 @@ export const formComponentTemplate = ( ), validate: validate.positiveInt, }; + case "liquidity_pool_id": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: null, + }; + case "operation": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: validate.positiveInt, + }; case "order": return { render: (templ: TemplateRenderOrderProps) => ( @@ -209,6 +258,22 @@ export const formComponentTemplate = ( ), validate: validate.publicKey, }; + case "transaction": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: validate.transactionHash, + }; default: return null; } diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index be7a11aa..c0f11c07 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -14,9 +14,7 @@ type ExploreEndpointsPagesProps = { docsUrl: string; docsLabel?: string; requestMethod: "GET" | "POST"; - endpointPath: string; - endpointPathParams: string; - endpointParams: string; + endpointUrlTemplate: string; requiredParams: string; isStreaming?: boolean; custom?: AnyObject; @@ -42,9 +40,8 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { "https://developers.stellar.org/network/horizon/resources/list-all-accounts", docsLabel: "accounts", requestMethod: "GET", - endpointPath: "/accounts", - endpointPathParams: "", - endpointParams: "sponsor,signer,asset,cursor,limit,order", + endpointUrlTemplate: + "/accounts/{?sponsor,signer,asset,cursor,limit,order}", requiredParams: "", isStreaming: true, custom: { @@ -63,9 +60,7 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { "https://developers.stellar.org/network/horizon/resources/retrieve-an-account", docsLabel: "account", requestMethod: "GET", - endpointPath: "/accounts", - endpointPathParams: "account_id", - endpointParams: "", + endpointUrlTemplate: "/accounts/{account_id}", requiredParams: "account_id", isStreaming: true, }, @@ -84,9 +79,8 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { "https://developers.stellar.org/network/horizon/resources/list-all-assets", docsLabel: "assets", requestMethod: "GET", - endpointPath: "/assets", - endpointPathParams: "", - endpointParams: "asset_code,asset_issuer,cursor,order,limit", + endpointUrlTemplate: + "/assets{?asset_code,asset_issuer,cursor,order,limit}", requiredParams: "", isStreaming: true, }, @@ -103,11 +97,10 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { form: { docsUrl: "https://developers.stellar.org/network/horizon/resources/list-all-claimable-balances", - docsLabel: "claimable balances", + docsLabel: "claimable balance", requestMethod: "GET", - endpointPath: "/claimable_balances", - endpointPathParams: "", - endpointParams: "sponsor,asset,claimant,cursor,limit,order", + endpointUrlTemplate: + "/claimable_balances/{?sponsor,asset,claimant,cursor,limit,order}", requiredParams: "", isStreaming: false, }, @@ -120,9 +113,7 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { "https://developers.stellar.org/network/horizon/resources/retrieve-a-claimable-balance", docsLabel: "claimable balances", requestMethod: "GET", - endpointPath: "/claimable_balances", - endpointPathParams: "claimable_balance_id", - endpointParams: "", + endpointUrlTemplate: "/claimable_balances/{claimable_balance_id}", requiredParams: "claimable_balance_id", isStreaming: false, }, @@ -136,32 +127,85 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { { route: Routes.EXPLORE_ENDPOINTS_EFFECTS, label: "All Effects", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/list-all-effects", + docsLabel: "effects", + requestMethod: "GET", + endpointUrlTemplate: "/effects{?cursor,limit,order}", + requiredParams: "", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_EFFECTS_ACCOUNT, label: "Effects for Account", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/get-effects-by-account-id", + docsLabel: "effects for account", + requestMethod: "GET", + endpointUrlTemplate: + "/accounts/{account_id}/effects{?cursor,limit,order}", + requiredParams: "account_id", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_EFFECTS_LEDGER, label: "Effects for Ledger", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-a-ledgers-effects", + docsLabel: "effects for ledger", + requestMethod: "GET", + endpointUrlTemplate: + "/ledgers/{ledger}/effects{?cursor,limit,order}", + requiredParams: "ledger", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_EFFECTS_LIQUIDITY_POOL, label: "Effects for Liquidity Pool", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-related-effects", + docsLabel: "effects for liquidity pool", + requestMethod: "GET", + endpointUrlTemplate: + "/liquidity_pools/{liquidity_pool_id}/effects{?cursor,limit,order}", + requiredParams: "liquidity_pool_id", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_EFFECTS_OPERATION, label: "Effects for Operation", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-an-operations-effects", + docsLabel: "effects for operation", + requestMethod: "GET", + endpointUrlTemplate: + "/operations/{operation}/effects{?cursor,limit,order}", + requiredParams: "operation", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_EFFECTS_TRANSACTION, label: "Effects for Transaction", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-a-transactions-effects", + docsLabel: "effects for transaction", + requestMethod: "GET", + endpointUrlTemplate: + "/transactions/{transaction}/effects{?cursor,limit,order}", + requiredParams: "transaction", + isStreaming: true, + }, }, ], }, diff --git a/src/validate/index.ts b/src/validate/index.ts index 45e11173..2912381c 100644 --- a/src/validate/index.ts +++ b/src/validate/index.ts @@ -2,10 +2,12 @@ import { asset } from "./methods/asset"; import { assetCode } from "./methods/assetCode"; import { positiveInt } from "./methods/positiveInt"; import { publicKey } from "./methods/publicKey"; +import { transactionHash } from "./methods/transactionHash"; export const validate = { asset, assetCode, positiveInt, publicKey, + transactionHash, }; diff --git a/src/validate/methods/transactionHash.ts b/src/validate/methods/transactionHash.ts new file mode 100644 index 00000000..5485e418 --- /dev/null +++ b/src/validate/methods/transactionHash.ts @@ -0,0 +1,11 @@ +export const transactionHash = (hash: string | undefined) => { + if (!hash) { + return false; + } + + if (hash.match(/^[0-9a-f]{64}$/g) === null) { + return "Transaction hash is invalid."; + } + + return false; +}; From fbf1a39437919b77704b0505702f3a8e5596b24a Mon Sep 17 00:00:00 2001 From: Iveta Date: Fri, 29 Mar 2024 15:33:07 -0400 Subject: [PATCH 04/24] Fee Stats --- .../(sidebar)/explore-endpoints/[[...pages]]/page.tsx | 8 ++++---- src/constants/exploreEndpointsPages.ts | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index c24a4d5b..3f75486e 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -341,15 +341,15 @@ export default function ExploreEndpoints() { }; const renderFields = () => { - if (!pageData) { - return null; - } - const allFields = sanitizeArray([ ...urlPathParams.split(","), ...urlParams.split(","), ]); + if (!pageData || allFields.length === 0) { + return null; + } + return (
diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index c0f11c07..835ff1cf 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -216,7 +216,15 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { { route: Routes.EXPLORE_ENDPOINTS_FEE_STATS, label: "All Fee Stats", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/aggregations/fee-stats/object", + docsLabel: "fee stats", + requestMethod: "GET", + endpointUrlTemplate: "/fee_stats", + requiredParams: "", + isStreaming: true, + }, }, ], }, From daf725d37a34c2f0a9b2b00187bcc6db276cd907 Mon Sep 17 00:00:00 2001 From: Iveta Date: Fri, 29 Mar 2024 15:41:13 -0400 Subject: [PATCH 05/24] Ledgers --- src/constants/exploreEndpointsPages.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index 835ff1cf..92089f9e 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -235,12 +235,28 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { { route: Routes.EXPLORE_ENDPOINTS_LEDGERS, label: "All Ledgers", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/ledgers", + docsLabel: "ledgers", + requestMethod: "GET", + endpointUrlTemplate: "/ledgers{?cursor,limit,order}", + requiredParams: "", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_LEDGERS_SINGLE, label: "Single Ledger", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-a-ledger", + docsLabel: "ledger", + requestMethod: "GET", + endpointUrlTemplate: "/ledgers/{ledger}", + requiredParams: "ledger", + isStreaming: true, + }, }, ], }, From b647c95a65f04245c1434f8b15a11d47b5a579af Mon Sep 17 00:00:00 2001 From: Iveta Date: Fri, 29 Mar 2024 16:11:37 -0400 Subject: [PATCH 06/24] Offers --- .../explore-endpoints/[[...pages]]/page.tsx | 4 +- src/components/formComponentTemplate.tsx | 64 +++++++++++++++++++ src/constants/exploreEndpointsPages.ts | 42 +++++++++++- 3 files changed, 106 insertions(+), 4 deletions(-) diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index 3f75486e..46716faf 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -258,7 +258,7 @@ export default function ExploreEndpoints() { return false; } - if (param === "asset") { + if (["asset", "selling", "buying"].includes(param)) { if (value.type === "native") { return "native"; } @@ -382,6 +382,8 @@ export default function ExploreEndpoints() { switch (f) { case "asset": + case "selling": + case "buying": return component.render({ value: params[f], error: formError[f], diff --git a/src/components/formComponentTemplate.tsx b/src/components/formComponentTemplate.tsx index aa86d753..5df64d5d 100644 --- a/src/components/formComponentTemplate.tsx +++ b/src/components/formComponentTemplate.tsx @@ -109,6 +109,23 @@ export const formComponentTemplate = ( ), validate: validate.publicKey, }; + case "buying": + return { + render: (templ: TemplateRenderAssetProps) => ( + + ), + validate: validate.asset, + }; case "claimable_balance_id": return { render: (templ: TemplateRenderProps) => ( @@ -199,6 +216,21 @@ export const formComponentTemplate = ( ), validate: null, }; + case "offer_id": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: null, + }; case "operation": return { render: (templ: TemplateRenderProps) => ( @@ -228,6 +260,38 @@ export const formComponentTemplate = ( ), validate: null, }; + case "seller": + return { + render: (templ: TemplateRenderProps) => ( + + ), + validate: validate.publicKey, + }; + case "selling": + return { + render: (templ: TemplateRenderAssetProps) => ( + + ), + validate: validate.asset, + }; case "signer": return { render: (templ: TemplateRenderProps) => ( diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index 92089f9e..94e0ad6f 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -283,17 +283,53 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { { route: Routes.EXPLORE_ENDPOINTS_OFFERS, label: "All Offers", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/offers", + docsLabel: "offers", + requestMethod: "GET", + endpointUrlTemplate: + "/offers{?sponsor,seller,selling,buying,cursor,limit,order}", + requiredParams: "", + isStreaming: true, + custom: { + selling: { + assetInput: "issued", + includeNative: true, + }, + buying: { + assetInput: "issued", + includeNative: true, + }, + }, + }, }, { route: Routes.EXPLORE_ENDPOINTS_OFFERS_SINGLE, label: "Single Offer", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/get-offer-by-offer-id", + docsLabel: "offer", + requestMethod: "GET", + endpointUrlTemplate: "/offers/{offer_id}", + requiredParams: "offer_id", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_OFFERS_ACCOUNT, label: "Offers for Account", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/get-offers-by-account-id", + docsLabel: "offers for account", + requestMethod: "GET", + endpointUrlTemplate: + "/accounts/{account_id}/offers{?cursor,limit,order}", + requiredParams: "account_id", + isStreaming: true, + }, }, ], }, From 5ce66cd7d9a584e56a515fa713df6e5bbb699c8e Mon Sep 17 00:00:00 2001 From: Iveta Date: Fri, 29 Mar 2024 16:57:19 -0400 Subject: [PATCH 07/24] Operations --- .../explore-endpoints/[[...pages]]/page.tsx | 5 +- src/components/formComponentTemplate.tsx | 21 ++++++ src/constants/exploreEndpointsPages.ts | 65 +++++++++++++++++-- 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index 46716faf..dac2e54c 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -254,7 +254,7 @@ export default function ExploreEndpoints() { const getParamRequestValue = (param: string) => { const value = parseJsonString(params[param]); - if (!value) { + if (!value && typeof value !== "boolean") { return false; } @@ -266,7 +266,7 @@ export default function ExploreEndpoints() { return `${value.code}:${value.issuer}`; } - return value; + return `${value}`; }; // Build search params keeping the same params order @@ -398,6 +398,7 @@ export default function ExploreEndpoints() { }, }); case "order": + case "include_failed": return component.render({ value: params[f], error: formError[f], diff --git a/src/components/formComponentTemplate.tsx b/src/components/formComponentTemplate.tsx index 5df64d5d..b264de62 100644 --- a/src/components/formComponentTemplate.tsx +++ b/src/components/formComponentTemplate.tsx @@ -11,6 +11,7 @@ import { validate } from "@/validate"; import { AnyObject, AssetObjectValue } from "@/types/types"; import { TextPicker } from "./FormElements/TextPicker"; import { PositiveIntPicker } from "./FormElements/PositiveIntPicker"; +import { IncludeFailedPicker } from "./FormElements/IncludeFailedPicker"; type TemplateRenderProps = { value: string | undefined; @@ -35,6 +36,13 @@ type TemplateRenderOrderProps = { isRequired?: boolean; }; +type TemplateRenderIncludeFailedProps = { + value: string | undefined; + // eslint-disable-next-line no-unused-vars + onChange: (optionId: string | undefined, optionValue?: boolean) => void; + isRequired?: boolean; +}; + type FormComponentTemplate = { // eslint-disable-next-line no-unused-vars render: (...args: any[]) => JSX.Element; @@ -170,6 +178,19 @@ export const formComponentTemplate = ( ), validate: null, }; + case "include_failed": + return { + render: (templ: TemplateRenderIncludeFailedProps) => ( + + ), + validate: null, + }; case "ledger": return { render: (templ: TemplateRenderProps) => ( diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index 94e0ad6f..1f5f5379 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -340,32 +340,85 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { { route: Routes.EXPLORE_ENDPOINTS_OPERATIONS, label: "All Operations", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/operations", + docsLabel: "operations", + requestMethod: "GET", + endpointUrlTemplate: + "/operations{?cursor,limit,order,include_failed}", + requiredParams: "", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_OPERATIONS_SINGLE, label: "Single Operation", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-an-operation", + docsLabel: "operation", + requestMethod: "GET", + endpointUrlTemplate: "/operations/{operation}", + requiredParams: "operation", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_OPERATIONS_ACCOUNT, label: "Operations for Account", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/get-operations-by-account-id", + docsLabel: "operations for account", + requestMethod: "GET", + endpointUrlTemplate: + "/accounts/{account_id}/operations{?cursor,limit,order,include_failed}", + requiredParams: "account_id", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_OPERATIONS_LEDGER, label: "Operations for Ledger", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-a-ledgers-operations", + docsLabel: "operations for ledger", + requestMethod: "GET", + endpointUrlTemplate: + "/ledgers/{ledger}/operations{?cursor,limit,order,include_failed}", + requiredParams: "ledger", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_OPERATIONS_LIQUIDITY_POOL, label: "Operations for Liquidity Pool", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/lp-retrieve-related-operations", + docsLabel: "operations for liquidity pool", + requestMethod: "GET", + endpointUrlTemplate: + "/liquidity_pools/{liquidity_pool_id}/operations{?cursor,limit,order,include_failed}", + requiredParams: "liquidity_pool_id", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_OPERATIONS_TRANSACTION, label: "Operations for Transaction", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-a-transactions-operations", + docsLabel: "operations for transaction", + requestMethod: "GET", + endpointUrlTemplate: + "/transactions/{transaction}/operations{?cursor,limit,order}", + requiredParams: "transaction", + isStreaming: true, + }, }, ], }, From e1a1fe7f8fd6102d5625f5e1e5d96c51bb4d401e Mon Sep 17 00:00:00 2001 From: Iveta Date: Mon, 1 Apr 2024 10:34:59 -0400 Subject: [PATCH 08/24] Payments --- src/constants/exploreEndpointsPages.ts | 45 +++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index 1f5f5379..cc9ca3a9 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -461,22 +461,59 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { { route: Routes.EXPLORE_ENDPOINTS_PAYMENTS, label: "All Payments", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/list-all-payments", + docsLabel: "payments", + requestMethod: "GET", + endpointUrlTemplate: + "/payments{?cursor,limit,order,include_failed}", + requiredParams: "", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_PAYMENTS_ACCOUNT, label: "Payments for Account", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/get-payments-by-account-id", + docsLabel: "payments for account", + requestMethod: "GET", + endpointUrlTemplate: + "/accounts/{account_id}/payments{?cursor,limit,order,include_failed}", + requiredParams: "account_id", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_PAYMENTS_LEDGER, label: "Payments for Ledger", - form: undefined, + form: { + docsUrl: + "https://developers.stellar.org/network/horizon/resources/retrieve-a-ledgers-payments", + docsLabel: "payments for ledger", + requestMethod: "GET", + endpointUrlTemplate: + "/ledgers/{ledger}/payments{?cursor,limit,order,include_failed}", + requiredParams: "ledger", + isStreaming: true, + }, }, { route: Routes.EXPLORE_ENDPOINTS_PAYMENTS_TRANSACTION, label: "Payments for Transaction", - form: undefined, + form: { + docsUrl: + // TODO: no link in the docs for payments for transaction + "https://developers.stellar.org/network/horizon/resources/transactions", + docsLabel: "payments for transaction", + requestMethod: "GET", + endpointUrlTemplate: + "/transactions/{transaction}/payments{?cursor,limit,order}", + requiredParams: "transaction", + isStreaming: true, + }, }, ], }, From b0eff13c2b3a6973a1285787e587adda0eb2f5a0 Mon Sep 17 00:00:00 2001 From: Iveta Date: Mon, 1 Apr 2024 14:45:37 -0400 Subject: [PATCH 09/24] Transactions --- .../explore-endpoints/[[...pages]]/page.tsx | 94 +++++++++++++------ src/components/FormElements/XdrPicker.tsx | 40 ++++++++ src/components/NetworkIndicator/styles.scss | 2 +- src/components/formComponentTemplate.tsx | 22 ++++- src/constants/exploreEndpointsPages.ts | 64 +++++++++++-- src/query/useExploreEndpoint.ts | 26 ++++- src/styles/globals.scss | 11 +++ src/validate/index.ts | 2 + src/validate/methods/xdr.ts | 7 ++ 9 files changed, 228 insertions(+), 40 deletions(-) create mode 100644 src/components/FormElements/XdrPicker.tsx create mode 100644 src/validate/methods/xdr.ts diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index dac2e54c..5071b918 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -11,6 +11,7 @@ import { Input, Link, Text, + Textarea, } from "@stellar/design-system"; import { useQueryClient } from "@tanstack/react-query"; @@ -115,7 +116,12 @@ export default function ExploreEndpoints() { refetch, isSuccess, isError, - } = useExploreEndpoint(requestUrl); + } = useExploreEndpoint( + requestUrl, + // There is only one endpoint request for POST, using params directly for + // simplicity. + pageData?.requestMethod === "POST" ? { tx: params.tx ?? "" } : undefined, + ); const responseEl = useRef(null); @@ -248,6 +254,11 @@ export default function ExploreEndpoints() { }; const baseUrl = `${endpointNetwork.horizonUrl}${parseUrlPath(urlPath)}`; + + if (pageData?.requestMethod === "POST") { + return baseUrl; + } + const searchParams = new URLSearchParams(); const templateParams = urlParams?.split(","); @@ -305,38 +316,65 @@ export default function ExploreEndpoints() { }, delay); }; + const renderPostPayload = () => { + if (pageData?.requestMethod === "POST") { + return ( +
+