Skip to content

Conversation

@jeremydw
Copy link
Member

I keep accidentally malforming timestamps when writing scripts that use setRawDoc which prevents the Root CMS UI from working. This adds some basic safeguards when using setRawDoc to the structure/type of the Root CMS system fields to help avoid that.

@jeremydw jeremydw requested a review from stevenle February 10, 2026 03:24
}
if (sys.publishingLocked.until !== undefined) {
const until = sys.publishingLocked.until;
if (
Copy link
Member

Choose a reason for hiding this comment

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

this timestamp conversion logic seems to be duplicated here and above, maybe move the logic to a normalization function, e.g.

until: convertToTimetamp(until)

}
// Otherwise, it's invalid.
else {
throw new Error(
Copy link
Member

@stevenle stevenle Feb 10, 2026

Choose a reason for hiding this comment

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

this error might catch callers off guard, because when creating a new doc you should be able to save only the fields and the system should automatically set a few default sys values if it's not provided.

  • if createdAt and modifiedAt are undefined, it should be set to the current time
  • if createdBy and modifiedBy are undefined, set it to the current user (i don't think there's currently a way to figure out "who" the current user is, so maybe default it to root-cms-client if not specified via a argument option)
  • if locales is empty, set it to [<default locale> or 'en']

* }, { mode: 'draft' });
* ```
*/
async setRawDoc(
Copy link
Member

Choose a reason for hiding this comment

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

one other validation that should be added to this is to ensure the slug/collection/id fields are correct in the updated doc.

there are cases where we might to something like:

setRawDoc('Pages', 'bar', getRawDoc('Pages', 'foo'))

in this case a user would be incorrectly setting the new doc to id = 'Pages/foo' which is incorrect

@jeremydw
Copy link
Member Author

done - updated all the feedback. LMK if you'd prefer different defaults or different comments!


// Set default values for required timestamp fields if not provided.
const now = Timestamp.now();
if (result.createdAt === undefined || result.createdAt === null) {
Copy link
Member

Choose a reason for hiding this comment

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

the === undefined || === null pattern is repeated a few times, i think from a readability perspective i'd prefer this wrapped in a helper function, e.g. i think closure library uses isDef() for this.

i'd also prefer to "read" this with the positive value first and defaulting after, e.g. "if defined, do this, otherwise default to some value".

if (isDef(result.createdAt)) { 
  // normalize value
} else {
  // set default value
}

if (!result.createdBy || typeof result.createdBy !== 'string') {
result.createdBy = 'root-cms-client';
}

Copy link
Member

Choose a reason for hiding this comment

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

nit: remove this newline so it feels like the comment above applies to both codeblocks.

@jeremydw
Copy link
Member Author

done

) {
// Ensure id, collection, and slug fields match the parameters to prevent data inconsistencies.
const expectedId = `${collectionId}/${slug}`;
if (data.id !== undefined && data.id !== expectedId) {
Copy link
Member

Choose a reason for hiding this comment

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

these if() statements are unnecessary, you can go ahead and override all of these values to ensure correctness.


// Validate and normalize sys fields to prevent data integrity issues.
if (data.sys) {
data.sys = validateSysFields(data.sys);
Copy link
Member

Choose a reason for hiding this comment

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

in case the caller only passes the fields to setRawDoc() maybe we should go ahead and default to an empty object here, otherwise an error would be thrown in validateSysFields().

@jeremydw
Copy link
Member Author

done!

}

// Ensure id, collection, and slug fields match the parameters to prevent data inconsistencies.
const expectedId = `${collectionId}/${slug}`;
Copy link
Member

Choose a reason for hiding this comment

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

for the slug can you pass it through a normalizer function (there might be one in here already), i.e. convert / to --?


// Validate and normalize sys fields to prevent data integrity issues.
// Default to empty object if sys is not provided.
if (!data.sys) {
Copy link
Member

Choose a reason for hiding this comment

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

remove this if() statement and update to data.sys = validateSysFields(data.sys || {}) below

@jeremydw
Copy link
Member Author

updated; and made a few updates elsewhere to use normalizeSlug

@stevenle stevenle merged commit 34d0178 into main Feb 13, 2026
1 check passed
@stevenle stevenle deleted the feat/validate-setrawdoc branch February 13, 2026 00:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants