From a248ab1f45cf1983a59cab75bba54161669a6eed Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 25 Aug 2025 14:58:04 -0400 Subject: [PATCH 1/5] Add missing files to install script --- scripts/install_app.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/install_app.sh b/scripts/install_app.sh index c99d0dc..c87b889 100755 --- a/scripts/install_app.sh +++ b/scripts/install_app.sh @@ -66,9 +66,15 @@ psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../db/nft_actions.s psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../db/main_loop.sql" echo "Installing API endpoints..." +# Install type definitions first +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/types/nft_type.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/types/nft_instance.sql" + # Install endpoint schema and functions psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/endpoint_schema.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/get_version.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/get_nft_types.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/get_nft_instances.sql" echo "Granting permissions..." psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -c "SET ROLE nfttracker_owner; GRANT USAGE ON SCHEMA ${NFTTRACKER_SCHEMA} to nfttracker_user;" -- GitLab From ea920446a5803153d21a66a935b280671556ac6d Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 25 Aug 2025 15:02:07 -0400 Subject: [PATCH 2/5] Change nft_owner to nfttracker_owner --- endpoints/get_nft_instances.sql | 2 +- endpoints/get_nft_types.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/endpoints/get_nft_instances.sql b/endpoints/get_nft_instances.sql index f0593ec..100f129 100644 --- a/endpoints/get_nft_instances.sql +++ b/endpoints/get_nft_instances.sql @@ -1,4 +1,4 @@ -SET ROLE nft_owner; +SET ROLE nfttracker_owner; /** openapi:paths /nfts/{creator}/{symbol}: diff --git a/endpoints/get_nft_types.sql b/endpoints/get_nft_types.sql index 6f2883e..55b2984 100644 --- a/endpoints/get_nft_types.sql +++ b/endpoints/get_nft_types.sql @@ -1,4 +1,4 @@ -SET ROLE nft_owner; +SET ROLE nfttracker_owner; /** openapi:paths /nfts: -- GitLab From ad1e9d9979fe57a6ab97904e7247c32417d96af7 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 25 Aug 2025 15:23:34 -0400 Subject: [PATCH 3/5] move types into nfttracker_endpoints --- endpoints/endpoint_schema.sql | 9 ++++----- endpoints/get_nft_instances.sql | 8 ++++---- endpoints/get_nft_types.sql | 8 ++++---- endpoints/types/nft_instance.sql | 6 +++--- endpoints/types/nft_type.sql | 6 +++--- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/endpoints/endpoint_schema.sql b/endpoints/endpoint_schema.sql index 8517c1e..0f2cf9c 100644 --- a/endpoints/endpoint_schema.sql +++ b/endpoints/endpoint_schema.sql @@ -34,7 +34,6 @@ DO $__$ END IF; CREATE SCHEMA IF NOT EXISTS nfttracker_endpoints AUTHORIZATION nfttracker_owner; - CREATE SCHEMA IF NOT EXISTS nfttracker_backend AUTHORIZATION nfttracker_owner; EXECUTE FORMAT( 'create or replace function nfttracker_endpoints.root() returns json as $_$ @@ -45,7 +44,7 @@ DO $__$ { "components": { "schemas": { - "nfttracker_backend.nft_type": { + "nfttracker_endpoints.nft_type": { "type": "object", "properties": { "id": { @@ -91,7 +90,7 @@ DO $__$ } } }, - "nfttracker_backend.nft_instance": { + "nfttracker_endpoints.nft_instance": { "type": "object", "properties": { "id": { @@ -221,7 +220,7 @@ DO $__$ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/nfttracker_backend.nft_instance" + "$ref": "#/components/schemas/nfttracker_endpoints.nft_instance" }, "example": { "id": 1, @@ -258,7 +257,7 @@ DO $__$ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/nfttracker_backend.nft_type" + "$ref": "#/components/schemas/nfttracker_endpoints.nft_type" }, "example": { "id": 1, diff --git a/endpoints/get_nft_instances.sql b/endpoints/get_nft_instances.sql index 100f129..405644c 100644 --- a/endpoints/get_nft_instances.sql +++ b/endpoints/get_nft_instances.sql @@ -33,11 +33,11 @@ SET ROLE nfttracker_owner; description: | Issued NFT instances of given symbol - * Returns `nfttracker_backend.nft_instance` + * Returns `nfttracker_endpoints.nft_instance` content: application/json: schema: - $ref: '#/components/schemas/nfttracker_backend.nft_instance' + $ref: '#/components/schemas/nfttracker_endpoints.nft_instance' example: { "id": 1, "holder": "alice", @@ -57,7 +57,7 @@ CREATE OR REPLACE FUNCTION nfttracker_endpoints.get_nft_instances( "creator" TEXT, "symbol" TEXT ) -RETURNS nfttracker_backend.nft_instance +RETURNS nfttracker_endpoints.nft_instance -- openapi-generated-code-end LANGUAGE 'plpgsql' STABLE AS @@ -77,7 +77,7 @@ BEGIN i.soulbound, i.created_at, i.updated_at - )::nfttracker_backend.nft_instance + )::nfttracker_endpoints.nft_instance FROM nfttracker_app.instances AS i INNER JOIN nfttracker_app.types AS t ON i.type_id = t.id LEFT JOIN hafd.accounts AS h ON i.holder = h.id diff --git a/endpoints/get_nft_types.sql b/endpoints/get_nft_types.sql index 55b2984..d7b25d6 100644 --- a/endpoints/get_nft_types.sql +++ b/endpoints/get_nft_types.sql @@ -20,11 +20,11 @@ SET ROLE nfttracker_owner; description: | Registered NFT types - * Returns `nfttracker_backend.nft_type` + * Returns `nfttracker_endpoints.nft_type` content: application/json: schema: - $ref: '#/components/schemas/nfttracker_backend.nft_type' + $ref: '#/components/schemas/nfttracker_endpoints.nft_type' example: { "id": 1, "creator": "alice", @@ -39,7 +39,7 @@ SET ROLE nfttracker_owner; -- openapi-generated-code-begin DROP FUNCTION IF EXISTS nfttracker_endpoints.get_nft_types; CREATE OR REPLACE FUNCTION nfttracker_endpoints.get_nft_types() -RETURNS nfttracker_backend.nft_type +RETURNS nfttracker_endpoints.nft_type -- openapi-generated-code-end LANGUAGE 'plpgsql' STABLE AS @@ -58,7 +58,7 @@ BEGIN t.created_at, t.updated_at, ARRAY_AGG(DISTINCT a.name) FILTER (WHERE a.name IS NOT NULL) - )::nfttracker_backend.nft_type + )::nfttracker_endpoints.nft_type FROM nfttracker_app.types AS t LEFT JOIN nfttracker_app.authorized_issuers AS ai ON t.id = ai.type_id LEFT JOIN hafd.accounts AS a ON ai.account_id = a.id diff --git a/endpoints/types/nft_instance.sql b/endpoints/types/nft_instance.sql index ece8954..01c1c79 100644 --- a/endpoints/types/nft_instance.sql +++ b/endpoints/types/nft_instance.sql @@ -1,5 +1,5 @@ /** openapi:components:schemas -nfttracker_backend.nft_instance: +nfttracker_endpoints.nft_instance: type: object properties: id: @@ -30,8 +30,8 @@ nfttracker_backend.nft_instance: description: the timestamp this instance was last modified */ -- openapi-generated-code-begin -DROP TYPE IF EXISTS nfttracker_backend.nft_instance CASCADE; -CREATE TYPE nfttracker_backend.nft_instance AS ( +DROP TYPE IF EXISTS nfttracker_endpoints.nft_instance CASCADE; +CREATE TYPE nfttracker_endpoints.nft_instance AS ( "id" INT, "holder" TEXT, "data" TEXT, diff --git a/endpoints/types/nft_type.sql b/endpoints/types/nft_type.sql index 9762928..2445ceb 100644 --- a/endpoints/types/nft_type.sql +++ b/endpoints/types/nft_type.sql @@ -1,5 +1,5 @@ /** openapi:components:schemas -nfttracker_backend.nft_type: +nfttracker_endpoints.nft_type: type: object properties: id: @@ -35,8 +35,8 @@ nfttracker_backend.nft_type: description: list of accounts that can issue instance of this NFT type */ -- openapi-generated-code-begin -DROP TYPE IF EXISTS nfttracker_backend.nft_type CASCADE; -CREATE TYPE nfttracker_backend.nft_type AS ( +DROP TYPE IF EXISTS nfttracker_endpoints.nft_type CASCADE; +CREATE TYPE nfttracker_endpoints.nft_type AS ( "id" INT, "creator" TEXT, "owner" TEXT, -- GitLab From 12a65a6ea2a892dcdfe2e63b77104366ce62d567 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 25 Aug 2025 15:46:42 -0400 Subject: [PATCH 4/5] Change 'GET nfts' to return an array --- endpoints/endpoint_schema.sql | 62 +++++++++++++++++++-------------- endpoints/get_nft_instances.sql | 46 +++++++++++++----------- endpoints/get_nft_types.sql | 52 ++++++++++++++------------- 3 files changed, 89 insertions(+), 71 deletions(-) diff --git a/endpoints/endpoint_schema.sql b/endpoints/endpoint_schema.sql index 0f2cf9c..eba0d35 100644 --- a/endpoints/endpoint_schema.sql +++ b/endpoints/endpoint_schema.sql @@ -216,24 +216,29 @@ DO $__$ ], "responses": { "200": { - "description": "Issued NFT instances of given symbol\n\n* Returns `nfttracker_backend.nft_instance`\n", + "description": "Issued NFT instances of given symbol\n\n* Returns `nfttracker_endpoints.nft_instance`\n", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/nfttracker_endpoints.nft_instance" + "type": "array", + "items": { + "$ref": "#/components/schemas/nfttracker_endpoints.nft_instance" + } }, - "example": { - "id": 1, - "holder": "alice", - "data": "{\"key\": \"value\"}", - "tags": [ - "item", - "collectible" - ], - "soulbound": false, - "created_at": "2025-08-22T12:00:00", - "updated_at": "2025-08-22T12:00:00" - } + "example": [ + { + "id": 1, + "holder": "alice", + "data": "{\"key\": \"value\"}", + "tags": [ + "item", + "collectible" + ], + "soulbound": false, + "created_at": "2025-08-22T12:00:00", + "updated_at": "2025-08-22T12:00:00" + } + ] } } }, @@ -253,22 +258,27 @@ DO $__$ "operationId": "nfttracker_endpoints.get_nft_types", "responses": { "200": { - "description": "Registered NFT types\n\n* Returns `nfttracker_backend.nft_type`\n", + "description": "Registered NFT types\n\n* Returns `nfttracker_endpoints.nft_type`\n", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/nfttracker_endpoints.nft_type" + "type": "array", + "items": { + "$ref": "#/components/schemas/nfttracker_endpoints.nft_type" + } }, - "example": { - "id": 1, - "creator": "alice", - "owner": "bob", - "symbol": "TEST", - "name": "Test symbol", - "max_count": 10, - "created_at": "2025-08-22T12:00:00", - "updated_at": "2025-08-22T12:00:00" - } + "example": [ + { + "id": 1, + "creator": "alice", + "owner": "bob", + "symbol": "TEST", + "name": "Test symbol", + "max_count": 10, + "created_at": "2025-08-22T12:00:00", + "updated_at": "2025-08-22T12:00:00" + } + ] } } } diff --git a/endpoints/get_nft_instances.sql b/endpoints/get_nft_instances.sql index 405644c..2b6a820 100644 --- a/endpoints/get_nft_instances.sql +++ b/endpoints/get_nft_instances.sql @@ -37,8 +37,10 @@ SET ROLE nfttracker_owner; content: application/json: schema: - $ref: '#/components/schemas/nfttracker_endpoints.nft_instance' - example: { + type: array + items: + $ref: '#/components/schemas/nfttracker_endpoints.nft_instance' + example: [{ "id": 1, "holder": "alice", "data": "{\"key\": \"value\"}", @@ -46,7 +48,7 @@ SET ROLE nfttracker_owner; "soulbound": false, "created_at": "2025-08-22T12:00:00", "updated_at": "2025-08-22T12:00:00" - } + }] '404': description: | creator/symbol combination does not exist @@ -57,7 +59,7 @@ CREATE OR REPLACE FUNCTION nfttracker_endpoints.get_nft_instances( "creator" TEXT, "symbol" TEXT ) -RETURNS nfttracker_endpoints.nft_instance +RETURNS nfttracker_endpoints.nft_instance[] -- openapi-generated-code-end LANGUAGE 'plpgsql' STABLE AS @@ -68,23 +70,25 @@ DECLARE BEGIN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=2"}]', true); - RETURN ( - SELECT ROW( - i.id, - h.name, - i.data, - i.tags, - i.soulbound, - i.created_at, - i.updated_at - )::nfttracker_endpoints.nft_instance - FROM nfttracker_app.instances AS i - INNER JOIN nfttracker_app.types AS t ON i.type_id = t.id - LEFT JOIN hafd.accounts AS h ON i.holder = h.id - WHERE t.symbol = _symbol - AND t.creator = (SELECT id FROM hafd.accounts WHERE name = _creator) - ORDER BY t.id - LIMIT 1 + RETURN COALESCE( + ARRAY( + SELECT ROW( + i.id, + h.name, + i.data, + i.tags, + i.soulbound, + i.created_at, + i.updated_at + )::nfttracker_endpoints.nft_instance + FROM nfttracker_app.instances AS i + INNER JOIN nfttracker_app.types AS t ON i.type_id = t.id + LEFT JOIN hafd.accounts AS h ON i.holder = h.id + WHERE t.symbol = _symbol + AND t.creator = (SELECT id FROM hafd.accounts WHERE name = _creator) + ORDER BY i.id + ), + ARRAY[]::nfttracker_endpoints.nft_instance[] ); END $$; diff --git a/endpoints/get_nft_types.sql b/endpoints/get_nft_types.sql index d7b25d6..8798f8e 100644 --- a/endpoints/get_nft_types.sql +++ b/endpoints/get_nft_types.sql @@ -24,8 +24,10 @@ SET ROLE nfttracker_owner; content: application/json: schema: - $ref: '#/components/schemas/nfttracker_endpoints.nft_type' - example: { + type: array + items: + $ref: '#/components/schemas/nfttracker_endpoints.nft_type' + example: [{ "id": 1, "creator": "alice", "owner": "bob", @@ -34,12 +36,12 @@ SET ROLE nfttracker_owner; "max_count": 10, "created_at": "2025-08-22T12:00:00", "updated_at": "2025-08-22T12:00:00" - } + }] */ -- openapi-generated-code-begin DROP FUNCTION IF EXISTS nfttracker_endpoints.get_nft_types; CREATE OR REPLACE FUNCTION nfttracker_endpoints.get_nft_types() -RETURNS nfttracker_endpoints.nft_type +RETURNS nfttracker_endpoints.nft_type[] -- openapi-generated-code-end LANGUAGE 'plpgsql' STABLE AS @@ -47,26 +49,28 @@ $$ BEGIN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=2"}]', true); - RETURN ( - SELECT ROW( - t.id, - c.name, - o.name, - t.symbol, - t.name, - t.max_count, - t.created_at, - t.updated_at, - ARRAY_AGG(DISTINCT a.name) FILTER (WHERE a.name IS NOT NULL) - )::nfttracker_endpoints.nft_type - FROM nfttracker_app.types AS t - LEFT JOIN nfttracker_app.authorized_issuers AS ai ON t.id = ai.type_id - LEFT JOIN hafd.accounts AS a ON ai.account_id = a.id - LEFT JOIN hafd.accounts AS c ON t.creator = c.id - LEFT JOIN hafd.accounts AS o ON t.owner = o.id - GROUP BY t.id, c.name, o.name, t.symbol, t.name, t.max_count, t.created_at, t.updated_at - ORDER BY t.id - LIMIT 1 + RETURN COALESCE( + ARRAY( + SELECT ROW( + t.id, + c.name, + o.name, + t.symbol, + t.name, + t.max_count, + t.created_at, + t.updated_at, + ARRAY_AGG(DISTINCT a.name) FILTER (WHERE a.name IS NOT NULL) + )::nfttracker_endpoints.nft_type + FROM nfttracker_app.types AS t + LEFT JOIN nfttracker_app.authorized_issuers AS ai ON t.id = ai.type_id + LEFT JOIN hafd.accounts AS a ON ai.account_id = a.id + LEFT JOIN hafd.accounts AS c ON t.creator = c.id + LEFT JOIN hafd.accounts AS o ON t.owner = o.id + GROUP BY t.id, c.name, o.name, t.symbol, t.name, t.max_count, t.created_at, t.updated_at + ORDER BY t.id + ), + ARRAY[]::nfttracker_endpoints.nft_type[] ); END $$; -- GitLab From 73e7323326a5fb2e19546ca22e1d5845c8c25622 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 25 Aug 2025 16:03:31 -0400 Subject: [PATCH 5/5] Fix URL in OpenAPI docs --- scripts/install_app.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/install_app.sh b/scripts/install_app.sh index c87b889..a876a4b 100755 --- a/scripts/install_app.sh +++ b/scripts/install_app.sh @@ -12,6 +12,7 @@ OPTIONS: --postgres-port=PORT PostgreSQL port (default: 5432) --postgres-user=USERNAME PostgreSQL user name (default: haf_admin) --postgres-url=URL PostgreSQL URL (if set, overrides three previous options, empty by default) + --swagger-url=URL Server URL for OpenAPI documentation (default: localhost) --help,-h,-? Displays this help message EOF } @@ -21,6 +22,7 @@ POSTGRES_HOST=${POSTGRES_HOST:-"localhost"} POSTGRES_PORT=${POSTGRES_PORT:-5432} POSTGRES_URL=${POSTGRES_URL:-""} NFTTRACKER_SCHEMA=${NFTTRACKER_SCHEMA:-"nfttracker_app"} +SWAGGER_URL=${SWAGGER_URL:-"localhost"} while [ $# -gt 0 ]; do case "$1" in @@ -39,6 +41,9 @@ while [ $# -gt 0 ]; do --schema=*) NFTTRACKER_SCHEMA="${1#*=}" ;; + --swagger-url=*) + SWAGGER_URL="${1#*=}" + ;; --help|-h|-?) print_help exit 0 @@ -71,7 +76,7 @@ psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/types/ psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/types/nft_instance.sql" # Install endpoint schema and functions -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/endpoint_schema.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -c "SET custom.swagger_url = '$SWAGGER_URL';" -f "$SCRIPTPATH/../endpoints/endpoint_schema.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/get_version.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/get_nft_types.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../endpoints/get_nft_instances.sql" -- GitLab