@peernova/cuneiform-sf 1.0.2 → 1.0.4-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +81 -30
- package/README.md +59 -95
- package/lib/adapters/connection-facade.d.ts +458 -0
- package/lib/adapters/connection-facade.js +379 -0
- package/lib/adapters/connection-facade.js.map +1 -0
- package/lib/adapters/errors.d.ts +547 -0
- package/lib/adapters/errors.js +937 -0
- package/lib/adapters/errors.js.map +1 -0
- package/lib/adapters/index.d.ts +33 -0
- package/lib/adapters/index.js +50 -0
- package/lib/adapters/index.js.map +1 -0
- package/lib/adapters/lifecycle.d.ts +119 -0
- package/lib/adapters/lifecycle.js +94 -0
- package/lib/adapters/lifecycle.js.map +1 -0
- package/lib/adapters/rest/cache.d.ts +69 -0
- package/lib/adapters/rest/cache.js +133 -0
- package/lib/adapters/rest/cache.js.map +1 -0
- package/lib/adapters/rest/index.d.ts +11 -0
- package/lib/adapters/rest/index.js +18 -0
- package/lib/adapters/rest/index.js.map +1 -0
- package/lib/adapters/rest/profiling-rest-client.d.ts +137 -0
- package/lib/adapters/rest/profiling-rest-client.js +115 -0
- package/lib/adapters/rest/profiling-rest-client.js.map +1 -0
- package/lib/adapters/rest/rest-api-adapter.d.ts +389 -0
- package/lib/adapters/rest/rest-api-adapter.js +747 -0
- package/lib/adapters/rest/rest-api-adapter.js.map +1 -0
- package/lib/adapters/rest/types.d.ts +34 -0
- package/lib/adapters/rest/types.js +9 -0
- package/lib/adapters/rest/types.js.map +1 -0
- package/lib/adapters/retry.d.ts +91 -0
- package/lib/adapters/retry.js +215 -0
- package/lib/adapters/retry.js.map +1 -0
- package/lib/adapters/soql/cuneiform-query-builder.d.ts +391 -0
- package/lib/adapters/soql/cuneiform-query-builder.js +559 -0
- package/lib/adapters/soql/cuneiform-query-builder.js.map +1 -0
- package/lib/adapters/soql/index.d.ts +13 -0
- package/lib/adapters/soql/index.js +21 -0
- package/lib/adapters/soql/index.js.map +1 -0
- package/lib/adapters/soql/soql-query-adapter.d.ts +141 -0
- package/lib/adapters/soql/soql-query-adapter.js +259 -0
- package/lib/adapters/soql/soql-query-adapter.js.map +1 -0
- package/lib/adapters/soql/types.d.ts +37 -0
- package/lib/adapters/soql/types.js +19 -0
- package/lib/adapters/soql/types.js.map +1 -0
- package/lib/adapters/testing/index.d.ts +37 -0
- package/lib/adapters/testing/index.js +20 -0
- package/lib/adapters/testing/index.js.map +1 -0
- package/lib/adapters/testing/mock-connection.d.ts +77 -0
- package/lib/adapters/testing/mock-connection.js +207 -0
- package/lib/adapters/testing/mock-connection.js.map +1 -0
- package/lib/adapters/testing/mock-logger.d.ts +29 -0
- package/lib/adapters/testing/mock-logger.js +57 -0
- package/lib/adapters/testing/mock-logger.js.map +1 -0
- package/lib/adapters/testing/mock-mcp-adapters.d.ts +32 -0
- package/lib/adapters/testing/mock-mcp-adapters.js +52 -0
- package/lib/adapters/testing/mock-mcp-adapters.js.map +1 -0
- package/lib/adapters/testing/mock-oclif-config.d.ts +22 -0
- package/lib/adapters/testing/mock-oclif-config.js +90 -0
- package/lib/adapters/testing/mock-oclif-config.js.map +1 -0
- package/lib/adapters/testing/mock-rest-adapter.d.ts +26 -0
- package/lib/adapters/testing/mock-rest-adapter.js +243 -0
- package/lib/adapters/testing/mock-rest-adapter.js.map +1 -0
- package/lib/adapters/testing/mock-salesforce-connection.d.ts +40 -0
- package/lib/adapters/testing/mock-salesforce-connection.js +61 -0
- package/lib/adapters/testing/mock-salesforce-connection.js.map +1 -0
- package/lib/adapters/testing/mock-soql-adapter.d.ts +30 -0
- package/lib/adapters/testing/mock-soql-adapter.js +120 -0
- package/lib/adapters/testing/mock-soql-adapter.js.map +1 -0
- package/lib/adapters/testing/mock-tooling-adapter.d.ts +24 -0
- package/lib/adapters/testing/mock-tooling-adapter.js +163 -0
- package/lib/adapters/testing/mock-tooling-adapter.js.map +1 -0
- package/lib/adapters/testing/stub-connection.d.ts +93 -0
- package/lib/adapters/testing/stub-connection.js +97 -0
- package/lib/adapters/testing/stub-connection.js.map +1 -0
- package/lib/adapters/testing/stub-rest-adapter.d.ts +52 -0
- package/lib/adapters/testing/stub-rest-adapter.js +58 -0
- package/lib/adapters/testing/stub-rest-adapter.js.map +1 -0
- package/lib/adapters/testing/stub-soql-adapter.d.ts +56 -0
- package/lib/adapters/testing/stub-soql-adapter.js +50 -0
- package/lib/adapters/testing/stub-soql-adapter.js.map +1 -0
- package/lib/adapters/testing/types.d.ts +71 -0
- package/lib/adapters/testing/types.js +9 -0
- package/lib/adapters/testing/types.js.map +1 -0
- package/lib/adapters/tooling/index.d.ts +10 -0
- package/lib/adapters/tooling/index.js +17 -0
- package/lib/adapters/tooling/index.js.map +1 -0
- package/lib/adapters/tooling/tooling-api-adapter.d.ts +157 -0
- package/lib/adapters/tooling/tooling-api-adapter.js +339 -0
- package/lib/adapters/tooling/tooling-api-adapter.js.map +1 -0
- package/lib/adapters/tooling/types.d.ts +81 -0
- package/lib/adapters/tooling/types.js +9 -0
- package/lib/adapters/tooling/types.js.map +1 -0
- package/lib/adapters/types.d.ts +112 -0
- package/lib/adapters/types.js +169 -0
- package/lib/adapters/types.js.map +1 -0
- package/lib/base/cuneiform-command.d.ts +152 -0
- package/lib/base/cuneiform-command.js +243 -0
- package/lib/base/cuneiform-command.js.map +1 -0
- package/lib/commands/cuneiform/compatibility/check.d.ts +43 -0
- package/lib/commands/cuneiform/compatibility/check.js +114 -0
- package/lib/commands/cuneiform/compatibility/check.js.map +1 -0
- package/lib/commands/cuneiform/definition/create.d.ts +119 -0
- package/lib/commands/cuneiform/definition/create.js +693 -0
- package/lib/commands/cuneiform/definition/create.js.map +1 -0
- package/lib/commands/cuneiform/definition/export.d.ts +57 -0
- package/lib/commands/cuneiform/definition/export.js +133 -0
- package/lib/commands/cuneiform/definition/export.js.map +1 -0
- package/lib/commands/cuneiform/definition/get.d.ts +86 -0
- package/lib/commands/cuneiform/definition/get.js +270 -0
- package/lib/commands/cuneiform/definition/get.js.map +1 -0
- package/lib/commands/cuneiform/definition/import.d.ts +54 -0
- package/lib/commands/cuneiform/definition/import.js +118 -0
- package/lib/commands/cuneiform/definition/import.js.map +1 -0
- package/lib/commands/cuneiform/definition/list.d.ts +110 -0
- package/lib/commands/cuneiform/definition/list.js +344 -0
- package/lib/commands/cuneiform/definition/list.js.map +1 -0
- package/lib/commands/cuneiform/definition/purge.d.ts +105 -0
- package/lib/commands/cuneiform/definition/purge.js +533 -0
- package/lib/commands/cuneiform/definition/purge.js.map +1 -0
- package/lib/commands/cuneiform/definition/update.d.ts +58 -0
- package/lib/commands/cuneiform/definition/update.js +206 -0
- package/lib/commands/cuneiform/definition/update.js.map +1 -0
- package/lib/commands/cuneiform/mcp/serve.d.ts +56 -0
- package/lib/commands/cuneiform/mcp/serve.js +109 -0
- package/lib/commands/cuneiform/mcp/serve.js.map +1 -0
- package/lib/commands/cuneiform/object/describe.d.ts +61 -0
- package/lib/commands/cuneiform/object/describe.js +461 -0
- package/lib/commands/cuneiform/object/describe.js.map +1 -0
- package/lib/commands/cuneiform/object/list.d.ts +111 -0
- package/lib/commands/cuneiform/object/list.js +239 -0
- package/lib/commands/cuneiform/object/list.js.map +1 -0
- package/lib/commands/cuneiform/org/details.d.ts +99 -0
- package/lib/commands/cuneiform/org/details.js +521 -0
- package/lib/commands/cuneiform/org/details.js.map +1 -0
- package/lib/commands/cuneiform/org/reset.d.ts +46 -0
- package/lib/commands/cuneiform/org/reset.js +135 -0
- package/lib/commands/cuneiform/org/reset.js.map +1 -0
- package/lib/commands/cuneiform/profile/request/cancel.d.ts +59 -0
- package/lib/commands/cuneiform/profile/request/cancel.js +202 -0
- package/lib/commands/cuneiform/profile/request/cancel.js.map +1 -0
- package/lib/commands/cuneiform/profile/request/delete.d.ts +59 -0
- package/lib/commands/cuneiform/profile/request/delete.js +223 -0
- package/lib/commands/cuneiform/profile/request/delete.js.map +1 -0
- package/lib/commands/cuneiform/profile/request/list.d.ts +35 -0
- package/lib/commands/cuneiform/profile/request/list.js +102 -0
- package/lib/commands/cuneiform/profile/request/list.js.map +1 -0
- package/lib/commands/cuneiform/profile.d.ts +90 -0
- package/lib/commands/cuneiform/profile.js +322 -0
- package/lib/commands/cuneiform/profile.js.map +1 -0
- package/lib/commands/cuneiform/summary/purge.d.ts +77 -0
- package/lib/commands/cuneiform/summary/purge.js +429 -0
- package/lib/commands/cuneiform/summary/purge.js.map +1 -0
- package/lib/commands/cuneiform/summary/reprofile.d.ts +60 -0
- package/lib/commands/cuneiform/summary/reprofile.js +236 -0
- package/lib/commands/cuneiform/summary/reprofile.js.map +1 -0
- package/lib/commands/cuneiform/summary/stop.d.ts +59 -0
- package/lib/commands/cuneiform/summary/stop.js +234 -0
- package/lib/commands/cuneiform/summary/stop.js.map +1 -0
- package/lib/commands/cuneiform/user/details.d.ts +73 -0
- package/lib/commands/cuneiform/user/details.js +391 -0
- package/lib/commands/cuneiform/user/details.js.map +1 -0
- package/lib/constants/index.d.ts +8 -0
- package/lib/constants/index.js +16 -0
- package/lib/constants/index.js.map +1 -0
- package/lib/constants/namespace-constants.d.ts +91 -0
- package/lib/constants/namespace-constants.js +211 -0
- package/lib/constants/namespace-constants.js.map +1 -0
- package/lib/debug/command-debug-proxy.d.ts +101 -0
- package/lib/debug/command-debug-proxy.js +171 -0
- package/lib/debug/command-debug-proxy.js.map +1 -0
- package/lib/debug/debug-logger.d.ts +85 -0
- package/lib/debug/debug-logger.js +133 -0
- package/lib/debug/debug-logger.js.map +1 -0
- package/lib/debug/index.d.ts +12 -0
- package/lib/debug/index.js +20 -0
- package/lib/debug/index.js.map +1 -0
- package/lib/debug/service-debug-proxy.d.ts +30 -0
- package/lib/debug/service-debug-proxy.js +102 -0
- package/lib/debug/service-debug-proxy.js.map +1 -0
- package/lib/hooks/prerun.d.ts +25 -0
- package/lib/hooks/prerun.js +47 -0
- package/lib/hooks/prerun.js.map +1 -0
- package/lib/mcp/config/mcp-config.d.ts +55 -0
- package/lib/mcp/config/mcp-config.js +51 -0
- package/lib/mcp/config/mcp-config.js.map +1 -0
- package/lib/mcp/config/pagination.d.ts +96 -0
- package/lib/mcp/config/pagination.js +108 -0
- package/lib/mcp/config/pagination.js.map +1 -0
- package/lib/mcp/config/system-prompts.d.ts +18 -0
- package/lib/mcp/config/system-prompts.js +92 -0
- package/lib/mcp/config/system-prompts.js.map +1 -0
- package/lib/mcp/errors.d.ts +23 -0
- package/lib/mcp/errors.js +27 -0
- package/lib/mcp/errors.js.map +1 -0
- package/lib/mcp/schemas/input-schemas.d.ts +327 -0
- package/lib/mcp/schemas/input-schemas.js +302 -0
- package/lib/mcp/schemas/input-schemas.js.map +1 -0
- package/lib/mcp/server.d.ts +40 -0
- package/lib/mcp/server.js +316 -0
- package/lib/mcp/server.js.map +1 -0
- package/lib/mcp/tools/contactpoint-tools.d.ts +14 -0
- package/lib/mcp/tools/contactpoint-tools.js +34 -0
- package/lib/mcp/tools/contactpoint-tools.js.map +1 -0
- package/lib/mcp/tools/definition-io-tools.d.ts +19 -0
- package/lib/mcp/tools/definition-io-tools.js +152 -0
- package/lib/mcp/tools/definition-io-tools.js.map +1 -0
- package/lib/mcp/tools/definition-tools.d.ts +51 -0
- package/lib/mcp/tools/definition-tools.js +199 -0
- package/lib/mcp/tools/definition-tools.js.map +1 -0
- package/lib/mcp/tools/index.d.ts +37 -0
- package/lib/mcp/tools/index.js +88 -0
- package/lib/mcp/tools/index.js.map +1 -0
- package/lib/mcp/tools/object-tools.d.ts +22 -0
- package/lib/mcp/tools/object-tools.js +306 -0
- package/lib/mcp/tools/object-tools.js.map +1 -0
- package/lib/mcp/tools/org-tools.d.ts +14 -0
- package/lib/mcp/tools/org-tools.js +177 -0
- package/lib/mcp/tools/org-tools.js.map +1 -0
- package/lib/mcp/tools/profile-tools.d.ts +59 -0
- package/lib/mcp/tools/profile-tools.js +213 -0
- package/lib/mcp/tools/profile-tools.js.map +1 -0
- package/lib/mcp/tools/summary-tools.d.ts +14 -0
- package/lib/mcp/tools/summary-tools.js +38 -0
- package/lib/mcp/tools/summary-tools.js.map +1 -0
- package/lib/mcp/tools/tool-factory.d.ts +63 -0
- package/lib/mcp/tools/tool-factory.js +146 -0
- package/lib/mcp/tools/tool-factory.js.map +1 -0
- package/lib/mcp/tools/user-tools.d.ts +25 -0
- package/lib/mcp/tools/user-tools.js +167 -0
- package/lib/mcp/tools/user-tools.js.map +1 -0
- package/lib/models/date-literal.d.ts +211 -0
- package/lib/models/date-literal.js +615 -0
- package/lib/models/date-literal.js.map +1 -0
- package/lib/models/object-describe-types.d.ts +173 -0
- package/lib/models/object-describe-types.js +9 -0
- package/lib/models/object-describe-types.js.map +1 -0
- package/lib/models/profile-request-types.d.ts +118 -0
- package/lib/models/profile-request-types.js +23 -0
- package/lib/models/profile-request-types.js.map +1 -0
- package/lib/models/profiling-execution-types.d.ts +154 -0
- package/lib/models/profiling-execution-types.js +14 -0
- package/lib/models/profiling-execution-types.js.map +1 -0
- package/lib/models/service-result.d.ts +114 -0
- package/lib/models/service-result.js +81 -0
- package/lib/models/service-result.js.map +1 -0
- package/lib/models/sfdmu-types.d.ts +53 -0
- package/lib/models/sfdmu-types.js +23 -0
- package/lib/models/sfdmu-types.js.map +1 -0
- package/lib/models/status-types.d.ts +38 -0
- package/lib/models/status-types.js +12 -0
- package/lib/models/status-types.js.map +1 -0
- package/lib/models/summary-bulk-types.d.ts +61 -0
- package/lib/models/summary-bulk-types.js +23 -0
- package/lib/models/summary-bulk-types.js.map +1 -0
- package/lib/models/user-details-types.d.ts +163 -0
- package/lib/models/user-details-types.js +9 -0
- package/lib/models/user-details-types.js.map +1 -0
- package/lib/models/year-range.d.ts +78 -0
- package/lib/models/year-range.js +153 -0
- package/lib/models/year-range.js.map +1 -0
- package/lib/operations/CompatibilityCheckOperation.d.ts +62 -0
- package/lib/operations/CompatibilityCheckOperation.js +102 -0
- package/lib/operations/CompatibilityCheckOperation.js.map +1 -0
- package/lib/operations/DefinitionCreateOperation.d.ts +411 -0
- package/lib/operations/DefinitionCreateOperation.js +1121 -0
- package/lib/operations/DefinitionCreateOperation.js.map +1 -0
- package/lib/operations/DefinitionExportOperation.d.ts +155 -0
- package/lib/operations/DefinitionExportOperation.js +281 -0
- package/lib/operations/DefinitionExportOperation.js.map +1 -0
- package/lib/operations/DefinitionImportOperation.d.ts +144 -0
- package/lib/operations/DefinitionImportOperation.js +357 -0
- package/lib/operations/DefinitionImportOperation.js.map +1 -0
- package/lib/operations/DefinitionListOperation.d.ts +66 -0
- package/lib/operations/DefinitionListOperation.js +108 -0
- package/lib/operations/DefinitionListOperation.js.map +1 -0
- package/lib/operations/DefinitionPurgeOperation.d.ts +199 -0
- package/lib/operations/DefinitionPurgeOperation.js +465 -0
- package/lib/operations/DefinitionPurgeOperation.js.map +1 -0
- package/lib/operations/DefinitionUpdateOperation.d.ts +78 -0
- package/lib/operations/DefinitionUpdateOperation.js +142 -0
- package/lib/operations/DefinitionUpdateOperation.js.map +1 -0
- package/lib/operations/OrgDetailsOperation.d.ts +253 -0
- package/lib/operations/OrgDetailsOperation.js +456 -0
- package/lib/operations/OrgDetailsOperation.js.map +1 -0
- package/lib/operations/OrgResetOperation.d.ts +114 -0
- package/lib/operations/OrgResetOperation.js +209 -0
- package/lib/operations/OrgResetOperation.js.map +1 -0
- package/lib/operations/ProfileOperation.d.ts +187 -0
- package/lib/operations/ProfileOperation.js +373 -0
- package/lib/operations/ProfileOperation.js.map +1 -0
- package/lib/operations/ProfileRequestCancelOperation.d.ts +59 -0
- package/lib/operations/ProfileRequestCancelOperation.js +137 -0
- package/lib/operations/ProfileRequestCancelOperation.js.map +1 -0
- package/lib/operations/ProfileRequestDeleteOperation.d.ts +64 -0
- package/lib/operations/ProfileRequestDeleteOperation.js +134 -0
- package/lib/operations/ProfileRequestDeleteOperation.js.map +1 -0
- package/lib/operations/ProfileRequestListOperation.d.ts +39 -0
- package/lib/operations/ProfileRequestListOperation.js +61 -0
- package/lib/operations/ProfileRequestListOperation.js.map +1 -0
- package/lib/operations/SummaryPurgeOperation.d.ts +134 -0
- package/lib/operations/SummaryPurgeOperation.js +257 -0
- package/lib/operations/SummaryPurgeOperation.js.map +1 -0
- package/lib/operations/SummaryReprofileOperation.d.ts +88 -0
- package/lib/operations/SummaryReprofileOperation.js +174 -0
- package/lib/operations/SummaryReprofileOperation.js.map +1 -0
- package/lib/operations/SummaryStopOperation.d.ts +87 -0
- package/lib/operations/SummaryStopOperation.js +175 -0
- package/lib/operations/SummaryStopOperation.js.map +1 -0
- package/lib/services/BulkExecutionService.d.ts +120 -0
- package/lib/services/BulkExecutionService.js +535 -0
- package/lib/services/BulkExecutionService.js.map +1 -0
- package/lib/services/CompatibilityService.d.ts +81 -0
- package/lib/services/CompatibilityService.js +118 -0
- package/lib/services/CompatibilityService.js.map +1 -0
- package/lib/services/ConfigureMode.d.ts +85 -0
- package/lib/services/ConfigureMode.js +390 -0
- package/lib/services/ConfigureMode.js.map +1 -0
- package/lib/services/ContactPointService.d.ts +111 -0
- package/lib/services/ContactPointService.js +286 -0
- package/lib/services/ContactPointService.js.map +1 -0
- package/lib/services/DataAvailabilityService.d.ts +81 -0
- package/lib/services/DataAvailabilityService.js +128 -0
- package/lib/services/DataAvailabilityService.js.map +1 -0
- package/lib/services/DefinitionFieldGenerationService.d.ts +309 -0
- package/lib/services/DefinitionFieldGenerationService.js +795 -0
- package/lib/services/DefinitionFieldGenerationService.js.map +1 -0
- package/lib/services/DefinitionQueryBuilder.d.ts +59 -0
- package/lib/services/DefinitionQueryBuilder.js +234 -0
- package/lib/services/DefinitionQueryBuilder.js.map +1 -0
- package/lib/services/ObjectDescribeService.d.ts +436 -0
- package/lib/services/ObjectDescribeService.js +869 -0
- package/lib/services/ObjectDescribeService.js.map +1 -0
- package/lib/services/ObjectFilteringService.d.ts +400 -0
- package/lib/services/ObjectFilteringService.js +878 -0
- package/lib/services/ObjectFilteringService.js.map +1 -0
- package/lib/services/ObjectListCommandService.d.ts +429 -0
- package/lib/services/ObjectListCommandService.js +873 -0
- package/lib/services/ObjectListCommandService.js.map +1 -0
- package/lib/services/ObjectListService.d.ts +201 -0
- package/lib/services/ObjectListService.js +345 -0
- package/lib/services/ObjectListService.js.map +1 -0
- package/lib/services/OrgInfoService.d.ts +485 -0
- package/lib/services/OrgInfoService.js +1122 -0
- package/lib/services/OrgInfoService.js.map +1 -0
- package/lib/services/PollingService.d.ts +105 -0
- package/lib/services/PollingService.js +117 -0
- package/lib/services/PollingService.js.map +1 -0
- package/lib/services/ProfileRequestService.d.ts +186 -0
- package/lib/services/ProfileRequestService.js +555 -0
- package/lib/services/ProfileRequestService.js.map +1 -0
- package/lib/services/ProfilingDefinitionService.d.ts +535 -0
- package/lib/services/ProfilingDefinitionService.js +981 -0
- package/lib/services/ProfilingDefinitionService.js.map +1 -0
- package/lib/services/ProfilingExecutionService.d.ts +122 -0
- package/lib/services/ProfilingExecutionService.js +320 -0
- package/lib/services/ProfilingExecutionService.js.map +1 -0
- package/lib/services/ProfilingSummaryService.d.ts +292 -0
- package/lib/services/ProfilingSummaryService.js +685 -0
- package/lib/services/ProfilingSummaryService.js.map +1 -0
- package/lib/services/RecordTypeService.d.ts +129 -0
- package/lib/services/RecordTypeService.js +284 -0
- package/lib/services/RecordTypeService.js.map +1 -0
- package/lib/services/SFDMUService.d.ts +133 -0
- package/lib/services/SFDMUService.js +295 -0
- package/lib/services/SFDMUService.js.map +1 -0
- package/lib/services/TabDetectionService.d.ts +105 -0
- package/lib/services/TabDetectionService.js +206 -0
- package/lib/services/TabDetectionService.js.map +1 -0
- package/lib/services/UnconfigureMode.d.ts +74 -0
- package/lib/services/UnconfigureMode.js +378 -0
- package/lib/services/UnconfigureMode.js.map +1 -0
- package/lib/services/UserConfigurationService.d.ts +155 -0
- package/lib/services/UserConfigurationService.js +573 -0
- package/lib/services/UserConfigurationService.js.map +1 -0
- package/lib/services/UserConfigurationTypes.d.ts +181 -0
- package/lib/services/UserConfigurationTypes.js +14 -0
- package/lib/services/UserConfigurationTypes.js.map +1 -0
- package/lib/services/UserReadinessService.d.ts +330 -0
- package/lib/services/UserReadinessService.js +831 -0
- package/lib/services/UserReadinessService.js.map +1 -0
- package/lib/services/constants.d.ts +53 -0
- package/lib/services/constants.js +71 -0
- package/lib/services/constants.js.map +1 -0
- package/lib/services/namespace-constants.d.ts +1 -0
- package/lib/services/namespace-constants.js +11 -0
- package/lib/services/namespace-constants.js.map +1 -0
- package/lib/services/validation.d.ts +47 -0
- package/lib/services/validation.js +119 -0
- package/lib/services/validation.js.map +1 -0
- package/lib/utils/batch-processor.d.ts +13 -0
- package/lib/utils/batch-processor.js +39 -0
- package/lib/utils/batch-processor.js.map +1 -0
- package/lib/utils/formatting/availability-grid.d.ts +81 -0
- package/lib/utils/formatting/availability-grid.js +94 -0
- package/lib/utils/formatting/availability-grid.js.map +1 -0
- package/lib/utils/formatting/business-process-grid.d.ts +51 -0
- package/lib/utils/formatting/business-process-grid.js +58 -0
- package/lib/utils/formatting/business-process-grid.js.map +1 -0
- package/lib/utils/formatting/command-display.d.ts +154 -0
- package/lib/utils/formatting/command-display.js +154 -0
- package/lib/utils/formatting/command-display.js.map +1 -0
- package/lib/utils/formatting/definition-create-display.d.ts +118 -0
- package/lib/utils/formatting/definition-create-display.js +231 -0
- package/lib/utils/formatting/definition-create-display.js.map +1 -0
- package/lib/utils/formatting/empty-states.d.ts +35 -0
- package/lib/utils/formatting/empty-states.js +70 -0
- package/lib/utils/formatting/empty-states.js.map +1 -0
- package/lib/utils/formatting/errors.d.ts +33 -0
- package/lib/utils/formatting/errors.js +72 -0
- package/lib/utils/formatting/errors.js.map +1 -0
- package/lib/utils/formatting/field-types.d.ts +32 -0
- package/lib/utils/formatting/field-types.js +88 -0
- package/lib/utils/formatting/field-types.js.map +1 -0
- package/lib/utils/formatting/index.d.ts +29 -0
- package/lib/utils/formatting/index.js +28 -0
- package/lib/utils/formatting/index.js.map +1 -0
- package/lib/utils/formatting/indicators.d.ts +113 -0
- package/lib/utils/formatting/indicators.js +161 -0
- package/lib/utils/formatting/indicators.js.map +1 -0
- package/lib/utils/formatting/loading-messages.d.ts +37 -0
- package/lib/utils/formatting/loading-messages.js +50 -0
- package/lib/utils/formatting/loading-messages.js.map +1 -0
- package/lib/utils/formatting/namespace-display.d.ts +31 -0
- package/lib/utils/formatting/namespace-display.js +64 -0
- package/lib/utils/formatting/namespace-display.js.map +1 -0
- package/lib/utils/formatting/numbers.d.ts +73 -0
- package/lib/utils/formatting/numbers.js +187 -0
- package/lib/utils/formatting/numbers.js.map +1 -0
- package/lib/utils/formatting/object-describe-display.d.ts +114 -0
- package/lib/utils/formatting/object-describe-display.js +440 -0
- package/lib/utils/formatting/object-describe-display.js.map +1 -0
- package/lib/utils/formatting/object-list-display.d.ts +213 -0
- package/lib/utils/formatting/object-list-display.js +672 -0
- package/lib/utils/formatting/object-list-display.js.map +1 -0
- package/lib/utils/formatting/org-identity.d.ts +15 -0
- package/lib/utils/formatting/org-identity.js +28 -0
- package/lib/utils/formatting/org-identity.js.map +1 -0
- package/lib/utils/formatting/record-age-grid.d.ts +41 -0
- package/lib/utils/formatting/record-age-grid.js +56 -0
- package/lib/utils/formatting/record-age-grid.js.map +1 -0
- package/lib/utils/formatting/sections.d.ts +108 -0
- package/lib/utils/formatting/sections.js +150 -0
- package/lib/utils/formatting/sections.js.map +1 -0
- package/lib/utils/formatting/tables.d.ts +90 -0
- package/lib/utils/formatting/tables.js +113 -0
- package/lib/utils/formatting/tables.js.map +1 -0
- package/lib/utils/formatting/user-details-display.d.ts +101 -0
- package/lib/utils/formatting/user-details-display.js +425 -0
- package/lib/utils/formatting/user-details-display.js.map +1 -0
- package/lib/utils/pagination/index.d.ts +11 -0
- package/lib/utils/pagination/index.js +18 -0
- package/lib/utils/pagination/index.js.map +1 -0
- package/lib/utils/pagination/keypress-reader.d.ts +20 -0
- package/lib/utils/pagination/keypress-reader.js +63 -0
- package/lib/utils/pagination/keypress-reader.js.map +1 -0
- package/lib/utils/pagination/paginate-output.d.ts +48 -0
- package/lib/utils/pagination/paginate-output.js +136 -0
- package/lib/utils/pagination/paginate-output.js.map +1 -0
- package/messages/compatibility.check.md +71 -0
- package/messages/cuneiform.access.md +138 -0
- package/messages/definition.create.md +511 -0
- package/messages/definition.export.md +84 -0
- package/messages/definition.get.md +147 -0
- package/messages/definition.import.md +65 -0
- package/messages/definition.list.md +264 -0
- package/messages/definition.purge.md +318 -0
- package/messages/definition.update.md +118 -0
- package/messages/mcp.serve.md +66 -0
- package/messages/object.describe.md +201 -0
- package/messages/object.list.md +443 -0
- package/messages/org.details.md +386 -0
- package/messages/org.reset.md +71 -0
- package/messages/profile.md +231 -0
- package/messages/profile.request.cancel.md +143 -0
- package/messages/profile.request.delete.md +139 -0
- package/messages/profile.request.list.md +89 -0
- package/messages/summary.purge.md +218 -0
- package/messages/summary.reprofile.md +150 -0
- package/messages/summary.stop.md +157 -0
- package/messages/user.details.md +501 -0
- package/oclif.lock +2887 -2149
- package/oclif.manifest.json +2813 -31
- package/package.json +94 -19
- package/lib/commands/cuneiform/about.d.ts +0 -13
- package/lib/commands/cuneiform/about.js +0 -26
- package/lib/commands/cuneiform/about.js.map +0 -1
- package/lib/commands/hello/world.d.ts +0 -14
- package/lib/commands/hello/world.js +0 -27
- package/lib/commands/hello/world.js.map +0 -1
- package/lib/index.d.ts +0 -2
- package/lib/index.js +0 -2
- package/lib/index.js.map +0 -1
- package/messages/cuneiform.about.md +0 -19
- package/messages/hello.world.md +0 -29
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, PeerNova, Inc. All Rights Reserved.
|
|
3
|
+
* PROPRIETARY AND CONFIDENTIAL. Unauthorized copying, modification,
|
|
4
|
+
* or distribution is strictly prohibited. Use is governed by the
|
|
5
|
+
* Master Subscription Agreement (MSA) between PeerNova, Inc. and the
|
|
6
|
+
* licensee. See LICENSE file in the repo root.
|
|
7
|
+
*/
|
|
8
|
+
import { CuneiformQueryBuilder } from '../adapters/soql/cuneiform-query-builder.js';
|
|
9
|
+
import { createSuccessResult, createFailureResult } from '../models/service-result.js';
|
|
10
|
+
import { ServiceErrorCodes } from '../adapters/errors.js';
|
|
11
|
+
import { CUNEIFORM_NAMESPACE } from './namespace-constants.js';
|
|
12
|
+
import { validateRequiredString } from './validation.js';
|
|
13
|
+
/**
|
|
14
|
+
* Permission set names required for Cuneiform operations.
|
|
15
|
+
*/
|
|
16
|
+
export const REQUIRED_PERMISSION_SETS = [
|
|
17
|
+
'Cuneiform_for_CRM_PRO_Administrative_User',
|
|
18
|
+
'Cuneiform_for_CRM_Global_Profiling_Support',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Maps required permission set API names to their human-readable labels.
|
|
22
|
+
* Used for user-facing display across both details and configure commands.
|
|
23
|
+
*/
|
|
24
|
+
export const REQUIRED_PERMISSION_SET_LABELS = {
|
|
25
|
+
[REQUIRED_PERMISSION_SETS[0]]: 'Cuneiform for CRM PRO Administrative User',
|
|
26
|
+
[REQUIRED_PERMISSION_SETS[1]]: 'Cuneiform for CRM Global Profiling Support',
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Org types that allow configure operations.
|
|
30
|
+
* Production is the only blocked type.
|
|
31
|
+
*/
|
|
32
|
+
export const CONFIGURE_ALLOWED_ORG_TYPES = ['Developer', 'Sandbox', 'Scratch', 'Trial'];
|
|
33
|
+
/**
|
|
34
|
+
* Service for validating user readiness to use Cuneiform.
|
|
35
|
+
*
|
|
36
|
+
* Checks whether a user has the required permission sets and configuration
|
|
37
|
+
* profile to execute Cuneiform operations. Also detects if Cuneiform is
|
|
38
|
+
* installed in the org.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const service = new UserReadinessService({ soqlAdapter });
|
|
43
|
+
*
|
|
44
|
+
* const result = await service.checkReadiness(userId);
|
|
45
|
+
* if (result.success) {
|
|
46
|
+
* if (result.data.isReady) {
|
|
47
|
+
* console.log('User is ready to use Cuneiform');
|
|
48
|
+
* } else {
|
|
49
|
+
* console.log('Issues:', result.data.messages.join(', '));
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export class UserReadinessService {
|
|
55
|
+
soqlAdapter;
|
|
56
|
+
logger;
|
|
57
|
+
restClient;
|
|
58
|
+
constructor(config) {
|
|
59
|
+
this.soqlAdapter = config.soqlAdapter;
|
|
60
|
+
this.logger = config.logger;
|
|
61
|
+
this.restClient = config.restClient;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Checks whether configure operations are allowed in the given org type.
|
|
65
|
+
*
|
|
66
|
+
* Only Production orgs are blocked. Developer, Sandbox, Scratch, and Trial
|
|
67
|
+
* orgs are all allowed to run configure operations.
|
|
68
|
+
*
|
|
69
|
+
* @param orgType - The classified org type
|
|
70
|
+
* @returns true if configure is allowed, false for Production
|
|
71
|
+
*/
|
|
72
|
+
static isConfigureAllowed(orgType) {
|
|
73
|
+
return CONFIGURE_ALLOWED_ORG_TYPES.includes(orgType);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Creates an empty UserReadiness result for error cases.
|
|
77
|
+
*/
|
|
78
|
+
static createEmptyReadiness(userId) {
|
|
79
|
+
return {
|
|
80
|
+
userId,
|
|
81
|
+
isReady: false,
|
|
82
|
+
isCuneiformInstalled: false,
|
|
83
|
+
permissionSets: {
|
|
84
|
+
hasAllRequired: false,
|
|
85
|
+
assignments: [],
|
|
86
|
+
missingRequired: [...REQUIRED_PERMISSION_SETS],
|
|
87
|
+
},
|
|
88
|
+
configProfile: { isConfigured: false },
|
|
89
|
+
messages: [],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Creates an empty UserInfo result for error cases.
|
|
94
|
+
*/
|
|
95
|
+
static createEmptyUserInfo(userId) {
|
|
96
|
+
return {
|
|
97
|
+
id: userId,
|
|
98
|
+
username: '',
|
|
99
|
+
fullName: '',
|
|
100
|
+
email: '',
|
|
101
|
+
profileName: '',
|
|
102
|
+
roleName: null,
|
|
103
|
+
isActive: false,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Checks if an error message indicates Cuneiform namespace is not found.
|
|
108
|
+
*
|
|
109
|
+
* This typically means the Cuneiform for Salesforce package is not installed in the org.
|
|
110
|
+
* The Salesforce API returns "sObject type not supported" or similar when
|
|
111
|
+
* querying namespaced objects that don't exist.
|
|
112
|
+
*/
|
|
113
|
+
static isCuneiformNamespaceNotFoundError(message) {
|
|
114
|
+
const lowerMessage = message.toLowerCase();
|
|
115
|
+
return (lowerMessage.includes('sobject type') &&
|
|
116
|
+
(lowerMessage.includes('not supported') || lowerMessage.includes('invalid')));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Maps the ISV REST API payload to the canonical UserReadiness shape.
|
|
120
|
+
*
|
|
121
|
+
* The Apex endpoint returns flat booleans and differently-named fields
|
|
122
|
+
* (e.g. `assigned` instead of `isAssigned`, `cuneiformInstalled` instead
|
|
123
|
+
* of `isCuneiformInstalled`). This mapper bridges the wire format to
|
|
124
|
+
* the TypeScript domain type so downstream code (buildResult, display)
|
|
125
|
+
* works unchanged.
|
|
126
|
+
*/
|
|
127
|
+
static mapRestPayloadToUserReadiness(userId, payload) {
|
|
128
|
+
const assignments = (payload.permissionSets ?? []).map((ps) => ({
|
|
129
|
+
name: ps.name,
|
|
130
|
+
label: ps.label,
|
|
131
|
+
isAssigned: ps.assigned,
|
|
132
|
+
isRequired: REQUIRED_PERMISSION_SETS.includes(ps.name),
|
|
133
|
+
}));
|
|
134
|
+
return {
|
|
135
|
+
userId,
|
|
136
|
+
// Normalize isReady: the ISV REST endpoint may return isReady=true when only
|
|
137
|
+
// permission sets are assigned but API-only profiling is not enabled at the org
|
|
138
|
+
// level. Override to false when apiOnlyProfilingEnabled is not explicitly true
|
|
139
|
+
// so the display layer stays consistent with validateProfilingAccess() gate 3.
|
|
140
|
+
isReady: payload.isReady,
|
|
141
|
+
isCuneiformInstalled: payload.cuneiformInstalled,
|
|
142
|
+
permissionSets: {
|
|
143
|
+
hasAllRequired: payload.allPermissionsAssigned,
|
|
144
|
+
assignments,
|
|
145
|
+
missingRequired: payload.missingPermissionSets ?? [],
|
|
146
|
+
},
|
|
147
|
+
configProfile: {
|
|
148
|
+
isConfigured: payload.hasConfigurationProfile,
|
|
149
|
+
profileLabel: payload.configurationProfileLabel,
|
|
150
|
+
profileDeveloperName: payload.configurationProfileDeveloperName,
|
|
151
|
+
apiOnlyProfilingEnabled: payload.apiOnlyProfilingEnabled,
|
|
152
|
+
selfRegistrationEnabled: payload.selfRegistrationEnabled,
|
|
153
|
+
},
|
|
154
|
+
messages: [],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Retrieves user identity information.
|
|
159
|
+
*
|
|
160
|
+
* Queries the User object with Profile and UserRole relationships to build
|
|
161
|
+
* a complete user identity record. Handles null UserRole gracefully since
|
|
162
|
+
* users may not have a role assigned.
|
|
163
|
+
*
|
|
164
|
+
* @param userId - The Salesforce user ID to query
|
|
165
|
+
* @returns ServiceResult containing UserInfo
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const result = await service.getUserInfo(userId);
|
|
170
|
+
* if (result.success) {
|
|
171
|
+
* console.log(`User: ${result.data.fullName} (${result.data.profileName})`);
|
|
172
|
+
* if (result.data.roleName) {
|
|
173
|
+
* console.log(`Role: ${result.data.roleName}`);
|
|
174
|
+
* }
|
|
175
|
+
* }
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
async getUserInfo(userId) {
|
|
179
|
+
const startTime = Date.now();
|
|
180
|
+
const emptyResult = UserReadinessService.createEmptyUserInfo(userId);
|
|
181
|
+
// Validate userId
|
|
182
|
+
const validationError = validateRequiredString(userId, 'User ID');
|
|
183
|
+
if (validationError) {
|
|
184
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_ID_REQUIRED, validationError, {
|
|
185
|
+
metadata: { duration: Date.now() - startTime },
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
this.logger?.log(`Querying user info for: ${userId}`);
|
|
190
|
+
const soql = new CuneiformQueryBuilder()
|
|
191
|
+
.select(['Id', 'Username', 'Name', 'Email', 'Profile.Name', 'UserRole.Name', 'IsActive'])
|
|
192
|
+
.from('User')
|
|
193
|
+
.where('Id', '=', userId)
|
|
194
|
+
.limit(1)
|
|
195
|
+
.toSOQL();
|
|
196
|
+
const result = await this.soqlAdapter.query(soql);
|
|
197
|
+
if (!result.success) {
|
|
198
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_INFO_QUERY_FAILED, result.message ?? 'User query failed', {
|
|
199
|
+
metadata: { duration: Date.now() - startTime },
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (result.data.records.length === 0) {
|
|
203
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_INFO_QUERY_FAILED, `User not found: ${userId}`, {
|
|
204
|
+
metadata: { duration: Date.now() - startTime },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const record = result.data.records[0];
|
|
208
|
+
const userInfo = {
|
|
209
|
+
id: record.Id,
|
|
210
|
+
username: record.Username,
|
|
211
|
+
fullName: record.Name,
|
|
212
|
+
email: record.Email,
|
|
213
|
+
profileName: record.Profile.Name,
|
|
214
|
+
roleName: record.UserRole?.Name ?? null,
|
|
215
|
+
isActive: record.IsActive,
|
|
216
|
+
};
|
|
217
|
+
const duration = Date.now() - startTime;
|
|
218
|
+
this.logger?.log(`User info retrieved: ${userInfo.fullName}`);
|
|
219
|
+
return createSuccessResult(userInfo, {
|
|
220
|
+
message: `User: ${userInfo.fullName}`,
|
|
221
|
+
metadata: { duration },
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
226
|
+
this.logger?.log(`User info query failed: ${errorMessage}`);
|
|
227
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_INFO_QUERY_FAILED, errorMessage, {
|
|
228
|
+
metadata: { duration: Date.now() - startTime },
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Checks complete user readiness for Cuneiform operations.
|
|
234
|
+
*
|
|
235
|
+
* Validates:
|
|
236
|
+
* 1. Cuneiform is installed (by querying CMT)
|
|
237
|
+
* 2. User has required permission sets
|
|
238
|
+
* 3. Configuration profile is set up
|
|
239
|
+
*
|
|
240
|
+
* @param userId - The Salesforce user ID to check
|
|
241
|
+
* @returns ServiceResult containing UserReadiness
|
|
242
|
+
*/
|
|
243
|
+
async checkReadiness(userId) {
|
|
244
|
+
const startTime = Date.now();
|
|
245
|
+
// Validate userId
|
|
246
|
+
const validationError = validateRequiredString(userId, 'User ID');
|
|
247
|
+
if (validationError) {
|
|
248
|
+
const emptyResult = UserReadinessService.createEmptyReadiness(userId);
|
|
249
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_ID_REQUIRED, validationError, {
|
|
250
|
+
metadata: { duration: Date.now() - startTime },
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// REST API path: delegate to server when restClient is available.
|
|
254
|
+
// Falls back to local SOQL if REST returns unexpected results.
|
|
255
|
+
const restFallbackResult = await this.tryRestReadiness(userId, startTime);
|
|
256
|
+
if (restFallbackResult) {
|
|
257
|
+
return restFallbackResult;
|
|
258
|
+
}
|
|
259
|
+
const messages = [];
|
|
260
|
+
try {
|
|
261
|
+
this.logger?.log(`Checking readiness for user: ${userId}`);
|
|
262
|
+
// Check permission sets and config profile in parallel
|
|
263
|
+
const [permissionResult, configResult] = await Promise.all([
|
|
264
|
+
this.hasRequiredPermissionSets(userId),
|
|
265
|
+
this.hasConfigProfile(),
|
|
266
|
+
]);
|
|
267
|
+
// Determine if Cuneiform is installed
|
|
268
|
+
let isCuneiformInstalled = true;
|
|
269
|
+
if (!configResult.success && configResult.errorCode === ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND) {
|
|
270
|
+
isCuneiformInstalled = false;
|
|
271
|
+
messages.push('Cuneiform for Salesforce is not installed in this org');
|
|
272
|
+
}
|
|
273
|
+
// Build permission set status
|
|
274
|
+
let permissionSets;
|
|
275
|
+
if (!permissionResult.success) {
|
|
276
|
+
permissionSets = {
|
|
277
|
+
hasAllRequired: false,
|
|
278
|
+
assignments: [],
|
|
279
|
+
missingRequired: [...REQUIRED_PERMISSION_SETS],
|
|
280
|
+
};
|
|
281
|
+
messages.push(`Permission check failed: ${permissionResult.message ?? 'Unknown error'}`);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
permissionSets = permissionResult.data;
|
|
285
|
+
if (!permissionSets.hasAllRequired) {
|
|
286
|
+
messages.push(`Missing permission sets: ${permissionSets.missingRequired.join(', ')}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Build config profile status
|
|
290
|
+
let configProfile;
|
|
291
|
+
if (!configResult.success) {
|
|
292
|
+
configProfile = { isConfigured: false };
|
|
293
|
+
if (configResult.errorCode !== ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND) {
|
|
294
|
+
messages.push(`Config profile check failed: ${configResult.message ?? 'Unknown error'}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
configProfile = configResult.data;
|
|
299
|
+
if (!configProfile.isConfigured) {
|
|
300
|
+
messages.push('No active configuration profile found');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Determine overall readiness — mirrors the gate chain in validateProfilingAccess().
|
|
304
|
+
// configProfile.isConfigured alone is not enough: the org must also have API-only
|
|
305
|
+
// profiling explicitly enabled, or the user will hit E4659 on every other command.
|
|
306
|
+
const isReady = isCuneiformInstalled &&
|
|
307
|
+
permissionSets.hasAllRequired &&
|
|
308
|
+
configProfile.isConfigured &&
|
|
309
|
+
configProfile.apiOnlyProfilingEnabled === true;
|
|
310
|
+
if (isReady) {
|
|
311
|
+
messages.push('User is ready to use Cuneiform');
|
|
312
|
+
}
|
|
313
|
+
const result = {
|
|
314
|
+
userId,
|
|
315
|
+
isReady,
|
|
316
|
+
isCuneiformInstalled,
|
|
317
|
+
permissionSets,
|
|
318
|
+
configProfile,
|
|
319
|
+
messages,
|
|
320
|
+
};
|
|
321
|
+
const duration = Date.now() - startTime;
|
|
322
|
+
this.logger?.log(`Readiness check complete: ${isReady ? 'READY' : 'NOT READY'}`);
|
|
323
|
+
return createSuccessResult(result, {
|
|
324
|
+
message: isReady ? 'User is ready' : 'User is not ready',
|
|
325
|
+
metadata: { duration },
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
330
|
+
this.logger?.log(`Readiness check failed: ${errorMessage}`);
|
|
331
|
+
const emptyResult = UserReadinessService.createEmptyReadiness(userId);
|
|
332
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_READINESS_QUERY_FAILED, errorMessage, {
|
|
333
|
+
metadata: { duration: Date.now() - startTime },
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Checks if the user has required Cuneiform permission sets.
|
|
339
|
+
*
|
|
340
|
+
* @param userId - The Salesforce user ID to check
|
|
341
|
+
* @returns ServiceResult containing PermissionSetStatus
|
|
342
|
+
*/
|
|
343
|
+
async hasRequiredPermissionSets(userId) {
|
|
344
|
+
const startTime = Date.now();
|
|
345
|
+
// Validate userId
|
|
346
|
+
const validationError = validateRequiredString(userId, 'User ID');
|
|
347
|
+
if (validationError) {
|
|
348
|
+
const emptyResult = {
|
|
349
|
+
hasAllRequired: false,
|
|
350
|
+
assignments: [],
|
|
351
|
+
missingRequired: [...REQUIRED_PERMISSION_SETS],
|
|
352
|
+
};
|
|
353
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_ID_REQUIRED, validationError, {
|
|
354
|
+
metadata: { duration: Date.now() - startTime },
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
this.logger?.log(`Checking permission sets for user: ${userId}`);
|
|
359
|
+
// Step 1: Check direct PermissionSetAssignment records
|
|
360
|
+
const directSoql = new CuneiformQueryBuilder()
|
|
361
|
+
.select([
|
|
362
|
+
'Id',
|
|
363
|
+
'PermissionSet.Id',
|
|
364
|
+
'PermissionSet.Name',
|
|
365
|
+
'PermissionSet.Label',
|
|
366
|
+
'PermissionSet.NamespacePrefix',
|
|
367
|
+
])
|
|
368
|
+
.from('PermissionSetAssignment')
|
|
369
|
+
.where('AssigneeId', '=', userId)
|
|
370
|
+
.andWhereIn('PermissionSet.Name', [...REQUIRED_PERMISSION_SETS])
|
|
371
|
+
.andWhere('PermissionSet.NamespacePrefix', '=', CUNEIFORM_NAMESPACE)
|
|
372
|
+
.toSOQL();
|
|
373
|
+
const directResult = await this.soqlAdapter.query(directSoql);
|
|
374
|
+
if (!directResult.success) {
|
|
375
|
+
const emptyResult = {
|
|
376
|
+
hasAllRequired: false,
|
|
377
|
+
assignments: [],
|
|
378
|
+
missingRequired: [...REQUIRED_PERMISSION_SETS],
|
|
379
|
+
};
|
|
380
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, directResult.message ?? 'Permission set query failed', {
|
|
381
|
+
metadata: { duration: Date.now() - startTime },
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
// Build map of assigned permission set names to their labels
|
|
385
|
+
const assignedLabels = new Map(directResult.data.records.map((r) => [r.PermissionSet.Name, r.PermissionSet.Label]));
|
|
386
|
+
// Step 2: If any required permission sets are still missing, check group-based access
|
|
387
|
+
const directMissing = REQUIRED_PERMISSION_SETS.filter((name) => !assignedLabels.has(name));
|
|
388
|
+
if (directMissing.length > 0) {
|
|
389
|
+
const groupLabels = await this.getGroupAssignedPermissionSets(userId, directMissing);
|
|
390
|
+
for (const [name, label] of groupLabels) {
|
|
391
|
+
assignedLabels.set(name, label);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Build final status from combined direct + group results
|
|
395
|
+
const assignments = REQUIRED_PERMISSION_SETS.map((name) => ({
|
|
396
|
+
name,
|
|
397
|
+
label: assignedLabels.get(name),
|
|
398
|
+
isAssigned: assignedLabels.has(name),
|
|
399
|
+
isRequired: true,
|
|
400
|
+
}));
|
|
401
|
+
const missingRequired = REQUIRED_PERMISSION_SETS.filter((name) => !assignedLabels.has(name));
|
|
402
|
+
const hasAllRequired = missingRequired.length === 0;
|
|
403
|
+
const status = {
|
|
404
|
+
hasAllRequired,
|
|
405
|
+
assignments,
|
|
406
|
+
missingRequired,
|
|
407
|
+
};
|
|
408
|
+
const duration = Date.now() - startTime;
|
|
409
|
+
this.logger?.log(`Permission check: ${hasAllRequired ? 'all assigned' : `missing ${missingRequired.length}`}`);
|
|
410
|
+
return createSuccessResult(status, {
|
|
411
|
+
message: hasAllRequired
|
|
412
|
+
? 'All required permission sets assigned'
|
|
413
|
+
: `Missing ${missingRequired.length} permission sets`,
|
|
414
|
+
metadata: { duration },
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
419
|
+
this.logger?.log(`Permission set check failed: ${errorMessage}`);
|
|
420
|
+
const emptyResult = {
|
|
421
|
+
hasAllRequired: false,
|
|
422
|
+
assignments: [],
|
|
423
|
+
missingRequired: [...REQUIRED_PERMISSION_SETS],
|
|
424
|
+
};
|
|
425
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, errorMessage, {
|
|
426
|
+
metadata: { duration: Date.now() - startTime },
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Retrieves the ID of a permission set by name and namespace.
|
|
432
|
+
*
|
|
433
|
+
* Queries the PermissionSet object directly to find a permission set by
|
|
434
|
+
* its Name and NamespacePrefix. This method returns the permission set ID
|
|
435
|
+
* for use in permission set assignment operations.
|
|
436
|
+
*
|
|
437
|
+
* @param permissionSetName - The permission set API name (e.g., 'Cuneiform_for_CRM_Global_Profiling_Support')
|
|
438
|
+
* @param namespacePrefix - The namespace prefix (e.g., 'pnova')
|
|
439
|
+
* @returns ServiceResult containing the permission set ID or null if not found
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```typescript
|
|
443
|
+
* const result = await service.getPermissionSetId(
|
|
444
|
+
* 'Cuneiform_for_CRM_Global_Profiling_Support',
|
|
445
|
+
* 'pnova'
|
|
446
|
+
* );
|
|
447
|
+
* if (result.success && result.data) {
|
|
448
|
+
* console.log(`Permission Set ID: ${result.data}`);
|
|
449
|
+
* } else if (result.success && result.data === null) {
|
|
450
|
+
* console.log('Permission set not found in org');
|
|
451
|
+
* }
|
|
452
|
+
* ```
|
|
453
|
+
*/
|
|
454
|
+
async getPermissionSetId(permissionSetName, namespacePrefix) {
|
|
455
|
+
const startTime = Date.now();
|
|
456
|
+
const emptyResult = null;
|
|
457
|
+
// Validate permissionSetName
|
|
458
|
+
const nameError = validateRequiredString(permissionSetName, 'Permission set name');
|
|
459
|
+
if (nameError) {
|
|
460
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, nameError, {
|
|
461
|
+
metadata: { duration: Date.now() - startTime },
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
// Validate namespacePrefix
|
|
465
|
+
const namespaceError = validateRequiredString(namespacePrefix, 'Namespace prefix');
|
|
466
|
+
if (namespaceError) {
|
|
467
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, namespaceError, {
|
|
468
|
+
metadata: { duration: Date.now() - startTime },
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
this.logger?.log(`Querying permission set: ${namespacePrefix}__${permissionSetName}`);
|
|
473
|
+
const soql = new CuneiformQueryBuilder()
|
|
474
|
+
.select(['Id'])
|
|
475
|
+
.from('PermissionSet')
|
|
476
|
+
.where('Name', '=', permissionSetName)
|
|
477
|
+
.andWhere('NamespacePrefix', '=', namespacePrefix)
|
|
478
|
+
.limit(1)
|
|
479
|
+
.toSOQL();
|
|
480
|
+
const result = await this.soqlAdapter.query(soql);
|
|
481
|
+
if (!result.success) {
|
|
482
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, result.message ?? 'Permission set query failed', {
|
|
483
|
+
metadata: { duration: Date.now() - startTime },
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
// Permission set not found - return null (not an error)
|
|
487
|
+
if (result.data.records.length === 0) {
|
|
488
|
+
const duration = Date.now() - startTime;
|
|
489
|
+
this.logger?.log(`Permission set not found: ${namespacePrefix}__${permissionSetName}`);
|
|
490
|
+
return createSuccessResult(null, {
|
|
491
|
+
message: `Permission set not found: ${namespacePrefix}__${permissionSetName}`,
|
|
492
|
+
warnings: [`Permission set '${permissionSetName}' not found in namespace '${namespacePrefix}'`],
|
|
493
|
+
metadata: { duration },
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
const permissionSetId = result.data.records[0].Id;
|
|
497
|
+
const duration = Date.now() - startTime;
|
|
498
|
+
this.logger?.log(`Permission set found: ${permissionSetId}`);
|
|
499
|
+
return createSuccessResult(permissionSetId, {
|
|
500
|
+
message: `Permission set found: ${permissionSetId}`,
|
|
501
|
+
metadata: { duration },
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
506
|
+
this.logger?.log(`Permission set query failed: ${errorMessage}`);
|
|
507
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, errorMessage, {
|
|
508
|
+
metadata: { duration: Date.now() - startTime },
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Checks if a configuration profile is configured.
|
|
514
|
+
*
|
|
515
|
+
* Queries the Cuneiform Active Configuration CMT to determine if a profile
|
|
516
|
+
* is set up. If the CMT doesn't exist, returns E4654 indicating Cuneiform
|
|
517
|
+
* is not installed.
|
|
518
|
+
*
|
|
519
|
+
* @returns ServiceResult containing ConfigProfileStatus
|
|
520
|
+
*/
|
|
521
|
+
async hasConfigProfile() {
|
|
522
|
+
const startTime = Date.now();
|
|
523
|
+
try {
|
|
524
|
+
this.logger?.log('Checking configuration profile...');
|
|
525
|
+
const soql = new CuneiformQueryBuilder()
|
|
526
|
+
.select([
|
|
527
|
+
'Id',
|
|
528
|
+
'DeveloperName',
|
|
529
|
+
'pnova__Active_Configuration_Profile__r.DeveloperName',
|
|
530
|
+
'pnova__Active_Configuration_Profile__r.Label',
|
|
531
|
+
'pnova__Active_Configuration_Profile__r.pnova__Enable_API_Only_Profiling__c',
|
|
532
|
+
'pnova__Active_Configuration_Profile__r.pnova__Enable_API_Only_Profiling_Registration__c',
|
|
533
|
+
])
|
|
534
|
+
.from('pnova__Active_Configuration__mdt')
|
|
535
|
+
.where('DeveloperName', '=', 'Active_Configuration')
|
|
536
|
+
.limit(1)
|
|
537
|
+
.toSOQL();
|
|
538
|
+
const result = await this.soqlAdapter.query(soql);
|
|
539
|
+
// Check for namespace not found error (Cuneiform not installed)
|
|
540
|
+
if (!result.success) {
|
|
541
|
+
if (UserReadinessService.isCuneiformNamespaceNotFoundError(result.message ?? '')) {
|
|
542
|
+
return createFailureResult({ isConfigured: false }, ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND, 'Cuneiform namespace not found - package may not be installed', { metadata: { duration: Date.now() - startTime } });
|
|
543
|
+
}
|
|
544
|
+
return createFailureResult({ isConfigured: false }, ServiceErrorCodes.USER_CONFIG_PROFILE_CHECK_FAILED, result.message ?? 'Config profile query failed', { metadata: { duration: Date.now() - startTime } });
|
|
545
|
+
}
|
|
546
|
+
// No config record found
|
|
547
|
+
if (result.data.records.length === 0) {
|
|
548
|
+
const duration = Date.now() - startTime;
|
|
549
|
+
return createSuccessResult({ isConfigured: false }, {
|
|
550
|
+
message: 'No active configuration found',
|
|
551
|
+
metadata: { duration },
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
const record = result.data.records[0];
|
|
555
|
+
const profile = record.pnova__Active_Configuration_Profile__r;
|
|
556
|
+
// Config record exists but no profile linked
|
|
557
|
+
if (!profile) {
|
|
558
|
+
const duration = Date.now() - startTime;
|
|
559
|
+
return createSuccessResult({ isConfigured: false }, {
|
|
560
|
+
message: 'Configuration exists but no profile is linked',
|
|
561
|
+
metadata: { duration },
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
const status = {
|
|
565
|
+
isConfigured: true,
|
|
566
|
+
profileDeveloperName: profile.DeveloperName,
|
|
567
|
+
profileLabel: profile.Label,
|
|
568
|
+
apiOnlyProfilingEnabled: profile.pnova__Enable_API_Only_Profiling__c,
|
|
569
|
+
selfRegistrationEnabled: profile.pnova__Enable_API_Only_Profiling_Registration__c,
|
|
570
|
+
};
|
|
571
|
+
const duration = Date.now() - startTime;
|
|
572
|
+
this.logger?.log(`Config profile found: ${status.profileLabel ?? 'Unknown'}`);
|
|
573
|
+
return createSuccessResult(status, {
|
|
574
|
+
message: `Active profile: ${status.profileLabel ?? 'Unknown'}`,
|
|
575
|
+
metadata: { duration },
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
580
|
+
this.logger?.log(`Config profile check failed: ${errorMessage}`);
|
|
581
|
+
// Check for namespace not found in exception
|
|
582
|
+
if (UserReadinessService.isCuneiformNamespaceNotFoundError(errorMessage)) {
|
|
583
|
+
return createFailureResult({ isConfigured: false }, ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND, 'Cuneiform namespace not found - package may not be installed', { metadata: { duration: Date.now() - startTime } });
|
|
584
|
+
}
|
|
585
|
+
return createFailureResult({ isConfigured: false }, ServiceErrorCodes.USER_CONFIG_PROFILE_CHECK_FAILED, errorMessage, { metadata: { duration: Date.now() - startTime } });
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Validates that the user has profiling access to use Cuneiform commands.
|
|
590
|
+
*
|
|
591
|
+
* Checks in order:
|
|
592
|
+
* 1. Cuneiform is installed in the org
|
|
593
|
+
* 2. Required permission sets are assigned
|
|
594
|
+
* 3. Global Profiling is enabled
|
|
595
|
+
*
|
|
596
|
+
* Returns success if all checks pass. Returns a specific error code for the
|
|
597
|
+
* first failing check, with a user-friendly message including remediation steps.
|
|
598
|
+
*
|
|
599
|
+
* @param userId - The Salesforce user ID to validate
|
|
600
|
+
* @returns ServiceResult<void> — success or failure with specific error code
|
|
601
|
+
*/
|
|
602
|
+
async validateProfilingAccess(userId) {
|
|
603
|
+
const startTime = Date.now();
|
|
604
|
+
const readinessResult = await this.checkReadiness(userId);
|
|
605
|
+
if (!readinessResult.success) {
|
|
606
|
+
return createFailureResult(undefined, readinessResult.errorCode ?? ServiceErrorCodes.USER_READINESS_QUERY_FAILED, readinessResult.message ?? 'Readiness check failed', { metadata: { duration: Date.now() - startTime } });
|
|
607
|
+
}
|
|
608
|
+
const readiness = readinessResult.data;
|
|
609
|
+
// Gate 1: Cuneiform must be installed
|
|
610
|
+
if (!readiness.isCuneiformInstalled) {
|
|
611
|
+
return createFailureResult(undefined, ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND, 'Cuneiform for Salesforce is not installed in this org.', { metadata: { duration: Date.now() - startTime } });
|
|
612
|
+
}
|
|
613
|
+
// Gate 2: Required permission sets must be assigned
|
|
614
|
+
if (!readiness.permissionSets.hasAllRequired) {
|
|
615
|
+
const missingLabels = readiness.permissionSets.missingRequired.map((name) => REQUIRED_PERMISSION_SET_LABELS[name] ?? name);
|
|
616
|
+
return createFailureResult(undefined, ServiceErrorCodes.PROFILING_ACCESS_DENIED, "Cuneiform is installed, but you don't have profiling access. " +
|
|
617
|
+
`Missing permission sets: ${missingLabels.join(', ')}. ` +
|
|
618
|
+
'Run "sf cuneiform user details --target-org <org>" for a full readiness diagnostic.', { metadata: { duration: Date.now() - startTime } });
|
|
619
|
+
}
|
|
620
|
+
// Gate 3: Global Profiling must be explicitly enabled
|
|
621
|
+
// Checks both: (a) config profile must exist, and (b) apiOnlyProfilingEnabled must be exactly true.
|
|
622
|
+
// When apiOnlyProfilingEnabled is undefined (e.g., Salesforce field returns null), access is denied.
|
|
623
|
+
if (!readiness.configProfile.isConfigured || readiness.configProfile.apiOnlyProfilingEnabled !== true) {
|
|
624
|
+
return createFailureResult(undefined, ServiceErrorCodes.GLOBAL_PROFILING_NOT_ENABLED, 'Cuneiform is installed and your permission sets are assigned, but Global Profiling is not enabled. ' +
|
|
625
|
+
'Run "sf cuneiform user details --target-org <org> --configure" to enable it, ' +
|
|
626
|
+
'or ask your Salesforce administrator to enable API-Only Profiling in the Configuration Profile.', { metadata: { duration: Date.now() - startTime } });
|
|
627
|
+
}
|
|
628
|
+
// Gate 4: Self-registration must be enabled for CLI self-provisioning
|
|
629
|
+
// This gate only applies to the configure flow (checked by callers), but validateProfilingAccess
|
|
630
|
+
// does NOT enforce it — it validates read access only. The configure flow checks this gate separately.
|
|
631
|
+
// See UserConfigurationService.buildConfigurationActions() for the enforcement point.
|
|
632
|
+
return createSuccessResult(undefined, {
|
|
633
|
+
message: 'Profiling access validated',
|
|
634
|
+
metadata: { duration: Date.now() - startTime },
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Checks the feature status via the ISV profiling status REST endpoint.
|
|
639
|
+
*
|
|
640
|
+
* Calls `GET /services/apexrest/pnova/v1/profiling/status` to determine whether
|
|
641
|
+
* API-Based Profiling is enabled for this organization. This is Gate 0 — if the
|
|
642
|
+
* feature is not enabled, all other gates are moot.
|
|
643
|
+
*
|
|
644
|
+
* Graceful degradation:
|
|
645
|
+
* - On 404 (endpoint not found): returns success with `featureEnabled: true`.
|
|
646
|
+
* Older ISV packages may not expose this endpoint; we don't block them.
|
|
647
|
+
* - On other errors: returns failure with `FEATURE_STATUS_CHECK_FAILED`.
|
|
648
|
+
*
|
|
649
|
+
* @param restAdapter - The REST API adapter for making the HTTP request
|
|
650
|
+
* @returns ServiceResult containing FeatureStatus
|
|
651
|
+
*
|
|
652
|
+
* @example
|
|
653
|
+
* ```typescript
|
|
654
|
+
* const result = await service.checkFeatureStatus(restAdapter);
|
|
655
|
+
* if (result.success && !result.data.featureEnabled) {
|
|
656
|
+
* // Block command execution — feature is disabled
|
|
657
|
+
* }
|
|
658
|
+
* ```
|
|
659
|
+
*/
|
|
660
|
+
async checkFeatureStatus(restAdapter) {
|
|
661
|
+
const startTime = Date.now();
|
|
662
|
+
const statusUrl = '/services/apexrest/pnova/v1/profiling/status';
|
|
663
|
+
try {
|
|
664
|
+
this.logger?.log('Checking feature status via REST endpoint');
|
|
665
|
+
const result = await restAdapter.request({
|
|
666
|
+
url: statusUrl,
|
|
667
|
+
method: 'GET',
|
|
668
|
+
});
|
|
669
|
+
if (result.success) {
|
|
670
|
+
const duration = Date.now() - startTime;
|
|
671
|
+
this.logger?.log(`Feature status: featureEnabled=${String(result.data.featureEnabled)}`);
|
|
672
|
+
return createSuccessResult(result.data, {
|
|
673
|
+
message: `Feature status: ${result.data.featureEnabled ? 'enabled' : 'disabled'}`,
|
|
674
|
+
metadata: { duration },
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
// Check for 404 — graceful degradation for older ISV packages
|
|
678
|
+
const errorMessage = result.message ?? '';
|
|
679
|
+
if (errorMessage.includes('404') || errorMessage.includes('Not Found') || errorMessage.includes('NOT_FOUND')) {
|
|
680
|
+
this.logger?.log('Feature status endpoint not found (404) — assuming enabled (older ISV package)');
|
|
681
|
+
const duration = Date.now() - startTime;
|
|
682
|
+
return createSuccessResult({
|
|
683
|
+
featureEnabled: true,
|
|
684
|
+
apiBasedProfilingEnabled: true,
|
|
685
|
+
selfRegistrationEnabled: false,
|
|
686
|
+
packageVersion: 'unknown',
|
|
687
|
+
}, {
|
|
688
|
+
message: 'Feature status endpoint not available — assuming enabled (older ISV package)',
|
|
689
|
+
warnings: ['Status endpoint not found; feature status assumed enabled for backward compatibility'],
|
|
690
|
+
metadata: { duration },
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
// Other errors — return failure
|
|
694
|
+
const duration = Date.now() - startTime;
|
|
695
|
+
return createFailureResult({
|
|
696
|
+
featureEnabled: false,
|
|
697
|
+
apiBasedProfilingEnabled: false,
|
|
698
|
+
selfRegistrationEnabled: false,
|
|
699
|
+
packageVersion: 'unknown',
|
|
700
|
+
}, ServiceErrorCodes.FEATURE_STATUS_CHECK_FAILED, errorMessage || 'Feature status check failed', { metadata: { duration } });
|
|
701
|
+
}
|
|
702
|
+
catch (error) {
|
|
703
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
704
|
+
this.logger?.log(`Feature status check failed: ${errorMessage}`);
|
|
705
|
+
// Check for 404 in exception messages too
|
|
706
|
+
if (errorMessage.includes('404') || errorMessage.includes('Not Found') || errorMessage.includes('NOT_FOUND')) {
|
|
707
|
+
const duration = Date.now() - startTime;
|
|
708
|
+
return createSuccessResult({
|
|
709
|
+
featureEnabled: true,
|
|
710
|
+
apiBasedProfilingEnabled: true,
|
|
711
|
+
selfRegistrationEnabled: false,
|
|
712
|
+
packageVersion: 'unknown',
|
|
713
|
+
}, {
|
|
714
|
+
message: 'Feature status endpoint not available — assuming enabled (older ISV package)',
|
|
715
|
+
warnings: ['Status endpoint not found; feature status assumed enabled for backward compatibility'],
|
|
716
|
+
metadata: { duration },
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
return createFailureResult({
|
|
720
|
+
featureEnabled: false,
|
|
721
|
+
apiBasedProfilingEnabled: false,
|
|
722
|
+
selfRegistrationEnabled: false,
|
|
723
|
+
packageVersion: 'unknown',
|
|
724
|
+
}, ServiceErrorCodes.FEATURE_STATUS_CHECK_FAILED, errorMessage, { metadata: { duration: Date.now() - startTime } });
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Attempts REST delegation for user readiness.
|
|
729
|
+
* Returns the successful result, or null to signal fallback to the local SOQL path.
|
|
730
|
+
* Falls back when REST returns cuneiformInstalled=false (Apex endpoint bug on some orgs).
|
|
731
|
+
*/
|
|
732
|
+
async tryRestReadiness(userId, startTime) {
|
|
733
|
+
if (!this.restClient) {
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
const restResult = await this.checkReadinessViaRest(userId, startTime);
|
|
737
|
+
if (restResult.success && restResult.data.isCuneiformInstalled) {
|
|
738
|
+
return restResult;
|
|
739
|
+
}
|
|
740
|
+
this.logger?.log('REST readiness returned not-installed or failed — falling back to local SOQL');
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Delegates user readiness check to the ISV REST API.
|
|
745
|
+
*
|
|
746
|
+
* @param userId - The Salesforce user ID to check
|
|
747
|
+
* @param startTime - Timestamp for duration tracking
|
|
748
|
+
* @returns ServiceResult containing UserReadiness from REST endpoint
|
|
749
|
+
*/
|
|
750
|
+
async checkReadinessViaRest(userId, startTime) {
|
|
751
|
+
this.logger?.log('Delegating user readiness to ISV REST API (user-readiness)');
|
|
752
|
+
const emptyResult = UserReadinessService.createEmptyReadiness(userId);
|
|
753
|
+
const restResult = await this.restClient.checkUserReadiness(userId);
|
|
754
|
+
if (!restResult.success) {
|
|
755
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_READINESS_QUERY_FAILED, restResult.message ?? 'REST user-readiness failed', {
|
|
756
|
+
metadata: { duration: Date.now() - startTime },
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
const response = restResult.data;
|
|
760
|
+
if (!response.success) {
|
|
761
|
+
const errorMsg = response.errors?.length > 0 ? response.errors[0] : 'Server-side readiness check failed';
|
|
762
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.USER_READINESS_QUERY_FAILED, errorMsg, {
|
|
763
|
+
metadata: { duration: Date.now() - startTime },
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
const mappedResult = response.result
|
|
767
|
+
? UserReadinessService.mapRestPayloadToUserReadiness(userId, response.result)
|
|
768
|
+
: emptyResult;
|
|
769
|
+
return createSuccessResult(mappedResult, {
|
|
770
|
+
message: 'User readiness checked via REST API',
|
|
771
|
+
metadata: { duration: Date.now() - startTime, strategyUsed: 'rest-api' },
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Checks for required permission sets accessible through PermissionSetGroup membership.
|
|
776
|
+
*
|
|
777
|
+
* Two-step query approach for precision:
|
|
778
|
+
* 1. Get the specific PermissionSetGroup IDs assigned to this user
|
|
779
|
+
* 2. Check if those specific groups contain any of the missing required permission sets
|
|
780
|
+
*
|
|
781
|
+
* Both queries are tightly filtered to avoid pulling excess records:
|
|
782
|
+
* - Group query: filtered by AssigneeId AND PermissionSetGroupId != null
|
|
783
|
+
* - Component query: filtered by specific group IDs, permission set names, AND namespace
|
|
784
|
+
*
|
|
785
|
+
* @param userId - The Salesforce user ID
|
|
786
|
+
* @param missingNames - Permission set names not found via direct assignment
|
|
787
|
+
* @returns Map of permission set names to labels found through group membership
|
|
788
|
+
*/
|
|
789
|
+
async getGroupAssignedPermissionSets(userId, missingNames) {
|
|
790
|
+
const foundLabels = new Map();
|
|
791
|
+
try {
|
|
792
|
+
// Step 2a: Get the user's PermissionSetGroup assignments
|
|
793
|
+
// Precise filter: only this user, only rows that ARE group assignments
|
|
794
|
+
const groupSoql = new CuneiformQueryBuilder()
|
|
795
|
+
.select(['PermissionSetGroupId'])
|
|
796
|
+
.from('PermissionSetAssignment')
|
|
797
|
+
.where('AssigneeId', '=', userId)
|
|
798
|
+
.andWhereNull('PermissionSetGroupId', false)
|
|
799
|
+
.toSOQL();
|
|
800
|
+
const groupResult = await this.soqlAdapter.query(groupSoql);
|
|
801
|
+
if (!groupResult.success || groupResult.data.records.length === 0) {
|
|
802
|
+
return foundLabels;
|
|
803
|
+
}
|
|
804
|
+
const groupIds = groupResult.data.records.map((r) => r.PermissionSetGroupId);
|
|
805
|
+
this.logger?.log(`User belongs to ${groupIds.length} permission set group(s)`);
|
|
806
|
+
// Step 2b: Check if those groups contain any of the missing required permission sets
|
|
807
|
+
// Precise filter: only the user's groups, only the missing names, only our namespace
|
|
808
|
+
const componentSoql = new CuneiformQueryBuilder()
|
|
809
|
+
.select(['PermissionSet.Name', 'PermissionSet.Label'])
|
|
810
|
+
.from('PermissionSetGroupComponent')
|
|
811
|
+
.where('PermissionSet.NamespacePrefix', '=', CUNEIFORM_NAMESPACE)
|
|
812
|
+
.andWhereIn('PermissionSetGroupId', groupIds)
|
|
813
|
+
.andWhereIn('PermissionSet.Name', [...missingNames])
|
|
814
|
+
.toSOQL();
|
|
815
|
+
const componentResult = await this.soqlAdapter.query(componentSoql);
|
|
816
|
+
if (!componentResult.success) {
|
|
817
|
+
return foundLabels;
|
|
818
|
+
}
|
|
819
|
+
for (const record of componentResult.data.records) {
|
|
820
|
+
foundLabels.set(record.PermissionSet.Name, record.PermissionSet.Label);
|
|
821
|
+
}
|
|
822
|
+
this.logger?.log(`Found ${foundLabels.size} permission set(s) via group membership`);
|
|
823
|
+
}
|
|
824
|
+
catch (error) {
|
|
825
|
+
// Group check is supplementary — don't fail the entire permission check
|
|
826
|
+
this.logger?.log(`Group permission check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
827
|
+
}
|
|
828
|
+
return foundLabels;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
//# sourceMappingURL=UserReadinessService.js.map
|