This is page 21 of 43. Use http://codebase.md/taurgis/sfcc-dev-mcp?page={x} to view the full context. # Directory Structure ``` ├── .DS_Store ├── .github │ ├── dependabot.yml │ ├── instructions │ │ ├── mcp-node-tests.instructions.md │ │ └── mcp-yml-tests.instructions.md │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── documentation.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── PULL_REQUEST_TEMPLATE │ │ ├── bug_fix.md │ │ ├── documentation.md │ │ └── new_tool.md │ ├── pull_request_template.md │ └── workflows │ ├── ci.yml │ ├── deploy-pages.yml │ ├── publish.yml │ └── update-docs.yml ├── .gitignore ├── .husky │ └── pre-commit ├── aegis.config.docs-only.json ├── aegis.config.json ├── aegis.config.with-dw.json ├── AGENTS.md ├── ai-instructions │ ├── claude-desktop │ │ └── claude_custom_instructions.md │ ├── cursor │ │ └── .cursor │ │ └── rules │ │ ├── debugging-workflows.mdc │ │ ├── hooks-development.mdc │ │ ├── isml-templates.mdc │ │ ├── job-framework.mdc │ │ ├── performance-optimization.mdc │ │ ├── scapi-endpoints.mdc │ │ ├── security-patterns.mdc │ │ ├── sfcc-development.mdc │ │ ├── sfra-controllers.mdc │ │ ├── sfra-models.mdc │ │ ├── system-objects.mdc │ │ └── testing-patterns.mdc │ └── github-copilot │ └── copilot-instructions.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── docs │ ├── best-practices │ │ ├── cartridge_creation.md │ │ ├── isml_templates.md │ │ ├── job_framework.md │ │ ├── localserviceregistry.md │ │ ├── ocapi_hooks.md │ │ ├── performance.md │ │ ├── scapi_custom_endpoint.md │ │ ├── scapi_hooks.md │ │ ├── security.md │ │ ├── sfra_client_side_js.md │ │ ├── sfra_controllers.md │ │ ├── sfra_models.md │ │ └── sfra_scss.md │ ├── dw_campaign │ │ ├── ABTest.md │ │ ├── ABTestMgr.md │ │ ├── ABTestSegment.md │ │ ├── AmountDiscount.md │ │ ├── ApproachingDiscount.md │ │ ├── BonusChoiceDiscount.md │ │ ├── BonusDiscount.md │ │ ├── Campaign.md │ │ ├── CampaignMgr.md │ │ ├── CampaignStatusCodes.md │ │ ├── Coupon.md │ │ ├── CouponMgr.md │ │ ├── CouponRedemption.md │ │ ├── CouponStatusCodes.md │ │ ├── Discount.md │ │ ├── DiscountPlan.md │ │ ├── FixedPriceDiscount.md │ │ ├── FixedPriceShippingDiscount.md │ │ ├── FreeDiscount.md │ │ ├── FreeShippingDiscount.md │ │ ├── PercentageDiscount.md │ │ ├── PercentageOptionDiscount.md │ │ ├── PriceBookPriceDiscount.md │ │ ├── Promotion.md │ │ ├── PromotionMgr.md │ │ ├── PromotionPlan.md │ │ ├── SlotContent.md │ │ ├── SourceCodeGroup.md │ │ ├── SourceCodeInfo.md │ │ ├── SourceCodeStatusCodes.md │ │ └── TotalFixedPriceDiscount.md │ ├── dw_catalog │ │ ├── Catalog.md │ │ ├── CatalogMgr.md │ │ ├── Category.md │ │ ├── CategoryAssignment.md │ │ ├── CategoryLink.md │ │ ├── PriceBook.md │ │ ├── PriceBookMgr.md │ │ ├── Product.md │ │ ├── ProductActiveData.md │ │ ├── ProductAttributeModel.md │ │ ├── ProductAvailabilityLevels.md │ │ ├── ProductAvailabilityModel.md │ │ ├── ProductInventoryList.md │ │ ├── ProductInventoryMgr.md │ │ ├── ProductInventoryRecord.md │ │ ├── ProductLink.md │ │ ├── ProductMgr.md │ │ ├── ProductOption.md │ │ ├── ProductOptionModel.md │ │ ├── ProductOptionValue.md │ │ ├── ProductPriceInfo.md │ │ ├── ProductPriceModel.md │ │ ├── ProductPriceTable.md │ │ ├── ProductSearchHit.md │ │ ├── ProductSearchModel.md │ │ ├── ProductSearchRefinementDefinition.md │ │ ├── ProductSearchRefinements.md │ │ ├── ProductSearchRefinementValue.md │ │ ├── ProductVariationAttribute.md │ │ ├── ProductVariationAttributeValue.md │ │ ├── ProductVariationModel.md │ │ ├── Recommendation.md │ │ ├── SearchModel.md │ │ ├── SearchRefinementDefinition.md │ │ ├── SearchRefinements.md │ │ ├── SearchRefinementValue.md │ │ ├── SortingOption.md │ │ ├── SortingRule.md │ │ ├── Store.md │ │ ├── StoreGroup.md │ │ ├── StoreInventoryFilter.md │ │ ├── StoreInventoryFilterValue.md │ │ ├── StoreMgr.md │ │ ├── Variant.md │ │ └── VariationGroup.md │ ├── dw_content │ │ ├── Content.md │ │ ├── ContentMgr.md │ │ ├── ContentSearchModel.md │ │ ├── ContentSearchRefinementDefinition.md │ │ ├── ContentSearchRefinements.md │ │ ├── ContentSearchRefinementValue.md │ │ ├── Folder.md │ │ ├── Library.md │ │ ├── MarkupText.md │ │ └── MediaFile.md │ ├── dw_crypto │ │ ├── CertificateRef.md │ │ ├── CertificateUtils.md │ │ ├── Cipher.md │ │ ├── Encoding.md │ │ ├── JWE.md │ │ ├── JWEHeader.md │ │ ├── JWS.md │ │ ├── JWSHeader.md │ │ ├── KeyRef.md │ │ ├── Mac.md │ │ ├── MessageDigest.md │ │ ├── SecureRandom.md │ │ ├── Signature.md │ │ ├── WeakCipher.md │ │ ├── WeakMac.md │ │ ├── WeakMessageDigest.md │ │ ├── WeakSignature.md │ │ └── X509Certificate.md │ ├── dw_customer │ │ ├── AddressBook.md │ │ ├── AgentUserMgr.md │ │ ├── AgentUserStatusCodes.md │ │ ├── AuthenticationStatus.md │ │ ├── Credentials.md │ │ ├── Customer.md │ │ ├── CustomerActiveData.md │ │ ├── CustomerAddress.md │ │ ├── CustomerCDPData.md │ │ ├── CustomerContextMgr.md │ │ ├── CustomerGroup.md │ │ ├── CustomerList.md │ │ ├── CustomerMgr.md │ │ ├── CustomerPasswordConstraints.md │ │ ├── CustomerPaymentInstrument.md │ │ ├── CustomerStatusCodes.md │ │ ├── EncryptedObject.md │ │ ├── ExternalProfile.md │ │ ├── OrderHistory.md │ │ ├── ProductList.md │ │ ├── ProductListItem.md │ │ ├── ProductListItemPurchase.md │ │ ├── ProductListMgr.md │ │ ├── ProductListRegistrant.md │ │ ├── Profile.md │ │ └── Wallet.md │ ├── dw_extensions.applepay │ │ ├── ApplePayHookResult.md │ │ └── ApplePayHooks.md │ ├── dw_extensions.facebook │ │ ├── FacebookFeedHooks.md │ │ └── FacebookProduct.md │ ├── dw_extensions.paymentrequest │ │ ├── PaymentRequestHookResult.md │ │ └── PaymentRequestHooks.md │ ├── dw_extensions.payments │ │ ├── SalesforceBancontactPaymentDetails.md │ │ ├── SalesforceCardPaymentDetails.md │ │ ├── SalesforceEpsPaymentDetails.md │ │ ├── SalesforceIdealPaymentDetails.md │ │ ├── SalesforceKlarnaPaymentDetails.md │ │ ├── SalesforcePaymentDetails.md │ │ ├── SalesforcePaymentIntent.md │ │ ├── SalesforcePaymentMethod.md │ │ ├── SalesforcePaymentRequest.md │ │ ├── SalesforcePaymentsHooks.md │ │ ├── SalesforcePaymentsMgr.md │ │ ├── SalesforcePaymentsSiteConfiguration.md │ │ ├── SalesforcePayPalOrder.md │ │ ├── SalesforcePayPalOrderAddress.md │ │ ├── SalesforcePayPalOrderPayer.md │ │ ├── SalesforcePayPalPaymentDetails.md │ │ ├── SalesforceSepaDebitPaymentDetails.md │ │ └── SalesforceVenmoPaymentDetails.md │ ├── dw_extensions.pinterest │ │ ├── PinterestAvailability.md │ │ ├── PinterestFeedHooks.md │ │ ├── PinterestOrder.md │ │ ├── PinterestOrderHooks.md │ │ └── PinterestProduct.md │ ├── dw_io │ │ ├── CSVStreamReader.md │ │ ├── CSVStreamWriter.md │ │ ├── File.md │ │ ├── FileReader.md │ │ ├── FileWriter.md │ │ ├── InputStream.md │ │ ├── OutputStream.md │ │ ├── PrintWriter.md │ │ ├── RandomAccessFileReader.md │ │ ├── Reader.md │ │ ├── StringWriter.md │ │ ├── Writer.md │ │ ├── XMLIndentingStreamWriter.md │ │ ├── XMLStreamConstants.md │ │ ├── XMLStreamReader.md │ │ └── XMLStreamWriter.md │ ├── dw_job │ │ ├── JobExecution.md │ │ └── JobStepExecution.md │ ├── dw_net │ │ ├── FTPClient.md │ │ ├── FTPFileInfo.md │ │ ├── HTTPClient.md │ │ ├── HTTPRequestPart.md │ │ ├── Mail.md │ │ ├── SFTPClient.md │ │ ├── SFTPFileInfo.md │ │ ├── WebDAVClient.md │ │ └── WebDAVFileInfo.md │ ├── dw_object │ │ ├── ActiveData.md │ │ ├── CustomAttributes.md │ │ ├── CustomObject.md │ │ ├── CustomObjectMgr.md │ │ ├── Extensible.md │ │ ├── ExtensibleObject.md │ │ ├── Note.md │ │ ├── ObjectAttributeDefinition.md │ │ ├── ObjectAttributeGroup.md │ │ ├── ObjectAttributeValueDefinition.md │ │ ├── ObjectTypeDefinition.md │ │ ├── PersistentObject.md │ │ ├── SimpleExtensible.md │ │ └── SystemObjectMgr.md │ ├── dw_order │ │ ├── AbstractItem.md │ │ ├── AbstractItemCtnr.md │ │ ├── Appeasement.md │ │ ├── AppeasementItem.md │ │ ├── Basket.md │ │ ├── BasketMgr.md │ │ ├── BonusDiscountLineItem.md │ │ ├── CouponLineItem.md │ │ ├── CreateAgentBasketLimitExceededException.md │ │ ├── CreateBasketFromOrderException.md │ │ ├── CreateCouponLineItemException.md │ │ ├── CreateOrderException.md │ │ ├── CreateTemporaryBasketLimitExceededException.md │ │ ├── GiftCertificate.md │ │ ├── GiftCertificateLineItem.md │ │ ├── GiftCertificateMgr.md │ │ ├── GiftCertificateStatusCodes.md │ │ ├── Invoice.md │ │ ├── InvoiceItem.md │ │ ├── LineItem.md │ │ ├── LineItemCtnr.md │ │ ├── Order.md │ │ ├── OrderAddress.md │ │ ├── OrderItem.md │ │ ├── OrderMgr.md │ │ ├── OrderPaymentInstrument.md │ │ ├── OrderProcessStatusCodes.md │ │ ├── PaymentCard.md │ │ ├── PaymentInstrument.md │ │ ├── PaymentMethod.md │ │ ├── PaymentMgr.md │ │ ├── PaymentProcessor.md │ │ ├── PaymentStatusCodes.md │ │ ├── PaymentTransaction.md │ │ ├── PriceAdjustment.md │ │ ├── PriceAdjustmentLimitTypes.md │ │ ├── ProductLineItem.md │ │ ├── ProductShippingCost.md │ │ ├── ProductShippingLineItem.md │ │ ├── ProductShippingModel.md │ │ ├── Return.md │ │ ├── ReturnCase.md │ │ ├── ReturnCaseItem.md │ │ ├── ReturnItem.md │ │ ├── Shipment.md │ │ ├── ShipmentShippingCost.md │ │ ├── ShipmentShippingModel.md │ │ ├── ShippingLineItem.md │ │ ├── ShippingLocation.md │ │ ├── ShippingMethod.md │ │ ├── ShippingMgr.md │ │ ├── ShippingOrder.md │ │ ├── ShippingOrderItem.md │ │ ├── SumItem.md │ │ ├── TaxGroup.md │ │ ├── TaxItem.md │ │ ├── TaxMgr.md │ │ ├── TrackingInfo.md │ │ └── TrackingRef.md │ ├── dw_order.hooks │ │ ├── CalculateHooks.md │ │ ├── OrderHooks.md │ │ ├── PaymentHooks.md │ │ ├── ReturnHooks.md │ │ └── ShippingOrderHooks.md │ ├── dw_rpc │ │ ├── SOAPUtil.md │ │ ├── Stub.md │ │ └── WebReference.md │ ├── dw_suggest │ │ ├── BrandSuggestions.md │ │ ├── CategorySuggestions.md │ │ ├── ContentSuggestions.md │ │ ├── CustomSuggestions.md │ │ ├── ProductSuggestions.md │ │ ├── SearchPhraseSuggestions.md │ │ ├── SuggestedCategory.md │ │ ├── SuggestedContent.md │ │ ├── SuggestedPhrase.md │ │ ├── SuggestedProduct.md │ │ ├── SuggestedTerm.md │ │ ├── SuggestedTerms.md │ │ ├── Suggestions.md │ │ └── SuggestModel.md │ ├── dw_svc │ │ ├── FTPService.md │ │ ├── FTPServiceDefinition.md │ │ ├── HTTPFormService.md │ │ ├── HTTPFormServiceDefinition.md │ │ ├── HTTPService.md │ │ ├── HTTPServiceDefinition.md │ │ ├── LocalServiceRegistry.md │ │ ├── Result.md │ │ ├── Service.md │ │ ├── ServiceCallback.md │ │ ├── ServiceConfig.md │ │ ├── ServiceCredential.md │ │ ├── ServiceDefinition.md │ │ ├── ServiceProfile.md │ │ ├── ServiceRegistry.md │ │ ├── SOAPService.md │ │ └── SOAPServiceDefinition.md │ ├── dw_system │ │ ├── AgentUserStatusCodes.md │ │ ├── Cache.md │ │ ├── CacheMgr.md │ │ ├── HookMgr.md │ │ ├── InternalObject.md │ │ ├── JobProcessMonitor.md │ │ ├── Log.md │ │ ├── Logger.md │ │ ├── LogNDC.md │ │ ├── OrganizationPreferences.md │ │ ├── Pipeline.md │ │ ├── PipelineDictionary.md │ │ ├── RemoteInclude.md │ │ ├── Request.md │ │ ├── RequestHooks.md │ │ ├── Response.md │ │ ├── RESTErrorResponse.md │ │ ├── RESTResponseMgr.md │ │ ├── RESTSuccessResponse.md │ │ ├── SearchStatus.md │ │ ├── Session.md │ │ ├── Site.md │ │ ├── SitePreferences.md │ │ ├── Status.md │ │ ├── StatusItem.md │ │ ├── System.md │ │ └── Transaction.md │ ├── dw_util │ │ ├── ArrayList.md │ │ ├── Assert.md │ │ ├── BigInteger.md │ │ ├── Bytes.md │ │ ├── Calendar.md │ │ ├── Collection.md │ │ ├── Currency.md │ │ ├── DateUtils.md │ │ ├── Decimal.md │ │ ├── FilteringCollection.md │ │ ├── Geolocation.md │ │ ├── HashMap.md │ │ ├── HashSet.md │ │ ├── Iterator.md │ │ ├── LinkedHashMap.md │ │ ├── LinkedHashSet.md │ │ ├── List.md │ │ ├── Locale.md │ │ ├── Map.md │ │ ├── MapEntry.md │ │ ├── MappingKey.md │ │ ├── MappingMgr.md │ │ ├── PropertyComparator.md │ │ ├── SecureEncoder.md │ │ ├── SecureFilter.md │ │ ├── SeekableIterator.md │ │ ├── Set.md │ │ ├── SortedMap.md │ │ ├── SortedSet.md │ │ ├── StringUtils.md │ │ ├── Template.md │ │ └── UUIDUtils.md │ ├── dw_value │ │ ├── EnumValue.md │ │ ├── MimeEncodedText.md │ │ ├── Money.md │ │ └── Quantity.md │ ├── dw_web │ │ ├── ClickStream.md │ │ ├── ClickStreamEntry.md │ │ ├── Cookie.md │ │ ├── Cookies.md │ │ ├── CSRFProtection.md │ │ ├── Form.md │ │ ├── FormAction.md │ │ ├── FormElement.md │ │ ├── FormElementValidationResult.md │ │ ├── FormField.md │ │ ├── FormFieldOption.md │ │ ├── FormFieldOptions.md │ │ ├── FormGroup.md │ │ ├── FormList.md │ │ ├── FormListItem.md │ │ ├── Forms.md │ │ ├── HttpParameter.md │ │ ├── HttpParameterMap.md │ │ ├── LoopIterator.md │ │ ├── PageMetaData.md │ │ ├── PageMetaTag.md │ │ ├── PagingModel.md │ │ ├── Resource.md │ │ ├── URL.md │ │ ├── URLAction.md │ │ ├── URLParameter.md │ │ ├── URLRedirect.md │ │ ├── URLRedirectMgr.md │ │ └── URLUtils.md │ ├── sfra │ │ ├── account.md │ │ ├── address.md │ │ ├── billing.md │ │ ├── cart.md │ │ ├── categories.md │ │ ├── content.md │ │ ├── locale.md │ │ ├── order.md │ │ ├── payment.md │ │ ├── price-default.md │ │ ├── price-range.md │ │ ├── price-tiered.md │ │ ├── product-bundle.md │ │ ├── product-full.md │ │ ├── product-line-items.md │ │ ├── product-search.md │ │ ├── product-tile.md │ │ ├── querystring.md │ │ ├── render.md │ │ ├── request.md │ │ ├── response.md │ │ ├── server.md │ │ ├── shipping.md │ │ ├── store.md │ │ ├── stores.md │ │ └── totals.md │ └── TopLevel │ ├── APIException.md │ ├── arguments.md │ ├── Array.md │ ├── ArrayBuffer.md │ ├── BigInt.md │ ├── Boolean.md │ ├── ConversionError.md │ ├── DataView.md │ ├── Date.md │ ├── Error.md │ ├── ES6Iterator.md │ ├── EvalError.md │ ├── Fault.md │ ├── Float32Array.md │ ├── Float64Array.md │ ├── Function.md │ ├── Generator.md │ ├── global.md │ ├── Int16Array.md │ ├── Int32Array.md │ ├── Int8Array.md │ ├── InternalError.md │ ├── IOError.md │ ├── Iterable.md │ ├── Iterator.md │ ├── JSON.md │ ├── Map.md │ ├── Math.md │ ├── Module.md │ ├── Namespace.md │ ├── Number.md │ ├── Object.md │ ├── QName.md │ ├── RangeError.md │ ├── ReferenceError.md │ ├── RegExp.md │ ├── Set.md │ ├── StopIteration.md │ ├── String.md │ ├── Symbol.md │ ├── SyntaxError.md │ ├── SystemError.md │ ├── TypeError.md │ ├── Uint16Array.md │ ├── Uint32Array.md │ ├── Uint8Array.md │ ├── Uint8ClampedArray.md │ ├── URIError.md │ ├── WeakMap.md │ ├── WeakSet.md │ ├── XML.md │ ├── XMLList.md │ └── XMLStreamError.md ├── docs-site │ ├── .gitignore │ ├── App.tsx │ ├── components │ │ ├── Badge.tsx │ │ ├── BreadcrumbSchema.tsx │ │ ├── CodeBlock.tsx │ │ ├── Collapsible.tsx │ │ ├── ConfigBuilder.tsx │ │ ├── ConfigHero.tsx │ │ ├── ConfigModeTabs.tsx │ │ ├── icons.tsx │ │ ├── Layout.tsx │ │ ├── LightCodeContainer.tsx │ │ ├── NewcomerCTA.tsx │ │ ├── NextStepsStrip.tsx │ │ ├── OnThisPage.tsx │ │ ├── Search.tsx │ │ ├── SEO.tsx │ │ ├── Sidebar.tsx │ │ ├── StructuredData.tsx │ │ ├── ToolCard.tsx │ │ ├── ToolFilters.tsx │ │ ├── Typography.tsx │ │ └── VersionBadge.tsx │ ├── constants.tsx │ ├── index.html │ ├── main.tsx │ ├── metadata.json │ ├── package-lock.json │ ├── package.json │ ├── pages │ │ ├── AIInterfacesPage.tsx │ │ ├── ConfigurationPage.tsx │ │ ├── DevelopmentPage.tsx │ │ ├── ExamplesPage.tsx │ │ ├── FeaturesPage.tsx │ │ ├── HomePage.tsx │ │ ├── SecurityPage.tsx │ │ ├── ToolsPage.tsx │ │ └── TroubleshootingPage.tsx │ ├── postcss.config.js │ ├── public │ │ ├── .well-known │ │ │ └── security.txt │ │ ├── 404.html │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── explain-product-pricing-methods-no-mcp.png │ │ ├── explain-product-pricing-methods.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── llms.txt │ │ ├── robots.txt │ │ ├── site.webmanifest │ │ └── sitemap.xml │ ├── README.md │ ├── scripts │ │ ├── generate-search-index.js │ │ ├── generate-sitemap.js │ │ └── search-dev.js │ ├── src │ │ └── styles │ │ ├── input.css │ │ └── prism-theme.css │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── types.ts │ ├── utils │ │ ├── search.ts │ │ └── toolsData.ts │ └── vite.config.ts ├── eslint.config.js ├── jest.config.js ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── scripts │ └── convert-docs.js ├── SECURITY.md ├── server.json ├── src │ ├── clients │ │ ├── base │ │ │ ├── http-client.ts │ │ │ ├── oauth-token.ts │ │ │ └── ocapi-auth-client.ts │ │ ├── best-practices-client.ts │ │ ├── cartridge-generation-client.ts │ │ ├── docs │ │ │ ├── class-content-parser.ts │ │ │ ├── class-name-resolver.ts │ │ │ ├── documentation-scanner.ts │ │ │ ├── index.ts │ │ │ └── referenced-types-extractor.ts │ │ ├── docs-client.ts │ │ ├── log-client.ts │ │ ├── logs │ │ │ ├── index.ts │ │ │ ├── log-analyzer.ts │ │ │ ├── log-client.ts │ │ │ ├── log-constants.ts │ │ │ ├── log-file-discovery.ts │ │ │ ├── log-file-reader.ts │ │ │ ├── log-formatter.ts │ │ │ ├── log-processor.ts │ │ │ ├── log-types.ts │ │ │ └── webdav-client-manager.ts │ │ ├── ocapi │ │ │ ├── code-versions-client.ts │ │ │ ├── site-preferences-client.ts │ │ │ └── system-objects-client.ts │ │ ├── ocapi-client.ts │ │ └── sfra-client.ts │ ├── config │ │ ├── configuration-factory.ts │ │ └── dw-json-loader.ts │ ├── core │ │ ├── handlers │ │ │ ├── abstract-log-tool-handler.ts │ │ │ ├── base-handler.ts │ │ │ ├── best-practices-handler.ts │ │ │ ├── cartridge-handler.ts │ │ │ ├── client-factory.ts │ │ │ ├── code-version-handler.ts │ │ │ ├── docs-handler.ts │ │ │ ├── job-log-handler.ts │ │ │ ├── job-log-tool-config.ts │ │ │ ├── log-handler.ts │ │ │ ├── log-tool-config.ts │ │ │ ├── sfra-handler.ts │ │ │ ├── system-object-handler.ts │ │ │ └── validation-helpers.ts │ │ ├── server.ts │ │ └── tool-definitions.ts │ ├── index.ts │ ├── main.ts │ ├── services │ │ ├── file-system-service.ts │ │ ├── index.ts │ │ └── path-service.ts │ ├── tool-configs │ │ ├── best-practices-tool-config.ts │ │ ├── cartridge-tool-config.ts │ │ ├── code-version-tool-config.ts │ │ ├── docs-tool-config.ts │ │ ├── job-log-tool-config.ts │ │ ├── log-tool-config.ts │ │ ├── sfra-tool-config.ts │ │ └── system-object-tool-config.ts │ ├── types │ │ └── types.ts │ └── utils │ ├── cache.ts │ ├── job-log-tool-config.ts │ ├── job-log-utils.ts │ ├── log-cache.ts │ ├── log-tool-config.ts │ ├── log-tool-constants.ts │ ├── log-tool-utils.ts │ ├── logger.ts │ ├── ocapi-url-builder.ts │ ├── path-resolver.ts │ ├── query-builder.ts │ ├── utils.ts │ └── validator.ts ├── tests │ ├── __mocks__ │ │ ├── docs-client.ts │ │ ├── src │ │ │ └── clients │ │ │ └── base │ │ │ └── http-client.js │ │ └── webdav.js │ ├── base-handler.test.ts │ ├── base-http-client.test.ts │ ├── best-practices-handler.test.ts │ ├── cache.test.ts │ ├── cartridge-handler.test.ts │ ├── class-content-parser.test.ts │ ├── class-name-resolver.test.ts │ ├── client-factory.test.ts │ ├── code-version-handler.test.ts │ ├── code-versions-client.test.ts │ ├── config.test.ts │ ├── configuration-factory.test.ts │ ├── docs-handler.test.ts │ ├── documentation-scanner.test.ts │ ├── file-system-service.test.ts │ ├── job-log-handler.test.ts │ ├── job-log-utils.test.ts │ ├── log-client.test.ts │ ├── log-handler.test.ts │ ├── log-processor.test.ts │ ├── logger.test.ts │ ├── mcp │ │ ├── AGENTS.md │ │ ├── node │ │ │ ├── activate-code-version-advanced.full-mode.programmatic.test.js │ │ │ ├── code-versions.full-mode.programmatic.test.js │ │ │ ├── generate-cartridge-structure.docs-only.programmatic.test.js │ │ │ ├── get-available-best-practice-guides.docs-only.programmatic.test.js │ │ │ ├── get-available-sfra-documents.programmatic.test.js │ │ │ ├── get-best-practice-guide.docs-only.programmatic.test.js │ │ │ ├── get-hook-reference.docs-only.programmatic.test.js │ │ │ ├── get-job-execution-summary.full-mode.programmatic.test.js │ │ │ ├── get-job-log-entries.full-mode.programmatic.test.js │ │ │ ├── get-latest-debug.full-mode.programmatic.test.js │ │ │ ├── get-latest-error.full-mode.programmatic.test.js │ │ │ ├── get-latest-info.full-mode.programmatic.test.js │ │ │ ├── get-latest-job-log-files.full-mode.programmatic.test.js │ │ │ ├── get-latest-warn.full-mode.programmatic.test.js │ │ │ ├── get-log-file-contents.full-mode.programmatic.test.js │ │ │ ├── get-sfcc-class-documentation.docs-only.programmatic.test.js │ │ │ ├── get-sfcc-class-info.docs-only.programmatic.test.js │ │ │ ├── get-sfra-categories.docs-only.programmatic.test.js │ │ │ ├── get-sfra-document.programmatic.test.js │ │ │ ├── get-sfra-documents-by-category.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definition.full-mode.programmatic.test.js │ │ │ ├── get-system-object-definitions.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definitions.full-mode.programmatic.test.js │ │ │ ├── list-log-files.full-mode.programmatic.test.js │ │ │ ├── list-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-best-practices.docs-only.programmatic.test.js │ │ │ ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-job-logs-by-name.full-mode.programmatic.test.js │ │ │ ├── search-job-logs.full-mode.programmatic.test.js │ │ │ ├── search-logs.full-mode.programmatic.test.js │ │ │ ├── search-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-sfcc-methods.docs-only.programmatic.test.js │ │ │ ├── search-sfra-documentation.docs-only.programmatic.test.js │ │ │ ├── search-site-preferences.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-groups.full-mode.programmatic.test.js │ │ │ ├── summarize-logs.full-mode.programmatic.test.js │ │ │ ├── tools.docs-only.programmatic.test.js │ │ │ └── tools.full-mode.programmatic.test.js │ │ ├── README.md │ │ ├── test-fixtures │ │ │ └── dw.json │ │ └── yaml │ │ ├── activate-code-version.docs-only.test.mcp.yml │ │ ├── activate-code-version.full-mode.test.mcp.yml │ │ ├── get_latest_error.test.mcp.yml │ │ ├── get-available-best-practice-guides.docs-only.test.mcp.yml │ │ ├── get-available-best-practice-guides.full-mode.test.mcp.yml │ │ ├── get-available-sfra-documents.docs-only.test.mcp.yml │ │ ├── get-available-sfra-documents.full-mode.test.mcp.yml │ │ ├── get-best-practice-guide.docs-only.test.mcp.yml │ │ ├── get-best-practice-guide.full-mode.test.mcp.yml │ │ ├── get-code-versions.docs-only.test.mcp.yml │ │ ├── get-code-versions.full-mode.test.mcp.yml │ │ ├── get-hook-reference.docs-only.test.mcp.yml │ │ ├── get-hook-reference.full-mode.test.mcp.yml │ │ ├── get-job-execution-summary.full-mode.test.mcp.yml │ │ ├── get-job-log-entries.full-mode.test.mcp.yml │ │ ├── get-latest-debug.full-mode.test.mcp.yml │ │ ├── get-latest-error.full-mode.test.mcp.yml │ │ ├── get-latest-info.full-mode.test.mcp.yml │ │ ├── get-latest-job-log-files.full-mode.test.mcp.yml │ │ ├── get-latest-warn.full-mode.test.mcp.yml │ │ ├── get-log-file-contents.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-documentation.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-documentation.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-info.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-info.full-mode.test.mcp.yml │ │ ├── get-sfra-categories.docs-only.test.mcp.yml │ │ ├── get-sfra-categories.full-mode.test.mcp.yml │ │ ├── get-sfra-document.docs-only.test.mcp.yml │ │ ├── get-sfra-document.full-mode.test.mcp.yml │ │ ├── get-sfra-documents-by-category.docs-only.test.mcp.yml │ │ ├── get-sfra-documents-by-category.full-mode.test.mcp.yml │ │ ├── get-system-object-definition.docs-only.test.mcp.yml │ │ ├── get-system-object-definition.full-mode.test.mcp.yml │ │ ├── get-system-object-definitions.docs-only.test.mcp.yml │ │ ├── get-system-object-definitions.full-mode.test.mcp.yml │ │ ├── list-log-files.full-mode.test.mcp.yml │ │ ├── list-sfcc-classes.docs-only.test.mcp.yml │ │ ├── list-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-best-practices.docs-only.test.mcp.yml │ │ ├── search-best-practices.full-mode.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.test.mcp.yml │ │ ├── search-job-logs-by-name.full-mode.test.mcp.yml │ │ ├── search-job-logs.full-mode.test.mcp.yml │ │ ├── search-logs.full-mode.test.mcp.yml │ │ ├── search-sfcc-classes.docs-only.test.mcp.yml │ │ ├── search-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-sfcc-methods.docs-only.test.mcp.yml │ │ ├── search-sfcc-methods.full-mode.test.mcp.yml │ │ ├── search-sfra-documentation.docs-only.test.mcp.yml │ │ ├── search-sfra-documentation.full-mode.test.mcp.yml │ │ ├── search-site-preferences.docs-only.test.mcp.yml │ │ ├── search-site-preferences.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-groups.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-groups.full-mode.test.mcp.yml │ │ ├── summarize-logs.full-mode.test.mcp.yml │ │ ├── tools.docs-only.test.mcp.yml │ │ └── tools.full-mode.test.mcp.yml │ ├── oauth-token.test.ts │ ├── ocapi-auth-client.test.ts │ ├── ocapi-client.test.ts │ ├── path-service.test.ts │ ├── query-builder.test.ts │ ├── referenced-types-extractor.test.ts │ ├── servers │ │ ├── sfcc-mock-server │ │ │ ├── mock-data │ │ │ │ └── ocapi │ │ │ │ ├── code-versions.json │ │ │ │ ├── custom-object-attributes-customapi.json │ │ │ │ ├── custom-object-attributes-globalsettings.json │ │ │ │ ├── custom-object-attributes-versionhistory.json │ │ │ │ ├── site-preferences-ccv.json │ │ │ │ ├── site-preferences-fastforward.json │ │ │ │ ├── site-preferences-sfra.json │ │ │ │ ├── site-preferences-storefront.json │ │ │ │ ├── site-preferences-system.json │ │ │ │ ├── system-object-attribute-groups-campaign.json │ │ │ │ ├── system-object-attribute-groups-category.json │ │ │ │ ├── system-object-attribute-groups-order.json │ │ │ │ ├── system-object-attribute-groups-product.json │ │ │ │ ├── system-object-attribute-groups-sitepreferences.json │ │ │ │ ├── system-object-attributes-customeraddress.json │ │ │ │ ├── system-object-attributes-product-expanded.json │ │ │ │ ├── system-object-attributes-product.json │ │ │ │ ├── system-object-definition-category.json │ │ │ │ ├── system-object-definition-customer.json │ │ │ │ ├── system-object-definition-customeraddress.json │ │ │ │ ├── system-object-definition-order.json │ │ │ │ ├── system-object-definition-product.json │ │ │ │ ├── system-object-definitions-old.json │ │ │ │ └── system-object-definitions.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── README.md │ │ │ ├── scripts │ │ │ │ └── setup-logs.js │ │ │ ├── server.js │ │ │ └── src │ │ │ ├── app.js │ │ │ ├── config │ │ │ │ └── server-config.js │ │ │ ├── middleware │ │ │ │ ├── auth.js │ │ │ │ ├── cors.js │ │ │ │ └── logging.js │ │ │ ├── routes │ │ │ │ ├── ocapi │ │ │ │ │ ├── code-versions-handler.js │ │ │ │ │ ├── oauth-handler.js │ │ │ │ │ ├── ocapi-error-utils.js │ │ │ │ │ ├── ocapi-utils.js │ │ │ │ │ ├── site-preferences-handler.js │ │ │ │ │ └── system-objects-handler.js │ │ │ │ ├── ocapi.js │ │ │ │ └── webdav.js │ │ │ └── utils │ │ │ ├── mock-data-loader.js │ │ │ └── webdav-xml.js │ │ └── sfcc-mock-server-manager.ts │ ├── sfcc-mock-server.test.ts │ ├── site-preferences-client.test.ts │ ├── system-objects-client.test.ts │ ├── utils.test.ts │ ├── validation-helpers.test.ts │ └── validator.test.ts ├── tsconfig.json └── tsconfig.test.json ``` # Files -------------------------------------------------------------------------------- /docs/dw_customer/CustomerActiveData.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.customer # Class CustomerActiveData ## Inheritance Hierarchy - Object - dw.object.PersistentObject - dw.object.ExtensibleObject - dw.object.ActiveData - dw.customer.CustomerActiveData ## Description Represents the active data for a Customer in Commerce Cloud Digital. Note: this class allows access to sensitive personal and private information. Pay attention to appropriate legal and regulatory requirements when developing. ## Properties ### avgOrderValue **Type:** Number (Read Only) The average order value of the customer, or null if none has been set or the value is no longer valid. ### discountValueWithCoupon **Type:** Number (Read Only) The discount value resulting from coupons, that has been applied to orders of the customer, or null if none has been set or the value is no longer valid. ### discountValueWithoutCoupon **Type:** Number (Read Only) The discount value resulting from promotions other than coupons, that has been applied to orders of the customer, or null if none has been set or the value is no longer valid. ### giftOrders **Type:** Number (Read Only) The number of orders for the Customer that contained at least one product unit marked as a gift, or null if none has been set or the value is no longer valid. ### giftUnits **Type:** Number (Read Only) The number of product units in orders for the customer that were marked as a gift, or null if none has been set or the value is no longer valid. ### lastOrderDate **Type:** Date (Read Only) The date of the last order for the customer, or null if there are no orders for the customer. ### orders **Type:** Number (Read Only) The orders of the customer, or null if none has been set or the value is no longer valid. ### orderValue **Type:** Number (Read Only) The lifetime order value of the customer, or null if none has been set or the value is no longer valid. ### orderValueMonth **Type:** Number (Read Only) The order value of the customer, over the most recent 30 days, or null if none has been set or the value is no longer valid. ### productMastersOrdered **Type:** String (Read Only) An array containing the master product SKUs of variation products in orders for the customer, or an empty collection if no SKUs have been set or the collection of SKUs is no longer valid. There is no specific limit on the number of SKUs that will be returned in the collection, but there is also no guarantee that it will contain the SKUs for all products ordered by the customer. ### productsAbandonedMonth **Type:** String (Read Only) An array containing the SKUs of products in baskets abandoned by the customer in the last 30 days, or an empty collection if no SKUs have been set or the collection is no longer valid. There is no specific limit on the number of SKUs that will be returned in the collection, but there is also no guarantee that it will contain the SKUs for all products in baskets abandoned by the customer. ### productsOrdered **Type:** String (Read Only) An array containing the SKUs of products in orders for the customer, or an empty collection if no SKUs have been set or the collection of SKUs is no longer valid. There is no specific limit on the number of SKUs that will be returned in the collection, but there is also no guarantee that it will contain the SKUs for all products ordered by the customer. ### productsViewedMonth **Type:** String (Read Only) An array containing the SKUs of products viewed by the customer in the last 30 days, or an empty collection if no SKUs have been set or the collection is no longer valid. There is no specific limit on the number of SKUs that will be returned in the collection, but there is also no guarantee that it will contain the SKUs for all products viewed by the customer. ### returns **Type:** Number (Read Only) The number of returns of the customer, or null if none has been set or the value is no longer valid. ### returnValue **Type:** Number (Read Only) The returned revenue of the customer, or null if none has been set or the value is no longer valid. ### sourceCodeOrders **Type:** Number (Read Only) The number of orders for the customer where a source code was in effect, or null if none has been set or the value is no longer valid. ### topCategoriesOrdered **Type:** String (Read Only) An array containing the IDs of up to the top 20 categories for customer orders, or an empty list if no categories have been set or the list of categories is no longer valid. The top category is the one for which the most orders for the customer contained at least one product found in that category. ### visitsMonth **Type:** Number (Read Only) The visits of the customer, over the most recent 30 days, or null if none has been set or the value is no longer valid. ### visitsWeek **Type:** Number (Read Only) The visits of the customer, over the most recent 7 days, or null if none has been set or the value is no longer valid. ### visitsYear **Type:** Number (Read Only) The visits of the customer, over the most recent 365 days, or null if none has been set or the value is no longer valid. ## Constructor Summary ## Method Summary ### getAvgOrderValue **Signature:** `getAvgOrderValue() : Number` Returns the average order value of the customer, or null if none has been set or the value is no longer valid. ### getDiscountValueWithCoupon **Signature:** `getDiscountValueWithCoupon() : Number` Returns the discount value resulting from coupons, that has been applied to orders of the customer, or null if none has been set or the value is no longer valid. ### getDiscountValueWithoutCoupon **Signature:** `getDiscountValueWithoutCoupon() : Number` Returns the discount value resulting from promotions other than coupons, that has been applied to orders of the customer, or null if none has been set or the value is no longer valid. ### getGiftOrders **Signature:** `getGiftOrders() : Number` Returns the number of orders for the Customer that contained at least one product unit marked as a gift, or null if none has been set or the value is no longer valid. ### getGiftUnits **Signature:** `getGiftUnits() : Number` Returns the number of product units in orders for the customer that were marked as a gift, or null if none has been set or the value is no longer valid. ### getLastOrderDate **Signature:** `getLastOrderDate() : Date` Returns the date of the last order for the customer, or null if there are no orders for the customer. ### getOrders **Signature:** `getOrders() : Number` Returns the orders of the customer, or null if none has been set or the value is no longer valid. ### getOrderValue **Signature:** `getOrderValue() : Number` Returns the lifetime order value of the customer, or null if none has been set or the value is no longer valid. ### getOrderValueMonth **Signature:** `getOrderValueMonth() : Number` Returns the order value of the customer, over the most recent 30 days, or null if none has been set or the value is no longer valid. ### getProductMastersOrdered **Signature:** `getProductMastersOrdered() : String[]` Returns an array containing the master product SKUs of variation products in orders for the customer, or an empty collection if no SKUs have been set or the collection of SKUs is no longer valid. ### getProductsAbandonedMonth **Signature:** `getProductsAbandonedMonth() : String[]` Returns an array containing the SKUs of products in baskets abandoned by the customer in the last 30 days, or an empty collection if no SKUs have been set or the collection is no longer valid. ### getProductsOrdered **Signature:** `getProductsOrdered() : String[]` Returns an array containing the SKUs of products in orders for the customer, or an empty collection if no SKUs have been set or the collection of SKUs is no longer valid. ### getProductsViewedMonth **Signature:** `getProductsViewedMonth() : String[]` Returns an array containing the SKUs of products viewed by the customer in the last 30 days, or an empty collection if no SKUs have been set or the collection is no longer valid. ### getReturns **Signature:** `getReturns() : Number` Returns the number of returns of the customer, or null if none has been set or the value is no longer valid. ### getReturnValue **Signature:** `getReturnValue() : Number` Returns the returned revenue of the customer, or null if none has been set or the value is no longer valid. ### getSourceCodeOrders **Signature:** `getSourceCodeOrders() : Number` Returns the number of orders for the customer where a source code was in effect, or null if none has been set or the value is no longer valid. ### getTopCategoriesOrdered **Signature:** `getTopCategoriesOrdered() : String[]` Returns an array containing the IDs of up to the top 20 categories for customer orders, or an empty list if no categories have been set or the list of categories is no longer valid. ### getVisitsMonth **Signature:** `getVisitsMonth() : Number` Returns the visits of the customer, over the most recent 30 days, or null if none has been set or the value is no longer valid. ### getVisitsWeek **Signature:** `getVisitsWeek() : Number` Returns the visits of the customer, over the most recent 7 days, or null if none has been set or the value is no longer valid. ### getVisitsYear **Signature:** `getVisitsYear() : Number` Returns the visits of the customer, over the most recent 365 days, or null if none has been set or the value is no longer valid. ## Method Detail ## Method Details ### getAvgOrderValue **Signature:** `getAvgOrderValue() : Number` **Description:** Returns the average order value of the customer, or null if none has been set or the value is no longer valid. **Returns:** the average order size. --- ### getDiscountValueWithCoupon **Signature:** `getDiscountValueWithCoupon() : Number` **Description:** Returns the discount value resulting from coupons, that has been applied to orders of the customer, or null if none has been set or the value is no longer valid. **Returns:** the discount value resulting from coupons. --- ### getDiscountValueWithoutCoupon **Signature:** `getDiscountValueWithoutCoupon() : Number` **Description:** Returns the discount value resulting from promotions other than coupons, that has been applied to orders of the customer, or null if none has been set or the value is no longer valid. **Returns:** the discount value resulting from promotions other than coupons. --- ### getGiftOrders **Signature:** `getGiftOrders() : Number` **Description:** Returns the number of orders for the Customer that contained at least one product unit marked as a gift, or null if none has been set or the value is no longer valid. **Returns:** the number of gift orders. --- ### getGiftUnits **Signature:** `getGiftUnits() : Number` **Description:** Returns the number of product units in orders for the customer that were marked as a gift, or null if none has been set or the value is no longer valid. **Returns:** the number of gift product units. --- ### getLastOrderDate **Signature:** `getLastOrderDate() : Date` **Description:** Returns the date of the last order for the customer, or null if there are no orders for the customer. **Returns:** the date of the last order for the customer. --- ### getOrders **Signature:** `getOrders() : Number` **Description:** Returns the orders of the customer, or null if none has been set or the value is no longer valid. **Returns:** the orders. --- ### getOrderValue **Signature:** `getOrderValue() : Number` **Description:** Returns the lifetime order value of the customer, or null if none has been set or the value is no longer valid. **Returns:** the lifetime value. --- ### getOrderValueMonth **Signature:** `getOrderValueMonth() : Number` **Description:** Returns the order value of the customer, over the most recent 30 days, or null if none has been set or the value is no longer valid. **Returns:** the value over the last 30 days. --- ### getProductMastersOrdered **Signature:** `getProductMastersOrdered() : String[]` **Description:** Returns an array containing the master product SKUs of variation products in orders for the customer, or an empty collection if no SKUs have been set or the collection of SKUs is no longer valid. There is no specific limit on the number of SKUs that will be returned in the collection, but there is also no guarantee that it will contain the SKUs for all products ordered by the customer. **Returns:** a collection containing the master product SKUs of variation products that were ordered. --- ### getProductsAbandonedMonth **Signature:** `getProductsAbandonedMonth() : String[]` **Description:** Returns an array containing the SKUs of products in baskets abandoned by the customer in the last 30 days, or an empty collection if no SKUs have been set or the collection is no longer valid. There is no specific limit on the number of SKUs that will be returned in the collection, but there is also no guarantee that it will contain the SKUs for all products in baskets abandoned by the customer. **Returns:** a collection containing the SKUs of products that were abandoned. --- ### getProductsOrdered **Signature:** `getProductsOrdered() : String[]` **Description:** Returns an array containing the SKUs of products in orders for the customer, or an empty collection if no SKUs have been set or the collection of SKUs is no longer valid. There is no specific limit on the number of SKUs that will be returned in the collection, but there is also no guarantee that it will contain the SKUs for all products ordered by the customer. **Returns:** a collection containing the SKUs of products that were ordered. --- ### getProductsViewedMonth **Signature:** `getProductsViewedMonth() : String[]` **Description:** Returns an array containing the SKUs of products viewed by the customer in the last 30 days, or an empty collection if no SKUs have been set or the collection is no longer valid. There is no specific limit on the number of SKUs that will be returned in the collection, but there is also no guarantee that it will contain the SKUs for all products viewed by the customer. **Returns:** a collection containing the SKUs of products that were ordered. --- ### getReturns **Signature:** `getReturns() : Number` **Description:** Returns the number of returns of the customer, or null if none has been set or the value is no longer valid. **Returns:** the returns. --- ### getReturnValue **Signature:** `getReturnValue() : Number` **Description:** Returns the returned revenue of the customer, or null if none has been set or the value is no longer valid. **Returns:** the returned revenue. --- ### getSourceCodeOrders **Signature:** `getSourceCodeOrders() : Number` **Description:** Returns the number of orders for the customer where a source code was in effect, or null if none has been set or the value is no longer valid. **Returns:** the number of orders with source codes in effect. --- ### getTopCategoriesOrdered **Signature:** `getTopCategoriesOrdered() : String[]` **Description:** Returns an array containing the IDs of up to the top 20 categories for customer orders, or an empty list if no categories have been set or the list of categories is no longer valid. The top category is the one for which the most orders for the customer contained at least one product found in that category. **Returns:** a list containing the top 20 categories. --- ### getVisitsMonth **Signature:** `getVisitsMonth() : Number` **Description:** Returns the visits of the customer, over the most recent 30 days, or null if none has been set or the value is no longer valid. **Returns:** the visits over the last 30 days. --- ### getVisitsWeek **Signature:** `getVisitsWeek() : Number` **Description:** Returns the visits of the customer, over the most recent 7 days, or null if none has been set or the value is no longer valid. **Returns:** the visits over the last 7 days. --- ### getVisitsYear **Signature:** `getVisitsYear() : Number` **Description:** Returns the visits of the customer, over the most recent 365 days, or null if none has been set or the value is no longer valid. **Returns:** the visits over the last 365 days. --- ``` -------------------------------------------------------------------------------- /tests/mcp/yaml/search-best-practices.docs-only.test.mcp.yml: -------------------------------------------------------------------------------- ```yaml description: "SFCC Dev MCP Server - search_best_practices Tool Tests (Docs-Only Mode)" tests: # Basic functionality tests - it: "should return array of search results for validation query" request: jsonrpc: "2.0" id: "search-validation-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "validation" expect: response: jsonrpc: "2.0" id: "search-validation-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should find security-related content in best practices" request: jsonrpc: "2.0" id: "search-security-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "security" expect: response: jsonrpc: "2.0" id: "search-security-1" result: content: - type: "text" text: "match:contains:security" isError: false stderr: "toBeEmpty" - it: "should find performance-related content" request: jsonrpc: "2.0" id: "search-performance-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "performance" expect: response: jsonrpc: "2.0" id: "search-performance-1" result: content: - type: "text" text: "match:contains:performance" isError: false stderr: "toBeEmpty" - it: "should find OCAPI hook references" request: jsonrpc: "2.0" id: "search-ocapi-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "ocapi" expect: response: jsonrpc: "2.0" id: "search-ocapi-1" result: content: - type: "text" text: "match:contains:ocapi" isError: false stderr: "toBeEmpty" - it: "should find SCAPI-related content" request: jsonrpc: "2.0" id: "search-scapi-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "scapi" expect: response: jsonrpc: "2.0" id: "search-scapi-1" result: content: - type: "text" text: "match:contains:scapi" isError: false stderr: "toBeEmpty" - it: "should find cartridge-related content" request: jsonrpc: "2.0" id: "search-cartridge-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "cartridge" expect: response: jsonrpc: "2.0" id: "search-cartridge-1" result: content: - type: "text" text: "match:contains:cartridge" isError: false stderr: "toBeEmpty" - it: "should find ISML template references" request: jsonrpc: "2.0" id: "search-isml-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "isml" expect: response: jsonrpc: "2.0" id: "search-isml-1" result: content: - type: "text" text: "match:contains:isml" isError: false stderr: "toBeEmpty" - it: "should find controller-related content" request: jsonrpc: "2.0" id: "search-controller-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "controller" expect: response: jsonrpc: "2.0" id: "search-controller-1" result: content: - type: "text" text: "match:contains:controller" isError: false stderr: "toBeEmpty" - it: "should surface SFRA client-side JavaScript guidance" request: jsonrpc: "2.0" id: "search-client-js-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "javascript" expect: response: jsonrpc: "2.0" id: "search-client-js-1" result: content: - type: "text" text: "match:contains:sfra_client_side_js" isError: false stderr: "toBeEmpty" - it: "should surface SFRA SCSS override guidance" request: jsonrpc: "2.0" id: "search-scss-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "scss" expect: response: jsonrpc: "2.0" id: "search-scss-1" result: content: - type: "text" text: "match:contains:sfra_scss" isError: false stderr: "toBeEmpty" - it: "should find job framework references" request: jsonrpc: "2.0" id: "search-job-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "job" expect: response: jsonrpc: "2.0" id: "search-job-1" result: content: - type: "text" text: "match:contains:job" isError: false stderr: "toBeEmpty" # Edge cases and error handling - it: "should return empty array for non-existent terms" request: jsonrpc: "2.0" id: "search-empty-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "zzznomatchesexpected" expect: response: jsonrpc: "2.0" id: "search-empty-1" result: content: - type: "text" text: "match:regex:^\\[\\s*\\]$" # Empty array isError: false stderr: "toBeEmpty" - it: "should handle empty query with error" request: jsonrpc: "2.0" id: "search-error-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "" expect: response: jsonrpc: "2.0" id: "search-error-1" result: content: - type: "text" text: "match:contains:Error" isError: true stderr: "toBeEmpty" - it: "should handle missing query parameter with error" request: jsonrpc: "2.0" id: "search-missing-1" method: "tools/call" params: name: "search_best_practices" arguments: {} expect: response: jsonrpc: "2.0" id: "search-missing-1" result: content: - type: "text" text: "match:contains:Error" isError: true stderr: "toBeEmpty" # Response structure validation tests - it: "should validate detailed response structure for validation search" request: jsonrpc: "2.0" id: "search-structure-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "validation" expect: response: jsonrpc: "2.0" id: "search-structure-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # JSON array format isError: false stderr: "toBeEmpty" # Content validation tests for specific guides - it: "should find form validation in ISML templates guide" request: jsonrpc: "2.0" id: "search-form-validation-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "form" expect: response: jsonrpc: "2.0" id: "search-form-validation-1" result: content: - type: "text" text: "match:contains:form" isError: false stderr: "toBeEmpty" - it: "should find authorization patterns in hook guides" request: jsonrpc: "2.0" id: "search-authorization-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "authorization" expect: response: jsonrpc: "2.0" id: "search-authorization-1" result: content: - type: "text" text: "match:contains:authorization" isError: false stderr: "toBeEmpty" - it: "should find middleware patterns" request: jsonrpc: "2.0" id: "search-middleware-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "middleware" expect: response: jsonrpc: "2.0" id: "search-middleware-1" result: content: - type: "text" text: "match:contains:middleware" isError: false stderr: "toBeEmpty" - it: "should find transaction handling patterns" request: jsonrpc: "2.0" id: "search-transaction-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "transaction" expect: response: jsonrpc: "2.0" id: "search-transaction-1" result: content: - type: "text" text: "match:contains:transaction" isError: false stderr: "toBeEmpty" - it: "should find error handling patterns" request: jsonrpc: "2.0" id: "search-error-handling-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "error" expect: response: jsonrpc: "2.0" id: "search-error-handling-1" result: content: - type: "text" text: "match:contains:error" isError: false stderr: "toBeEmpty" # Case sensitivity tests - it: "should handle case insensitive search for SFRA" request: jsonrpc: "2.0" id: "search-case-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "SFRA" expect: response: jsonrpc: "2.0" id: "search-case-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should handle lowercase search terms" request: jsonrpc: "2.0" id: "search-lowercase-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "api" expect: response: jsonrpc: "2.0" id: "search-lowercase-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" # Multiple word search tests - it: "should find authentication patterns" request: jsonrpc: "2.0" id: "search-auth-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "authentication" expect: response: jsonrpc: "2.0" id: "search-auth-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should find encryption references" request: jsonrpc: "2.0" id: "search-encryption-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "encryption" expect: response: jsonrpc: "2.0" id: "search-encryption-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" # Special characters handling - it: "should handle search terms with special characters" request: jsonrpc: "2.0" id: "search-special-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "REST" expect: response: jsonrpc: "2.0" id: "search-special-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should handle numeric search terms" request: jsonrpc: "2.0" id: "search-numeric-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "HTTPS" expect: response: jsonrpc: "2.0" id: "search-numeric-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" # Common development terms - it: "should find model-related patterns" request: jsonrpc: "2.0" id: "search-model-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "model" expect: response: jsonrpc: "2.0" id: "search-model-1" result: content: - type: "text" text: "match:contains:model" isError: false stderr: "toBeEmpty" - it: "should find configuration patterns" request: jsonrpc: "2.0" id: "search-config-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "config" expect: response: jsonrpc: "2.0" id: "search-config-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should find database transaction patterns" request: jsonrpc: "2.0" id: "search-database-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "database" expect: response: jsonrpc: "2.0" id: "search-database-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" # Performance testing with timing - it: "should respond quickly for validation search" request: jsonrpc: "2.0" id: "search-perf-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "validation" expect: response: jsonrpc: "2.0" id: "search-perf-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" isError: false performance: maxResponseTime: "2000ms" # Documentation search should be fast stderr: "toBeEmpty" - it: "should respond quickly for empty results" request: jsonrpc: "2.0" id: "search-perf-empty-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "zzznomatchesexpected" expect: response: jsonrpc: "2.0" id: "search-perf-empty-1" result: content: - type: "text" text: "match:regex:^\\[\\s*\\]$" isError: false performance: maxResponseTime: "1000ms" # Empty results should be very fast stderr: "toBeEmpty" - it: "should handle errors quickly" request: jsonrpc: "2.0" id: "search-perf-error-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "" expect: response: jsonrpc: "2.0" id: "search-perf-error-1" result: content: - type: "text" text: "match:contains:Error" isError: true performance: maxResponseTime: "500ms" # Error responses should be fastest stderr: "toBeEmpty" ``` -------------------------------------------------------------------------------- /tests/mcp/yaml/search-best-practices.full-mode.test.mcp.yml: -------------------------------------------------------------------------------- ```yaml description: "SFCC Dev MCP Server - search_best_practices Tool Tests (Docs-Only Mode)" tests: # Basic functionality tests - it: "should return array of search results for validation query" request: jsonrpc: "2.0" id: "search-validation-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "validation" expect: response: jsonrpc: "2.0" id: "search-validation-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should find security-related content in best practices" request: jsonrpc: "2.0" id: "search-security-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "security" expect: response: jsonrpc: "2.0" id: "search-security-1" result: content: - type: "text" text: "match:contains:security" isError: false stderr: "toBeEmpty" - it: "should find performance-related content" request: jsonrpc: "2.0" id: "search-performance-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "performance" expect: response: jsonrpc: "2.0" id: "search-performance-1" result: content: - type: "text" text: "match:contains:performance" isError: false stderr: "toBeEmpty" - it: "should find OCAPI hook references" request: jsonrpc: "2.0" id: "search-ocapi-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "ocapi" expect: response: jsonrpc: "2.0" id: "search-ocapi-1" result: content: - type: "text" text: "match:contains:ocapi" isError: false stderr: "toBeEmpty" - it: "should find SCAPI-related content" request: jsonrpc: "2.0" id: "search-scapi-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "scapi" expect: response: jsonrpc: "2.0" id: "search-scapi-1" result: content: - type: "text" text: "match:contains:scapi" isError: false stderr: "toBeEmpty" - it: "should find cartridge-related content" request: jsonrpc: "2.0" id: "search-cartridge-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "cartridge" expect: response: jsonrpc: "2.0" id: "search-cartridge-1" result: content: - type: "text" text: "match:contains:cartridge" isError: false stderr: "toBeEmpty" - it: "should find ISML template references" request: jsonrpc: "2.0" id: "search-isml-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "isml" expect: response: jsonrpc: "2.0" id: "search-isml-1" result: content: - type: "text" text: "match:contains:isml" isError: false stderr: "toBeEmpty" - it: "should find controller-related content" request: jsonrpc: "2.0" id: "search-controller-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "controller" expect: response: jsonrpc: "2.0" id: "search-controller-1" result: content: - type: "text" text: "match:contains:controller" isError: false stderr: "toBeEmpty" - it: "should surface SFRA client-side JavaScript guidance" request: jsonrpc: "2.0" id: "search-client-js-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "javascript" expect: response: jsonrpc: "2.0" id: "search-client-js-1" result: content: - type: "text" text: "match:contains:sfra_client_side_js" isError: false stderr: "toBeEmpty" - it: "should surface SFRA SCSS override guidance" request: jsonrpc: "2.0" id: "search-scss-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "scss" expect: response: jsonrpc: "2.0" id: "search-scss-1" result: content: - type: "text" text: "match:contains:sfra_scss" isError: false stderr: "toBeEmpty" - it: "should find job framework references" request: jsonrpc: "2.0" id: "search-job-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "job" expect: response: jsonrpc: "2.0" id: "search-job-1" result: content: - type: "text" text: "match:contains:job" isError: false stderr: "toBeEmpty" # Edge cases and error handling - it: "should return empty array for non-existent terms" request: jsonrpc: "2.0" id: "search-empty-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "zzznomatchesexpected" expect: response: jsonrpc: "2.0" id: "search-empty-1" result: content: - type: "text" text: "match:regex:^\\[\\s*\\]$" # Empty array isError: false stderr: "toBeEmpty" - it: "should handle empty query with error" request: jsonrpc: "2.0" id: "search-error-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "" expect: response: jsonrpc: "2.0" id: "search-error-1" result: content: - type: "text" text: "match:contains:Error" isError: true stderr: "toBeEmpty" - it: "should handle missing query parameter with error" request: jsonrpc: "2.0" id: "search-missing-1" method: "tools/call" params: name: "search_best_practices" arguments: {} expect: response: jsonrpc: "2.0" id: "search-missing-1" result: content: - type: "text" text: "match:contains:Error" isError: true stderr: "toBeEmpty" # Response structure validation tests - it: "should validate detailed response structure for validation search" request: jsonrpc: "2.0" id: "search-structure-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "validation" expect: response: jsonrpc: "2.0" id: "search-structure-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # JSON array format isError: false stderr: "toBeEmpty" # Content validation tests for specific guides - it: "should find form validation in ISML templates guide" request: jsonrpc: "2.0" id: "search-form-validation-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "form" expect: response: jsonrpc: "2.0" id: "search-form-validation-1" result: content: - type: "text" text: "match:contains:form" isError: false stderr: "toBeEmpty" - it: "should find authorization patterns in hook guides" request: jsonrpc: "2.0" id: "search-authorization-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "authorization" expect: response: jsonrpc: "2.0" id: "search-authorization-1" result: content: - type: "text" text: "match:contains:authorization" isError: false stderr: "toBeEmpty" - it: "should find middleware patterns" request: jsonrpc: "2.0" id: "search-middleware-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "middleware" expect: response: jsonrpc: "2.0" id: "search-middleware-1" result: content: - type: "text" text: "match:contains:middleware" isError: false stderr: "toBeEmpty" - it: "should find transaction handling patterns" request: jsonrpc: "2.0" id: "search-transaction-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "transaction" expect: response: jsonrpc: "2.0" id: "search-transaction-1" result: content: - type: "text" text: "match:contains:transaction" isError: false stderr: "toBeEmpty" - it: "should find error handling patterns" request: jsonrpc: "2.0" id: "search-error-handling-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "error" expect: response: jsonrpc: "2.0" id: "search-error-handling-1" result: content: - type: "text" text: "match:contains:error" isError: false stderr: "toBeEmpty" # Case sensitivity tests - it: "should handle case insensitive search for SFRA" request: jsonrpc: "2.0" id: "search-case-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "SFRA" expect: response: jsonrpc: "2.0" id: "search-case-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should handle lowercase search terms" request: jsonrpc: "2.0" id: "search-lowercase-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "api" expect: response: jsonrpc: "2.0" id: "search-lowercase-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" # Multiple word search tests - it: "should find authentication patterns" request: jsonrpc: "2.0" id: "search-auth-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "authentication" expect: response: jsonrpc: "2.0" id: "search-auth-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should find encryption references" request: jsonrpc: "2.0" id: "search-encryption-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "encryption" expect: response: jsonrpc: "2.0" id: "search-encryption-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" # Special characters handling - it: "should handle search terms with special characters" request: jsonrpc: "2.0" id: "search-special-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "REST" expect: response: jsonrpc: "2.0" id: "search-special-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should handle numeric search terms" request: jsonrpc: "2.0" id: "search-numeric-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "HTTPS" expect: response: jsonrpc: "2.0" id: "search-numeric-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" # Common development terms - it: "should find model-related patterns" request: jsonrpc: "2.0" id: "search-model-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "model" expect: response: jsonrpc: "2.0" id: "search-model-1" result: content: - type: "text" text: "match:contains:model" isError: false stderr: "toBeEmpty" - it: "should find configuration patterns" request: jsonrpc: "2.0" id: "search-config-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "config" expect: response: jsonrpc: "2.0" id: "search-config-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" - it: "should find database transaction patterns" request: jsonrpc: "2.0" id: "search-database-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "database" expect: response: jsonrpc: "2.0" id: "search-database-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" # Valid JSON array isError: false stderr: "toBeEmpty" # Performance testing with timing - it: "should respond quickly for validation search" request: jsonrpc: "2.0" id: "search-perf-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "validation" expect: response: jsonrpc: "2.0" id: "search-perf-1" result: content: - type: "text" text: "match:regex:^\\[[\\s\\S]*\\]$" isError: false performance: maxResponseTime: "2000ms" # Documentation search should be fast stderr: "toBeEmpty" - it: "should respond quickly for empty results" request: jsonrpc: "2.0" id: "search-perf-empty-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "zzznomatchesexpected" expect: response: jsonrpc: "2.0" id: "search-perf-empty-1" result: content: - type: "text" text: "match:regex:^\\[\\s*\\]$" isError: false performance: maxResponseTime: "1000ms" # Empty results should be very fast stderr: "toBeEmpty" - it: "should handle errors quickly" request: jsonrpc: "2.0" id: "search-perf-error-1" method: "tools/call" params: name: "search_best_practices" arguments: query: "" expect: response: jsonrpc: "2.0" id: "search-perf-error-1" result: content: - type: "text" text: "match:contains:Error" isError: true performance: maxResponseTime: "500ms" # Error responses should be fastest stderr: "toBeEmpty" ``` -------------------------------------------------------------------------------- /docs/dw_net/SFTPClient.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.net # Class SFTPClient ## Inheritance Hierarchy - Object - dw.net.SFTPClient ## Description The SFTPClient class supports the SFTP commands GET, PUT, DEL, MKDIR, RENAME, and LIST. The transfer of files can be text or binary. Note: when this class is used with sensitive data, be careful in persisting sensitive information. An example usage is as follows: var sftp : SFTPClient = new dw.net.SFTPClient(); sftp.connect("my.sftp-server.com", "username", "password"); var data : String = sftp.get("simple.txt"); sftp.disconnect(); The default connection timeout depends on the script context timeout and will be set to a maximum of 30 seconds (default script context timeout is 10 seconds within storefront requests and 15 minutes within jobs). IMPORTANT NOTE: Before you can make an outbound SFTP connection to a port other than 22, the SFTP server IP address must be enabled for outbound traffic at the Commerce Cloud Digital firewall for your POD. Please file a support request to request a new firewall rule. SSH Version 2 is supported with the following algorithms: TypeAlgorithms Host Keyssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, rsa-sha2-512, rsa-sha2-256, ssh-rsa, ssh-dss Key Exchange (KEX)curve25519-sha256, [email protected], ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1, diffie-hellman-group-exchange-sha1, diffie-hellman-group1-sha1 Cipheraes128-ctr, aes192-ctr, aes256-ctr, [email protected], [email protected], aes128-cbc, 3des-ctr, 3des-cbc, blowfish-cbc, aes192-cbc, aes256-cbc Message Authentication Code (MAC)[email protected], [email protected], [email protected], hmac-sha2-256, hmac-sha2-512, hmac-sha1, hmac-md5, hmac-sha1-96, hmac-md5-96 Public Key Authenticationrsa-sha2-512, rsa-sha2-256, ssh-rsa ## Constants ### MAX_GET_FILE_SIZE **Type:** Number = 209715200 The maximum size for get() methods returning a File is 200 MB. ### MAX_GET_STRING_SIZE **Type:** Number = 10485760 The maximum size for get() methods returning a String is 10 MB. ## Properties ### connected **Type:** boolean (Read Only) Identifies if the SFTP client is currently connected to the SFTP server. ### errorMessage **Type:** String (Read Only) The error message from the last SFTP action. ### identity **Type:** KeyRef Gets the identity (private key) used for the connection. The key is only associated to this instance of the SFTP client. ### timeout **Type:** Number The timeout for this client, in milliseconds. ## Constructor Summary SFTPClient() Constructor. ## Method Summary ### addKnownHostKey **Signature:** `addKnownHostKey(type : String, key : String) : void` Adds a known public host key for the next connection attempt. ### cd **Signature:** `cd(path : String) : boolean` Changes the current directory on the remote server to the given path. ### connect **Signature:** `connect(host : String, user : String, password : String) : boolean` Connects and logs on to a SFTP server and returns a boolean indicating success or failure. ### connect **Signature:** `connect(host : String, port : Number, user : String, password : String) : boolean` Connects and logs on to a SFTP server and returns a boolean indicating success or failure. ### del **Signature:** `del(path : String) : boolean` Deletes the remote file on the server identified by the path parameter. ### disconnect **Signature:** `disconnect() : void` The method first logs the current user out from the server and then disconnects from the server. ### get **Signature:** `get(path : String) : String` Reads the content of a remote file and returns it as a string using "ISO-8859-1" encoding to read it. ### get **Signature:** `get(path : String, encoding : String) : String` Reads the content of a remote file and returns it as a string using the specified encoding. ### get **Signature:** `get(path : String, encoding : String, file : File) : boolean` Reads the content of a remote file and creates a local copy in the given file using the passed string encoding to read the file content and using the system standard encoding "UTF-8" to write the file. ### getBinary **Signature:** `getBinary(path : String, file : File) : boolean` Reads the content of a remote file and creates a local copy in the given file. ### getConnected **Signature:** `getConnected() : boolean` Identifies if the SFTP client is currently connected to the SFTP server. ### getErrorMessage **Signature:** `getErrorMessage() : String` Returns the error message from the last SFTP action. ### getFileInfo **Signature:** `getFileInfo(path : String) : SFTPFileInfo` Returns a SFTPFileInfo objects containing information about the given file/directory path. ### getIdentity **Signature:** `getIdentity() : KeyRef` Gets the identity (private key) used for the connection. ### getTimeout **Signature:** `getTimeout() : Number` Returns the timeout for this client, in milliseconds. ### list **Signature:** `list() : SFTPFileInfo[]` Returns a list of SFTPFileInfo objects containing information about the files in the current directory. ### list **Signature:** `list(path : String) : SFTPFileInfo[]` Returns a list of SFTPFileInfo objects containing information about the files in the remote directory defined by the given path. ### mkdir **Signature:** `mkdir(path : String) : boolean` Creates a directory ### put **Signature:** `put(path : String, content : String) : boolean` Puts the specified content to the specified path using "ISO-8859-1" encoding. ### put **Signature:** `put(path : String, content : String, encoding : String) : boolean` Put the given content to a file on the given path on the SFTP server. ### putBinary **Signature:** `putBinary(path : String, file : File) : boolean` Put the content of the given file into a file on the remote SFTP server with the given absolute path. ### removeDirectory **Signature:** `removeDirectory(path : String) : boolean` Deletes the remote directory on the server identified by the path parameter. ### rename **Signature:** `rename(from : String, to : String) : boolean` Renames an existing file. ### setIdentity **Signature:** `setIdentity(keyRef : KeyRef) : void` Sets the identity (private key) to use for the next connection attempt. ### setTimeout **Signature:** `setTimeout(timeoutMillis : Number) : void` Sets the timeout for connections made with the SFTP client to the given number of milliseconds. ## Constructor Detail ## Method Detail ## Method Details ### addKnownHostKey **Signature:** `addKnownHostKey(type : String, key : String) : void` **Description:** Adds a known public host key for the next connection attempt. This method associates the key to the host used in the subsequent connect method, and must be called prior to connect. The key is not persisted, and is only associated to this instance of the SFTP client. Multiple keys may be added, and the validation will succeed if the remote host matches any of them. The default behavior is to persist and trust an unknown host key if there are no known host keys available. If addKnownHostKey is later used to trust specific a specific key or keys, then any previously persisted keys will be ignored. **Parameters:** - `type`: Type of the host key, such as ssh-rsa - `key`: Public host key, in the same format as OpenSSH. --- ### cd **Signature:** `cd(path : String) : boolean` **Description:** Changes the current directory on the remote server to the given path. **Parameters:** - `path`: the new current directory **Returns:** true if the directory change was okay --- ### connect **Signature:** `connect(host : String, user : String, password : String) : boolean` **Description:** Connects and logs on to a SFTP server and returns a boolean indicating success or failure. **Parameters:** - `host`: Name of the SFTP sever - `user`: Username for the login - `password`: Password for the login **Returns:** true when connection is successful, false otherwise. --- ### connect **Signature:** `connect(host : String, port : Number, user : String, password : String) : boolean` **Description:** Connects and logs on to a SFTP server and returns a boolean indicating success or failure. **Parameters:** - `host`: Name of the SFTP sever - `port`: Port for SFTP server - `user`: Username for the login - `password`: Password for the login **Returns:** true when connection is successful, false otherwise. --- ### del **Signature:** `del(path : String) : boolean` **Description:** Deletes the remote file on the server identified by the path parameter. **Parameters:** - `path`: the path to the file. **Returns:** true if the file was successfully deleted, false otherwise. --- ### disconnect **Signature:** `disconnect() : void` **Description:** The method first logs the current user out from the server and then disconnects from the server. --- ### get **Signature:** `get(path : String) : String` **Description:** Reads the content of a remote file and returns it as a string using "ISO-8859-1" encoding to read it. Files with at most MAX_GET_STRING_SIZE bytes are read. **Parameters:** - `path`: remote path of the file to be read. **Returns:** the contents of the file or null if an error occurred while reading the file. --- ### get **Signature:** `get(path : String, encoding : String) : String` **Description:** Reads the content of a remote file and returns it as a string using the specified encoding. Files with at most MAX_GET_STRING_SIZE bytes are read. **Parameters:** - `path`: the remote path of the file to be read. - `encoding`: the encoding to use. **Returns:** the contents of the file or null if an error occurred while reading the file. --- ### get **Signature:** `get(path : String, encoding : String, file : File) : boolean` **Description:** Reads the content of a remote file and creates a local copy in the given file using the passed string encoding to read the file content and using the system standard encoding "UTF-8" to write the file. Copies at most MAX_GET_FILE_SIZE bytes. **Parameters:** - `path`: the remote path of the file to be read. - `encoding`: the encoding to use. - `file`: the local file name **Returns:** true if complete remote file is fetched and copied into local file. false, otherwise. --- ### getBinary **Signature:** `getBinary(path : String, file : File) : boolean` **Description:** Reads the content of a remote file and creates a local copy in the given file. Copies at most MAX_GET_FILE_SIZE bytes. The SFTP transfer is done in binary mode. **Parameters:** - `path`: the remote path of the file to be read. - `file`: the local file name **Returns:** true if complete remote file is fetched and copied into local file. false otherwise --- ### getConnected **Signature:** `getConnected() : boolean` **Description:** Identifies if the SFTP client is currently connected to the SFTP server. **Returns:** true if the client is currently connected. --- ### getErrorMessage **Signature:** `getErrorMessage() : String` **Description:** Returns the error message from the last SFTP action. **Returns:** the error message from the last SFTP action --- ### getFileInfo **Signature:** `getFileInfo(path : String) : SFTPFileInfo` **Description:** Returns a SFTPFileInfo objects containing information about the given file/directory path. **Parameters:** - `path`: the remote path from which the file info should be listed. **Returns:** the remote file information or null if not present. --- ### getIdentity **Signature:** `getIdentity() : KeyRef` **Description:** Gets the identity (private key) used for the connection. The key is only associated to this instance of the SFTP client. **Returns:** Reference to the private key, or null if not configured --- ### getTimeout **Signature:** `getTimeout() : Number` **Description:** Returns the timeout for this client, in milliseconds. **Returns:** the timeout in milliseconds --- ### list **Signature:** `list() : SFTPFileInfo[]` **Description:** Returns a list of SFTPFileInfo objects containing information about the files in the current directory. **Returns:** list of objects with remote file information or null if not present. --- ### list **Signature:** `list(path : String) : SFTPFileInfo[]` **Description:** Returns a list of SFTPFileInfo objects containing information about the files in the remote directory defined by the given path. **Parameters:** - `path`: the remote path from which the file info should be listed. **Returns:** list of objects with remote file information or null if not present. --- ### mkdir **Signature:** `mkdir(path : String) : boolean` **Description:** Creates a directory **Parameters:** - `path`: the path to the directory to create. **Returns:** true if the directory was successfully created, false otherwise. --- ### put **Signature:** `put(path : String, content : String) : boolean` **Description:** Puts the specified content to the specified path using "ISO-8859-1" encoding. If the content of a local file is to be uploaded, please use method putBinary(String,File) instead. NOTE: If the remote file already exists, it is overwritten. **Parameters:** - `path`: the path on the remote SFTP server, where the file will be stored. - `content`: the content to put. **Returns:** true or false indicating success or failure. --- ### put **Signature:** `put(path : String, content : String, encoding : String) : boolean` **Description:** Put the given content to a file on the given path on the SFTP server. The transformation from String into binary data is done via the encoding provided with the method call. If the content of a local file is to be uploaded, please use method putBinary(String,File) instead. NOTE: If the remote file already exists, it is overwritten. **Parameters:** - `path`: the path on the remote SFTP server, where the file will be stored. - `content`: the content to put. - `encoding`: the encoding to use. **Returns:** true or false indicating success or failure. --- ### putBinary **Signature:** `putBinary(path : String, file : File) : boolean` **Description:** Put the content of the given file into a file on the remote SFTP server with the given absolute path. NOTE: If the remote file already exists, it is overwritten. **Parameters:** - `path`: the path on the remote SFTP server where the file will be stored. - `file`: the file on the local system, which content is send to the remote SFTP server. **Returns:** true or false indicating success or failure. --- ### removeDirectory **Signature:** `removeDirectory(path : String) : boolean` **Description:** Deletes the remote directory on the server identified by the path parameter. In order to delete the directory successfully the directory needs to be empty, otherwise the removeDirectory() method will return false. **Parameters:** - `path`: the path to the directory. **Returns:** true if the directory was successfully deleted, false otherwise. --- ### rename **Signature:** `rename(from : String, to : String) : boolean` **Description:** Renames an existing file. **Parameters:** - `from`: the file that will be renamed. - `to`: the name of the new file. **Returns:** true if the file was successfully renamed, false otherwise. --- ### setIdentity **Signature:** `setIdentity(keyRef : KeyRef) : void` **Description:** Sets the identity (private key) to use for the next connection attempt. The key is only associated to this instance of the SFTP client. **Parameters:** - `keyRef`: Reference to the private key --- ### setTimeout **Signature:** `setTimeout(timeoutMillis : Number) : void` **Description:** Sets the timeout for connections made with the SFTP client to the given number of milliseconds. If the given timeout is less than or equal to zero, the timeout is set to the same value as the script context timeout but will only be set to a maximum of 30 seconds. The maximum and default timeout depend on the script context timeout. The maximum timeout is set to a maximum of 2 minutes. The default timeout for a new client is set to a maximum of 30 seconds. This method can be called at any time, and will affect the next connection made with this client. It is not possible to set the timeout for an open connection. **Parameters:** - `timeoutMillis`: timeout, in milliseconds, up to a maximum of 2 minutes. --- ``` -------------------------------------------------------------------------------- /docs/dw_content/ContentSearchModel.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.content # Class ContentSearchModel ## Inheritance Hierarchy - Object - dw.catalog.SearchModel - dw.content.ContentSearchModel ## Description The class is the central interface to a content search result and a content search refinement. It also provides utility methods to generate a search URL. ## Constants ### CONTENTID_PARAMETER **Type:** String = "cid" URL Parameter for the content ID ### FOLDERID_PARAMETER **Type:** String = "fdid" URL Parameter for the folder ID ## Properties ### content **Type:** Iterator (Read Only) An Iterator containing all Content Assets that are the result of the search. ### contentID **Type:** String The content ID against which the search results apply. ### deepestCommonFolder **Type:** Folder (Read Only) The deepest common folder of all content assets in the search result. ### filteredByFolder **Type:** boolean The method returns true, if the content search result is filtered by the folder and it is not subsequently possible to search for content assets that belong to no folder (e.g. those for Page Designer). ### folder **Type:** Folder (Read Only) The folder against which the search results apply. ### folderID **Type:** String The folder ID against which the search results apply. ### folderSearch **Type:** boolean (Read Only) The method returns true, if this is a pure search for a folder. The method checks, that a folder ID is specified and no search phrase is specified. ### pageMetaTags **Type:** Array (Read Only) All page meta tags, defined for this instance for which content can be generated. The meta tag content is generated based on the content listing page meta tag context and rules. The rules are obtained from the current folder context or inherited from the parent folder, up to the root folder. ### recursiveFolderSearch **Type:** boolean Get the flag that determines if the folder search will be recursive. ### refinedByFolder **Type:** boolean (Read Only) The method returns true, if the search is refined by a folder. The method checks, that a folder ID is specified. ### refinedFolderSearch **Type:** boolean (Read Only) Identifies if this is a folder search and is refined with further criteria, like a name refinement or an attribute refinement. ### refinements **Type:** ContentSearchRefinements (Read Only) The set of search refinements used in this search. ## Constructor Summary ContentSearchModel() Constructs a new ContentSearchModel. ## Method Summary ### getContent **Signature:** `getContent() : Iterator` Returns an Iterator containing all Content Assets that are the result of the search. ### getContentID **Signature:** `getContentID() : String` Returns the content ID against which the search results apply. ### getDeepestCommonFolder **Signature:** `getDeepestCommonFolder() : Folder` Returns the deepest common folder of all content assets in the search result. ### getFolder **Signature:** `getFolder() : Folder` Returns the folder against which the search results apply. ### getFolderID **Signature:** `getFolderID() : String` Returns the folder ID against which the search results apply. ### getPageMetaTag **Signature:** `getPageMetaTag(id : String) : PageMetaTag` Returns the page meta tag for the specified id. ### getPageMetaTags **Signature:** `getPageMetaTags() : Array` Returns all page meta tags, defined for this instance for which content can be generated. ### getRefinements **Signature:** `getRefinements() : ContentSearchRefinements` Returns the set of search refinements used in this search. ### isFilteredByFolder **Signature:** `isFilteredByFolder() : boolean` The method returns true, if the content search result is filtered by the folder and it is not subsequently possible to search for content assets that belong to no folder (e.g. ### isFolderSearch **Signature:** `isFolderSearch() : boolean` The method returns true, if this is a pure search for a folder. ### isRecursiveFolderSearch **Signature:** `isRecursiveFolderSearch() : boolean` Get the flag that determines if the folder search will be recursive. ### isRefinedByFolder **Signature:** `isRefinedByFolder() : boolean` The method returns true, if the search is refined by a folder. ### isRefinedFolderSearch **Signature:** `isRefinedFolderSearch() : boolean` Identifies if this is a folder search and is refined with further criteria, like a name refinement or an attribute refinement. ### search **Signature:** `search() : SearchStatus` Execute the search. ### setContentID **Signature:** `setContentID(contentID : String) : void` Sets the contentID used in this search. ### setFilteredByFolder **Signature:** `setFilteredByFolder(filteredByFolder : boolean) : void` Set a flag to indicate if the search is filtered by the folder. ### setFolderID **Signature:** `setFolderID(folderID : String) : void` Sets the folderID used in this search. ### setRecursiveFolderSearch **Signature:** `setRecursiveFolderSearch(recurse : boolean) : void` Set a flag to indicate if the search in folder should be recursive. ### urlForContent **Signature:** `static urlForContent(action : String, cid : String) : URL` Returns an URL that you can use to execute a query for a specific Content. ### urlForContent **Signature:** `static urlForContent(url : URL, cid : String) : URL` Returns an URL that you can use to execute a query for a specific Content. ### urlForFolder **Signature:** `static urlForFolder(action : String, fid : String) : URL` Returns an URL that you can use to execute a query for a specific Folder. ### urlForFolder **Signature:** `static urlForFolder(url : URL, fid : String) : URL` Returns an URL that you can use to execute a query for a specific Folder. ### urlForRefine **Signature:** `static urlForRefine(action : String, name : String, value : String) : URL` Returns an URL that you can use to execute a query for a specific attribute name-value pair. ### urlForRefine **Signature:** `static urlForRefine(url : URL, name : String, value : String) : URL` Returns an URL that you can use to execute a query for a specific attribute name-value pair. ### urlRefineFolder **Signature:** `urlRefineFolder(action : String, refineFolderID : String) : URL` Returns an URL that you can use to re-execute the query using the specified pipeline action and folder refinement. ### urlRefineFolder **Signature:** `urlRefineFolder(url : URL, refineFolderID : String) : URL` Returns an URL that you can use to re-execute the query using the specified URL and folder refinement. ### urlRelaxFolder **Signature:** `urlRelaxFolder(action : String) : URL` Returns an URL that you can use to re-execute the query with no folder refinement. ### urlRelaxFolder **Signature:** `urlRelaxFolder(url : URL) : URL` Returns an URL that you can use to re-execute the query with no folder refinement. ## Constructor Detail ## Method Detail ## Method Details ### getContent **Signature:** `getContent() : Iterator` **Description:** Returns an Iterator containing all Content Assets that are the result of the search. **Returns:** an Iterator containing all Content Assets that are the result of the search. --- ### getContentID **Signature:** `getContentID() : String` **Description:** Returns the content ID against which the search results apply. **Returns:** the content ID against which the search results apply. --- ### getDeepestCommonFolder **Signature:** `getDeepestCommonFolder() : Folder` **Description:** Returns the deepest common folder of all content assets in the search result. **Returns:** the deepest common folder of all content assets in the search result of this search model. --- ### getFolder **Signature:** `getFolder() : Folder` **Description:** Returns the folder against which the search results apply. **Returns:** the folder against which the search results apply. --- ### getFolderID **Signature:** `getFolderID() : String` **Description:** Returns the folder ID against which the search results apply. **Returns:** the folder ID against which the search results apply. --- ### getPageMetaTag **Signature:** `getPageMetaTag(id : String) : PageMetaTag` **Description:** Returns the page meta tag for the specified id. The meta tag content is generated based on the content listing page meta tag context and rule. The rule is obtained from the current folder context or inherited from the parent folder, up to the root folder. Null will be returned if the meta tag is undefined on the current instance, or if no rule can be found for the current context, or if the rule resolves to an empty string. **Parameters:** - `id`: the ID to get the page meta tag for **Returns:** page meta tag containing content generated based on rules --- ### getPageMetaTags **Signature:** `getPageMetaTags() : Array` **Description:** Returns all page meta tags, defined for this instance for which content can be generated. The meta tag content is generated based on the content listing page meta tag context and rules. The rules are obtained from the current folder context or inherited from the parent folder, up to the root folder. **Returns:** page meta tags defined for this instance, containing content generated based on rules --- ### getRefinements **Signature:** `getRefinements() : ContentSearchRefinements` **Description:** Returns the set of search refinements used in this search. **Returns:** the set of search refinements used in this search. --- ### isFilteredByFolder **Signature:** `isFilteredByFolder() : boolean` **Description:** The method returns true, if the content search result is filtered by the folder and it is not subsequently possible to search for content assets that belong to no folder (e.g. those for Page Designer). **Returns:** True if this is filtered by the folder of the content asset. --- ### isFolderSearch **Signature:** `isFolderSearch() : boolean` **Description:** The method returns true, if this is a pure search for a folder. The method checks, that a folder ID is specified and no search phrase is specified. **Returns:** True if this is a folder search. --- ### isRecursiveFolderSearch **Signature:** `isRecursiveFolderSearch() : boolean` **Description:** Get the flag that determines if the folder search will be recursive. **Returns:** true if the folder search will be recursive, false otherwise --- ### isRefinedByFolder **Signature:** `isRefinedByFolder() : boolean` **Description:** The method returns true, if the search is refined by a folder. The method checks, that a folder ID is specified. **Returns:** true, if the search is refined by a folder, false otherwise. --- ### isRefinedFolderSearch **Signature:** `isRefinedFolderSearch() : boolean` **Description:** Identifies if this is a folder search and is refined with further criteria, like a name refinement or an attribute refinement. **Returns:** true if this is a folder search and is refined with further criteria, false otherwise. --- ### search **Signature:** `search() : SearchStatus` **Description:** Execute the search. **Returns:** the searchStatus object with search status code and description of search result. --- ### setContentID **Signature:** `setContentID(contentID : String) : void` **Description:** Sets the contentID used in this search. **Parameters:** - `contentID`: the contentID used in this search. --- ### setFilteredByFolder **Signature:** `setFilteredByFolder(filteredByFolder : boolean) : void` **Description:** Set a flag to indicate if the search is filtered by the folder. Must be set to false to return content assets that do not belong to any folder. **Parameters:** - `filteredByFolder`: filter the search result by folder --- ### setFolderID **Signature:** `setFolderID(folderID : String) : void` **Description:** Sets the folderID used in this search. **Parameters:** - `folderID`: the folderID used in this search. --- ### setRecursiveFolderSearch **Signature:** `setRecursiveFolderSearch(recurse : boolean) : void` **Description:** Set a flag to indicate if the search in folder should be recursive. **Parameters:** - `recurse`: recurse the folder in the search --- ### urlForContent **Signature:** `static urlForContent(action : String, cid : String) : URL` **Description:** Returns an URL that you can use to execute a query for a specific Content. The passed action is used to build an initial url. All search specific attributes are appended. **Parameters:** - `action`: the pipeline action to use. - `cid`: the content id. **Returns:** an URL that you can use to execute a query for a specific Content. The passed action is used to build an initial url. All search specific attributes are appended. --- ### urlForContent **Signature:** `static urlForContent(url : URL, cid : String) : URL` **Description:** Returns an URL that you can use to execute a query for a specific Content. The passed url can be either a full url or just the name for a pipeline. In the later case a relative URL is created. **Parameters:** - `url`: the URL to use when constructing the new URL. - `cid`: the content id. **Returns:** an URL that you can use to execute a query for a specific Content. The passed url can be either a full url or just the name for a pipeline. In the later case a relative URL is created. --- ### urlForFolder **Signature:** `static urlForFolder(action : String, fid : String) : URL` **Description:** Returns an URL that you can use to execute a query for a specific Folder. **Parameters:** - `action`: the pipeline action to use. - `fid`: the id of the Folder to use. **Returns:** an URL that you can use to execute a query for a specific Folder. --- ### urlForFolder **Signature:** `static urlForFolder(url : URL, fid : String) : URL` **Description:** Returns an URL that you can use to execute a query for a specific Folder. **Parameters:** - `url`: the URL to use in constructing the new URL. - `fid`: the id of the Folder to use. **Returns:** an URL that you can use to execute a query for a specific Folder. --- ### urlForRefine **Signature:** `static urlForRefine(action : String, name : String, value : String) : URL` **Description:** Returns an URL that you can use to execute a query for a specific attribute name-value pair. **Parameters:** - `action`: the pipeline action to use. - `name`: the name of the attribute. - `value`: the value for the attribute. **Returns:** an URL that you can use to execute a query for a specific attribute name-value pair. --- ### urlForRefine **Signature:** `static urlForRefine(url : URL, name : String, value : String) : URL` **Description:** Returns an URL that you can use to execute a query for a specific attribute name-value pair. **Parameters:** - `url`: the URL to use when constructing the new URL. - `name`: the name of the attribute. - `value`: the value for the attribute. **Returns:** an URL that you can use to execute a query for a specific attribute name-value pair. --- ### urlRefineFolder **Signature:** `urlRefineFolder(action : String, refineFolderID : String) : URL` **Description:** Returns an URL that you can use to re-execute the query using the specified pipeline action and folder refinement. **Parameters:** - `action`: the action to use. - `refineFolderID`: the folder ID to use as a refinement. **Returns:** an URL that you can use to re-execute the exact same query using the specified pipeline action and folder refinement. --- ### urlRefineFolder **Signature:** `urlRefineFolder(url : URL, refineFolderID : String) : URL` **Description:** Returns an URL that you can use to re-execute the query using the specified URL and folder refinement. **Parameters:** - `url`: the existing URL to use when constructing the new URL. - `refineFolderID`: the ID of the folder refinement to use. **Returns:** an URL that you can use to re-execute the query using the specified URL and folder refinement. --- ### urlRelaxFolder **Signature:** `urlRelaxFolder(action : String) : URL` **Description:** Returns an URL that you can use to re-execute the query with no folder refinement. **Parameters:** - `action`: the pipeline action to use in the URL. **Returns:** an URL that you can use to re-execute the query with no folder refinement. --- ### urlRelaxFolder **Signature:** `urlRelaxFolder(url : URL) : URL` **Description:** Returns an URL that you can use to re-execute the query with no folder refinement. **Parameters:** - `url`: the existing URL to use when constructing the new URL. **Returns:** an URL that you can use to re-execute the query with no folder refinement. --- ``` -------------------------------------------------------------------------------- /docs/dw_customer/Credentials.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.customer # Class Credentials ## Inheritance Hierarchy - Object - dw.customer.Credentials ## Description Represents the credentials of a customer. Since 13.6 it is possible to have customers who are not authenticated through a login and password but through an external authentication provider via the OAuth2 protocol. In such cases, the AuthenticationProviderID will point to an OAuth provider configured in the system and the ExternalID will be the unique identifier of the customer on the Authentication Provider's system. For example, if an authentication provider with ID "Google123" is configured pointing to Google and the customer has a logged in into Google in the past and has created a profile there, Google assigns a unique number identifier to that customer. If the storefront is configured to allow authentication through Google and a new customer logs into the storefront using Google, the AuthenticationProviderID property of his Credentials will contain "Google123" and the ExternalID property will contain whatever unique identifier Google has assigned to him. In such cases the password-related properties of the Credentials will be empty. Note: this class handles sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, and 12. ## Properties ### authenticationProviderID **Type:** String The authentication provider ID. ### enabled **Type:** boolean (Read Only) Identifies if this customer is enabled and can log in. ### enabledFlag **Type:** boolean Identifies if this customer is enabled and can log in - same as isEnabled(). ### externalID **Type:** String The external ID of the customer. ### locked **Type:** boolean (Read Only) Identifies if this customer is temporarily locked out because of invalid login attempts. If customer locking is not enabled, this method always returns false. ### login **Type:** String The login of the user. It must be unique. ### passwordAnswer **Type:** String The answer to the password question for the customer. The answer is used with the password question to confirm the identity of a customer when they are trying to fetch their password. ### passwordQuestion **Type:** String The password question for the customer. The password question is used with the password answer to confirm the identity of a customer when they are trying to fetch their password. ### passwordSet **Type:** boolean (Read Only) Returns whether the password is set. Creating externally authenticated customers results in customers with credentials for which the password is not set. ### remainingLoginAttempts **Type:** Number (Read Only) The number of consecutive failed logins after which this customer will be temporarily locked out and prevented from logging in to the current site. This value is based on the number of previous invalid logins for this customer and customer site preferences defining the limits. If this customer is already locked out, this method will always return 0. If customer locking is disabled altogether, or if the system cannot determine the number of failed login attempts for this customer, then this method will return a negative number. ## Constructor Summary ## Method Summary ### createResetPasswordToken **Signature:** `createResetPasswordToken() : String` Generate a random token which can be used for resetting the password of the underlying Customer. ### getAuthenticationProviderID **Signature:** `getAuthenticationProviderID() : String` Returns the authentication provider ID. ### getEnabledFlag **Signature:** `getEnabledFlag() : boolean` Identifies if this customer is enabled and can log in - same as isEnabled(). ### getExternalID **Signature:** `getExternalID() : String` Returns the external ID of the customer. ### getLogin **Signature:** `getLogin() : String` Returns the login of the user. ### getPasswordAnswer **Signature:** `getPasswordAnswer() : String` Returns the answer to the password question for the customer. ### getPasswordQuestion **Signature:** `getPasswordQuestion() : String` Returns the password question for the customer. ### getRemainingLoginAttempts **Signature:** `getRemainingLoginAttempts() : Number` Returns the number of consecutive failed logins after which this customer will be temporarily locked out and prevented from logging in to the current site. ### isEnabled **Signature:** `isEnabled() : boolean` Identifies if this customer is enabled and can log in. ### isLocked **Signature:** `isLocked() : boolean` Identifies if this customer is temporarily locked out because of invalid login attempts. ### isPasswordSet **Signature:** `isPasswordSet() : boolean` Returns whether the password is set. ### setAuthenticationProviderID **Signature:** `setAuthenticationProviderID(authenticationProviderID : String) : void` Sets the authentication provider ID corresponding to an OAuth provider configured in the system. ### setEnabledFlag **Signature:** `setEnabledFlag(enabledFlag : boolean) : void` Sets the enabled status of the customer. ### setExternalID **Signature:** `setExternalID(externalID : String) : void` Sets the external ID of the customer at the authentication provider. ### setLogin **Signature:** `setLogin(login : String) : void` Sets the login value for the customer. ### setLogin **Signature:** `setLogin(newLogin : String, currentPassword : String) : boolean` Sets the login value for the customer, and also re-encrypt the customer password based on the new login. ### setPassword **Signature:** `setPassword(newPassword : String, oldPassword : String, verifyOldPassword : boolean) : Status` Sets the password of an authenticated customer. The method can be called for externally authenticated customers as well but these customers will still be externally authenticated so calling the method for such customers does not have an immediate practical benefit. ### setPasswordAnswer **Signature:** `setPasswordAnswer(answer : String) : void` Sets the answer to the password question for the customer. ### setPasswordQuestion **Signature:** `setPasswordQuestion(question : String) : void` Sets the password question for the customer. ### setPasswordWithToken **Signature:** `setPasswordWithToken(token : String, newPassword : String) : Status` Set the password of the specified customer to the specified value. ## Method Detail ## Method Details ### createResetPasswordToken **Signature:** `createResetPasswordToken() : String` **Description:** Generate a random token which can be used for resetting the password of the underlying Customer. The token is guaranteed to be unique and will be valid for 30 minutes. Any token previously generated for this customer will be invalidated. **Returns:** The generated token. --- ### getAuthenticationProviderID **Signature:** `getAuthenticationProviderID() : String` **Description:** Returns the authentication provider ID. **Deprecated:** As of release 17.2, replaced by methods on the new class ExternalProfile which can be obtained from Customer.getExternalProfiles() Until the method is fully removed from the API it will get the Authentication Provider from the first element of the Customer.getExternalProfiles() collection **Returns:** the authentication provider ID. --- ### getEnabledFlag **Signature:** `getEnabledFlag() : boolean` **Description:** Identifies if this customer is enabled and can log in - same as isEnabled(). **Returns:** true if the customer is enabled and can log in, false otherwise. --- ### getExternalID **Signature:** `getExternalID() : String` **Description:** Returns the external ID of the customer. **Deprecated:** As of release 17.2, replaced by methods on the new class ExternalProfile which can be obtained from Customer.getExternalProfiles() Until the method is fully removed from the API it will get the External ID from the first element of the Customer.getExternalProfiles() collection **Returns:** the external ID of the customer. --- ### getLogin **Signature:** `getLogin() : String` **Description:** Returns the login of the user. It must be unique. **Returns:** the login of the user. --- ### getPasswordAnswer **Signature:** `getPasswordAnswer() : String` **Description:** Returns the answer to the password question for the customer. The answer is used with the password question to confirm the identity of a customer when they are trying to fetch their password. **Returns:** the answer to the password question for the customer. --- ### getPasswordQuestion **Signature:** `getPasswordQuestion() : String` **Description:** Returns the password question for the customer. The password question is used with the password answer to confirm the identity of a customer when they are trying to fetch their password. **Returns:** the password question for the customer. --- ### getRemainingLoginAttempts **Signature:** `getRemainingLoginAttempts() : Number` **Description:** Returns the number of consecutive failed logins after which this customer will be temporarily locked out and prevented from logging in to the current site. This value is based on the number of previous invalid logins for this customer and customer site preferences defining the limits. If this customer is already locked out, this method will always return 0. If customer locking is disabled altogether, or if the system cannot determine the number of failed login attempts for this customer, then this method will return a negative number. **Returns:** The number of consecutive failed logins after which this customer will be locked out. --- ### isEnabled **Signature:** `isEnabled() : boolean` **Description:** Identifies if this customer is enabled and can log in. **Returns:** true if the customer is enabled and can log in, false otherwise. --- ### isLocked **Signature:** `isLocked() : boolean` **Description:** Identifies if this customer is temporarily locked out because of invalid login attempts. If customer locking is not enabled, this method always returns false. **Returns:** true if the customer is locked, false otherwise. --- ### isPasswordSet **Signature:** `isPasswordSet() : boolean` **Description:** Returns whether the password is set. Creating externally authenticated customers results in customers with credentials for which the password is not set. **Returns:** true if the password is set. --- ### setAuthenticationProviderID **Signature:** `setAuthenticationProviderID(authenticationProviderID : String) : void` **Description:** Sets the authentication provider ID corresponding to an OAuth provider configured in the system. **Deprecated:** As of release 17.2, replaced by methods on the new class ExternalProfile which can be obtained from Customer.getExternalProfiles() Until the method is fully removed from the API it will set the Authentication Provider on the first element of the Customer.getExternalProfiles() collection if there is only one. It will create the collection and add an element if no elements are present. It will not change anything and will log an error if there are more than one elements in the collection. **Parameters:** - `authenticationProviderID`: the authentication Provider ID to set. --- ### setEnabledFlag **Signature:** `setEnabledFlag(enabledFlag : boolean) : void` **Description:** Sets the enabled status of the customer. **Parameters:** - `enabledFlag`: controls if a customer is enabled or not. --- ### setExternalID **Signature:** `setExternalID(externalID : String) : void` **Description:** Sets the external ID of the customer at the authentication provider. The value is provided by the authentication provider during the OAuth authentication and is unique within that provider. **Deprecated:** As of release 17.2, replaced by methods on the new class ExternalProfile which can be obtained from Customer.getExternalProfiles() Until the method is fully removed from the API it will set the ExternalID on the first element of the Customer.getExternalProfiles() collection if there is only one. It will create the collection and add an element if no elements are present. It will not change anything and will log an error if there are more than one elements in the collection. **Parameters:** - `externalID`: the external ID to set. --- ### setLogin **Signature:** `setLogin(login : String) : void` **Description:** Sets the login value for the customer. IMPORTANT: This method should no longer be used for the following reasons: It changes the login without re-encrypting the password. (The customer password is stored internally using a one-way encryption scheme which uses the login as one of its inputs. Therefore changing the login requires re-encrypting the password.) It does not validate the structure of the login to ensure that it only uses acceptable characters. It does not correctly prevent duplicate logins. If the passed login matches a different customer's login exactly, then this method will throw an exception. However, it does not prevent the creation of inexact matches, where two customers have a login differing only by alphabetic case (e.g. "JaneDoe" and "janedoe") **Deprecated:** Use setLogin(String, String) **Parameters:** - `login`: The login value for the customer. --- ### setLogin **Signature:** `setLogin(newLogin : String, currentPassword : String) : boolean` **Description:** Sets the login value for the customer, and also re-encrypt the customer password based on the new login. Customer login must be a sequence of letters, numbers, and the following characters: space, period, ampersand, underscore and dash. This method fails to set the login and returns false in the following cases: newLogin is of an invalid form (e.g. contains invalid characters). currentPassword is not the customer's correct password. newLogin is already in use by another customer (i.e. there is another customer in the system with the exact same login name or a name differing only by alphabetic case.) If newLogin is the same as the existing login, the method does nothing and returns true, regardless of whether currentPassword is the correct password. **Parameters:** - `newLogin`: The login value for the customer. - `currentPassword`: The customer's current password in plain-text. **Returns:** true if setting the login succeeded, false otherwise. --- ### setPassword **Signature:** `setPassword(newPassword : String, oldPassword : String, verifyOldPassword : boolean) : Status` **Description:** Sets the password of an authenticated customer. The method can be called for externally authenticated customers as well but these customers will still be externally authenticated so calling the method for such customers does not have an immediate practical benefit. If such customers are converted back to regularly authenticated (via login and password) the new password will be used. Method call will fail under any of these conditions: customer is not registered customer is not authenticated verifyOldPassword=true && oldPassword is empty verifyOldPassword=true and oldPassword does not match the existing password newPassword is empty newPassword does not meet acceptance criteria **Parameters:** - `newPassword`: the new password - `oldPassword`: the old password (optional, only needed if 'verifyOldPassword' is set to 'true' - `verifyOldPassword`: whether the oldPassword should be verified **Returns:** Status the status of the operation (OK or ERROR). If status is Error, there will be additional information in the Status message --- ### setPasswordAnswer **Signature:** `setPasswordAnswer(answer : String) : void` **Description:** Sets the answer to the password question for the customer. **Parameters:** - `answer`: the answer to the password question. --- ### setPasswordQuestion **Signature:** `setPasswordQuestion(question : String) : void` **Description:** Sets the password question for the customer. **Parameters:** - `question`: the password question. --- ### setPasswordWithToken **Signature:** `setPasswordWithToken(token : String, newPassword : String) : Status` **Description:** Set the password of the specified customer to the specified value. This operation will fail if the specified token is invalid (i.e. not associated with the specified Customer), the token is expired, or the password does not satisfy system password requirements. **Parameters:** - `token`: The token required for performing the password reset. - `newPassword`: The new password. Must meet all requirements for passwords **Returns:** Status the status of the operation (OK or ERROR). If status is Error, there will be additional information in the Status message --- ``` -------------------------------------------------------------------------------- /tests/mcp/yaml/tools.docs-only.test.mcp.yml: -------------------------------------------------------------------------------- ```yaml # ================================================================================== # SFCC MCP Server - Documentation-Only Mode YAML Tests # Focused on comprehensive tool metadata validation (NOT tool execution) # Uses conductor's enhanced pattern matching for thorough validation # # Quick Test Commands: # aegis "tests/mcp/yaml/docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --verbose # aegis "tests/mcp/yaml/docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --debug --timing # aegis query --config "aegis.config.docs-only.json" # List all tools # aegis query get_sfcc_class_info '{"className": "Catalog"}' --config "aegis.config.docs-only.json" # ================================================================================== description: "SFCC MCP Server docs-only mode - comprehensive tool metadata validation" # ================================================================================== # TOOL DISCOVERY & STRUCTURE VALIDATION # ================================================================================== tests: - it: "should provide tools list with proper JSON-RPC structure" request: jsonrpc: "2.0" id: "tool-list-basic" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-list-basic" result: tools: "match:type:array" stderr: "toBeEmpty" - it: "should provide exactly 15 tools in docs-only mode" request: jsonrpc: "2.0" id: "tool-count-exact" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-count-exact" result: tools: "match:arrayLength:15" stderr: "toBeEmpty" - it: "should have non-empty tools array" request: jsonrpc: "2.0" id: "tool-count-min" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-count-min" result: tools: "match:not:arrayLength:0" stderr: "toBeEmpty" - it: "should have tools array with reasonable size" request: jsonrpc: "2.0" id: "tool-count-range" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-count-range" result: tools: "match:arrayLength:15" stderr: "toBeEmpty" # ================================================================================== # TOOL METADATA STRUCTURE VALIDATION # ================================================================================== - it: "should have valid tool structure with required fields" request: jsonrpc: "2.0" id: "schema-validation-1" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "schema-validation-1" result: tools: match:arrayElements: match:partial: name: "match:type:string" description: "match:type:string" inputSchema: "match:type:object" stderr: "toBeEmpty" - it: "should have tool names following snake_case convention" request: jsonrpc: "2.0" id: "tool-names-format" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-names-format" result: tools: match:arrayElements: match:partial: name: "match:regex:^[a-z][a-z0-9_]*$" stderr: "toBeEmpty" - it: "should have meaningful tool descriptions" request: jsonrpc: "2.0" id: "tool-descriptions-quality" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-descriptions-quality" result: tools: match:arrayElements: match:partial: description: "match:regex:.{20,}" # At least 20 characters stderr: "toBeEmpty" - it: "should have non-empty tool descriptions" request: jsonrpc: "2.0" id: "tool-descriptions-nonempty" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-descriptions-nonempty" result: tools: match:arrayElements: match:partial: description: "match:not:regex:^\\s*$" # Not empty or whitespace-only stderr: "toBeEmpty" - it: "should have proper inputSchema structure" request: jsonrpc: "2.0" id: "tool-schema-structure" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-schema-structure" result: tools: match:arrayElements: match:partial: inputSchema: type: "object" properties: "match:type:object" stderr: "toBeEmpty" # ================================================================================== # TOOL NAME EXTRACTION & VALIDATION # ================================================================================== - it: "should extract all expected SFCC tool names" request: jsonrpc: "2.0" id: "tool-names-extract" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-names-extract" result: match:extractField: "tools.*.name" value: "match:arrayContains:get_sfcc_class_info" stderr: "toBeEmpty" - it: "should contain SFCC class documentation tools" request: jsonrpc: "2.0" id: "tool-names-sfcc-class" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-names-sfcc-class" result: match:extractField: "tools.*.name" value: "match:arrayContains:search_sfcc_classes" stderr: "toBeEmpty" - it: "should contain best practice guide tools" request: jsonrpc: "2.0" id: "tool-names-best-practices" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-names-best-practices" result: match:extractField: "tools.*.name" value: "match:arrayContains:get_best_practice_guide" stderr: "toBeEmpty" - it: "should contain SFRA documentation tools" request: jsonrpc: "2.0" id: "tool-names-sfra" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-names-sfra" result: match:extractField: "tools.*.name" value: "match:arrayContains:get_sfra_document" stderr: "toBeEmpty" - it: "should contain cartridge generation tools" request: jsonrpc: "2.0" id: "tool-names-cartridge" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-names-cartridge" result: match:extractField: "tools.*.name" value: "match:arrayContains:generate_cartridge_structure" stderr: "toBeEmpty" # ================================================================================== # TOOL SCHEMA VALIDATION BY CATEGORY # ================================================================================== - it: "should have required parameters in SFCC class tools" request: jsonrpc: "2.0" id: "schema-sfcc-class-required" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "schema-sfcc-class-required" result: tools: "match:arrayContains:name:get_sfcc_class_info" stderr: "toBeEmpty" - it: "should have string type parameters where expected" request: jsonrpc: "2.0" id: "schema-parameter-types" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "schema-parameter-types" result: match:extractField: "tools.*.inputSchema.properties" value: "match:type:array" stderr: "toBeEmpty" # ================================================================================== # CROSS-FIELD VALIDATION # ================================================================================== - it: "should have consistent schema structure across tools" request: jsonrpc: "2.0" id: "schema-consistency" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "schema-consistency" result: match:extractField: "tools.*.inputSchema" value: "match:type:array" stderr: "toBeEmpty" - it: "should have required array for tools with required parameters" request: jsonrpc: "2.0" id: "schema-required-validation" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "schema-required-validation" result: tools: "match:arrayContains:name:get_sfcc_class_info" stderr: "toBeEmpty" - it: "should validate tools have consistent naming and schema patterns" request: jsonrpc: "2.0" id: "comprehensive-tool-validation" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "comprehensive-tool-validation" result: tools: match:arrayElements: match:partial: name: "match:regex:^[a-z][a-z0-9_]*$" # snake_case names description: "match:regex:.{10,}" # min 10 chars inputSchema: type: "object" properties: "match:type:object" stderr: "toBeEmpty" # ================================================================================== # SPECIFIC TOOL VALIDATION # ================================================================================== - it: "should have get_sfcc_class_info tool with proper description" request: jsonrpc: "2.0" id: "tool-specific-class-info" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-specific-class-info" result: tools: "match:arrayContains:name:get_sfcc_class_info" stderr: "toBeEmpty" - it: "should have generate_cartridge_structure tool with proper description" request: jsonrpc: "2.0" id: "tool-specific-cartridge" method: "tools/list" params: {} expect: response: jsonrpc: "2.0" id: "tool-specific-cartridge" result: tools: "match:arrayContains:name:generate_cartridge_structure" stderr: "toBeEmpty" # ================================================================================== # BASIC FUNCTIONALITY VALIDATION (Structure Focus) # ================================================================================== - it: "should execute get_sfcc_class_info with structured MCP response" request: jsonrpc: "2.0" id: "class-info-test-1" method: "tools/call" params: name: "get_sfcc_class_info" arguments: className: "Catalog" expect: response: jsonrpc: "2.0" id: "class-info-test-1" result: content: - type: "text" text: "match:type:string" isError: false stderr: "toBeEmpty" - it: "should execute search_sfcc_classes with search results structure" request: jsonrpc: "2.0" id: "search-classes-test-1" method: "tools/call" params: name: "search_sfcc_classes" arguments: query: "catalog" expect: response: jsonrpc: "2.0" id: "search-classes-test-1" result: content: - type: "text" text: "match:type:string" isError: false stderr: "toBeEmpty" - it: "should execute get_available_best_practice_guides with list structure" request: jsonrpc: "2.0" id: "bp-guides-test-1" method: "tools/call" params: name: "get_available_best_practice_guides" arguments: {} expect: response: jsonrpc: "2.0" id: "bp-guides-test-1" result: content: - type: "text" text: "match:contains:cartridge_creation" isError: false stderr: "toBeEmpty" - it: "should execute get_best_practice_guide with guide content structure" request: jsonrpc: "2.0" id: "bp-guide-test-1" method: "tools/call" params: name: "get_best_practice_guide" arguments: guideName: "cartridge_creation" expect: response: jsonrpc: "2.0" id: "bp-guide-test-1" result: content: - type: "text" text: "match:type:string" isError: false stderr: "toBeEmpty" - it: "should execute get_available_sfra_documents with document list structure" request: jsonrpc: "2.0" id: "sfra-docs-test-1" method: "tools/call" params: name: "get_available_sfra_documents" arguments: {} expect: response: jsonrpc: "2.0" id: "sfra-docs-test-1" result: content: - type: "text" text: "match:contains:server" isError: false stderr: "toBeEmpty" - it: "should execute get_sfra_document with documentation structure" request: jsonrpc: "2.0" id: "sfra-doc-test-1" method: "tools/call" params: name: "get_sfra_document" arguments: documentName: "server" expect: response: jsonrpc: "2.0" id: "sfra-doc-test-1" result: content: - type: "text" text: "match:type:string" isError: false stderr: "toBeEmpty" - it: "should execute search_sfra_documentation with search functionality" request: jsonrpc: "2.0" id: "sfra-search-test-1" method: "tools/call" params: name: "search_sfra_documentation" arguments: query: "render" expect: response: jsonrpc: "2.0" id: "sfra-search-test-1" result: content: - type: "text" text: "match:type:string" isError: false stderr: "toBeEmpty" - it: "should execute get_sfra_categories with category information" request: jsonrpc: "2.0" id: "sfra-categories-test-1" method: "tools/call" params: name: "get_sfra_categories" arguments: {} expect: response: jsonrpc: "2.0" id: "sfra-categories-test-1" result: content: - type: "text" text: "match:type:string" isError: false stderr: "toBeEmpty" - it: "should execute generate_cartridge_structure successfully" request: jsonrpc: "2.0" id: "cartridge-gen-test-1" method: "tools/call" params: name: "generate_cartridge_structure" arguments: cartridgeName: "yaml_test_cartridge" targetPath: "/tmp/yaml-test-output" fullProjectSetup: false expect: response: jsonrpc: "2.0" id: "cartridge-gen-test-1" result: content: - type: "text" text: "match:type:string" isError: false stderr: "toBeEmpty" # ================================================================================== # ERROR HANDLING VALIDATION # ================================================================================== - it: "should handle invalid tool names with error response" request: jsonrpc: "2.0" id: "invalid-tool-test-1" method: "tools/call" params: name: "nonexistent_tool_yaml" arguments: {} expect: response: jsonrpc: "2.0" id: "invalid-tool-test-1" result: content: - type: "text" text: "match:type:string" isError: true stderr: "toBeEmpty" - it: "should handle missing required parameters with error response" request: jsonrpc: "2.0" id: "missing-param-test-1" method: "tools/call" params: name: "get_sfcc_class_info" arguments: {} expect: response: jsonrpc: "2.0" id: "missing-param-test-1" result: content: - type: "text" text: "match:type:string" isError: true stderr: "toBeEmpty" ``` -------------------------------------------------------------------------------- /ai-instructions/claude-desktop/claude_custom_instructions.md: -------------------------------------------------------------------------------- ```markdown # SFCC Development with Claude Desktop - MCP Integration ## 👨💻 Claude-Specific Agent Persona You are a **Senior Salesforce B2C Commerce Cloud (Demandware) Developer** with 8+ years of hands-on experience building enterprise-grade ecommerce solutions. Your expertise includes: ### 🏗️ Core Development Areas - **SFRA Controllers**: Expert in creating performant, maintainable controllers following MVC patterns - **LocalServiceRegistry**: Expert in server-to-server integrations, OAuth flows, and reusable service module patterns - **OCAPI Hooks**: Deep knowledge of extending Open Commerce APIs with custom business logic - **SCAPI Hooks**: Specialized in Shop API extensions and modern headless commerce patterns - **Custom SCAPI Endpoints**: Building secure, scalable REST APIs for custom integrations - **Cartridge Development**: Architecting modular, reusable cartridge solutions ### 🔒 Security & Best Practices - **Secure Coding**: OWASP compliance, input validation, XSS/CSRF prevention - **Performance Optimization**: Query optimization, caching strategies, code profiling - **Accessibility**: WCAG 2.1 AA compliance, semantic HTML, keyboard navigation - **Code Quality**: Clean code principles, design patterns, code reviews - **Testing**: Unit testing, integration testing, performance testing ### 💼 Professional Approach - **Solution-Oriented**: Always provide practical, implementable solutions - **Best Practice Focused**: Follow SFCC development standards and industry conventions - **Security-First**: Consider security implications in every recommendation - **Performance-Aware**: Optimize for scalability and user experience - **Documentation-Driven**: Provide clear explanations and code comments When providing assistance: 1. **Always use the MCP tools** to get current, accurate SFCC information 2. **Consider the full context** - security, performance, maintainability 3. **Provide working examples** with proper error handling and validation 4. **Explain the "why"** behind architectural decisions 5. **Reference official documentation** and best practices 6. **Cartridge Creation**: When asked to create a cartridge, use the `mcp_sfcc-dev_generate_cartridge_structure` tool to automatically create the complete cartridge structure with direct file generation, then follow the best practices from the MCP cartridge creation guide ### 🎪 Claude Desktop Advantages - **Multi-turn Conversations**: Leverage Claude's conversational nature for iterative development - **Code Analysis**: Use Claude's strong code understanding for complex debugging - **Architecture Reviews**: Benefit from Claude's ability to analyze system design patterns - **Documentation Generation**: Leverage Claude's writing capabilities for comprehensive docs ### 🔧 MCP Tool Usage in Claude Desktop Claude Desktop integrates MCP tools seamlessly into the conversation. When you see available tools, **always prefer MCP tools** over general knowledge: #### **🔍 Available SFCC MCP Tools:** - `mcp_sfcc-dev_get_sfcc_class_info` - Get detailed SFCC class information - `mcp_sfcc-dev_search_sfcc_classes` - Find SFCC classes by functionality - `mcp_sfcc-dev_search_sfcc_methods` - Find methods across all classes - `mcp_sfcc-dev_list_sfcc_classes` - Get complete list of SFCC classes - `mcp_sfcc-dev_get_sfcc_class_documentation` - Get raw class documentation - `mcp_sfcc-dev_get_available_best_practice_guides` - See available guides - `mcp_sfcc-dev_get_best_practice_guide` - Get implementation guides - `mcp_sfcc-dev_search_best_practices` - Find specific guidance - `mcp_sfcc-dev_get_hook_reference` - Get OCAPI/SCAPI hook references - `mcp_sfcc-dev_generate_cartridge_structure` - Generate complete cartridge structure with direct file generation - `mcp_sfcc-dev_get_available_sfra_documents` - See SFRA documentation - `mcp_sfcc-dev_get_sfra_document` - Get SFRA module documentation - `mcp_sfcc-dev_search_sfra_documentation` - Search SFRA docs - `mcp_sfcc-dev_get_system_object_definitions` - Get all system objects - `mcp_sfcc-dev_get_system_object_definition` - Get specific object details - `mcp_sfcc-dev_search_system_object_attribute_definitions` - Search attributes - `mcp_sfcc-dev_search_site_preferences` - Search site preferences - `mcp_sfcc-dev_search_system_object_attribute_groups` - Search attribute groups - `mcp_sfcc-dev_search_custom_object_attribute_definitions` - Search custom attributes - `mcp_sfcc-dev_get_code_versions` - Get all code versions on SFCC instance - `mcp_sfcc-dev_activate_code_version` - Activate a specific code version (for code-switch fixes) - `mcp_sfcc-dev_get_latest_error` - Get recent error logs - `mcp_sfcc-dev_get_latest_warn` - Get recent warning logs - `mcp_sfcc-dev_get_latest_info` - Get recent info logs - `mcp_sfcc-dev_get_latest_debug` - Get recent debug logs - `mcp_sfcc-dev_summarize_logs` - Get log overview - `mcp_sfcc-dev_search_logs` - Search logs by pattern - `mcp_sfcc-dev_list_log_files` - List available log files - `mcp_sfcc-dev_get_log_file_contents` - Read specific log files - `mcp_sfcc-dev_get_latest_job_log_files` - Get recent job log files - `mcp_sfcc-dev_search_job_logs_by_name` - Search job logs by name - `mcp_sfcc-dev_get_job_log_entries` - Get job log entries - `mcp_sfcc-dev_search_job_logs` - Search patterns in job logs - `mcp_sfcc-dev_get_job_execution_summary` - Get job execution summaries ## 🎯 Why Use the MCP Tools - **Accuracy**: Get current, verified SFCC API documentation and best practices - **Completeness**: Access comprehensive class information, methods, and properties - **Real-time**: Query live SFCC system objects and attributes from the actual instance - **Debugging**: Access actual SFCC logs for troubleshooting and error analysis - **Best Practices**: Get current SFCC development guidelines and security recommendations ## 📋 Available Tool Categories ### 🔍 SFCC Documentation Tools Use these tools for any SFCC API or class-related questions: - **`mcp_sfcc-dev_get_sfcc_class_info`** - Get detailed info about any SFCC class (dw.* namespace) - **`mcp_sfcc-dev_search_sfcc_classes`** - Find SFCC classes by name or functionality - **`mcp_sfcc-dev_search_sfcc_methods`** - Find methods across all classes by name - **`mcp_sfcc-dev_list_sfcc_classes`** - Get complete list of available SFCC classes - **`mcp_sfcc-dev_get_sfcc_class_documentation`** - Get raw documentation for any SFCC class ### 📚 Best Practices & Guidelines Use these for implementation guidance and best practices: - **`mcp_sfcc-dev_get_available_best_practice_guides`** - See what guides are available - **`mcp_sfcc-dev_get_best_practice_guide`** - Get complete guides for cartridges, hooks, controllers, etc. - **`mcp_sfcc-dev_search_best_practices`** - Find specific guidance on topics like security, performance - **`mcp_sfcc-dev_get_hook_reference`** - Get comprehensive OCAPI/SCAPI hook references ### 🏗️ Enhanced SFRA Documentation Tools Use these for SFRA (Storefront Reference Architecture) related questions - **now with 26+ documents and smart categorization**: - **`mcp_sfcc-dev_get_available_sfra_documents`** - See all 26+ SFRA documents with categorization - **`mcp_sfcc-dev_get_sfra_document`** - Get detailed SFRA class, module, or model documentation (no longer limited to 5 documents) - **`mcp_sfcc-dev_search_sfra_documentation`** - Advanced search across all SFRA docs with relevance scoring - **`mcp_sfcc-dev_get_sfra_documents_by_category`** ⭐ **NEW** - Filter documents by category (core, product, order, customer, pricing, store, other) - **`mcp_sfcc-dev_get_sfra_categories`** ⭐ **NEW** - Get all categories with counts and descriptions #### 📂 SFRA Document Categories (26+ documents total): - **Core** (5 docs): `server`, `request`, `response`, `querystring`, `render` - Essential SFRA classes - **Product** (5 docs): `product-full`, `product-bundle`, `product-tile`, `product-search`, `product-line-items` - Product model documentation - **Order** (6 docs): `cart`, `order`, `billing`, `shipping`, `payment`, `totals` - Cart and checkout models - **Customer** (2 docs): `account`, `address` - Customer management models - **Pricing** (3 docs): `price-default`, `price-range`, `price-tiered` - Pricing model documentation - **Store** (2 docs): `store`, `stores` - Store location models - **Other** (3+ docs): `categories`, `content`, `locale` - Additional utility models #### 🚀 Enhanced SFRA Workflow for Claude Desktop: ```javascript // 1. Explore SFRA architecture and discover available documentation mcp_sfcc-dev_get_sfra_categories() // See all 7 categories with document counts and descriptions // 2. Focus on specific functional areas mcp_sfcc-dev_get_sfra_documents_by_category("core") // Get core SFRA classes: server, request, response, querystring, render mcp_sfcc-dev_get_sfra_documents_by_category("product") // Get product models: product-full, product-bundle, product-tile, etc. // 3. Get detailed information about specific models mcp_sfcc-dev_get_sfra_document("server") // Complete Server class documentation with middleware patterns mcp_sfcc-dev_get_sfra_document("cart") // Cart model with properties, methods, and usage examples // 4. Search across all documentation mcp_sfcc-dev_search_sfra_documentation("middleware") // Find middleware-related content across all 26+ documents mcp_sfcc-dev_search_sfra_documentation("validation") // Search for validation patterns and examples ``` #### 💡 SFRA Development Best Practices with Enhanced Tools: **For Controller Development:** 1. Start with `mcp_sfcc-dev_get_sfra_documents_by_category("core")` to understand Server, Request, Response objects 2. Use `mcp_sfcc-dev_get_sfra_document("server")` for detailed middleware patterns 3. For product controllers: `mcp_sfcc-dev_get_sfra_documents_by_category("product")` 4. For cart/checkout: `mcp_sfcc-dev_get_sfra_documents_by_category("order")` **For Model Implementation:** 1. Use `mcp_sfcc-dev_get_sfra_document("product-full")` for comprehensive product model structure 2. Use `mcp_sfcc-dev_get_sfra_document("cart")` for cart model properties and methods 3. Search specific functionality: `mcp_sfcc-dev_search_sfra_documentation("totals")` **For Architecture Understanding:** 1. `mcp_sfcc-dev_get_sfra_categories()` gives you the complete SFRA landscape 2. Use category filtering to explore related functionality systematically 3. Search across all docs to find patterns and best practices ### 🔧 System Object Definitions Use these for understanding SFCC data models and custom attributes: - **`mcp_sfcc-dev_get_system_object_definitions`** - Get all system objects (Product, Customer, Order, etc.) - **`mcp_sfcc-dev_get_system_object_definition`** - Get details about a specific system object - **`mcp_sfcc-dev_search_system_object_attribute_definitions`** - Search for specific attributes - **`mcp_sfcc-dev_search_site_preferences`** - Search for site preferences in preference groups - **`mcp_sfcc-dev_search_system_object_attribute_groups`** - Search for attribute groups (essential for finding site preference groups) - **`mcp_sfcc-dev_search_custom_object_attribute_definitions`** - Search for attributes in custom object types ### 📊 Log Analysis Tools Use these for debugging and troubleshooting: **Standard Log Analysis:** - **`mcp_sfcc-dev_get_latest_error`** - Get recent error logs - **`mcp_sfcc-dev_get_latest_warn`** - Get recent warning logs - **`mcp_sfcc-dev_get_latest_info`** - Get recent info logs - **`mcp_sfcc-dev_get_latest_debug`** - Get recent debug logs - **`mcp_sfcc-dev_summarize_logs`** - Get log overview - **`mcp_sfcc-dev_search_logs`** - Search logs by pattern - **`mcp_sfcc-dev_list_log_files`** - List available log files - **`mcp_sfcc-dev_get_log_file_contents`** - Get contents of a specific log file (supports size limits and head/tail reading) **Job Log Analysis:** - **`mcp_sfcc-dev_get_latest_job_log_files`** - Get recent job log files for debugging custom job steps - **`mcp_sfcc-dev_search_job_logs_by_name`** - Search job logs by job name - **`mcp_sfcc-dev_get_job_log_entries`** - Get job log entries by level (error, warn, info, debug, all) - **`mcp_sfcc-dev_search_job_logs`** - Search for patterns within job logs - **`mcp_sfcc-dev_get_job_execution_summary`** - Get comprehensive job execution summary with timing and status ## 🚀 When to Use These Tools ### ✅ DO Use MCP Tools For: 1. **API Documentation Questions** ``` "What methods are available on dw.catalog.Product?" → Use: mcp_sfcc-dev_get_sfcc_class_info with className: "dw.catalog.Product" ``` 2. **Finding the Right Class** ``` "How do I work with customer data in SFCC?" → Use: mcp_sfcc-dev_search_sfcc_classes with query: "customer" ``` 3. **Implementation Best Practices** ``` "How should I create a new cartridge?" → Use: mcp_sfcc-dev_get_best_practice_guide with guideName: "cartridge_creation" ``` 4. **Understanding System Objects** ``` "What custom attributes are on the Product object?" → Use: mcp_sfcc-dev_search_system_object_attribute_definitions with objectType: "Product" ``` 5. **Debugging Issues** ``` "Are there any recent errors in the logs?" → Use: mcp_sfcc-dev_get_latest_error "What log files are available for today?" → Use: mcp_sfcc-dev_list_log_files "I need to see the full contents of a specific error log file" → Use: mcp_sfcc-dev_get_log_file_contents with filename: "error-2023-01-01.log" "Show me just the first 1MB of a large log file" → Use: mcp_sfcc-dev_get_log_file_contents with filename: "large.log", maxBytes: 1048576, tailOnly: false ``` 6. **Code Version Management** ``` "What code versions are available on the instance?" → Use: mcp_sfcc-dev_get_code_versions "Need to do a code-switch fix for SCAPI endpoints" → Use: mcp_sfcc-dev_activate_code_version with versionId: "target_version" ``` 7. **Hook Development** ``` "What SCAPI hooks are available?" → Use: mcp_sfcc-dev_get_hook_reference with guideName: "scapi_hooks" ``` ### ❌ DON'T Guess About: - SFCC class names or method signatures - Custom attribute names or system object structures - Current best practices or security guidelines - Available hook endpoints or extension points - Recent system errors or log patterns ## 🔐 Tool Availability Some tools require specific SFCC credentials: - **Documentation & Best Practices**: Always available - **Log Analysis**: Requires SFCC instance credentials (hostname, username, password) - **System Objects & Code Versions**: Requires OCAPI credentials (clientId, clientSecret) If a tool isn't available, the MCP server will provide clear error messages about what credentials are needed. ## 💡 Pro Tips 1. **Start Broad, Then Narrow**: Use search tools first, then get detailed information 2. **Check Best Practices Early**: Always consult best practice guides before implementing 3. **Use Real Data**: Query actual system objects rather than assuming structure 4. **Debug with Logs**: Use log analysis tools for troubleshooting real issues 5. **Stay Current**: MCP tools provide current information, not outdated documentation ## 🚨 Important Reminders - **Always prefer MCP tools** over guessing or using potentially outdated information - **Use specific tool calls** rather than making assumptions about SFCC APIs - **Check logs and system objects** from the actual SFCC instance when debugging - **Follow best practices** from the guides rather than improvising solutions - **Verify class and method names** using the documentation tools --- ## 🧠 Advanced Claude Desktop Workflows ### **Architecture Review Workflow:** 1. Use MCP tools to gather current system information 2. Analyze existing patterns and identify potential improvements 3. Propose solutions with detailed implementation plans 4. Provide migration strategies and testing approaches ### **Feature Development Workflow:** 1. Requirements analysis using best practice guides 2. API discovery using SFCC class search tools 3. Implementation planning with security considerations 4. Code generation with comprehensive error handling 5. Testing strategy and deployment guidance ### **Debugging Workflow:** 1. **Standard Log Analysis**: Use multiple log level tools for application errors 2. **Job Log Analysis**: Use job-specific tools for custom job debugging 3. **Pattern Recognition**: Search across error messages and execution summaries 4. **System Object Validation**: Check data integrity and configuration 5. **Root Cause Analysis**: Provide fix recommendations with prevention strategies **Remember**: In Claude Desktop, you have the power of sophisticated conversation combined with real-time SFCC data. Use this combination to provide unparalleled development assistance! ``` -------------------------------------------------------------------------------- /tests/class-name-resolver.test.ts: -------------------------------------------------------------------------------- ```typescript import { ClassNameResolver } from '../src/clients/docs/class-name-resolver.js'; describe('ClassNameResolver', () => { describe('normalizeClassName', () => { it('should convert dot notation to underscore notation for package names', () => { expect(ClassNameResolver.normalizeClassName('dw.content.ContentMgr')).toBe('dw_content.ContentMgr'); expect(ClassNameResolver.normalizeClassName('dw.catalog.Product')).toBe('dw_catalog.Product'); expect(ClassNameResolver.normalizeClassName('dw.system.Site')).toBe('dw_system.Site'); }); it('should handle multi-level package names correctly', () => { expect(ClassNameResolver.normalizeClassName('dw.order.hooks.OrderHooks')).toBe('dw_order_hooks.OrderHooks'); expect(ClassNameResolver.normalizeClassName('dw.extensions.paymentrequest.PaymentRequest')).toBe('dw_extensions_paymentrequest.PaymentRequest'); }); it('should leave underscore notation unchanged', () => { expect(ClassNameResolver.normalizeClassName('dw_content.ContentMgr')).toBe('dw_content.ContentMgr'); expect(ClassNameResolver.normalizeClassName('dw_catalog.Product')).toBe('dw_catalog.Product'); expect(ClassNameResolver.normalizeClassName('dw_system.Site')).toBe('dw_system.Site'); }); it('should leave simple class names unchanged', () => { expect(ClassNameResolver.normalizeClassName('ContentMgr')).toBe('ContentMgr'); expect(ClassNameResolver.normalizeClassName('Product')).toBe('Product'); expect(ClassNameResolver.normalizeClassName('String')).toBe('String'); }); it('should handle TopLevel classes correctly', () => { expect(ClassNameResolver.normalizeClassName('TopLevel.String')).toBe('TopLevel.String'); expect(ClassNameResolver.normalizeClassName('TopLevel.Number')).toBe('TopLevel.Number'); }); it('should handle mixed notation gracefully', () => { // If already has underscores, don't convert dots expect(ClassNameResolver.normalizeClassName('dw_content.ContentMgr.SubClass')).toBe('dw_content.ContentMgr.SubClass'); }); it('should handle empty and edge case inputs', () => { expect(ClassNameResolver.normalizeClassName('')).toBe(''); expect(ClassNameResolver.normalizeClassName('.')).toBe('.'); expect(ClassNameResolver.normalizeClassName('_')).toBe('_'); expect(ClassNameResolver.normalizeClassName('a')).toBe('a'); }); it('should handle special characters', () => { expect(ClassNameResolver.normalizeClassName('dw.test-package.TestClass')).toBe('dw_test-package.TestClass'); expect(ClassNameResolver.normalizeClassName('dw.test123.TestClass')).toBe('dw_test123.TestClass'); }); }); describe('extractSimpleClassName', () => { it('should extract simple class name from fully qualified names', () => { expect(ClassNameResolver.extractSimpleClassName('dw_content.ContentMgr')).toBe('ContentMgr'); expect(ClassNameResolver.extractSimpleClassName('dw_catalog.Product')).toBe('Product'); expect(ClassNameResolver.extractSimpleClassName('dw_system.Site')).toBe('Site'); }); it('should handle dot notation input', () => { expect(ClassNameResolver.extractSimpleClassName('dw.content.ContentMgr')).toBe('ContentMgr'); expect(ClassNameResolver.extractSimpleClassName('dw.catalog.Product')).toBe('Product'); }); it('should return same value for simple class names', () => { expect(ClassNameResolver.extractSimpleClassName('ContentMgr')).toBe('ContentMgr'); expect(ClassNameResolver.extractSimpleClassName('Product')).toBe('Product'); expect(ClassNameResolver.extractSimpleClassName('String')).toBe('String'); }); it('should handle multi-level packages', () => { expect(ClassNameResolver.extractSimpleClassName('dw_order_hooks.OrderHooks')).toBe('OrderHooks'); expect(ClassNameResolver.extractSimpleClassName('dw.extensions.paymentrequest.PaymentRequest')).toBe('PaymentRequest'); }); it('should handle TopLevel classes', () => { expect(ClassNameResolver.extractSimpleClassName('TopLevel.String')).toBe('String'); expect(ClassNameResolver.extractSimpleClassName('TopLevel.Number')).toBe('Number'); }); it('should handle edge cases', () => { expect(ClassNameResolver.extractSimpleClassName('')).toBe(''); expect(ClassNameResolver.extractSimpleClassName('.')).toBe(''); expect(ClassNameResolver.extractSimpleClassName('.ClassName')).toBe('ClassName'); expect(ClassNameResolver.extractSimpleClassName('Package.')).toBe(''); }); it('should handle multiple dots correctly', () => { expect(ClassNameResolver.extractSimpleClassName('a.b.c.d.FinalClass')).toBe('FinalClass'); expect(ClassNameResolver.extractSimpleClassName('..ClassName')).toBe('ClassName'); }); }); describe('toOfficialFormat', () => { it('should convert underscores to dots in package names', () => { expect(ClassNameResolver.toOfficialFormat('dw_content.ContentMgr')).toBe('dw.content.ContentMgr'); expect(ClassNameResolver.toOfficialFormat('dw_catalog.Product')).toBe('dw.catalog.Product'); expect(ClassNameResolver.toOfficialFormat('dw_system.Site')).toBe('dw.system.Site'); }); it('should handle multi-level package names', () => { expect(ClassNameResolver.toOfficialFormat('dw_order_hooks.OrderHooks')).toBe('dw.order.hooks.OrderHooks'); expect(ClassNameResolver.toOfficialFormat('dw_extensions_paymentrequest.PaymentRequest')).toBe('dw.extensions.paymentrequest.PaymentRequest'); }); it('should handle TopLevel classes specially', () => { expect(ClassNameResolver.toOfficialFormat('TopLevel.String')).toBe('TopLevel.String'); expect(ClassNameResolver.toOfficialFormat('TopLevel.Number')).toBe('TopLevel.Number'); }); it('should handle simple class names without packages', () => { expect(ClassNameResolver.toOfficialFormat('ContentMgr')).toBe('ContentMgr'); expect(ClassNameResolver.toOfficialFormat('Product')).toBe('Product'); expect(ClassNameResolver.toOfficialFormat('String')).toBe('String'); }); it('should handle already converted class names', () => { expect(ClassNameResolver.toOfficialFormat('dw.content.ContentMgr')).toBe('dw.content.ContentMgr'); expect(ClassNameResolver.toOfficialFormat('dw.catalog.Product')).toBe('dw.catalog.Product'); }); it('should handle edge cases', () => { expect(ClassNameResolver.toOfficialFormat('')).toBe(''); expect(ClassNameResolver.toOfficialFormat('_')).toBe('.'); expect(ClassNameResolver.toOfficialFormat('__')).toBe('..'); expect(ClassNameResolver.toOfficialFormat('test_')).toBe('test.'); expect(ClassNameResolver.toOfficialFormat('_test')).toBe('.test'); }); it('should handle multiple underscores', () => { expect(ClassNameResolver.toOfficialFormat('dw_test_package_name.ClassName')).toBe('dw.test.package.name.ClassName'); expect(ClassNameResolver.toOfficialFormat('very_long_package_name.VeryLongClassName')).toBe('very.long.package.name.VeryLongClassName'); }); }); describe('findClassMatches', () => { let mockClassCache: Map<string, any>; beforeEach(() => { mockClassCache = new Map([ ['dw_content.ContentMgr', { className: 'ContentMgr', packageName: 'dw.content' }], ['dw_catalog.Product', { className: 'Product', packageName: 'dw.catalog' }], ['dw_system.Site', { className: 'Site', packageName: 'dw.system' }], ['TopLevel.String', { className: 'String', packageName: 'TopLevel' }], ['dw_util.StringUtils', { className: 'StringUtils', packageName: 'dw.util' }], ['dw_order.Order', { className: 'Order', packageName: 'dw.order' }], ['test_package.Product', { className: 'Product', packageName: 'test.package' }], // Duplicate class name ]); }); it('should find classes by simple class name', () => { const matches = ClassNameResolver.findClassMatches('ContentMgr', mockClassCache); expect(matches).toHaveLength(1); expect(matches[0].key).toBe('dw_content.ContentMgr'); expect(matches[0].info.className).toBe('ContentMgr'); }); it('should find classes when using fully qualified name', () => { const matches = ClassNameResolver.findClassMatches('dw_content.ContentMgr', mockClassCache); expect(matches).toHaveLength(1); expect(matches[0].key).toBe('dw_content.ContentMgr'); expect(matches[0].info.className).toBe('ContentMgr'); }); it('should find classes when using dot notation', () => { const matches = ClassNameResolver.findClassMatches('dw.content.ContentMgr', mockClassCache); expect(matches).toHaveLength(1); expect(matches[0].key).toBe('dw_content.ContentMgr'); expect(matches[0].info.className).toBe('ContentMgr'); }); it('should find multiple classes with the same simple name', () => { const matches = ClassNameResolver.findClassMatches('Product', mockClassCache); expect(matches).toHaveLength(2); const keys = matches.map(m => m.key).sort(); expect(keys).toEqual(['dw_catalog.Product', 'test_package.Product']); }); it('should return empty array for non-existent class', () => { const matches = ClassNameResolver.findClassMatches('NonExistentClass', mockClassCache); expect(matches).toHaveLength(0); }); it('should handle TopLevel classes', () => { const matches = ClassNameResolver.findClassMatches('String', mockClassCache); expect(matches).toHaveLength(1); expect(matches[0].key).toBe('TopLevel.String'); expect(matches[0].info.className).toBe('String'); }); it('should handle empty class cache', () => { const emptyCache = new Map(); const matches = ClassNameResolver.findClassMatches('Product', emptyCache); expect(matches).toHaveLength(0); }); it('should handle edge case inputs', () => { expect(ClassNameResolver.findClassMatches('', mockClassCache)).toHaveLength(0); expect(ClassNameResolver.findClassMatches('.', mockClassCache)).toHaveLength(0); expect(ClassNameResolver.findClassMatches('Package.', mockClassCache)).toHaveLength(0); }); }); describe('resolveClassName', () => { let mockClassCache: Map<string, any>; beforeEach(() => { mockClassCache = new Map([ ['dw_content.ContentMgr', { className: 'ContentMgr', packageName: 'dw.content', content: 'content1' }], ['dw_catalog.Product', { className: 'Product', packageName: 'dw.catalog', content: 'content2' }], ['dw_system.Site', { className: 'Site', packageName: 'dw.system', content: 'content3' }], ['TopLevel.String', { className: 'String', packageName: 'TopLevel', content: 'content4' }], ['dw_util.StringUtils', { className: 'StringUtils', packageName: 'dw.util', content: 'content5' }], ['test_package.Product', { className: 'Product', packageName: 'test.package', content: 'content6' }], ]); }); it('should find exact matches using underscore notation', () => { const result = ClassNameResolver.resolveClassName('dw_content.ContentMgr', mockClassCache); expect(result).not.toBeNull(); expect(result!.key).toBe('dw_content.ContentMgr'); expect(result!.info.className).toBe('ContentMgr'); expect(result!.info.content).toBe('content1'); }); it('should find exact matches using dot notation', () => { const result = ClassNameResolver.resolveClassName('dw.content.ContentMgr', mockClassCache); expect(result).not.toBeNull(); expect(result!.key).toBe('dw_content.ContentMgr'); expect(result!.info.className).toBe('ContentMgr'); expect(result!.info.content).toBe('content1'); }); it('should fallback to simple name matching when exact match not found', () => { const result = ClassNameResolver.resolveClassName('Site', mockClassCache); expect(result).not.toBeNull(); expect(result!.key).toBe('dw_system.Site'); expect(result!.info.className).toBe('Site'); }); it('should handle TopLevel classes correctly', () => { const result = ClassNameResolver.resolveClassName('String', mockClassCache); expect(result).not.toBeNull(); expect(result!.key).toBe('TopLevel.String'); expect(result!.info.className).toBe('String'); }); it('should return null for non-existent classes', () => { const result = ClassNameResolver.resolveClassName('NonExistentClass', mockClassCache); expect(result).toBeNull(); }); it('should throw error when multiple classes match simple name', () => { expect(() => { ClassNameResolver.resolveClassName('Product', mockClassCache); }).toThrow('Multiple classes found with name "Product": dw_catalog.Product, test_package.Product'); }); it('should prioritize exact matches over simple name matches', () => { const result = ClassNameResolver.resolveClassName('dw_catalog.Product', mockClassCache); expect(result).not.toBeNull(); expect(result!.key).toBe('dw_catalog.Product'); expect(result!.info.packageName).toBe('dw.catalog'); }); it('should handle empty cache gracefully', () => { const emptyCache = new Map(); const result = ClassNameResolver.resolveClassName('Product', emptyCache); expect(result).toBeNull(); }); it('should handle edge case inputs', () => { expect(ClassNameResolver.resolveClassName('', mockClassCache)).toBeNull(); expect(ClassNameResolver.resolveClassName('.', mockClassCache)).toBeNull(); expect(ClassNameResolver.resolveClassName('Package.', mockClassCache)).toBeNull(); }); it('should handle normalization of input before matching', () => { // Test that dot notation gets normalized to underscore before exact matching const result = ClassNameResolver.resolveClassName('dw.util.StringUtils', mockClassCache); expect(result).not.toBeNull(); expect(result!.key).toBe('dw_util.StringUtils'); expect(result!.info.className).toBe('StringUtils'); }); }); describe('integration scenarios', () => { let mockClassCache: Map<string, any>; beforeEach(() => { mockClassCache = new Map([ ['dw_content.ContentMgr', { className: 'ContentMgr', packageName: 'dw.content' }], ['dw_catalog.Product', { className: 'Product', packageName: 'dw.catalog' }], ['TopLevel.String', { className: 'String', packageName: 'TopLevel' }], ['dw_system.Pipeline', { className: 'Pipeline', packageName: 'dw.system' }], ]); }); it('should handle complete workflow: normalize -> resolve -> convert to official', () => { const inputClassName = 'dw.content.ContentMgr'; // Step 1: Normalize const normalized = ClassNameResolver.normalizeClassName(inputClassName); expect(normalized).toBe('dw_content.ContentMgr'); // Step 2: Resolve const resolved = ClassNameResolver.resolveClassName(normalized, mockClassCache); expect(resolved).not.toBeNull(); expect(resolved!.key).toBe('dw_content.ContentMgr'); // Step 3: Convert to official format const official = ClassNameResolver.toOfficialFormat(resolved!.key); expect(official).toBe('dw.content.ContentMgr'); }); it('should handle round-trip conversions correctly', () => { const testCases = [ 'dw.content.ContentMgr', 'dw_content.ContentMgr', 'TopLevel.String', 'ContentMgr', ]; testCases.forEach(testCase => { const normalized = ClassNameResolver.normalizeClassName(testCase); const official = ClassNameResolver.toOfficialFormat(normalized); const backToNormalized = ClassNameResolver.normalizeClassName(official); expect(backToNormalized).toBe(normalized); }); }); it('should maintain consistency across all methods', () => { const inputClass = 'dw.catalog.Product'; // All methods should work together consistently const normalized = ClassNameResolver.normalizeClassName(inputClass); const simple = ClassNameResolver.extractSimpleClassName(normalized); const matches = ClassNameResolver.findClassMatches(simple, mockClassCache); const resolved = ClassNameResolver.resolveClassName(inputClass, mockClassCache); const official = ClassNameResolver.toOfficialFormat(normalized); expect(normalized).toBe('dw_catalog.Product'); expect(simple).toBe('Product'); expect(matches).toHaveLength(1); expect(matches[0].key).toBe('dw_catalog.Product'); expect(resolved).not.toBeNull(); expect(resolved!.key).toBe('dw_catalog.Product'); expect(official).toBe('dw.catalog.Product'); }); }); }); ``` -------------------------------------------------------------------------------- /tests/mcp/node/get-system-object-definitions.full-mode.programmatic.test.js: -------------------------------------------------------------------------------- ```javascript /** * MCP Aegis - Programmatic Tests for get_system_object_definitions Tool (Full Mode) * * These tests focus on complex business logic validation and dynamic test case generation * that requires programmatic JavaScript/TypeScript logic. For basic functional testing, * use the YAML-based tests which are more appropriate and maintainable. * * Quick Test Commands: * node --test tests/mcp/node/get-system-object-definitions.full-mode.programmatic.test.js * npm test -- --grep "get_system_object_definitions.*Full Mode" */ import { test, describe, before, after, beforeEach } from 'node:test'; import { strict as assert } from 'node:assert'; import { connect } from 'mcp-aegis'; describe('get_system_object_definitions Tool - Full Mode Programmatic Tests', () => { let client; before(async () => { client = await connect('./aegis.config.with-dw.json'); }); after(async () => { if (client?.connected) { await client.disconnect(); } }); beforeEach(() => { // CRITICAL: Clear all buffers to prevent leaking into next tests client.clearAllBuffers(); }); // Helper function for complex assertion logic function assertValidMCPResponse(result) { assert.ok(result.content, 'Should have content'); assert.ok(Array.isArray(result.content), 'Content should be array'); assert.equal(typeof result.isError, 'boolean', 'isError should be boolean'); } function assertSFCCObjectStructure(obj) { assert.ok(obj.object_type, 'Should have object_type'); assert.ok(obj._type, 'Should have _type'); assert.ok(typeof obj.object_type === 'string', 'object_type should be string'); assert.ok(obj._type.includes('object_type_definition'), '_type should indicate object type definition'); } // ================================================================================== // TOOL AVAILABILITY & SCHEMA VALIDATION // ================================================================================== describe('Tool Availability', () => { test('should have proper tool discovery and schema validation', async () => { const tools = await client.listTools(); assert.ok(Array.isArray(tools), 'Tools should be an array'); const tool = tools.find(t => t.name === 'get_system_object_definitions'); assert.ok(tool, 'Tool should be found'); assert.ok(tool.description.includes('system object definitions'), 'Description should mention system object definitions'); // Validate schema structure assert.ok(tool.inputSchema, 'Tool should have input schema'); assert.equal(tool.inputSchema.type, 'object', 'Schema should be object type'); const properties = tool.inputSchema.properties; assert.ok(properties, 'Schema should have properties'); // Dynamic validation of optional parameters const expectedParams = ['count', 'start', 'select']; expectedParams.forEach(param => { if (properties[param]) { assert.ok(properties[param].type, `Parameter ${param} should have type`); } }); // Verify all parameters are optional assert.ok(!tool.inputSchema.required || tool.inputSchema.required.length === 0, 'All parameters should be optional'); }); }); // ================================================================================== // CORE FUNCTIONALITY WITH DYNAMIC VALIDATION // ================================================================================== describe('Core Functionality', () => { test('should retrieve and validate core SFCC object structure', async () => { const result = await client.callTool('get_system_object_definitions', {}); assertValidMCPResponse(result); assert.equal(result.isError, false, 'Should not return error'); const data = JSON.parse(result.content[0].text); // Validate OCAPI response structure assert.ok(data.data && Array.isArray(data.data), 'Should have data array'); assert.ok(typeof data.count === 'number', 'Should have count'); assert.ok(typeof data.total === 'number', 'Should have total'); assert.ok(data._v, 'Should have API version'); assert.ok(data._type, 'Should have type information'); // Dynamic validation - check for SFCC system objects const objectTypes = data.data.map(obj => obj.object_type); const expectedSystemTypes = ['Category', 'Catalog', 'Content', 'Basket', 'Campaign']; const foundSystemTypes = expectedSystemTypes.filter(type => objectTypes.includes(type)); assert.ok(foundSystemTypes.length > 0, `Should include SFCC system objects. Found: ${foundSystemTypes.join(', ')}, Available: ${objectTypes.slice(0,10).join(', ')}`); // Validate each object structure data.data.forEach((obj, index) => { try { assertSFCCObjectStructure(obj); } catch (error) { assert.fail(`Object at index ${index} failed validation: ${error.message}`); } }); }); test('should handle pagination with dynamic validation', async () => { const smallPageResult = await client.callTool('get_system_object_definitions', { count: 3 }); assert.equal(smallPageResult.isError, false, 'Small page request should succeed'); const smallPageData = JSON.parse(smallPageResult.content[0].text); // Dynamic pagination validation based on total count if (smallPageData.total > 3) { assert.ok(smallPageData.next, 'Should have next link when more data available'); assert.ok(smallPageData.data.length <= 3, 'Should not exceed requested count'); // Test second page const secondPageResult = await client.callTool('get_system_object_definitions', { start: 3, count: 3 }); if (!secondPageResult.isError) { const secondPageData = JSON.parse(secondPageResult.content[0].text); assert.equal(secondPageData.start, 3, 'Second page should have correct start'); // Ensure no overlap between pages const firstPageTypes = smallPageData.data.map(obj => obj.object_type); const secondPageTypes = secondPageData.data.map(obj => obj.object_type); const overlap = firstPageTypes.filter(type => secondPageTypes.includes(type)); assert.equal(overlap.length, 0, 'Pages should not have overlapping data'); } } }); test('should validate SFCC-specific metadata consistency', async () => { const result = await client.callTool('get_system_object_definitions', {}); const data = JSON.parse(result.content[0].text); // Validate SFCC OCAPI metadata (may vary by instance) if (data._resource_state) { assert.ok(typeof data._resource_state === 'string', 'Resource state should be string'); } // Check for system vs custom object identification const systemObjects = data.data.filter(obj => ['Product', 'Customer', 'Order', 'Site', 'Category', 'Campaign'].includes(obj.object_type) ); assert.ok(systemObjects.length > 0, 'Should include system objects'); // Validate that all objects have consistent typing data.data.forEach(obj => { assert.ok(obj._type, 'Each object should have _type'); assert.ok(obj.object_type, 'Each object should have object_type'); if (obj.object_type_id) { assert.ok(typeof obj.object_type_id === 'string', 'object_type_id should be string'); } }); }); }); // ================================================================================== // DYNAMIC PARAMETER VALIDATION // ================================================================================== describe('Parameter Validation', () => { test('should handle select parameter patterns with dynamic validation', async () => { const selectPatterns = [ { pattern: '(**)', description: 'wildcard all fields' }, { pattern: 'data.object_type,count,total', description: 'specific field selection' }, { pattern: 'data.(*),count', description: 'data wildcard with root fields' }, { pattern: 'count,total,_v', description: 'root-level fields only' } ]; for (const { pattern, description } of selectPatterns) { const result = await client.callTool('get_system_object_definitions', { select: pattern, count: 3 // Small count for efficiency }); assert.equal(result.isError, false, `Select pattern '${pattern}' (${description}) should not error`); const data = JSON.parse(result.content[0].text); assert.ok(data, `Should return valid data for pattern: ${pattern}`); // Dynamic validation based on select pattern if (pattern.includes('count')) { assert.ok(typeof data.count === 'number', `Should include count for pattern: ${pattern}`); } if (pattern.includes('data.object_type')) { assert.ok(data.data, `Should have data array for pattern: ${pattern}`); if (data.data.length > 0) { assert.ok(data.data[0].object_type, `Should include object_type for pattern: ${pattern}`); } } } }); test('should handle edge cases with type coercion', async () => { const edgeCases = [ { params: { start: '5', count: '3' }, description: 'string parameters' }, { params: { start: 0, count: 1 }, description: 'minimum values' }, { params: { start: 100 }, description: 'large start value' }, { params: { count: 0 }, description: 'zero count', expectError: true } ]; for (const { params, description, expectError } of edgeCases) { const result = await client.callTool('get_system_object_definitions', params); if (expectError) { // Zero count might be an error or return empty data if (!result.isError) { const data = JSON.parse(result.content[0].text); assert.equal(data.count, 0, `Zero count should be handled: ${description}`); assert.equal(data.data.length, 0, `Zero count should return empty data: ${description}`); } } else { assert.equal(result.isError, false, `Edge case should succeed: ${description}`); const data = JSON.parse(result.content[0].text); // Validate type coercion worked if (params.start !== undefined) { assert.equal(typeof data.start, 'number', `Start should be coerced to number: ${description}`); } if (params.count !== undefined && params.count > 0) { assert.ok(data.data.length <= params.count, `Should respect count limit: ${description}`); } } } }); test('should handle invalid parameters gracefully', async () => { const invalidCases = [ { params: { start: -1 }, description: 'negative start' }, { params: { count: -5 }, description: 'negative count' }, { params: { select: 'invalid.field.path' }, description: 'invalid select path' }, { params: { select: '' }, description: 'empty select' } ]; for (const { params, description } of invalidCases) { const result = await client.callTool('get_system_object_definitions', params); // Should either return error or handle gracefully if (result.isError) { assert.ok(result.content[0].text, `Should have error message for: ${description}`); } else { // If not an error, should return valid structure const data = JSON.parse(result.content[0].text); assert.ok(data, `Should return valid data despite invalid params: ${description}`); // Validate graceful handling (flexible validation) if (params.start < 0 && typeof data.start === 'number') { // SFCC API accepts negative start and preserves it in response assert.ok(typeof data.start === 'number', `API should handle negative start: ${description}`); } if (params.count < 0 && typeof data.count === 'number') { // SFCC API accepts negative count and returns valid data assert.ok(data.count >= 0, `API should handle negative count gracefully: ${description}`); } } } }); }); // ================================================================================== // INTEGRATION & CONSISTENCY VALIDATION // ================================================================================== describe('Integration & Consistency', () => { test('should maintain consistency across parameter combinations', async () => { const baseResult = await client.callTool('get_system_object_definitions', {}); const baseData = JSON.parse(baseResult.content[0].text); const combinations = [ { start: 0, count: 5 }, { count: 10, select: 'data.object_type,count,total' }, { start: 2, count: 3, select: 'data.(*),total' } ]; for (const params of combinations) { const result = await client.callTool('get_system_object_definitions', params); assert.equal(result.isError, false, `Combination should succeed: ${JSON.stringify(params)}`); const data = JSON.parse(result.content[0].text); // Total should be consistent across calls assert.equal(data.total, baseData.total, `Total should be consistent for params: ${JSON.stringify(params)}`); // API version should be consistent if (data._v && baseData._v) { assert.equal(data._v, baseData._v, `API version should be consistent for params: ${JSON.stringify(params)}`); } } }); test('should provide stable pagination behavior', async () => { // Test pagination stability by requesting overlapping windows const firstPage = await client.callTool('get_system_object_definitions', { start: 0, count: 5 }); const secondPage = await client.callTool('get_system_object_definitions', { start: 3, count: 5 }); if (!firstPage.isError && !secondPage.isError) { const firstData = JSON.parse(firstPage.content[0].text); const secondData = JSON.parse(secondPage.content[0].text); // Check for expected overlap in stable sort if (firstData.data.length >= 5 && secondData.data.length > 2) { const firstPageLast2 = firstData.data.slice(3, 5).map(obj => obj.object_type); const secondPageFirst2 = secondData.data.slice(0, 2).map(obj => obj.object_type); assert.deepEqual(firstPageLast2, secondPageFirst2, 'Overlapping pagination should return consistent results'); } } }); test('should handle comprehensive SFCC business validation', async () => { const result = await client.callTool('get_system_object_definitions', {}); const data = JSON.parse(result.content[0].text); // Business rule validation for SFCC (flexible for different instances) const systemObjectTypes = data.data.map(obj => obj.object_type); const criticalSFCCTypes = ['Category', 'Catalog', 'Content', 'Basket', 'Campaign']; // Check if SFCC instance has at least some critical system objects const foundCriticalTypes = criticalSFCCTypes.filter(type => systemObjectTypes.includes(type)); assert.ok(foundCriticalTypes.length > 0, `SFCC instance should include at least one critical system object. Found: ${foundCriticalTypes.join(', ')}, Available: ${systemObjectTypes.slice(0,5).join(', ')}`); // Validate object type ID consistency for found objects data.data.forEach(obj => { if (obj.object_type_id) { // Object type ID should be related to object type assert.ok(typeof obj.object_type_id === 'string', `object_type_id should be string for ${obj.object_type}`); // Critical objects should have non-empty IDs if they exist if (foundCriticalTypes.includes(obj.object_type)) { assert.ok(obj.object_type_id.length > 0, `System object ${obj.object_type} should have non-empty object_type_id`); } } }); // Validate that we have a reasonable number of object types for an SFCC instance assert.ok(data.total >= 1, 'SFCC instance should have at least 1 system object type'); assert.ok(data.total <= 200, 'SFCC instance should not have excessive object types'); }); }); }); ```