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

[Fix] Introducing Pagination Support for Notion API in Page and Database Responses #59

Merged
merged 9 commits into from
Apr 8, 2024

Conversation

brf153
Copy link
Contributor

@brf153 brf153 commented Jan 31, 2024

Issue(s)

closes #38

Acceptance Criteria fulfillment

  • Check how the pagination is working in the api
  • Make changes to searchPage and searchDatabase methods.

Proposed changes (including videos or screenshots):

Made changes to searchPage method. Now, we are getting all the pages made in a workspace.
Screenshot of the length of the final results after concatenating all the response.data.results
Screenshot from 2024-01-21 21-36-14

Signed-off-by: brf153 <153hsb@gmail.com>
Signed-off-by: brf153 <153hsb@gmail.com>
Signed-off-by: brf153 <153hsb@gmail.com>
Signed-off-by: brf153 <153hsb@gmail.com>
Signed-off-by: brf153 <153hsb@gmail.com>
Signed-off-by: brf153 <153hsb@gmail.com>
Signed-off-by: brf153 <153hsb@gmail.com>
@brf153
Copy link
Contributor Author

brf153 commented Jan 31, 2024

@Nabhag8848 I have resolved the merge conflicts, but unfortunately, I mistakenly force-pushed the changes, leading to the dissolution of my previous pull request. However, upon further inspection, I can confirm that the changes are still present.

@brf153
Copy link
Contributor Author

brf153 commented Jan 31, 2024

The Changes

WhatsApp.Video.2024-01-23.at.10.13.26.mp4
WhatsApp.Video.2024-01-24.at.10.30.00.mp4
WhatsApp.Video.2024-01-24.at.10.04.29.mp4

@Nabhag8848 Nabhag8848 added this to Pending Review in Notion App - Community Dashboard Feb 4, 2024
@Nabhag8848 Nabhag8848 added the enhancement New feature or request label Feb 29, 2024
@Nabhag8848
Copy link
Collaborator

@brf153 Reviewing this right now, Sorry for So much delay on this. 🙏🏻

@Nabhag8848 Nabhag8848 self-requested a review March 9, 2024 08:18
@brf153
Copy link
Contributor Author

brf153 commented Mar 9, 2024

@Nabhag8848 No problem at all! I appreciate you getting back to me. Take your time with the review, and let me know if there are any further changes required. Looking forward to hearing your thoughts!

Copy link
Collaborator

@Nabhag8848 Nabhag8848 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brf153 Added examples and comments for changes, lemme know once you are done, will review back, quickly.

Comment on lines 142 to 173

private async recursiveSearchPages(token: string, cursor: string): Promise<Array<IPage>> {
const response = await this.http.post(NotionApi.SEARCH, {
data: {
start_cursor: cursor,
},
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": NotionApi.CONTENT_TYPE,
"User-Agent": NotionApi.USER_AGENT,
"Notion-Version": this.NotionVersion,
},
});

if (response.statusCode.toString().startsWith("2")) {


let results: Array<IPage> = response.data.results;

if (response.data.has_more === true) {

const recursiveResults = await this.recursiveSearchPages(token, response.data.next_cursor);

results.push(...recursiveResults);
}

return results;
} else {
throw new AppsEngineException(`Error While Searching Pages: ${response.content}`);
}
}

Copy link
Collaborator

@Nabhag8848 Nabhag8848 Mar 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private async recursiveSearchPages(token: string, cursor: string): Promise<Array<IPage>> {
const response = await this.http.post(NotionApi.SEARCH, {
data: {
start_cursor: cursor,
},
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": NotionApi.CONTENT_TYPE,
"User-Agent": NotionApi.USER_AGENT,
"Notion-Version": this.NotionVersion,
},
});
if (response.statusCode.toString().startsWith("2")) {
let results: Array<IPage> = response.data.results;
if (response.data.has_more === true) {
const recursiveResults = await this.recursiveSearchPages(token, response.data.next_cursor);
results.push(...recursiveResults);
}
return results;
} else {
throw new AppsEngineException(`Error While Searching Pages: ${response.content}`);
}
}
- Better Approach: 

 public async searchPages(
        token: string,
        cursor?: string
    ): Promise<Array<IPage> | Error> {

        try {
            const data = {
                filter: {
                    value: NotionObjectTypes.PAGE,
                    property: NotionObjectTypes.PROPERTY,
                },
                start_cursor: cursor,
            };

            const response = await this.http.post(NotionApi.SEARCH, {
                data,
                headers: {
                    Authorization: `Bearer ${token}`,
                    "Content-Type": NotionApi.CONTENT_TYPE,
                    "User-Agent": NotionApi.USER_AGENT,
                    "Notion-Version": this.NotionVersion,
                },
            });

            if (!response.statusCode.toString().startsWith("2")) {
                return this.handleErrorResponse(
                    response.statusCode,
                    `Error While Searching Pages: `,
                    response.content
                );
            }

            const { results, next_cursor, has_more } = response.data;
            if (has_more === true) {
                const recursiveResults = await this.searchPages(
                    token,
                    next_cursor
                );

                if (recursiveResults instanceof Error) {
                    return recursiveResults;
                }

                results.push(...recursiveResults);
            }

            if (cursor) return results;

            const result: Array<IPage> = [];
            results.forEach(async (item) => {
                const pageObject = await this.getPageObjectFromResults(item);
                if (pageObject) {
                    result.push(pageObject);
                }
            })
            return result;
        } catch (err) {
            throw new AppsEngineException(err as string);
        }
    }

  • Hey @brf153 Can't we have instead implement recursion without using the helper extra function. similar to above, can we change THIS ONE and REST OF THE Implementation USING this apporach make sure we don't need to use extra method and avoid having duplicacy of code.

  • Also make sure about this case what if during the paginated requests one of the requests get error from server in that case we need to assume that whole request is Error. above example solves that. Your implementation doesn't include that.

  • lets use forEach loops and lets stick to code already have to maintain consistency.

Also there is a bug in getPageObjectFromResults:


private async getPageObjectFromResults(
        item,
        emoji: boolean = false
    ): Promise<IPage | null> {
        const typesWithTitleProperty = [
            NotionObjectTypes.WORKSPACE.toString(),
            NotionObjectTypes.PAGE_ID.toString(),
        ];
        const parentType: string = item.parent.type;
        const properties = item.properties;
        const pageId: string = item.id;

        if (typesWithTitleProperty.includes(parentType)) {
            const pageName: string =
                properties.title.title[0]?.text?.content ||
                NotionObjectTypes.UNTITLED;
            return this.returnPage(pageName, pageId, emoji);
        }

        // title property either be at first or last position
        const columns = Object.keys(properties);
        const firstColumn = columns[0];
        const lastColumn = columns[columns.length - 1];

        // title at first position and has subpage
        if (properties[firstColumn].title) { <- Removed the length statement here this is updated version, remove in your PR code as well
            const name: string =
                properties[firstColumn].title[0]?.text?.content ||
                NotionObjectTypes.UNTITLED;
            return this.returnPage(name, pageId, emoji);
        }

        //title at last position and has subpage
        if (properties[lastColumn].title) { <- Removed the length statement here this is updated version as well.
            const name: string =
                properties[lastColumn].title[0]?.text?.content ||
                NotionObjectTypes.UNTITLED;
            return this.returnPage(name, pageId, emoji);
        }

        return null;
    }


  • So when we get the page from the api: we need to make sure we get all the pages, removing that length statement solves the Untitled pages not being shown:
  1. parent page
  2. sub pages
  3. database row (this are also the pages)
  4. page without titled should get Untitled title

If you currently will request there are 140 pages, in your workspace you shared

Comment on lines 601 to 609
private getNotionApiHeaders(token: string): Record<string, string> {
return {
Authorization: `Bearer ${token}`,
"Content-Type": NotionApi.CONTENT_TYPE,
"User-Agent": NotionApi.USER_AGENT,
"Notion-Version": this.NotionVersion,
};
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • As you have Introduce this method, Lets change in entire file, where we are passing this exact same header and lets make the lines of code less and call this method instead of hard coding

  • INCLUDING the code changes i shared.

Eg: 
 const response = await this.http.post(NotionApi.SEARCH, {
        headers: this.getNotionApiHeaders(token)
 });

Comment on lines 1061 to 1188
public async searchDatabases(
token: string
): Promise<Array<IDatabase> | Error> {
public async recursiveSearchDatabases(token: string, startCursor?: string): Promise<Array<IDatabase>> {
try {
const response = await this.http.post(NotionApi.SEARCH, {
data: {
filter: {
value: NotionObjectTypes.DATABASE,
property: NotionObjectTypes.PROPERTY,
let response;

if (startCursor) {
response = await this.http.post(NotionApi.SEARCH, {
data: {
filter: {
value: NotionObjectTypes.DATABASE,
property: NotionObjectTypes.PROPERTY,
},
start_cursor: startCursor,
},
},
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": NotionApi.CONTENT_TYPE,
"User-Agent": NotionApi.USER_AGENT,
"Notion-Version": this.NotionVersion,
},
});
headers: this.getNotionApiHeaders(token),
});
} else {
response = await this.http.post(NotionApi.SEARCH, {
data: {
filter: {
value: NotionObjectTypes.DATABASE,
property: NotionObjectTypes.PROPERTY,
},
},
headers: this.getNotionApiHeaders(token),
});
}

if (!response.statusCode.toString().startsWith("2")) {
return this.handleErrorResponse(
response.statusCode,
`Error While Searching Databases: `,
response.content
);
throw new AppsEngineException(`Error While Searching Databases: ${response.content}`);
}

const { results } = response.data;

const { results, has_more, next_cursor } = response.data;
const result: Array<IDatabase> = [];

results.forEach(async (item) => {
const objectType: string = item?.[NotionObjectTypes.OBJECT];
const databaseObject = await this.getDatabaseObjectFromResults(
item
);

const databaseObject = await this.getDatabaseObjectFromResults(item);
result.push(databaseObject);
});

if (has_more) {
const recursiveResults = await this.recursiveSearchDatabases(token, next_cursor);
result.push(...recursiveResults);
}

return result;
} catch (err) {
throw new AppsEngineException(err as string);
}
}

public async searchDatabases(token: string): Promise<Array<IDatabase> | Error> {
try {
const results = await this.recursiveSearchDatabases(token);
return results;
} catch (err) {
throw new AppsEngineException(err as string);
}
}

Copy link
Collaborator

@Nabhag8848 Nabhag8848 Mar 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Implement making sure We have just one method and don't have extra helper method to avoid duplicacy as approached shared above.

  • Also make sure about this case what if during the paginated requests one of the requests get error from server in that case we need to assume that whole set of request is Error. above example solves that.

  • Applies to all three: searchDatabases, searchPages, searchPagesAndDatabases

@@ -492,31 +533,34 @@ export class NotionSDK implements INotionSDK {
}
}

public async searchPagesAndDatabases(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Change according to approach similar to shared.

  • Also make sure about this case what if during the paginated requests one of the requests get error from server in that case we need to assume that whole request is Error. above example solves that.

Comment on lines 540 to 552
if (startCursor) {
response = await this.http.post(NotionApi.SEARCH, {
data: {
start_cursor: startCursor,
},
headers: this.getNotionApiHeaders(token),
});
} else {
response = await this.http.post(NotionApi.SEARCH, {
headers: this.getNotionApiHeaders(token),
});
}

Copy link
Collaborator

@Nabhag8848 Nabhag8848 Mar 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • You can use conditional statments in object instead of making if else statement for request. implement in new implementation take reference from example code shared.

`Error While Searching Databases: `,
response.content
);
throw new AppsEngineException(`Error While Searching Databases: ${response.content}`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new AppsEngineException(`Error While Searching Databases: ${response.content}`);
return this.handleErrorResponse(
response.statusCode,
`Error While Searching Databases: `,
response.content
);
  • never throw an error for an usual error response, we are handling this error to later don't open a modal if we don't get the database

@brf153
Copy link
Contributor Author

brf153 commented Apr 3, 2024

@Nabhag8848 working on this. Sorry for the delay

@Nabhag8848
Copy link
Collaborator

Nabhag8848 commented Apr 4, 2024

@Nabhag8848 working on this. Sorry for the delay

No Worries, Thanks ! @brf153 Looking forward 🚀

… in the original function as recommended

Signed-off-by: brf153 <153hsb@gmail.com>
@brf153
Copy link
Contributor Author

brf153 commented Apr 6, 2024

@Nabhag8848 I have made the changes, and your suggestions have greatly helped me. Thank you for the recommendations.

@Nabhag8848
Copy link
Collaborator

Nabhag8848 commented Apr 6, 2024

@Nabhag8848 I have made the changes, and your suggestions have greatly helped me. Thank you for the recommendations.

@brf153 🙏 will review it, by tomorrow thanks!
There are Api Calls within NotionSDK which wtill doesn't use getAPiHeaders methods. i believe you can use that.

Try checking all the features once changing that and see if nothing is getting break.

Copy link
Collaborator

@Nabhag8848 Nabhag8848 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @brf153 Added some comments, few more Changes, we can merge this after. Lemme know once you are done.

Thanks !


const response = await this.http.post(NotionApi.SEARCH, {
data,
headers: this.getNotionApiHeaders(token),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent this header

Comment on lines 147 to 151
return this.handleErrorResponse(
err.statusCode,
`Error While Searching Databases: `,
err.content
);
Copy link
Collaborator

@Nabhag8848 Nabhag8848 Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you will have a look on previous comments again I meant Usual Errors, Errors which will go into Catch are not usual which means those errors are not known and weren't expected anytime.

Suggested change
return this.handleErrorResponse(
err.statusCode,
`Error While Searching Databases: `,
err.content
);
throw new AppsEngineException(err as string);

"User-Agent": NotionApi.USER_AGENT,
"Notion-Version": this.NotionVersion,
},
headers: this.getNotionApiHeaders(token),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent

"User-Agent": NotionApi.USER_AGENT,
"Notion-Version": this.NotionVersion,
},
headers: this.getNotionApiHeaders(token),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent

"Notion-Version": this.NotionVersion,
},
data,
headers: this.getNotionApiHeaders(token),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent

"User-Agent": NotionApi.USER_AGENT,
"Notion-Version": this.NotionVersion,
},
headers: this.getNotionApiHeaders(token),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent

"User-Agent": NotionApi.USER_AGENT,
"Notion-Version": this.NotionVersion,
},
headers: this.getNotionApiHeaders(token),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent

Comment on lines 1109 to 1113
return this.handleErrorResponse(
err.statusCode,
`Error While Searching Databases: `,
err.content
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return this.handleErrorResponse(
err.statusCode,
`Error While Searching Databases: `,
err.content
);
throw new AppsEngineException(err as string);

Reason mentioned above.

Comment on lines 550 to 554
return this.handleErrorResponse(
err.statusCode,
`Error While Searching Databases: `,
err.content
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return this.handleErrorResponse(
err.statusCode,
`Error While Searching Databases: `,
err.content
);
throw new AppsEngineException(err as string);

Reason mentiond above.

Comment on lines 200 to 202



Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove vertical spaces. 🙏🏻

@Nabhag8848 Nabhag8848 moved this from Pending Review to Changes Requested in Notion App - Community Dashboard Apr 8, 2024
…in catch errors

Signed-off-by: brf153 <153hsb@gmail.com>
@brf153
Copy link
Contributor Author

brf153 commented Apr 8, 2024

@Nabhag8848 made the changes

@brf153 brf153 requested a review from Nabhag8848 April 8, 2024 09:41
Copy link
Collaborator

@Nabhag8848 Nabhag8848 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🔥 @brf153 Very Thanks, Do you have any feature in mind to built ? / or have some suggestion on improvement around this app feel free to open issue.

@Nabhag8848 Nabhag8848 changed the title [Feat]: Introducing Pagination Support for Notion API in Page and Database Responses [Fix] Introducing Pagination Support for Notion API in Page and Database Responses Apr 8, 2024
@Nabhag8848 Nabhag8848 merged commit f28506d into RocketChat:main Apr 8, 2024
1 check passed
@brf153
Copy link
Contributor Author

brf153 commented Apr 8, 2024

LGTM 🔥 @brf153 Very Thanks, Do you have any feature in mind to built ? / or have some suggestion on improvement around this app feel free to open issue.

Sure I will open an issue as soon I get some idea for a feature

@Nabhag8848 Nabhag8848 moved this from Changes Requested to Done in Notion App - Community Dashboard Apr 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Development

Successfully merging this pull request may close these issues.

[Bug] Search of Pages and / or Database in SearchComponent Currently Doesn't Support Pagination
2 participants