feat(davinci-client): embed password policy in PASSWORD_VERIFY collector#572
feat(davinci-client): embed password policy in PASSWORD_VERIFY collector#572
Conversation
DV-16053: The DaVinci API moves passwordPolicy from the response root into the PASSWORD_VERIFY field component. This adds a new PasswordVerifyCollector type that reads the policy from the field and exposes it via output.passwordPolicy. - Add PasswordPolicy interface and PasswordVerifyField type - Add PasswordVerifyCollector with optional passwordPolicy in output - Split PASSWORD/PASSWORD_VERIFY into separate reducer cases - Update CollectorValueType, Collectors union, and type inference - Add unit tests, type tests, and updater narrowing tests - Update sample app to render password requirements from policy - Regenerate API reports
🦋 Changeset detectedLatest commit: 630f470 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 36 minutes and 3 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughIntroduces Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit 630f470
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
packages/davinci-client/src/lib/collector.types.ts (1)
193-210: Consider extendingSingleValueCollectorNoValueinstead of hand-rolling the shape.
PasswordVerifyCollectoris structurally identical toSingleValueCollectorNoValue<'PasswordVerifyCollector'>plus optionaloutput.passwordPolicy. Defining it as an intersection would reduce drift risk if the base shape evolves (e.g.,input.validation, etc.).Alternative definition
-export interface PasswordVerifyCollector { - category: 'SingleValueCollector'; - error: string | null; - type: 'PasswordVerifyCollector'; - id: string; - name: string; - input: { - key: string; - value: string | number | boolean; - type: string; - }; - output: { - key: string; - label: string; - type: string; - passwordPolicy?: PasswordPolicy; - }; -} +export type PasswordVerifyCollector = SingleValueCollectorNoValue<'PasswordVerifyCollector'> & { + output: { passwordPolicy?: PasswordPolicy }; +};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/davinci-client/src/lib/collector.types.ts` around lines 193 - 210, PasswordVerifyCollector duplicates the shape of SingleValueCollectorNoValue<'PasswordVerifyCollector'> and risks drifting; refactor PasswordVerifyCollector to extend or be defined as an intersection with SingleValueCollectorNoValue<'PasswordVerifyCollector'> and add the optional output.passwordPolicy field, so the type becomes SingleValueCollectorNoValue<'PasswordVerifyCollector'> & { output: { passwordPolicy?: PasswordPolicy } } (or equivalent), keeping the unique type tag 'PasswordVerifyCollector' and preserving existing fields like input/output.packages/davinci-client/src/lib/davinci.types.ts (1)
64-83: Consider modelingenvironmentonPasswordPolicy.The mock in
mock-form-fields.data.ts(lines 51-54) includesenvironment: { id: string }on the embedded policy, matching what PingOne returns. SincePasswordPolicyis the contract consumers will type against when readingcollector.output.passwordPolicy, missing this field means users accessingpolicy.environment?.idwould need a cast. Consider adding:Proposed addition
export interface PasswordPolicy { id?: string; + environment?: { id?: string }; name?: string;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/davinci-client/src/lib/davinci.types.ts` around lines 64 - 83, The PasswordPolicy interface is missing an environment property so consumers reading collector.output.passwordPolicy must cast to access environment.id; update the PasswordPolicy type to include environment?: { id?: string } (or a more complete environment shape if available) so the mock shape in mock-form-fields.data.ts matches the typed contract; ensure references to PasswordPolicy (e.g., usages of collector.output.passwordPolicy) compile without casts after this change.packages/davinci-client/src/lib/collector.utils.ts (1)
450-483: Duplicates error-building logic fromreturnSingleValueCollector.The
key/label/typepresence checks here mirror the block inreturnSingleValueCollector(lines 154-163). SincePasswordVerifyFieldis a compile-time-narrowed type with requiredkey,label,type, theseinchecks never fail at runtime for well-typed callers — they exist only as defensive parity with the shared helper. Consider delegating toreturnSingleValueCollectorwith a'PasswordVerifyCollector'branch, then addingpasswordPolicyon top, to keep validation centralized.Non-blocking; current implementation is correct.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/davinci-client/src/lib/collector.utils.ts` around lines 450 - 483, Replace the duplicated presence-check logic in returnPasswordVerifyCollector by calling the shared helper returnSingleValueCollector (passing the same field and idx and specifying type 'PasswordVerifyCollector' or otherwise using its branch) to produce the base collector, then extend/override that returned object's type and output to include the passwordPolicy when present; keep references to PasswordVerifyCollector and PasswordVerifyField so the resulting collector adds the passwordPolicy field on top of the single-value collector instead of reimplementing the key/label/type checks.e2e/davinci-app/components/password.ts (2)
46-54: PreferRegExp.test()overString.prototype.match()for boolean checks.
match()allocates a result array;test()returns a boolean directly and reads more intentionally here. Also, consider hoisting the regexes so they aren't recompiled on every iteration.♻️ Suggested refactor
+ const UPPER_RE = /^[A-Z]+$/; + const LOWER_RE = /^[a-z]+$/; + const DIGIT_RE = /^[0-9]+$/; if (policy.minCharacters) { for (const [charset, count] of Object.entries(policy.minCharacters)) { const li = document.createElement('li'); - if (charset.match(/^[A-Z]+$/)) { + if (UPPER_RE.test(charset)) { li.textContent = `At least ${count} uppercase letter(s)`; - } else if (charset.match(/^[a-z]+$/)) { + } else if (LOWER_RE.test(charset)) { li.textContent = `At least ${count} lowercase letter(s)`; - } else if (charset.match(/^[0-9]+$/)) { + } else if (DIGIT_RE.test(charset)) { li.textContent = `At least ${count} number(s)`; } else { li.textContent = `At least ${count} special character(s)`; } requirementsList.appendChild(li); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/davinci-app/components/password.ts` around lines 46 - 54, The code in password.ts uses String.prototype.match() for boolean checks inside the block that sets li.textContent; replace those match() calls with precompiled RegExp.test() calls to avoid allocating arrays and improve intent: create hoisted regex constants (e.g., UPPERCASE_REGEX, LOWERCASE_REGEX, DIGIT_REGEX, SPECIAL_REGEX) outside the loop or function and then use UPPERCASE_REGEX.test(charset), LOWERCASE_REGEX.test(charset), DIGIT_REGEX.test(charset), else SPECIAL_REGEX.test(charset) to pick the appropriate message for the li element (the code paths around li.textContent remain the same).
31-62: Optional: associate the requirements list with the password input for a11y.Even for an e2e sample, wiring
aria-describedbyon the<input>to an id on the<ul>(or usingaria-labelledbywith a heading) makes the intent visible to assistive tech and is a nicer reference pattern for SDK consumers to copy. Non-blocking.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/davinci-app/components/password.ts` around lines 31 - 62, Add an accessible reference between the rendered requirements list and the password input: give the created requirementsList element a stable id (e.g., "password-requirements-<unique>") and set that id on the password input's aria-describedby so assistive tech can read the requirements; locate the password input where the form is built (the password input variable or the element found via selector) and assign aria-describedby to match requirementsList.id only when requirementsList is appended (use the same conditional that appends requirementsList to formEl and clean up/reset the attribute if policy is not present).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@e2e/davinci-app/components/password.ts`:
- Around line 37-41: The current rendering uses policy.length.min and
policy.length.max directly, which produces "undefined" when one side is missing;
update the logic around policy.length to check length.min and length.max
independently (e.g., if both present render "min–max characters", if only min
render "at least {min} characters", if only max render "up to {max}
characters"), only create/append the li element to requirementsList when at
least one of length.min or length.max exists, and use the existing li variable
for the text content assignment.
---
Nitpick comments:
In `@e2e/davinci-app/components/password.ts`:
- Around line 46-54: The code in password.ts uses String.prototype.match() for
boolean checks inside the block that sets li.textContent; replace those match()
calls with precompiled RegExp.test() calls to avoid allocating arrays and
improve intent: create hoisted regex constants (e.g., UPPERCASE_REGEX,
LOWERCASE_REGEX, DIGIT_REGEX, SPECIAL_REGEX) outside the loop or function and
then use UPPERCASE_REGEX.test(charset), LOWERCASE_REGEX.test(charset),
DIGIT_REGEX.test(charset), else SPECIAL_REGEX.test(charset) to pick the
appropriate message for the li element (the code paths around li.textContent
remain the same).
- Around line 31-62: Add an accessible reference between the rendered
requirements list and the password input: give the created requirementsList
element a stable id (e.g., "password-requirements-<unique>") and set that id on
the password input's aria-describedby so assistive tech can read the
requirements; locate the password input where the form is built (the password
input variable or the element found via selector) and assign aria-describedby to
match requirementsList.id only when requirementsList is appended (use the same
conditional that appends requirementsList to formEl and clean up/reset the
attribute if policy is not present).
In `@packages/davinci-client/src/lib/collector.types.ts`:
- Around line 193-210: PasswordVerifyCollector duplicates the shape of
SingleValueCollectorNoValue<'PasswordVerifyCollector'> and risks drifting;
refactor PasswordVerifyCollector to extend or be defined as an intersection with
SingleValueCollectorNoValue<'PasswordVerifyCollector'> and add the optional
output.passwordPolicy field, so the type becomes
SingleValueCollectorNoValue<'PasswordVerifyCollector'> & { output: {
passwordPolicy?: PasswordPolicy } } (or equivalent), keeping the unique type tag
'PasswordVerifyCollector' and preserving existing fields like input/output.
In `@packages/davinci-client/src/lib/collector.utils.ts`:
- Around line 450-483: Replace the duplicated presence-check logic in
returnPasswordVerifyCollector by calling the shared helper
returnSingleValueCollector (passing the same field and idx and specifying type
'PasswordVerifyCollector' or otherwise using its branch) to produce the base
collector, then extend/override that returned object's type and output to
include the passwordPolicy when present; keep references to
PasswordVerifyCollector and PasswordVerifyField so the resulting collector adds
the passwordPolicy field on top of the single-value collector instead of
reimplementing the key/label/type checks.
In `@packages/davinci-client/src/lib/davinci.types.ts`:
- Around line 64-83: The PasswordPolicy interface is missing an environment
property so consumers reading collector.output.passwordPolicy must cast to
access environment.id; update the PasswordPolicy type to include environment?: {
id?: string } (or a more complete environment shape if available) so the mock
shape in mock-form-fields.data.ts matches the typed contract; ensure references
to PasswordPolicy (e.g., usages of collector.output.passwordPolicy) compile
without casts after this change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 369808f1-fb4b-4b90-aba0-5b1ec2fe0fe2
📒 Files selected for processing (17)
.changeset/embed-password-policy-in-component.mde2e/davinci-app/components/password.tse2e/davinci-app/main.tspackages/davinci-client/api-report/davinci-client.api.mdpackages/davinci-client/api-report/davinci-client.types.api.mdpackages/davinci-client/src/lib/client.types.tspackages/davinci-client/src/lib/collector.types.test-d.tspackages/davinci-client/src/lib/collector.types.tspackages/davinci-client/src/lib/collector.utils.test.tspackages/davinci-client/src/lib/collector.utils.tspackages/davinci-client/src/lib/davinci.types.tspackages/davinci-client/src/lib/mock-data/mock-form-fields.data.tspackages/davinci-client/src/lib/node.reducer.test.tspackages/davinci-client/src/lib/node.reducer.tspackages/davinci-client/src/lib/node.types.test-d.tspackages/davinci-client/src/lib/node.types.tspackages/davinci-client/src/lib/updater-narrowing.types.test-d.ts
| if (policy.length) { | ||
| const li = document.createElement('li'); | ||
| li.textContent = `${policy.length.min}–${policy.length.max} characters`; | ||
| requirementsList.appendChild(li); | ||
| } |
There was a problem hiding this comment.
Handle optional length.min / length.max independently.
PasswordPolicy.length has both min and max as optional. When only one is set, the current template renders e.g. "undefined–255 characters" or "8–undefined characters". Consider branching on each side (or guarding the whole block on both being present).
🛠️ Suggested fix
- if (policy.length) {
- const li = document.createElement('li');
- li.textContent = `${policy.length.min}–${policy.length.max} characters`;
- requirementsList.appendChild(li);
- }
+ if (policy.length && (policy.length.min != null || policy.length.max != null)) {
+ const li = document.createElement('li');
+ const { min, max } = policy.length;
+ if (min != null && max != null) {
+ li.textContent = `${min}–${max} characters`;
+ } else if (min != null) {
+ li.textContent = `At least ${min} characters`;
+ } else {
+ li.textContent = `At most ${max} characters`;
+ }
+ requirementsList.appendChild(li);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (policy.length) { | |
| const li = document.createElement('li'); | |
| li.textContent = `${policy.length.min}–${policy.length.max} characters`; | |
| requirementsList.appendChild(li); | |
| } | |
| if (policy.length && (policy.length.min != null || policy.length.max != null)) { | |
| const li = document.createElement('li'); | |
| const { min, max } = policy.length; | |
| if (min != null && max != null) { | |
| li.textContent = `${min}–${max} characters`; | |
| } else if (min != null) { | |
| li.textContent = `At least ${min} characters`; | |
| } else { | |
| li.textContent = `At most ${max} characters`; | |
| } | |
| requirementsList.appendChild(li); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@e2e/davinci-app/components/password.ts` around lines 37 - 41, The current
rendering uses policy.length.min and policy.length.max directly, which produces
"undefined" when one side is missing; update the logic around policy.length to
check length.min and length.max independently (e.g., if both present render
"min–max characters", if only min render "at least {min} characters", if only
max render "up to {max} characters"), only create/append the li element to
requirementsList when at least one of length.min or length.max exists, and use
the existing li variable for the text content assignment.
@forgerock/davinci-client
@forgerock/device-client
@forgerock/journey-client
@forgerock/oidc-client
@forgerock/protect
@forgerock/sdk-types
@forgerock/sdk-utilities
@forgerock/iframe-manager
@forgerock/sdk-logger
@forgerock/sdk-oidc
@forgerock/sdk-request-middleware
@forgerock/storage
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. ❌ Your project status has failed because the head coverage (15.79%) is below the target coverage (40.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #572 +/- ##
===========================================
- Coverage 70.90% 15.79% -55.12%
===========================================
Files 53 154 +101
Lines 2021 26704 +24683
Branches 377 1134 +757
===========================================
+ Hits 1433 4218 +2785
- Misses 588 22486 +21898
🚀 New features to boost your workflow:
|
|
Deployed e54d93c to https://ForgeRock.github.io/ping-javascript-sdk/pr-572/e54d93c6df2bad62c3ada8b9c54a587a6c304282 branch gh-pages in ForgeRock/ping-javascript-sdk |
📦 Bundle Size Analysis📦 Bundle Size Analysis🚨 Significant Changes🔻 @forgerock/device-client - 0.0 KB (-9.9 KB, -100.0%) 📊 Minor Changes📉 @forgerock/device-client - 9.9 KB (-0.0 KB) ➖ No Changes➖ @forgerock/oidc-client - 25.2 KB 14 packages analyzed • Baseline from latest Legend🆕 New package ℹ️ How bundle sizes are calculated
🔄 Updated automatically on each push to this PR |
…llback Component-level policy takes precedence; falls back to root-level passwordPolicy for backward compatibility during the migration period. - Add passwordPolicy? to DaVinciNextResponse - Thread root-level policy through node.slice → reducer → factory - returnPasswordVerifyCollector prefers field.passwordPolicy ?? rootPasswordPolicy - Add tests for fallback, precedence, and no-policy cases - Restore root-level passwordPolicy in mock data (Solution 3 shape)
Summary
PasswordVerifyCollectortype that readspasswordPolicyfrom thePASSWORD_VERIFYfield component (DV-16053)PASSWORD/PASSWORD_VERIFYinto separate reducer cases so each produces the correct collector typeWhat changed
PasswordPolicyinterface,PasswordVerifyFieldtype (split fromStandardField),PasswordVerifyCollectorinterface withoutput.passwordPolicy?returnPasswordVerifyCollector()— reads policy from field, omits key when absentPASSWORD→PasswordCollector(unchanged),PASSWORD_VERIFY→PasswordVerifyCollector(new)SingleValueCollectorTypes,InferSingleValueCollectorType,SingleValueCollectors,Collectors,CollectorValueTypePasswordVerifyCollectorhas a policyTest plan
nx run davinci-client:test— 282 tests pass (22 files)nx run davinci-client:typecheck— cleannx run davinci-client:lint— 0 errorsnx run @forgerock/davinci-client:nxBuild— cleanPASSWORD_VERIFY+ feature flagdv-16053-embed_password_policy_in_componentSummary by CodeRabbit
Release Notes