Skip to content
This repository was archived by the owner on Sep 24, 2025. It is now read-only.

fix (packages/nhost-js): added missing endpoints (storage) and baseURL (functions)#35

Merged
dbarrosop merged 3 commits into
mainfrom
missing-things
Jun 23, 2025
Merged

fix (packages/nhost-js): added missing endpoints (storage) and baseURL (functions)#35
dbarrosop merged 3 commits into
mainfrom
missing-things

Conversation

@dbarrosop

@dbarrosop dbarrosop commented Jun 23, 2025

Copy link
Copy Markdown
Member

PR Type

Enhancement


Description

  • Added missing storage endpoints for admin operations

  • Added baseURL property to functions client interface

  • Enabled presigned URL functionality for file access

  • Added comprehensive documentation for new endpoints


Changes walkthrough 📝

Relevant files
Enhancement
client.ts
Add baseURL property to functions client                                 

packages/nhost-js/src/functions/client.ts

  • Added baseURL property to Client interface
  • Exposed baseURL in the returned client object
  • +3/-0     
    client.ts
    Add missing storage admin endpoints and presigned URL       

    packages/nhost-js/src/storage/client.ts

  • Added 5 new response type interfaces for admin operations
  • Added 6 new methods: getPresignedURL, listOrphanedFiles,
    deleteOrphanedFiles, listBrokenMetadata, deleteBrokenMetadata,
    listFilesNotUploaded
  • Implemented complete method implementations with proper error handling
  • Exported new methods in client object
  • +324/-0 
    Documentation
    functions.mdx
    Document functions client baseURL property                             

    docs/reference/javascript/nhost-js/functions.mdx

    • Added documentation for new baseURL property in Client interface
    +8/-0     
    storage.mdx
    Document new storage endpoints and types                                 

    docs/reference/javascript/nhost-js/storage.mdx

  • Added documentation for 6 new storage methods
  • Added documentation for 4 new response type interfaces
  • Included parameter tables and return type information
  • +206/-0 
    Configuration changes
    storage.yaml
    Enable missing storage API endpoints in OpenAPI spec         

    packages/nhost-js/api/storage.yaml

  • Uncommented and enabled presigned URL endpoints
  • Uncommented and enabled admin operation endpoints
  • Added proper operationId fields for code generation
  • Enhanced descriptions with admin secret requirements
  • +379/-371

    Need help?
  • Type /help how to ... in the comments thread for any questions about PR-Agent usage.
  • Check out the documentation for more information.
  • @github-actions

    Copy link
    Copy Markdown

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 No relevant tests
    🔒 Security concerns

    Admin Secret Exposure:
    The new admin operations (listOrphanedFiles, deleteOrphanedFiles, listBrokenMetadata, deleteBrokenMetadata, listFilesNotUploaded) require Hasura admin secret authentication. While the API specification correctly indicates this requirement, the client implementation doesn't provide any validation or warnings about the sensitive nature of these operations. These methods could potentially be misused if the admin secret is not properly secured, leading to unauthorized access to administrative storage operations.

    ⚡ Recommended focus areas for review

    Code Duplication

    The new admin operation methods (getPresignedURL, listOrphanedFiles, deleteOrphanedFiles, listBrokenMetadata, deleteBrokenMetadata, listFilesNotUploaded) contain nearly identical error handling and response parsing logic. This creates maintenance overhead and potential inconsistencies.

    const getPresignedURL = async (
      id: string,
      options?: RequestInit,
    ): Promise<FetchResponse<PresignedURLResponse>> => {
      const url = baseURL + `/files/${id}/presignedurl`;
      const res = await fetch(url, {
        ...options,
        method: "GET",
        headers: {
          ...options?.headers,
        },
      });
    
      if (res.status >= 300) {
        const responseBody = [412].includes(res.status) ? null : await res.text();
        const payload: unknown = responseBody ? JSON.parse(responseBody) : {};
        throw new FetchError(payload, res.status, res.headers);
      }
    
      const responseBody = [204, 205, 304].includes(res.status)
        ? null
        : await res.text();
      const payload: PresignedURLResponse = responseBody
        ? JSON.parse(responseBody)
        : {};
    
      return {
        body: payload,
        status: res.status,
        headers: res.headers,
      } as FetchResponse<PresignedURLResponse>;
    };
    
    const listOrphanedFiles = async (
      options?: RequestInit,
    ): Promise<FetchResponse<ListOrphanedFilesResponse200>> => {
      const url = baseURL + `/ops/list-orphans`;
      const res = await fetch(url, {
        ...options,
        method: "POST",
        headers: {
          ...options?.headers,
        },
      });
    
      if (res.status >= 300) {
        const responseBody = [412].includes(res.status) ? null : await res.text();
        const payload: unknown = responseBody ? JSON.parse(responseBody) : {};
        throw new FetchError(payload, res.status, res.headers);
      }
    
      const responseBody = [204, 205, 304].includes(res.status)
        ? null
        : await res.text();
      const payload: ListOrphanedFilesResponse200 = responseBody
        ? JSON.parse(responseBody)
        : {};
    
      return {
        body: payload,
        status: res.status,
        headers: res.headers,
      } as FetchResponse<ListOrphanedFilesResponse200>;
    };
    
    const deleteOrphanedFiles = async (
      options?: RequestInit,
    ): Promise<FetchResponse<DeleteOrphanedFilesResponse200>> => {
      const url = baseURL + `/ops/delete-orphans`;
      const res = await fetch(url, {
        ...options,
        method: "POST",
        headers: {
          ...options?.headers,
        },
      });
    
      if (res.status >= 300) {
        const responseBody = [412].includes(res.status) ? null : await res.text();
        const payload: unknown = responseBody ? JSON.parse(responseBody) : {};
        throw new FetchError(payload, res.status, res.headers);
      }
    
      const responseBody = [204, 205, 304].includes(res.status)
        ? null
        : await res.text();
      const payload: DeleteOrphanedFilesResponse200 = responseBody
        ? JSON.parse(responseBody)
        : {};
    
      return {
        body: payload,
        status: res.status,
        headers: res.headers,
      } as FetchResponse<DeleteOrphanedFilesResponse200>;
    };
    
    const listBrokenMetadata = async (
      options?: RequestInit,
    ): Promise<FetchResponse<ListBrokenMetadataResponse200>> => {
      const url = baseURL + `/ops/list-broken-metadata`;
      const res = await fetch(url, {
        ...options,
        method: "POST",
        headers: {
          ...options?.headers,
        },
      });
    
      if (res.status >= 300) {
        const responseBody = [412].includes(res.status) ? null : await res.text();
        const payload: unknown = responseBody ? JSON.parse(responseBody) : {};
        throw new FetchError(payload, res.status, res.headers);
      }
    
      const responseBody = [204, 205, 304].includes(res.status)
        ? null
        : await res.text();
      const payload: ListBrokenMetadataResponse200 = responseBody
        ? JSON.parse(responseBody)
        : {};
    
      return {
        body: payload,
        status: res.status,
        headers: res.headers,
      } as FetchResponse<ListBrokenMetadataResponse200>;
    };
    
    const deleteBrokenMetadata = async (
      options?: RequestInit,
    ): Promise<FetchResponse<DeleteBrokenMetadataResponse200>> => {
      const url = baseURL + `/ops/delete-broken-metadata`;
      const res = await fetch(url, {
        ...options,
        method: "POST",
        headers: {
          ...options?.headers,
        },
      });
    
      if (res.status >= 300) {
        const responseBody = [412].includes(res.status) ? null : await res.text();
        const payload: unknown = responseBody ? JSON.parse(responseBody) : {};
        throw new FetchError(payload, res.status, res.headers);
      }
    
      const responseBody = [204, 205, 304].includes(res.status)
        ? null
        : await res.text();
      const payload: DeleteBrokenMetadataResponse200 = responseBody
        ? JSON.parse(responseBody)
        : {};
    
      return {
        body: payload,
        status: res.status,
        headers: res.headers,
      } as FetchResponse<DeleteBrokenMetadataResponse200>;
    };
    
    const listFilesNotUploaded = async (
      options?: RequestInit,
    ): Promise<FetchResponse<ListFilesNotUploadedResponse200>> => {
      const url = baseURL + `/ops/list-not-uploaded`;
      const res = await fetch(url, {
        ...options,
        method: "POST",
        headers: {
          ...options?.headers,
        },
      });
    
      if (res.status >= 300) {
        const responseBody = [412].includes(res.status) ? null : await res.text();
        const payload: unknown = responseBody ? JSON.parse(responseBody) : {};
        throw new FetchError(payload, res.status, res.headers);
      }
    
      const responseBody = [204, 205, 304].includes(res.status)
        ? null
        : await res.text();
      const payload: ListFilesNotUploadedResponse200 = responseBody
        ? JSON.parse(responseBody)
        : {};
    
      return {
        body: payload,
        status: res.status,
        headers: res.headers,
      } as FetchResponse<ListFilesNotUploadedResponse200>;
    };
    Documentation Quality

    The JSDoc comments for the new response interfaces (ListOrphanedFilesResponse200, DeleteOrphanedFilesResponse200, etc.) are incomplete with empty descriptions and malformed @Property tags that don't follow proper JSDoc syntax.

    /**
     * 
     @property files? (`string[]`) - */
    export interface ListOrphanedFilesResponse200 {
      /**
       *
       */
      files?: string[];
    }
    
    /**
     * 
     @property files? (`string[]`) - */
    export interface DeleteOrphanedFilesResponse200 {
      /**
       *
       */
      files?: string[];
    }
    
    /**
     * 
     @property metadata? (`FileSummary[]`) - */
    export interface ListBrokenMetadataResponse200 {
      /**
       *
       */
      metadata?: FileSummary[];
    }
    
    /**
     * 
     @property metadata? (`FileSummary[]`) - */
    export interface DeleteBrokenMetadataResponse200 {
      /**
       *
       */
      metadata?: FileSummary[];
    }
    
    /**
     * 
     @property metadata? (`FileSummary[]`) - */
    export interface ListFilesNotUploadedResponse200 {
      /**
       *
       */
      metadata?: FileSummary[];
    }
    API Inconsistency

    The presigned URL endpoint includes an 'excludeme' tag which suggests it may not be intended for public API exposure, but it's being implemented in the client. This needs clarification on the intended usage and visibility.

    - excludeme

    @github-actions

    Copy link
    Copy Markdown

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    General
    Fix malformed JSDoc comments

    The JSDoc comment has malformed syntax with an incomplete @property tag. Remove the
    incomplete @property annotation and provide a proper description for the interface.

    packages/nhost-js/src/storage/client.ts [284-292]

     /**
    - * 
    - @property files? (`string[]`) - */
    + * Response interface for listing orphaned files operation
    + */
     export interface ListOrphanedFilesResponse200 {
       /**
    -   *
    +   * Array of orphaned file identifiers
        */
       files?: string[];
     }
    Suggestion importance[1-10]: 6

    __

    Why: The suggestion correctly identifies malformed JSDoc syntax with incomplete @property tags and provides a cleaner, more descriptive documentation format. This improves code maintainability and documentation quality.

    Low

    @dbarrosop dbarrosop merged commit 9e375e0 into main Jun 23, 2025
    9 checks passed
    @dbarrosop dbarrosop deleted the missing-things branch June 23, 2025 11:46
    Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants