Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDK: Provide a client.getPage method to fetch page, content, nav using graphQL #30968

Open
6 tasks
Tracked by #30943
rjvelazco opened this issue Dec 17, 2024 · 4 comments · May be fixed by #31411
Open
6 tasks
Tracked by #30943

SDK: Provide a client.getPage method to fetch page, content, nav using graphQL #30968

rjvelazco opened this issue Dec 17, 2024 · 4 comments · May be fixed by #31411

Comments

@rjvelazco
Copy link
Contributor

rjvelazco commented Dec 17, 2024

Caution

THESE ARE BREAKING CHANGES AND SHOULD BE DOCUMENTED

Parent Issue

#30943

Overview of the Issue:

To enable a unified and flexible GraphQL querying system, we need to implement a method client.getPage that:

  1. Merges a base query with user-defined custom queries using GraphQL fragments.
  2. Ensures the base query includes all required fields for the Universal Visual Editor (UVE) to function correctly.
  3. Provides flexibility by supporting optional properties for page, content, and nav, requiring at least one.
  4. Processes the _map fields in the response to remove them from the output while maintaining their parsed values.

This method will be critical for both developers using the SDK and for internal UVE operations, ensuring consistent and optimized GraphQL usage.

POC:

#30814

Task

  1. Define the Base Page Query
  2. Implement the client.getPage Method:
    • Accept input for page, content, and nav queries.
    • Validate that at least one of page, content, or nav, is provided.
    • Merge the base query with user-provided custom queries using GraphQL fragments.
  3. Parse _map Fields:
  • Remove _map fields from the GraphQL response.
  • Parse and include _map values in the output data structure.
  1. Document the Method

Proposed Objective

Technical User Experience

Proposed Priority

Priority 2 - Important

Acceptance Criteria

  • The client.getPage method correctly merges the base query with custom queries using GraphQL fragments.
  • The method validates input to ensure at least one of page, content, or nav is provided.
  • _map fields are parsed and removed from the response.
  • The method returns the expected structure: { pageAsset, content, nav, query }.
  • Comprehensive unit tests are implemented for all use cases and edge cases.
  • Documentation in README.md provides clear guidance for usage and integration.

Sub-Tasks & Estimates

  1. Define the base GraphQL query constant.
  2. Implement the client.getPage method.
  3. Add validation for required inputs (page, content, or nav).
  4. Parse _map fields in the response.
  5. Write unit tests for the method.
  6. Update the README.md with usage examples and details.

Use Cases

Simple Use Cases

  1. Basic Pages with Required Params
const { pageAsset, query,  } = client.getPage({ url: '/home'  });
  1. Basic Pages with Optional Params
const { pageAsset, query } = client.getPage({url: '/home', language: "1", personaId: "personaId", site: ""});

Advance Use Cases

  1. Page with Content
const { pageAsset, query, content } = await client.getPage({
    url: '/blog',
    content: {
        blogs: `search(query: "+contentType: blog", limit: 5) {
            title
            author {
                firstName
                lastName
            }
        }`
    }
});
  1. Page With Navigation
const { pageAsset, query, nav } = await client.getPage({
    url: '/services',
    nav: {
        menu: `search(query: "+contentType: menu", limit: 1) {
            items {
                title
                link
            }
        }`
    }
});
  1. Page with Content and Navigation
const data = await client.getPage({
    url: '/services',
    content: {
        services: `search(query: "+contentType: service", limit: 10) {
            title
            description
        }`
    },
    nav: {
        menu: `search(query: "+contentType: menu", limit: 1) {
            items {
                title
                link
            }
        }`
    }
});
  1. Page with Page Fragments
const data = await client.getPage({
    url: '/dashboard',
    pageFragment: `
        containers {
            containerContentlets {
                contentlets {
                    ... on Banner {
                        title
                        image {
                            url
                        }
                        link
                    }
                    ... on Widget {
                        widgetType
                        data {
                            key
                            value
                        }
                    }
                }
            }
        }
    `,
    content: {
        dashboardItems: `search(query: "+contentType: dashboardItem", limit: 5) {
            title
            description
            ...on DashboardItem {
                metrics {
                    name
                    value
                }
            }
        }`
    },
    nav: {
        sidebar: `search(query: "+contentType: sidebar", limit: 1) {
            items {
                title
                link
            }
        }`
    }
});

Error Handling:

async function fetchPageContent() {
    try {
        const data = await client.getPage({
            url: '/dashboard',
            content: {
                dashboardItems: `search(query: "+contentType: dashboardItem", limit: 5) {
                    badPropertyResultInErros
                }`
            },
        });

        return data;
    } catch (error) {
        console.error('Error fetching page content:', error);
        return null;
    }
}

Developer Note: Documentation for getPageContent Method

Objective: Ensure comprehensive documentation for the getPageContent method to clarify expected arguments and their purposes.

Key Points to Document:
  1. Arguments Expected:
  • url (String): The only required property. It specifies the URL of the page to be fetched.
  • language (String, Optional): Specifies the language in which the page content should be retrieved.
  • pageFragment (String, Optional): A query string to fetch specific fragments of the page, useful for modular content.
  1. Optional Properties:
  • content (Object, Optional):

    • This property is used to fetch additional content that does not belong to the page itself but is desired alongside the page. For example, related blog posts or promotional items.
    • Each key in the content object should correspond to a query that retrieves the desired data.
  • nav (Object, Optional):

    • Used to fetch navigation-related data, such as menus, that complement the page layout.
  1. Error Handling:
  • Ensure that the documentation includes guidance on handling potential errors during the data retrieval process, as demonstrated in the provided error handling example.
Purpose of content:

The content argument is specifically designed to allow developers to fetch extra content that is not inherently part of the page's main structure. This enables the inclusion of dynamic or related content, enhancing the page's richness and user engagement.

@rjvelazco rjvelazco changed the title SDK: GraphQL Query Management and Merging Strategy SDK: Unified GraphQL Query Management with Fragment Merging Dec 17, 2024
@fmontes
Copy link
Member

fmontes commented Jan 17, 2025

The name of the method implies that only support GQL, needs better naming.

Add examples on how the method will be used, include basic and advanced examples.

Make sure is defined here the params that accepts and the response expected.

Also, what about error management? What about site overwrite? what about headers overwrite?

This is the key part of this enhancement, needs to me on point.

@rjvelazco
Copy link
Contributor Author

rjvelazco commented Jan 17, 2025

The name of the method implies that only GQL supports, needs better naming.

I 100% agree, we can discuss a new naming

Add examples on how the method will be used, include basic and advanced examples.

I'll add along with the Based query we need in order to make the page work

Make sure is defined here the params that accepts and the expected response.

agree

Also, what about error management? What about site overwrite? what about headers overwrite?

I'll revisit this and add the params we need to support.
The error management can be a separate ticket since it can increase the complexity

@rjvelazco rjvelazco changed the title SDK: Unified GraphQL Query Management with Fragment Merging SDK: Provide a client.getPageAsset method to fetch page, content, nav using graphQL Jan 22, 2025
@rjvelazco rjvelazco changed the title SDK: Provide a client.getPageAsset method to fetch page, content, nav using graphQL SDK: Provide a client. getPage method to fetch page, content, nav using graphQL Jan 22, 2025
@rjvelazco rjvelazco changed the title SDK: Provide a client. getPage method to fetch page, content, nav using graphQL SDK: Provide a client.getPage method to fetch page, content, nav using graphQL Jan 22, 2025
@rjvelazco
Copy link
Contributor Author

This is the base query needed for the UVE to work.

Note: You can find this and more in the POC

fragment DotCMSPage on DotPage {
_map
canEdit
canLock
canRead
template {
drawed
}
containers {
path
identifier
maxContentlets
containerStructures {
contentTypeVar
}
containerContentlets {
uuid
contentlets {
_map
}
}
}
layout {
header
footer
body {
rows {
columns {
leftOffset
styleClass
width
left
containers {
identifier
uuid
}
}
}
}
}
viewAs {
visitor {
persona {
name
}
}
language {
id
languageCode
countryCode
language
country
}
}
}

@rjvelazco rjvelazco moved this from In Progress to Current Sprint Backlog in dotCMS - Product Planning Feb 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Current Sprint Backlog
Development

Successfully merging a pull request may close this issue.

2 participants