Directory

Revisions: add new selectors to fetch entity revisions by ramonjd · Pull Request #54046 · WordPress/gutenberg · GitHub
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

Revisions: add new selectors to fetch entity revisions #54046

Merged
merged 9 commits into from
Nov 20, 2023

Conversation

ramonjd
Copy link
Member

@ramonjd ramonjd commented Aug 30, 2023

Tracking:

What?

This PR fulfils the dream of bringing post type revisions to the Block Editor via the Core Data API. Get excited.

It adds two new functions to the global API:

  • getRevisions()
  • getRevision()

Both have slightly similar function signatures to getEntityRecords and getEntityRecord respectively. E.g.,

getRevisions( 'postType', 'post', 12 ); // Get all revisions for post#12.
getRevision( 'postType', 'post', 12, 2 ); // Get the revision with an id of 2 for post#12 (the parent).

Also, the same query params can be passed:

getRevisions( 'postType', 'post', 12, { per_page: 4, page: 3 } ); 
getRevision( 'postType', 'post', 12, 2, { _fields: 'id,parent' } ); .

Why?

To get post revisions and unlock a treasure chest of UI explorations into how to display, compare and restore revisions in the Block Editor.

How

Entities receive the following new config entries:

  • getRevisionsUrl( parentId, revisionId ): to return the GET request URL to fetch a post's revisions or a single revision.
  • revisionURLParams: similar to baseURLParams to provide the default params for the revisions request.
  • supports: { revisions: Boolean } to indicate that an entity supports revisions. This information can also be fetched from /wp/v2/types?context=edit, but I suspect the response size is larger than what's received in the view context and causes the performance tests to fail. (see investigation below)

Usage and testing instructions

To test, add a post or page with some revisions. Also add a few revisions to global styles.

You can use the Core Data API in your browser's console.

// Gets revisions (default is 10 per page)
const parentGlobalStylesId = wp.data.select( 'core' ).__experimentalGetCurrentGlobalStylesId();
await wp.data.resolveSelect( 'core' ).getRevisions( 'root', 'globalStyles', parentGlobalStylesId )

// Gets all revisions
await wp.data.resolveSelect( 'core' ).getRevisions( 'root', 'globalStyles', parentGlobalStylesId, { per_page: -1 } );

// Get a single revision
await wp.data.resolveSelect( 'core' ).getRevision( 'postType', 'page', parentId, revisionId );

// Get a single revision. but return only the id, parent and date fields
await wp.data.resolveSelect( 'core' ).getRevision( 'postType', 'page', parentId, revisionId, { _fields: 'id,parent,date' } );

// Pagination
await wp.data.resolveSelect( 'core' ).getRevisions( 'postType', 'post', parentId, { per_page: 3, page: 2 } )

Test that revisions are removed when their parent is removed:

// You have a post with some revisions.
await wp.data.resolveSelect( 'core' ).getRevisions( 'postType', 'post', parentId ); // Should return revisions `[ { ... }, { ... } ]`

// Now remove the parent post.
await wp.data.dispatch( 'core' ).deleteEntityRecord( 'postType', 'post', parentId );

// Now try to fetch those revisions again!
await wp.data.resolveSelect( 'core' ).getRevisions( 'postType', 'post', parentId ); // Should now return `null`

To see getRevisions in action in the Site Editor, copy the patch below and apply it to this branch (pbpaste | git apply)

diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js b/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js
index 6e3573061a..87e7339ec7 100644
--- a/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js
+++ b/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js
@@ -32,14 +32,19 @@ export default function useGlobalStylesRevisions() {
 			__experimentalGetDirtyEntityRecords,
 			getCurrentUser,
 			getUsers,
-			getCurrentThemeGlobalStylesRevisions,
+			getRevisions,
+			__experimentalGetCurrentGlobalStylesId,
 			isResolving,
 		} = select( coreStore );
 		const dirtyEntityRecords = __experimentalGetDirtyEntityRecords();
 		const _currentUser = getCurrentUser();
 		const _isDirty = dirtyEntityRecords.length > 0;
 		const globalStylesRevisions =
-			getCurrentThemeGlobalStylesRevisions() || EMPTY_ARRAY;
+			getRevisions(
+				'root',
+				'globalStyles',
+				__experimentalGetCurrentGlobalStylesId()
+			) || EMPTY_ARRAY;
 		const _authors = getUsers( SITE_EDITOR_AUTHORS_QUERY ) || EMPTY_ARRAY;
 
 		return {

Then head to the Site Editor and open the revisions panel. Add some changes to Global Styles to make sure the revisions list updates as expected.

Screenshot 2023-11-01 at 1 48 15 pm

Follow up tasks/TODO

Tracked here:

Related

@ramonjd ramonjd added the [Type] Experimental Experimental feature or API. label Aug 30, 2023
@github-actions
Copy link

github-actions bot commented Aug 30, 2023

This pull request changed or added PHP files in previous commits, but none have been detected in the latest commit.

Thank you! ❤️

@github-actions
Copy link

github-actions bot commented Aug 30, 2023

Size Change: +559 B (0%)

Total Size: 1.7 MB

Filename Size Change
build/core-data/index.min.js 72.4 kB +559 B (+1%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 964 B
build/annotations/index.min.js 2.71 kB
build/api-fetch/index.min.js 2.29 kB
build/autop/index.min.js 2.11 kB
build/blob/index.min.js 590 B
build/block-directory/index.min.js 7.25 kB
build/block-directory/style-rtl.css 1.04 kB
build/block-directory/style.css 1.04 kB
build/block-editor/content-rtl.css 4.28 kB
build/block-editor/content.css 4.27 kB
build/block-editor/default-editor-styles-rtl.css 403 B
build/block-editor/default-editor-styles.css 403 B
build/block-editor/index.min.js 247 kB
build/block-editor/style-rtl.css 15.7 kB
build/block-editor/style.css 15.7 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 138 B
build/block-library/blocks/audio/theme.css 138 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/button/editor-rtl.css 587 B
build/block-library/blocks/button/editor.css 587 B
build/block-library/blocks/button/style-rtl.css 633 B
build/block-library/blocks/button/style.css 632 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 421 B
build/block-library/blocks/columns/style.css 421 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 647 B
build/block-library/blocks/cover/editor.css 650 B
build/block-library/blocks/cover/style-rtl.css 1.7 kB
build/block-library/blocks/cover/style.css 1.69 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 98 B
build/block-library/blocks/details/style.css 98 B
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 138 B
build/block-library/blocks/embed/theme.css 138 B
build/block-library/blocks/file/editor-rtl.css 316 B
build/block-library/blocks/file/editor.css 316 B
build/block-library/blocks/file/style-rtl.css 280 B
build/block-library/blocks/file/style.css 281 B
build/block-library/blocks/file/view.min.js 320 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/form-input/editor-rtl.css 229 B
build/block-library/blocks/form-input/editor.css 228 B
build/block-library/blocks/form-input/style-rtl.css 343 B
build/block-library/blocks/form-input/style.css 343 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 343 B
build/block-library/blocks/form-submission-notification/editor.css 342 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/view.min.js 452 B
build/block-library/blocks/freeform/editor-rtl.css 2.61 kB
build/block-library/blocks/freeform/editor.css 2.61 kB
build/block-library/blocks/gallery/editor-rtl.css 957 B
build/block-library/blocks/gallery/editor.css 962 B
build/block-library/blocks/gallery/style-rtl.css 1.55 kB
build/block-library/blocks/gallery/style.css 1.55 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 654 B
build/block-library/blocks/group/editor.css 654 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 189 B
build/block-library/blocks/heading/style.css 189 B
build/block-library/blocks/html/editor-rtl.css 340 B
build/block-library/blocks/html/editor.css 341 B
build/block-library/blocks/image/editor-rtl.css 849 B
build/block-library/blocks/image/editor.css 847 B
build/block-library/blocks/image/style-rtl.css 1.61 kB
build/block-library/blocks/image/style.css 1.6 kB
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/image/view.min.js 2.05 kB
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 505 B
build/block-library/blocks/media-text/style.css 503 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 671 B
build/block-library/blocks/navigation-link/editor.css 672 B
build/block-library/blocks/navigation-link/style-rtl.css 103 B
build/block-library/blocks/navigation-link/style.css 103 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation/editor-rtl.css 2.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.27 kB
build/block-library/blocks/navigation/style.css 2.26 kB
build/block-library/blocks/navigation/view.min.js 1.07 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 401 B
build/block-library/blocks/page-list/editor.css 401 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 235 B
build/block-library/blocks/paragraph/editor.css 235 B
build/block-library/blocks/paragraph/style-rtl.css 335 B
build/block-library/blocks/paragraph/style.css 335 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 508 B
build/block-library/blocks/post-comments-form/style.css 508 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 141 B
build/block-library/blocks/post-excerpt/style.css 141 B
build/block-library/blocks/post-featured-image/editor-rtl.css 666 B
build/block-library/blocks/post-featured-image/editor.css 662 B
build/block-library/blocks/post-featured-image/style-rtl.css 345 B
build/block-library/blocks/post-featured-image/style.css 345 B
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 409 B
build/block-library/blocks/post-template/style.css 408 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/style-rtl.css 69 B
build/block-library/blocks/post-time-to-read/style.css 69 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 335 B
build/block-library/blocks/pullquote/style.css 335 B
build/block-library/blocks/pullquote/theme-rtl.css 168 B
build/block-library/blocks/pullquote/theme.css 168 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 486 B
build/block-library/blocks/query/editor.css 486 B
build/block-library/blocks/query/style-rtl.css 312 B
build/block-library/blocks/query/style.css 308 B
build/block-library/blocks/query/view.min.js 637 B
build/block-library/blocks/quote/style-rtl.css 237 B
build/block-library/blocks/quote/style.css 237 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 140 B
build/block-library/blocks/read-more/style.css 140 B
build/block-library/blocks/rss/editor-rtl.css 149 B
build/block-library/blocks/rss/editor.css 149 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 184 B
build/block-library/blocks/search/editor.css 184 B
build/block-library/blocks/search/style-rtl.css 613 B
build/block-library/blocks/search/style.css 613 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/search/view.min.js 471 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 234 B
build/block-library/blocks/separator/style.css 234 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 329 B
build/block-library/blocks/shortcode/editor.css 329 B
build/block-library/blocks/site-logo/editor-rtl.css 760 B
build/block-library/blocks/site-logo/editor.css 760 B
build/block-library/blocks/site-logo/style-rtl.css 204 B
build/block-library/blocks/site-logo/style.css 204 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 682 B
build/block-library/blocks/social-links/editor.css 681 B
build/block-library/blocks/social-links/style-rtl.css 1.45 kB
build/block-library/blocks/social-links/style.css 1.45 kB
build/block-library/blocks/spacer/editor-rtl.css 359 B
build/block-library/blocks/spacer/editor.css 359 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 432 B
build/block-library/blocks/table/editor.css 432 B
build/block-library/blocks/table/style-rtl.css 646 B
build/block-library/blocks/table/style.css 645 B
build/block-library/blocks/table/theme-rtl.css 157 B
build/block-library/blocks/table/theme.css 157 B
build/block-library/blocks/tag-cloud/style-rtl.css 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 403 B
build/block-library/blocks/template-part/editor.css 403 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/term-description/style-rtl.css 111 B
build/block-library/blocks/term-description/style.css 111 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 191 B
build/block-library/blocks/video/style.css 191 B
build/block-library/blocks/video/theme-rtl.css 139 B
build/block-library/blocks/video/theme.css 139 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.11 kB
build/block-library/common.css 1.11 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 12.5 kB
build/block-library/editor.css 12.4 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 212 kB
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.5 kB
build/block-library/style.css 14.5 kB
build/block-library/theme-rtl.css 700 B
build/block-library/theme.css 705 B
build/block-serialization-default-parser/index.min.js 1.13 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/blocks/index.min.js 51.5 kB
build/commands/index.min.js 15.5 kB
build/commands/style-rtl.css 947 B
build/commands/style.css 942 B
build/components/index.min.js 256 kB
build/components/style-rtl.css 12 kB
build/components/style.css 12 kB
build/compose/index.min.js 12.7 kB
build/core-commands/index.min.js 2.72 kB
build/customize-widgets/index.min.js 12.1 kB
build/customize-widgets/style-rtl.css 1.43 kB
build/customize-widgets/style.css 1.43 kB
build/data-controls/index.min.js 651 B
build/data/index.min.js 8.87 kB
build/date/index.min.js 17.9 kB
build/deprecated/index.min.js 462 B
build/dom-ready/index.min.js 336 B
build/dom/index.min.js 4.68 kB
build/edit-post/classic-rtl.css 571 B
build/edit-post/classic.css 571 B
build/edit-post/index.min.js 35.9 kB
build/edit-post/style-rtl.css 7.75 kB
build/edit-post/style.css 7.74 kB
build/edit-site/index.min.js 209 kB
build/edit-site/style-rtl.css 14.2 kB
build/edit-site/style.css 14.2 kB
build/edit-widgets/index.min.js 17.2 kB
build/edit-widgets/style-rtl.css 4.65 kB
build/edit-widgets/style.css 4.65 kB
build/editor/index.min.js 46 kB
build/editor/style-rtl.css 3.58 kB
build/editor/style.css 3.58 kB
build/element/index.min.js 4.87 kB
build/escape-html/index.min.js 548 B
build/format-library/index.min.js 7.76 kB
build/format-library/style-rtl.css 577 B
build/format-library/style.css 577 B
build/hooks/index.min.js 1.57 kB
build/html-entities/index.min.js 454 B
build/i18n/index.min.js 3.61 kB
build/interactivity/index.min.js 11.4 kB
build/is-shallow-equal/index.min.js 535 B
build/keyboard-shortcuts/index.min.js 1.76 kB
build/keycodes/index.min.js 1.9 kB
build/list-reusable-blocks/index.min.js 2.11 kB
build/list-reusable-blocks/style-rtl.css 865 B
build/list-reusable-blocks/style.css 865 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 964 B
build/nux/index.min.js 2.01 kB
build/nux/style-rtl.css 775 B
build/nux/style.css 771 B
build/patterns/index.min.js 4.81 kB
build/patterns/style-rtl.css 567 B
build/patterns/style.css 566 B
build/plugins/index.min.js 1.81 kB
build/preferences-persistence/index.min.js 1.85 kB
build/preferences/index.min.js 1.26 kB
build/primitives/index.min.js 994 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 988 B
build/react-i18n/index.min.js 631 B
build/react-refresh-entry/index.min.js 9.46 kB
build/react-refresh-runtime/index.min.js 6.78 kB
build/redux-routine/index.min.js 2.71 kB
build/reusable-blocks/index.min.js 2.76 kB
build/reusable-blocks/style-rtl.css 265 B
build/reusable-blocks/style.css 265 B
build/rich-text/index.min.js 9.96 kB
build/router/index.min.js 1.79 kB
build/server-side-render/index.min.js 1.96 kB
build/shortcode/index.min.js 1.4 kB
build/style-engine/index.min.js 1.98 kB
build/token-list/index.min.js 587 B
build/url/index.min.js 3.83 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/vendors/react-dom.min.js 41.8 kB
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 967 B
build/warning/index.min.js 259 B
build/widgets/index.min.js 7.18 kB
build/widgets/style-rtl.css 1.18 kB
build/widgets/style.css 1.18 kB
build/wordcount/index.min.js 1.03 kB

compressed-size-action

Copy link
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

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

This is testing nicely for me so far. While it's a bit awkward passing values to the query object that won't really be used in the query, but instead will be used as part of the API url, it feels to me like a natural place for the code calling getEntityRecords to place it, and if it's something that's particular to the revisions kind then maybe it'd kinda work? Also I quite like how you're taking the value and then discarding it before passing the rest of the query on via getQueryArgs.

image

@ramonjd
Copy link
Member Author

ramonjd commented Aug 30, 2023

While it's a bit awkward passing values to the query object that won't really be used in the query, but instead will be used as part of the API url, it feels to me like a natural place for the code calling getEntityRecords to place it, and if it's something that's particular to the revisions kind then maybe it'd kinda work? Also I quite like how you're taking the value and then discarding it before passing the rest of the query on via getQueryArgs.

Thanks for looking at this idea 🙇🏻

I think my inexperience with this package shows 😄

But it seems to handle things, I guess we just need to work out a better interface. I think perhaps a wrapper for getEntityRecord(s) might work better?

For better or worse, the following also crossed my mind:

// A wrapper for `getEntityRevisions`
getEntityRevisionsRecords( 'revisions', `${ postType }`, `${ parentId }`, { ...query } );

// An extra options arg to house stuff. 
getEntityRecords( 'revisions', `${ postType }`, { ...query }, {
    parent: parentId,
} );

@ramonjd
Copy link
Member Author

ramonjd commented Sep 13, 2023

I've been tinkering with different ideas today. Jotting down some very random findings and questions.

At this stage Gutenberg only displays global styles revisions, but my instinct is to build something that other entity types (post, page, templates) can hook into.

The first question would therefore be: Where to place revisions in state?
Should revisions "belong" to specific entities, e.g., state.entities.records?.[ kind ]?.[ name ]?.revisions.

Loosely, I think it should be filed under entity name (post type), e.g.,

{
    revisions: {
        parentId: {
            items: { revisionId: { ...revisionData } },
            ...
        } 
   }
}

This is what I was trying in https://github.com/WordPress/gutenberg/compare/try/core-data-entity-revisions-under-name

Or could they be a separate top-level item state.revisions?.[ name ]

{
    revisions: {
        [ name ]: {
                parentId: {
                   items: { revisionId: { ...revisionData } },
                    ...
            } 
         }
   }
}

Either way, it would be great to use existing query reducers to track what's already in state. I haven't very far with that yet, but I suppose they'll need to be modified, along with a bunch of other code that expect specific properties.

By query, I mean modifying REST query params such as { page: 2, per_page: 40 } etc

I was thinking we could invalidate the query cache if the revision count has changed.

Furthermore, give the above we could, later, deprecate state.themeGlobalStyleRevisions[ currentGlobalStylesId ].

Maybe I'm overthinking it. It's a lot of work and might introduce bugs at first, so another approach would be to just implement global styles revisions (all new code and top-level state item) and custom query handling in state. state.themeGlobalStyleRevisions[ currentGlobalStylesId ] could still exist until deprecated, but it would return the default set (without query params). But all could introduce more tech debt. 🤷🏻

Or just concentrate on global styles revisions and not worry about caching state according to query (means more API calls) for now.

@youknowriad I know you've got a lot on, but when you get a spare moment it would be great to hear your thoughts. I'm wary of touching core data and breaking stuff, but don't want to repeat too much either. 🙏🏻

@youknowriad
Copy link
Contributor

This is a very quick though passing around here.

  • It seems there's no canonical revision id, so we're forced to pass a couple (id, parent)
  • I don't like that we're stretching the definition of "entities" to fetch "revisions". It feels to me like we're doing this just to reuse code but conceptually these are different: A revision is a property of another record, it's not a thing on its own. So IMO, maybe we should avoid using getEntityRecord and getEntityRecords for revisions and instead have a "supports: revisions" in the config of each entity which would automatically allow fetching revisions of that particular entity.

@ramonjd
Copy link
Member Author

ramonjd commented Sep 13, 2023

So IMO, maybe we should avoid using getEntityRecord and getEntityRecords for revisions and instead have a "supports: revisions" in the config of each entity which would automatically allow fetching revisions of that particular entity

I appreciate the feedback - it's very valuable, thank you. I'll have another poke around.

It feels to me like we're doing this just to reuse code

The main concern I had was not to reproduce logic surrounding query caching, but maybe it's best to do it longhand and look for consolidation opportunities later. It would at least suit me if I were to carry on: me having not much experience with the entity types and models in the plugin 😄

@youknowriad
Copy link
Contributor

I think it would be valuable to have other opinions here as well. cc @WordPress/gutenberg-core @jsnajdr

I agree that query caching and handling is a bit of a monster and that reusing it helps a lot. Maybe there are ways to reuse these sub-reducers/sub-selectors without allowing fetching revisions as entity records.

Anyway, I'm very open to more discussion and opinions here :)

@jsnajdr
Copy link
Member

jsnajdr commented Sep 13, 2023

To me post revisions seem to be a full-featured REST entity on its own. For a given post type and post ID, there is a collection of revisions, and also individual revisions. Each of them having their own REST resource URL.

The relationship between a post and its revisions is similar to the relationship between a post and its attachments (media) or its comments. They are all linked to in the _links meta-field in the /posts/$id response.

It seems there's no canonical revision id, so we're forced to pass a couple (id, parent)

Yes this is one thing that's unusual about revisions. A revision has a perfectly unique and canonical revision id -- after all, it's just a post like any other, with post type revision. But there's no REST URL where it could be fetched from using just the id. There must be the "parent post type + post id" prefix:

/wp/v2/posts/1/revisions/2

The endpoint doesn't even care if the revision 2's parent is really the post 1. If will happily return the same revision from URL like:

/wp/v2/pages/3/revisions/2

It merely checks that pages/3 exists, that's all.

@ntsekouras
Copy link
Contributor

ntsekouras commented Sep 13, 2023

Could making the revisions(version-history) embeddable in core help us with this? Would that be simpler or there are more nuances? 🤔

@youknowriad
Copy link
Contributor

If we were to consider revisions an entity.

  • What would getEntityRecords( 'post', 'revisions' ) return (query is optional) ? All the revisions of all the posts?
  • I'm also guessing we'd have to support tupples as ids: something like getEntityRecord( 'post', 'revisions', { postId: 10, revisionId: 13 } ) or come up with some convention getEntityRecord( 'post', 'revisions', '10|13' )

I think revisions are "sub entities" and not top level entities and in that sense, our framework doesn't support sub entities yet.

@ramonjd
Copy link
Member Author

ramonjd commented Sep 14, 2023

I'm really grateful for the discussion. I was swimming in the dark on this one 😄

What would getEntityRecords( 'post', 'revisions' ) return (query is optional) ? All the revisions of all the posts?

I'd guess that it would return a set of revisions according to the default collection params?

So max items 100, ordered by date descending.

I'm also guessing we'd have to support tupples as ids: something like getEntityRecord( 'post', 'revisions', { postId: 10, revisionId: 13 } ) or come up with some convention getEntityRecord( 'post', 'revisions', '10|13' )

I was smitten with an earlier idea of yours: to create a dedicate selector. This could remove the need for argument gymnastics.

getEntityRecordRevisions( 'post', parentId, { ...pageQueries } ) // returns all revisions constrained by any query params.

getEntityRecordRevision( 'post', parentId, [ ...revisionIds ] ) // returns specific revisions.

Could making the revisions(version-history) embeddable in core help us with this? Would that be simpler or there are more nuances?

Would that impact response size for posts with lots of revisions? Or could we embed a subset? 🤔

I think revisions are "sub entities" and not top level entities and in that sense, our framework doesn't support sub entities yet.

Interesting. As in state.entities[subEntity]? Or deeper, beside the same kind/name? E.g., state.entities.records[ kind ][ name ].revisions

Where do folks think it would make sense to build this into the existing state?

At first I was imagining something like this:

{
    entities: {
       // existing entity records
        records: {
            [ postType: kind ]: {
                [ post: name ]: {
                    queriedData: { items, itemIsComplete, queries },
                    edits: { ... },
                    saving: { ... },
                    ..., 
                }
            }
        },
        // sub entity 
        revisions: {
                [ post: name ]: {
                    [ parentId ]: {
                        queriedData: { items, queries, ... },
                    },
                    [ parentId ]: {
                        queriedData: { items, queries, ... },
                    },
                }
       }
    },
}

They are all linked to in the _links meta-field in the /posts/$id response.

I'm guessing we'd fetch the entity record first and then grab the revision URL? E.g, item?._links?.[ 'version-history' ]?.[ 0 ]?.href

I suppose I'm wondering if we need to care about the revisions URL in the same way we do about top-level entity baseURLs?

@youknowriad
Copy link
Contributor

Where do folks think it would make sense to build this into the existing state?

I think that's not important at all, it's implementation details, so whatever feels easy. For me, the thing to get right is the external API (selectors and actions).

@jsnajdr
Copy link
Member

jsnajdr commented Sep 14, 2023

What would getEntityRecords( 'post', 'revisions' ) return

This collection doesn't exist, at least there is no REST API to fetch it.

I'm also guessing we'd have to support tuples as ids

Yes, this is IMO the only thing where post revisions differ from other entities. And the tuple wouldn't be the id (key) but the entity name. Like:

getEntityRecords( 'postType', 'post:1:revisions' ); // collection
getEntityRecord( 'postType', 'post:1:revisions', 2 ); // item

The first two arguments, 'postType' and 'post:...', are kind of forced. Because to derive the REST URL for post revisions, like /wp/v2/posts/1/revisions we need the entity config for the post post type, because we need its rest_base and rest_namespace fields. And if you look at the getEntityRecord resolver:

https://github.com/WordPress/gutenberg/blob/trunk/packages/core-data/src/resolvers.js

it needs kind = 'postType' and name = 'post' to retrieve the config. Then it can work through the rest of the arguments (rest of name, key and query) to construct the revisions URL.

Could making the revisions(version-history) embeddable in core help us with this?

Not really because things that are embeddable are always already independent entities. Embbeding author or replies is just a shortcut that saves one REST request, or getEntityRecord call for something that's already an entity.

But here we don't even have an agreement that revisions are a real entity 🙂

I don't like that we're stretching the definition of "entities" to fetch "revisions". It feels to me like we're doing this just to reuse code but conceptually these are different:

If we started supporting sub-entities, how would the API look like? Some nested call like this?

getSubEntityRecords( getEntity( 'postType', 'post', 1 ), 'revisions' );

But that's not really different at all from a compound entity name:

getEntityRecords( 'postType', 'post:1:revisions' );

A revision is a property of another record, it's not a thing on its own.

I disagree here. Revisions are an entity on its own, simply because they have their own REST endpoint. That's the necessary and sufficient condition.

Comments and attachments are also "properties or another record": the REST response for a post contains links to them in the _links section (links like /media?parent=1 or /comments?post=1). And yet they are at the same time independent entities, there is no conflict is that.

Revisions just have a weird name or key, that's all.

@youknowriad
Copy link
Contributor

youknowriad commented Sep 14, 2023

This collection doesn't exist, at least there is no REST API to fetch it.

It doesn't matter if it doesn't exist, the API in core-data exists If { kind: "post", name: "revisions" } is defined as an entity.

getEntityRecords( 'postType', 'post:1:revisions' ); // collection
getEntityRecord( 'postType', 'post:1:revisions', 2 ); // item

While this makes more sense in terms of API calls . I don't think we ever planned for "name" to be this dynamic: aka create an entity per post ID. Also it doesn't make sense because the config for post:1:revisions and post:x:revisions is the same and we'd have to register a config each time we add a post and remove a config each time we remove a post. Entities are meant to be more static.

If we started supporting sub-entities, how would the API look like? Some nested call like this?

I don't think we need to necessarily support entities in a generic way from the start. I think we should start by just supporting "revisions" for entities

getEntityRecordRevision( kind, name, recordId, revisionId )
getEntityRecordRevisions( kind, name, recordId )

Potentially we could support sub entities more generically later, if there's a need to fetch more sub entities outside of revisions. but there's no need for a generic selector.

@jsnajdr
Copy link
Member

jsnajdr commented Sep 14, 2023

It doesn't matter if it doesn't exist, the API in core-data exists If { kind: "post", name: "revisions" } is defined as an entity.

If name is a tuple, then revisions name doesn't really exist, there is only post:1:revisions. But I get it, this problem exists, many selectors in core-data are autogenerated, for singular and plural variants. For example, if you call wp.data.select('core').getUnstableBases(), you'll see a really strange result. Only the singular selector makes sense for singleton entities like root/__unstableBase, but the plural selector is generated anyway.

I don't think we ever planned for "name" to be this dynamic: aka create an entity per post ID. Also it doesn't make sense because the config for post:1:revisions and post:x:revisions is the same

We never planned for this, but suprisingly, it could work very well 🙂 The configs for both posts are only almost the same: the baseURL is different because it includes the post ID. You use only the first part of the tuple name to retrieve the kind+name entity config, and then patch is slightly to create a revisions entity.

getEntityRecordRevision( kind, name, recordId, revisionId )

This is the same thing as getEntityRecord( kind, ${name}:${recordId}:revisions, revisionId ). And, what I think is most important for our discussion, I think the actual implementation will be 80% the same for getEntityRecordRevision and getEntityRecord with tuple name. It will have to retrieve the entity for kind+name, construct the base URL, then append /revisions/id to it, store the response in some .revisions subtree of Redux state... I don't see much difference there.

So, I don't mind starting with getEntityRecordRevision. Maybe it will turn out to be easily mergeable into an extended getEntityRecord, maybe not.

@ramonjd
Copy link
Member Author

ramonjd commented Sep 15, 2023

Once again, thanks for sharing your knowledge. 🍺

And, what I think is most important for our discussion, I think the actual implementation will be 80% the same for getEntityRecordRevision and getEntityRecord with tuple name. It will have to retrieve the entity for kind+name, construct the base URL, then append /revisions/id to it, store the response in some .revisions subtree of Redux state

This could be a silly question: I was wondering, could revisions be its own "kind"? That way it could run through the post types that support revisions.

I tried that using a tuple-like key:

wp.data.select( 'core' ).getEntityRecords( 'revisions', 'post:10' )

Here is state.entities.records tree:

Screenshot 2023-09-15 at 3 32 37 pm

A far few unnecessary properties that could be pruned from the reducer, e.g., saving/edits/deleting

To do this I created a callback getBaseUrl() in the entity config.

@jsnajdr
Copy link
Member

jsnajdr commented Sep 15, 2023

I was wondering, could revisions be its own "kind"?

To answer this we need to know what kind actually means -- why don't we have just entity name? Most of the time kind and name act as a compound key, in code like if (a.kind === b.kind && a.name === b.name ) or state[ kind ][ name ]. Why not merge them together?

The only place where kind is alone is when loading entity configs. root configs are hardcoded, postType configs are loaded from the /wp/v2/types REST endpoint, taxonomy configs from /wp/v2/taxonomies.

When deciding whether postTypeRevisions should be a new kind, we need to first ask: when getEntityRecord or other function needs the entity config for post revisions, where does it get it from? It's going to be derived from the postType config, because there is no other source for it. We'll construct a REST URL for a post, and then append /revisions to it.

The answer will follow from how the code for revisions looks like. Does it call

const config = select.getEntitiesConfig( 'postTypeRevisions' );
const revisionsUrl = urlFromConfig( config );

or

const config = select.getEntitiesConfig( 'postType' );
const revisionsUrl = urlFromConfig( config ) + '/revisions';

?

Both is possible, although I personally would intuitively prefer the second option: load entity configs for the postType kind, construct a URL for postType, and then derive the revisions URL from it. In a getEntityRecordsRevisions function, or in getEntityRecords when the name has the post:1:revisions form.

@ramonjd ramonjd force-pushed the try/core-data-entity-revisions branch from b9d768c to 0edea5d Compare September 18, 2023 05:36
@ramonjd
Copy link
Member Author

ramonjd commented Sep 18, 2023

I personally would intuitively prefer the second option: load entity configs for the postType kind, construct a URL for postType, and then derive the revisions URL from it. In a getEntityRecordsRevisions function, or in getEntityRecords when the name has the post:1:revisions form.

Interesting! Thanks again for helping on the thinking.

I had some time today so I hacked something into this POC PR just to test it out (and learn more about the innards of core-data)

// Get all
wp.data.select( 'core' ).getEntityRecords( 'postType', 'post:7:revisions' )

// Get one
wp.data.select( 'core' ).getEntityRecord( 'postType', 'post:7:revisions', 104 )

@ramonjd ramonjd force-pushed the try/core-data-entity-revisions branch 3 times, most recently from 6f0490d to 65e41f2 Compare September 22, 2023 03:38
@github-actions
Copy link

github-actions bot commented Sep 22, 2023

Flaky tests detected in 68a1581.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/6739002166
📝 Reported issues:

@ramonjd ramonjd force-pushed the try/core-data-entity-revisions branch 3 times, most recently from 21fcb95 to 37e23db Compare September 28, 2023 06:23
@ramonjd
Copy link
Member Author

ramonjd commented Nov 16, 2023

Add more explanatory documentation and examples to packages/core-data/README.md

I'll do this as a follow up.

Copy link
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

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

Just gave this another test, and I'm really liking the shape of the API via the getRevisions and getRevision methods 👍

One thing I noticed running locally is that when calling the getRevisions selector with different params, it'll immediately return the results of the previous call while it's resolving the request for the next call. I wasn't sure if this is just the way that these kinds of selectors are meant to work, or if there's a potential bug there. Here's an example:

image

In the above screengrab:

  • 1st call (per page of 2): returns null as no data has been fetched yet (I think this is expected)
  • 2nd call with same params: returns the data previously fetched (array of size 2)
  • 3rd call (per page of 3): returns the data previously fetched (array of size 2) — this is the response that seemed unexpected to me. What should it return immediately? Perhaps null because it's meant to be resolving the request?
  • 4th call (per page of 3): now returns the data fetched for 3 items
  • 5th call (same params): still returns expected results

Once data has been fetched via the REST API, all the responses to calling the selectors are as expected. It's just that first call with new params where it looks like it might be returning stale data immediately before it then does the call — in that case, I would have expected it to return null or undefined as in #54046 (comment) instead of the response from the previous call (that may have had different params).

Apologies if this is a red herring or if it's expected behaviour!

@ramonjd
Copy link
Member Author

ramonjd commented Nov 20, 2023

@andrewserong Thanks for testing again.

I had the same question. See comment.

TL;DR: it's expected 😄

I've updated the test description to use await wp.data.resolveSelect( 'core' ).getRevisions and await wp.data.resolveSelect( 'core' ).getRevision, which I think should be easier to test with as it'll return a promise that'll resolve with the results.

Screenshot 2023-11-20 at 7 11 30 pm

@jsnajdr
Copy link
Member

jsnajdr commented Nov 20, 2023

I had the same question. See #54046 (comment).

My original comment was about the null response you get on the first call when the response is not loaded yet. But here you describe a different situation: when the per_page: 2 response is loaded, and you ask for per_page: 3, you initially get the per_page: 2 response, and only when the REST response with per_page: 3 arrives, the selector starts returning the per_page: 3 data. I could also initially return null -- it's a new query -- but it doesn't. The per_page: 2 and per_page: 3 queries are not considered to be that separate. Learning about this was a TIL moment for me, too 🙂 But it works this way consistently across all core queries.

Copy link
Member

@jsnajdr jsnajdr left a comment

Choose a reason for hiding this comment

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

Giving a 👍 as this looks mergeable now.

@youknowriad
Copy link
Contributor

The difference for per_page is confusing to me too. I don't understand why we'd show page 2 results and then switch to page 3 results. If the returned result is the accumulation of all the pages, sure, they should be considered the same query but if we're actually returning just the page 3 in the end, I think that's a bug. But it's independent from this PR, maybe let's track it for now.

@ramonjd
Copy link
Member Author

ramonjd commented Nov 20, 2023

Thanks for the explanation, folks!

@andrewserong sorry, your comment flew over my head 🤦🏻 I get it now 😄

I can write up an issue for this.

@ramonjd
Copy link
Member Author

ramonjd commented Nov 20, 2023

The reason why I thought it was all fine is because I've been resolving each call via resolveSelect all this time.

So there are 2 x API calls as I'd expect, and the results also match what I'd expected, that is, the correct revision ids in the collection.

2023-11-21.07.45.01.mp4

when the per_page: 2 response is loaded, and you ask for per_page: 3, you initially get the per_page: 2 response, and only when the REST response with per_page: 3 arrives

Maybe we need to inform the master selector from the queried data selector somehow that the query key doesn't yet exist. My guess is that it's fetching it from the cache despite that. 🤷🏻

Not sure. I can take a quick looksy.

Thanks again all for helping with this PR. It's going to enable a lot of future work.

🍺 🍺 🍺 🍺 🍺 🍺 🍺 🍺 🍺

@ramonjd ramonjd added the Needs Dev Note Requires a developer note for a major WordPress release cycle label Nov 20, 2023
@ramonjd
Copy link
Member Author

ramonjd commented Nov 20, 2023

Note to self: this feature probably deserves a dev note post on https://make.wordpress.org/

Copy link
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

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

But it works this way consistently across all core queries.

Thanks for confirming and for the extra discussion, folks! +1 this PR sounds good to merge, and we can look into the issue of potentially stale data being returned separately 👍

Nice work getting this into good shape @ramonjd! ✨

@ramonjd ramonjd merged commit 8eb63dd into trunk Nov 20, 2023
51 checks passed
@ramonjd ramonjd deleted the try/core-data-entity-revisions branch November 20, 2023 22:44
@github-actions github-actions bot added this to the Gutenberg 17.2 milestone Nov 20, 2023
@ramonjd
Copy link
Member Author

ramonjd commented Nov 21, 2023

Maybe we need to inform the master selector from the queried data selector somehow that the query key doesn't yet exist. My guess is that it's fetching it from the cache despite that.

Issue:

Not sure if this is a viable fix, but tried it out:

@ramonjd
Copy link
Member Author

ramonjd commented Dec 5, 2023

Dev note

Milestones:

Gutenberg 17.2
WordPress 6.5

Two new selectors have to been introduced to Block Editor's Core Data API to fetch revisions and single revisions for post types that support revisions, for example, posts and pages, templates and global styles.

getRevisions( kind, name, recordKey, query )  fetches a post’s revisions where recordKey is the id of the parent post.

getRevision(kind, name, recordKey, revisionKey, query)  fetches a single post revision, where revisionKey is the id of the individual revision.

The functions use similar arguments to exisiting core data entity functions such as getEntityRecords with the addition of recordKey (post parent id) and revisionKey (single revision id).

Example usage:

// Returns a collection of revisions.
// `parentGlobalStylesId` is the id (int) of the parent post.
wp.data.select( 'core' ).getRevisions( 'root', 'globalStyles', parentGlobalStylesId, { per_page: -1 } );
 
// Paginated results.
// `parentId` is the id (int) of the parent post.
wp.data.select( 'core' ).getRevisions( 'postType', 'post', parentId, { per_page: 3, page: 2 } )
 
// Get a single revision object.
// `parentId` is the id (int) of the parent post.
// `revisionId` is the id (int) of the individual revision post.
wp.data.select( 'core' ).getRevision( 'postType', 'post', parentId, revisionId );
 
 
// Get a single revision with only the id, parent and date fields.
// `parentId` is the id (int) of the parent post.
// `revisionId` is the id (int) of the individual revision post.
wp.data.select( 'core' ).getRevision( 'postType', 'post', parentId, revisionId, { _fields: 'id,parent,date' } );

getRevisions and getRevision can also be used via useSelect in React components:

import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';

function MyComponent() {
    const pageRevisions = useSelect(
        ( select ) =>
            select( coreStore ).getRevisions( 'postType', 'page', pageId ),
        [ pageId ]
    );
    // Do something with pageRevisions...
}

In the background, these selectors’ corresponding resolvers call revisions REST API endpoints.

@vcanales vcanales added the Backport to Gutenberg RC Pull request that needs to be backported to a Gutenberg release candidate (RC) label Dec 6, 2023
@madhusudhand madhusudhand removed the Backport to Gutenberg RC Pull request that needs to be backported to a Gutenberg release candidate (RC) label Dec 19, 2023
@ramonjd ramonjd added has dev note when dev note is done (for upcoming WordPress release) and removed Needs Dev Note Requires a developer note for a major WordPress release cycle labels Feb 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has dev note when dev note is done (for upcoming WordPress release) [Package] Core data /packages/core-data [Type] New API New API to be used by plugin developers or package users.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants