This is page 40 of 61. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=true&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_net/WebDAVClient.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.net 2 | 3 | # Class WebDAVClient 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.net.WebDAVClient 9 | 10 | ## Description 11 | 12 | The WebDAVClient class supports the WebDAV methods GET, PUT, MKCOL, MOVE, COPY, PROPFIND,OPTIONS and DELETE. Note: when this class is used with sensitive data, be careful in persisting sensitive information to disk. The client can be used as shown in the following example: var webdavClient : WebDAVClient = new WebDAVClient("http://mywebdav.server.com","myusername", "mypassword"); var getString : String = webdavClient.get("myData.xml","UTF-8"); var message : String; if (webdavClient.succeeded()) { message = webDavClient.statusText; } else { // error handling message="An error occurred with status code "+webdavClient.statusCode; } var data : XML = new XML(getString); The WebDAV client supports the following authentication schemes: Basic authentication Digest authentication The methods of this class do not generally throw exceptions if the underlying WebDAV operation do not succeed.The result of a WebDAV operation can be checked using the methods succeeded(), getStatusCode(), and getStatusText(). Important note: This WebDAV client cannot be used to access the Commerce Cloud Digital server via WebDAV protocol. 13 | 14 | ## Constants 15 | 16 | ### DEFAULT_ENCODING 17 | 18 | **Type:** String = "UTF-8" 19 | 20 | The default encoding character set. 21 | 22 | ### DEFAULT_GET_FILE_SIZE 23 | 24 | **Type:** Number = 5242880 25 | 26 | The default size for get() returning a File is 5MB. 27 | 28 | ### DEFAULT_GET_STRING_SIZE 29 | 30 | **Type:** Number = 2097152 31 | 32 | The default size for get() returning a String is 2MB. 33 | 34 | ### DEPTH_0 35 | 36 | **Type:** Number = 0 37 | 38 | The depth of searching a WebDAV destination using the PROPFIND method - if that depth is given to the PROPFIND method as an input parameter the destination will be searched only on the level of the given path and a list of all containing files on that level will be returned [is not supported by every server]. 39 | 40 | ### DEPTH_1 41 | 42 | **Type:** Number = 1 43 | 44 | The depth of searching a WebDAV destination using the PROPFIND method - if that depth is given to the PROPFIND method as an input parameter the destination will be searched until one level under the given path and a list of all containing files in that two levels [/path and one level underneath] will be returned [is not supported by every server]. 45 | 46 | ### DEPTH_INIFINITY 47 | 48 | **Type:** Number = 2147483647 49 | 50 | The depth of searching a WebDAV destination using the PROPFIND method - if that depth is given to the PROPFIND method as an input parameter the destination will be fully searched and a list of all containing files will be returned [is not supported by every server]. 51 | 52 | ### MAX_GET_FILE_SIZE 53 | 54 | **Type:** Number = 209715200 55 | 56 | The maximum size for get() returning a File is forty times the default size for getting a file. The largest file allowed is 200MB. 57 | 58 | ### MAX_GET_STRING_SIZE 59 | 60 | **Type:** Number = 10485760 61 | 62 | The maximum size for get() returning a String is five times the default size for getting a String. The largest String allowed is 10MB. 63 | 64 | ## Properties 65 | 66 | ### allResponseHeaders 67 | 68 | **Type:** HashMap (Read Only) 69 | 70 | A HashMap of all response headers. 71 | 72 | ### statusCode 73 | 74 | **Type:** Number (Read Only) 75 | 76 | The status code after the execution of a method. 77 | 78 | ### statusText 79 | 80 | **Type:** String (Read Only) 81 | 82 | The status text after the execution of a method. 83 | 84 | ## Constructor Summary 85 | 86 | WebDAVClient(rootUrl : String, username : String, password : String) Creates a new client for the use at a server which requires authentication. 87 | 88 | WebDAVClient(rootUrl : String) Creates a new client for the use at a server which does not require authentication. 89 | 90 | ## Method Summary 91 | 92 | ### addRequestHeader 93 | 94 | **Signature:** `addRequestHeader(headerName : String, headerValue : String) : void` 95 | 96 | Adds a request header to the next WebDAV call. 97 | 98 | ### close 99 | 100 | **Signature:** `close() : void` 101 | 102 | Closes the current connection to the server. 103 | 104 | ### copy 105 | 106 | **Signature:** `copy(origin : String, destination : String) : boolean` 107 | 108 | Copies a file on the server from one place rootUrl/origin to the other rootUrl/destination. 109 | 110 | ### copy 111 | 112 | **Signature:** `copy(origin : String, destination : String, overwrite : boolean) : boolean` 113 | 114 | Copies a file on the server from one place rootUrl/origin to the other rootUrl/destination. 115 | 116 | ### copy 117 | 118 | **Signature:** `copy(origin : String, destination : String, overwrite : boolean, shallow : boolean) : boolean` 119 | 120 | Copies a file on the server from one place rootUrl/origin to the other rootUrl/destination. 121 | 122 | ### del 123 | 124 | **Signature:** `del(path : String) : boolean` 125 | 126 | Deletes a file or directory from the remote server that can be found under rootUrl/path. 127 | 128 | ### get 129 | 130 | **Signature:** `get(path : String) : String` 131 | 132 | Reads the content of a remote file or directory that can be found under rootUrl/path and returns a string representation of the data found in the DEFAULT_ENCODING encoding. 133 | 134 | ### get 135 | 136 | **Signature:** `get(path : String, encoding : String) : String` 137 | 138 | Reads the content of a remote file or directory that can be found under rootUrl/path and returns a string representation of the data found in the given encoding. 139 | 140 | ### get 141 | 142 | **Signature:** `get(path : String, encoding : String, maxGetSize : Number) : String` 143 | 144 | Reads the content of a remote file or directory that can be found under rootUrl/path and returns a string representation of the data found in the given encoding. 145 | 146 | ### get 147 | 148 | **Signature:** `get(path : String, file : File) : boolean` 149 | 150 | Reads the content of a remote file or directory that can be found under rootUrl/path in DEFAULT_ENCODING encoding and writes a File in the system's standard encoding, which is "UTF-8". 151 | 152 | ### get 153 | 154 | **Signature:** `get(path : String, file : File, maxFileSize : Number) : boolean` 155 | 156 | Reads the content of a remote file or directory that can be found under rootUrl/path in DEFAULT_ENCODING encoding and writes a File in the system's standard encoding, which is "UTF-8". 157 | 158 | ### get 159 | 160 | **Signature:** `get(path : String, file : File, encoding : String, maxFileSize : Number) : boolean` 161 | 162 | Reads the content of a remote file or directory that can be found under rootUrl/path in the passed encoding and writes a File in the system standard encoding, which is "UTF-8". 163 | 164 | ### getAllResponseHeaders 165 | 166 | **Signature:** `getAllResponseHeaders() : HashMap` 167 | 168 | Returns a HashMap of all response headers. 169 | 170 | ### getBinary 171 | 172 | **Signature:** `getBinary(path : String, file : File) : boolean` 173 | 174 | Reads the content of a remote binary file that can be found under rootUrl/path and creates a local copy in File. 175 | 176 | ### getBinary 177 | 178 | **Signature:** `getBinary(path : String, file : File, maxFileSize : Number) : boolean` 179 | 180 | Reads the content of a remote binary file that can be found under rootUrl/path and creates a local copy in File. 181 | 182 | ### getResponseHeader 183 | 184 | **Signature:** `getResponseHeader(header : String) : String` 185 | 186 | Returns a specified response header - multiple headers are separated by CRLF. 187 | 188 | ### getStatusCode 189 | 190 | **Signature:** `getStatusCode() : Number` 191 | 192 | Returns the status code after the execution of a method. 193 | 194 | ### getStatusText 195 | 196 | **Signature:** `getStatusText() : String` 197 | 198 | Returns the status text after the execution of a method. 199 | 200 | ### mkcol 201 | 202 | **Signature:** `mkcol(path : String) : boolean` 203 | 204 | Creates a directory on the remote server on the location rootUrl/path. 205 | 206 | ### move 207 | 208 | **Signature:** `move(origin : String, destination : String) : boolean` 209 | 210 | Moves a file on the server from one place rootUrl + "/" +origin to the other rootUrl/destination. 211 | 212 | ### move 213 | 214 | **Signature:** `move(origin : String, destination : String, overwrite : boolean) : boolean` 215 | 216 | Moves a file on the server from one place rootUrl/origin to the other rootUrl/destination Can also be used to rename a remote file. 217 | 218 | ### options 219 | 220 | **Signature:** `options(path : String) : String[]` 221 | 222 | Returns a list of methods which can be executed on the server location rootUrl/path. 223 | 224 | ### propfind 225 | 226 | **Signature:** `propfind(path : String) : WebDAVFileInfo[]` 227 | 228 | Get file listing of a remote location. 229 | 230 | ### propfind 231 | 232 | **Signature:** `propfind(path : String, depth : Number) : WebDAVFileInfo[]` 233 | 234 | Get file listing of a remote location. 235 | 236 | ### put 237 | 238 | **Signature:** `put(path : String, content : String) : boolean` 239 | 240 | Puts content encoded with DEFAULT_ENCODING into a remote located file at rootUrl/path. 241 | 242 | ### put 243 | 244 | **Signature:** `put(path : String, content : String, encoding : String) : boolean` 245 | 246 | Puts content encoded with the passed encoding into a remote located file at rootUrl/path. 247 | 248 | ### put 249 | 250 | **Signature:** `put(path : String, file : File) : boolean` 251 | 252 | Puts content out of a passed local file into a remote located file at rootUrl/path. 253 | 254 | ### succeeded 255 | 256 | **Signature:** `succeeded() : boolean` 257 | 258 | Returns true if the last executed WebDAV method was executed successfully - otherwise false. 259 | 260 | ## Constructor Detail 261 | 262 | ## Method Detail 263 | 264 | ## Method Details 265 | 266 | ### addRequestHeader 267 | 268 | **Signature:** `addRequestHeader(headerName : String, headerValue : String) : void` 269 | 270 | **Description:** Adds a request header to the next WebDAV call. 271 | 272 | **Parameters:** 273 | 274 | - `headerName`: name of the header. 275 | - `headerValue`: value of the header. 276 | 277 | --- 278 | 279 | ### close 280 | 281 | **Signature:** `close() : void` 282 | 283 | **Description:** Closes the current connection to the server. 284 | 285 | --- 286 | 287 | ### copy 288 | 289 | **Signature:** `copy(origin : String, destination : String) : boolean` 290 | 291 | **Description:** Copies a file on the server from one place rootUrl/origin to the other rootUrl/destination. If destination already exists it gets overwritten. Returns true if succeeded, otherwise false. 292 | 293 | **Parameters:** 294 | 295 | - `origin`: The origin where a file is located, relative to the rootUrl stated when instantiating the client. 296 | - `destination`: The destination where the file should be copied to, relative to the rootUrl stated when instantiating the client. 297 | 298 | **Returns:** 299 | 300 | true if succeeded, otherwise false. 301 | 302 | --- 303 | 304 | ### copy 305 | 306 | **Signature:** `copy(origin : String, destination : String, overwrite : boolean) : boolean` 307 | 308 | **Description:** Copies a file on the server from one place rootUrl/origin to the other rootUrl/destination. If the passed parameter overwrite is true and destination already exists it gets overwritten. Returns true if succeeded, otherwise false. 309 | 310 | **Parameters:** 311 | 312 | - `origin`: The origin where a file is located, relative to the rootUrl stated when instantiating the client. 313 | - `destination`: The destination where the file should be copied to, relative to the rootUrl stated when instantiating the client. 314 | - `overwrite`: A flag which determines whether the destination gets overwritten if it exists before copying. 315 | 316 | **Returns:** 317 | 318 | true if succeeded, otherwise false. 319 | 320 | --- 321 | 322 | ### copy 323 | 324 | **Signature:** `copy(origin : String, destination : String, overwrite : boolean, shallow : boolean) : boolean` 325 | 326 | **Description:** Copies a file on the server from one place rootUrl/origin to the other rootUrl/destination. If the passed parameter overwrite is true and destination already exists it gets overwritten. If the passed parameter shallow is true a flat copy mechanism is used. Returns true if succeeded, otherwise false. 327 | 328 | **Parameters:** 329 | 330 | - `origin`: The origin where a file is located, relative to the rootUrl stated when instantiating the client. 331 | - `destination`: The destination where the file should be copied to, relative to the rootUrl stated when instantiating the client. 332 | - `overwrite`: A flag which determines whether the destination gets overwritten if it exits before copying 333 | - `shallow`: A flag which determines how to copy the given data. 334 | 335 | **Returns:** 336 | 337 | true if succeeded, otherwise false. 338 | 339 | --- 340 | 341 | ### del 342 | 343 | **Signature:** `del(path : String) : boolean` 344 | 345 | **Description:** Deletes a file or directory from the remote server that can be found under rootUrl/path. Returns true if succeeded, otherwise false. 346 | 347 | **Parameters:** 348 | 349 | - `path`: The path of the file or collection to delete, relative to the rootUrl stated when instantiating the client. 350 | 351 | **Returns:** 352 | 353 | true if succeeded, otherwise false. 354 | 355 | --- 356 | 357 | ### get 358 | 359 | **Signature:** `get(path : String) : String` 360 | 361 | **Description:** Reads the content of a remote file or directory that can be found under rootUrl/path and returns a string representation of the data found in the DEFAULT_ENCODING encoding. If the remote location is a directory the result depends on the server configuration, some return an HTML formatted directory listing. Returns at most DEFAULT_GET_STRING_SIZE bytes. 362 | 363 | **Parameters:** 364 | 365 | - `path`: The path of the collection or file one wants to get, relative to the rootUrl stated when instantiating the client. 366 | 367 | **Returns:** 368 | 369 | returns the String representation of the data found on the given path. 370 | 371 | --- 372 | 373 | ### get 374 | 375 | **Signature:** `get(path : String, encoding : String) : String` 376 | 377 | **Description:** Reads the content of a remote file or directory that can be found under rootUrl/path and returns a string representation of the data found in the given encoding. If the remote location is a directory the result depends on the server configuration, some return an HTML formatted directory listing. Returns at most DEFAULT_GET_STRING_SIZE bytes. 378 | 379 | **Parameters:** 380 | 381 | - `path`: The path of the collection or file one wants to get - relative to the rootUrl stated when instantiating the client. 382 | - `encoding`: The encoding of the resulting String. 383 | 384 | **Returns:** 385 | 386 | returns the String representation of the data found on the given path in the given encoding. 387 | 388 | --- 389 | 390 | ### get 391 | 392 | **Signature:** `get(path : String, encoding : String, maxGetSize : Number) : String` 393 | 394 | **Description:** Reads the content of a remote file or directory that can be found under rootUrl/path and returns a string representation of the data found in the given encoding. If the remote location is a directory the result depends on the server configuration, some return an HTML formatted directory listing. Returns at most maxGetSize bytes. 395 | 396 | **Parameters:** 397 | 398 | - `path`: The path of the collection or file one wants to get - relative to the rootUrl stated when instantiating the client. 399 | - `encoding`: The encoding of the resulting String. 400 | - `maxGetSize`: The maximum size of data in bytes. Not to exceed MAX_GET_STRING_SIZE. 401 | 402 | **Returns:** 403 | 404 | returns the String representation of the data found on the given path in the given encoding. 405 | 406 | --- 407 | 408 | ### get 409 | 410 | **Signature:** `get(path : String, file : File) : boolean` 411 | 412 | **Description:** Reads the content of a remote file or directory that can be found under rootUrl/path in DEFAULT_ENCODING encoding and writes a File in the system's standard encoding, which is "UTF-8". If the remote location is a directory the result depends on the server configuration, some return an HTML formatted directory listing. Receives at most DEFAULT_GET_FILE_SIZE bytes which determines the file size of the local file. Returns true if succeeded otherwise false. 413 | 414 | **Parameters:** 415 | 416 | - `path`: The path of the collection or file one wants to get - relative to the rootUrl stated when instantiating the client. 417 | - `file`: The file to save the received data in. 418 | 419 | **Returns:** 420 | 421 | returns true if succeeded, otherwise false. 422 | 423 | --- 424 | 425 | ### get 426 | 427 | **Signature:** `get(path : String, file : File, maxFileSize : Number) : boolean` 428 | 429 | **Description:** Reads the content of a remote file or directory that can be found under rootUrl/path in DEFAULT_ENCODING encoding and writes a File in the system's standard encoding, which is "UTF-8". If the remote location is a directory the result depends on the server configuration, some return an HTML formatted directory listing. Receives at most maxFileSize bytes which determines the file size of the local file. Returns true if succeeded, otherwise false. 430 | 431 | **Parameters:** 432 | 433 | - `path`: The path of the collection or file one wants to get - relative to the rootUrl stated when instantiating the client. 434 | - `file`: The file to save the received data in. 435 | - `maxFileSize`: The maximum size of bytes to stream into the file. Not to exceed MAX_GET_FILE_SIZE. 436 | 437 | **Returns:** 438 | 439 | returns true if succeeded, otherwise false. 440 | 441 | --- 442 | 443 | ### get 444 | 445 | **Signature:** `get(path : String, file : File, encoding : String, maxFileSize : Number) : boolean` 446 | 447 | **Description:** Reads the content of a remote file or directory that can be found under rootUrl/path in the passed encoding and writes a File in the system standard encoding, which is "UTF-8". If the remote location is a directory the result depends on the server configuration, some return an HTML formatted directory listing. Receives at most maxFileSize bytes which determines the file size of the local file. Returns true if succeeded, otherwise false. 448 | 449 | **Parameters:** 450 | 451 | - `path`: The path of the collection or file one wants to get - relative to the rootUrl stated when instantiating the client. 452 | - `file`: The file to save the received data in. 453 | - `encoding`: The encoding to use when reading the remote file. 454 | - `maxFileSize`: The maximum number of bytes to stream into the file. Not to exceed MAX_GET_FILE_SIZE. 455 | 456 | **Returns:** 457 | 458 | returns true if succeeded, otherwise false. 459 | 460 | --- 461 | 462 | ### getAllResponseHeaders 463 | 464 | **Signature:** `getAllResponseHeaders() : HashMap` 465 | 466 | **Description:** Returns a HashMap of all response headers. 467 | 468 | **Returns:** 469 | 470 | all headers in a HashMap. 471 | 472 | --- 473 | 474 | ### getBinary 475 | 476 | **Signature:** `getBinary(path : String, file : File) : boolean` 477 | 478 | **Description:** Reads the content of a remote binary file that can be found under rootUrl/path and creates a local copy in File. If the remote location is a directory the result depends on the server configuration, some return an HTML formatted directory listing. Copies at most DEFAULT_GET_FILE_SIZE bytes. Returns true if succeeded, otherwise false. 479 | 480 | **Parameters:** 481 | 482 | - `path`: The path relative to rootUrl on the remote server including the file name. 483 | - `file`: The local file where the received binary data should be stored. 484 | 485 | **Returns:** 486 | 487 | true if succeeded, otherwise false. 488 | 489 | --- 490 | 491 | ### getBinary 492 | 493 | **Signature:** `getBinary(path : String, file : File, maxFileSize : Number) : boolean` 494 | 495 | **Description:** Reads the content of a remote binary file that can be found under rootUrl/path and creates a local copy in File. If the remote location is a directory the result depends on the server configuration, some return an HTML formatted directory listing. Copies at most maxFileSize bytes. Returns true if succeeded, otherwise false. 496 | 497 | **Parameters:** 498 | 499 | - `path`: The path relative to rootUrl on the remote server including the file name. 500 | - `file`: The file local file where the received binary data should be stored. 501 | - `maxFileSize`: The maximum number of bytes to stream into the file. Not to exceed MAX_GET_FILE_SIZE. 502 | 503 | **Returns:** 504 | 505 | true if succeeded, otherwise false. 506 | 507 | --- 508 | 509 | ### getResponseHeader 510 | 511 | **Signature:** `getResponseHeader(header : String) : String` 512 | 513 | **Description:** Returns a specified response header - multiple headers are separated by CRLF. 514 | 515 | **Parameters:** 516 | 517 | - `header`: The name of the header. 518 | 519 | **Returns:** 520 | 521 | The header - in case of multiple headers separated by CRLF. 522 | 523 | --- 524 | 525 | ### getStatusCode 526 | 527 | **Signature:** `getStatusCode() : Number` 528 | 529 | **Description:** Returns the status code after the execution of a method. 530 | 531 | **Returns:** 532 | 533 | the statusCode. 534 | 535 | --- 536 | 537 | ### getStatusText 538 | 539 | **Signature:** `getStatusText() : String` 540 | 541 | **Description:** Returns the status text after the execution of a method. 542 | 543 | **Returns:** 544 | 545 | the statusText. 546 | 547 | --- 548 | 549 | ### mkcol 550 | 551 | **Signature:** `mkcol(path : String) : boolean` 552 | 553 | **Description:** Creates a directory on the remote server on the location rootUrl/path. 554 | 555 | **Parameters:** 556 | 557 | - `path`: The path relative to the rootUrl stated when instantiating the client where the new collection should be created. 558 | 559 | **Returns:** 560 | 561 | true if succeeded, otherwise false. 562 | 563 | --- 564 | 565 | ### move 566 | 567 | **Signature:** `move(origin : String, destination : String) : boolean` 568 | 569 | **Description:** Moves a file on the server from one place rootUrl + "/" +origin to the other rootUrl/destination. If destination already exists it gets overwritten. Can also be used to rename a remote file. Returns true if succeeded, otherwise false. 570 | 571 | **Parameters:** 572 | 573 | - `origin`: The origin where a file is located, relative to the rootUrl stated when instantiating the client. 574 | - `destination`: The destination where the file should be moved to, relative to the rootUrl stated when instantiating the client. 575 | 576 | **Returns:** 577 | 578 | true if succeeded, otherwise false. 579 | 580 | --- 581 | 582 | ### move 583 | 584 | **Signature:** `move(origin : String, destination : String, overwrite : boolean) : boolean` 585 | 586 | **Description:** Moves a file on the server from one place rootUrl/origin to the other rootUrl/destination Can also be used to rename a remote file. If overwrite is true and destination already exists it gets overwritten. Returns true if succeeded, otherwise false. 587 | 588 | **Parameters:** 589 | 590 | - `origin`: The origin where a file is located, relative to the rootUrl stated when instantiating the client. 591 | - `destination`: The destination where the file should be moved to, relative to the rootUrl stated when instantiating the client. 592 | - `overwrite`: A flag which determines whether the destination gets overwritten if it exists before moving. 593 | 594 | **Returns:** 595 | 596 | true if succeeded, otherwise false. 597 | 598 | --- 599 | 600 | ### options 601 | 602 | **Signature:** `options(path : String) : String[]` 603 | 604 | **Description:** Returns a list of methods which can be executed on the server location rootUrl/path. 605 | 606 | **Parameters:** 607 | 608 | - `path`: The path relative to the rootUrl stated when instantiating the client one wants to get the options for. 609 | 610 | **Returns:** 611 | 612 | list of WebDav methods which can be executed on the given path. 613 | 614 | --- 615 | 616 | ### propfind 617 | 618 | **Signature:** `propfind(path : String) : WebDAVFileInfo[]` 619 | 620 | **Description:** Get file listing of a remote location. Returns a list of WebDAVFileInfo objects which contain information about the files and directories located on rootUrl/path and DEPTH_1 (1) level underneath. 621 | 622 | **Parameters:** 623 | 624 | - `path`: The path relative to the rootUrl stated when instantiating the client where to get information about the containing files from. 625 | 626 | **Returns:** 627 | 628 | an Array of WebDAVFileInfo objects which hold information about the files located on the server at the location. 629 | 630 | --- 631 | 632 | ### propfind 633 | 634 | **Signature:** `propfind(path : String, depth : Number) : WebDAVFileInfo[]` 635 | 636 | **Description:** Get file listing of a remote location. Returns a list of WebDAVFileInfo objects which contain information about the files and directories located on rootUrl/path and the passed depth underneath. 637 | 638 | **Parameters:** 639 | 640 | - `path`: The path relative to the rootUrl stated when instantiating the client where to get information about the containing files from. 641 | - `depth`: The level starting from rootUrl down to which the file information gets collected. 642 | 643 | **Returns:** 644 | 645 | an Array of WebDAVFileInfo objects which hold information about the files located on the server at the location. 646 | 647 | --- 648 | 649 | ### put 650 | 651 | **Signature:** `put(path : String, content : String) : boolean` 652 | 653 | **Description:** Puts content encoded with DEFAULT_ENCODING into a remote located file at rootUrl/path. Returns true if succeeded, otherwise false. If the content of a local file is to be uploaded, please use method put(String, File) instead. 654 | 655 | **Parameters:** 656 | 657 | - `path`: The path to put given content up to, relative to the rootUrl stated when instantiating the client. 658 | - `content`: The content that has to be pushed on to the server. 659 | 660 | **Returns:** 661 | 662 | true if succeeded, otherwise false. 663 | 664 | --- 665 | 666 | ### put 667 | 668 | **Signature:** `put(path : String, content : String, encoding : String) : boolean` 669 | 670 | **Description:** Puts content encoded with the passed encoding into a remote located file at rootUrl/path. Returns true if succeeded, otherwise false. If the content of a local file is to be uploaded, please use method put(String, File) instead. 671 | 672 | **Parameters:** 673 | 674 | - `path`: The path to put a given content up to, relative to the rootUrl stated when instantiating the client. 675 | - `content`: The content that has to be pushed on to a remote location. 676 | - `encoding`: The encoding in which the data should be stored on the server. 677 | 678 | **Returns:** 679 | 680 | true if succeeded, otherwise false. 681 | 682 | --- 683 | 684 | ### put 685 | 686 | **Signature:** `put(path : String, file : File) : boolean` 687 | 688 | **Description:** Puts content out of a passed local file into a remote located file at rootUrl/path. This method performs a binary file transfer. Returns true if succeeded, otherwise false. 689 | 690 | **Parameters:** 691 | 692 | - `path`: The path to put given content up to, relative to the rootUrl stated when instantiating the client. 693 | - `file`: The file to push up to the server. 694 | 695 | **Returns:** 696 | 697 | true if succeeded, otherwise false. 698 | 699 | --- 700 | 701 | ### succeeded 702 | 703 | **Signature:** `succeeded() : boolean` 704 | 705 | **Description:** Returns true if the last executed WebDAV method was executed successfully - otherwise false. See the code snippet above for an example how to use the succeed() method. 706 | 707 | **Returns:** 708 | 709 | true if the last executed WebDAV method was successful - otherwise false. 710 | 711 | **See Also:** 712 | 713 | WebDAVClient 714 | 715 | --- ``` -------------------------------------------------------------------------------- /ai-instructions/github-copilot/copilot-instructions.md: -------------------------------------------------------------------------------- ```markdown 1 | # SFCC Development AI Assistant Instructions 2 | 3 | ## 👨💻 Agent Persona 4 | 5 | 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: 6 | 7 | ### 🏗️ Core Development Areas 8 | - **SFRA Controllers**: Expert in creating performant, maintainable controllers following MVC patterns 9 | - **SFRA Models**: Deep knowledge of JSON object layer design, model architecture, and data transformation patterns 10 | - **LocalServiceRegistry**: Expert in server-to-server integrations, OAuth flows, and reusable service module patterns 11 | - **OCAPI Hooks**: Deep knowledge of extending Open Commerce APIs with custom business logic 12 | - **SCAPI Hooks**: Specialized in Shop API extensions and modern headless commerce patterns 13 | - **Custom SCAPI Endpoints**: Building secure, scalable REST APIs for custom integrations 14 | - **Cartridge Development**: Architecting modular, reusable cartridge solutions 15 | - **ISML Templates**: Expert in template development with security and performance optimization 16 | 17 | ### 🔒 Security & Best Practices 18 | - **Secure Coding**: OWASP compliance, input validation, XSS/CSRF prevention 19 | - **Performance Optimization**: Query optimization, caching strategies, code profiling 20 | - **Accessibility**: WCAG 2.1 AA compliance, semantic HTML, keyboard navigation 21 | - **Code Quality**: Clean code principles, design patterns, code reviews 22 | - **Testing**: Unit testing, integration testing, performance testing 23 | 24 | ### 💼 Professional Approach 25 | - **Solution-Oriented**: Always provide practical, implementable solutions 26 | - **Best Practice Focused**: Follow SFCC development standards and industry conventions 27 | - **Security-First**: Consider security implications in every recommendation 28 | - **Performance-Aware**: Optimize for scalability and user experience 29 | - **Documentation-Driven**: Provide clear explanations and code comments 30 | 31 | When providing assistance: 32 | 1. **Always use the MCP tools** to get current, accurate SFCC information 33 | 2. **Consider the full context** - security, performance, maintainability 34 | 4. **Explain the "why"** behind architectural decisions 35 | 5. **Reference official documentation** and best practices 36 | 6. **Cartridge Creation**: When asked to create a cartridge, follow the instructions in the MCP (especially creating the structure using sgmf-scripts, don't try to do this yourself) 37 | 38 | ### 🔧 Code Quality & Linting 39 | 40 | **SFCC Class Verification**: Before using any SFCC class, verify its existence and check for information about its methods and properties. Pay attention to deprecations and changes in the API. 41 | 42 | **ESLint Integration**: Before manually fixing linting errors: 43 | 1. **First attempt automatic fixes**: Run `npm run lint:fix` or `eslint --fix` if available 44 | 2. **Check results**: Use `get_errors` tool to verify what remains after auto-fix 45 | 3. **Manual fixes only**: Apply manual corrections only for issues that auto-fix couldn't resolve 46 | 4. **Validate changes**: Always run error checking after making manual corrections 47 | 48 | This approach ensures: 49 | - ✅ Consistent formatting and style enforcement 50 | - ✅ Automatic resolution of common linting issues 51 | - ✅ Reduced manual intervention for routine fixes 52 | - ✅ Focus on logic errors that require human judgment 53 | 54 | --- 55 | 56 | This project uses the **SFCC Development MCP Server** to provide accurate, up-to-date information about Salesforce B2C Commerce Cloud (SFCC) development. When working on SFCC-related tasks, **always use the available MCP tools** instead of relying on potentially outdated or inaccurate information from your training data. 57 | 58 | ## 🎯 Why Use the MCP Tools 59 | 60 | - **Accuracy**: Get current, verified SFCC API documentation and best practices 61 | - **Completeness**: Access comprehensive class information, methods, and properties 62 | - **Real-time**: Query live SFCC system objects and attributes from the actual instance 63 | - **Debugging**: Access actual SFCC logs for troubleshooting and error analysis 64 | - **Best Practices**: Get current SFCC development guidelines and security recommendations 65 | 66 | ## 📋 Available Tool Categories 67 | 68 | ### 🔍 SFCC Documentation Tools 69 | Use these tools for any SFCC API or class-related questions: 70 | 71 | - **`get_sfcc_class_info`** - Get detailed info about any SFCC class (dw.* namespace) 72 | - **`search_sfcc_classes`** - Find SFCC classes by name or functionality 73 | - **`search_sfcc_methods`** - Find methods across all classes by name 74 | - **`list_sfcc_classes`** - Get complete list of available SFCC classes 75 | - **`get_sfcc_class_documentation`** - Get raw documentation for any SFCC class 76 | 77 | ### 📚 Best Practices & Guidelines 78 | Use these for implementation guidance and best practices: 79 | 80 | - **`get_available_best_practice_guides`** - See what guides are available 81 | - **`get_best_practice_guide`** - Get complete guides for cartridges, hooks, controllers, etc. 82 | - **`search_best_practices`** - Find specific guidance on topics like security, performance 83 | - **`get_hook_reference`** - Get comprehensive OCAPI/SCAPI hook references 84 | 85 | ### 🏗️ SFRA Documentation Tools 86 | Use these for SFRA (Storefront Reference Architecture) related questions: 87 | 88 | - **`get_available_sfra_documents`** - See what SFRA documents are available 89 | - **`get_sfra_document`** - Get complete SFRA class/module documentation (includes all properties and methods) 90 | - **`search_sfra_documentation`** - Search across all SFRA documentation 91 | 92 | ### 🔧 System Object Definitions 93 | Use these for understanding SFCC data models and custom attributes: 94 | 95 | - **`get_system_object_definitions`** - Get all system objects (Product, Customer, Order, etc.) 96 | - **`get_system_object_definition`** - Get details about a specific system object 97 | - **`search_system_object_attribute_definitions`** - Search for specific attributes 98 | - **`search_site_preferences`** - Search for site preferences in preference groups 99 | - **`search_system_object_attribute_groups`** - Search for attribute groups (essential for finding site preference groups) 100 | - **`search_custom_object_attribute_definitions`** - Search for attributes in custom object types 101 | 102 | ### � Code Version Management 103 | Use these for deployment and code version operations: 104 | 105 | - **`get_code_versions`** - Get all code versions on the SFCC instance 106 | - **`activate_code_version`** - Activate a specific code version (useful for code-switch fixes) 107 | 108 | ### �📊 Log Analysis Tools 109 | Use these for debugging and troubleshooting: 110 | 111 | - **`get_latest_errors`** - Get recent error messages from SFCC logs 112 | - **`get_latest_warnings`** - Get recent warnings from SFCC logs 113 | - **`search_logs`** - Search logs for specific patterns or keywords 114 | - **`summarize_logs`** - Get overview of log activity and issues 115 | - **`list_log_files`** - List available log files with metadata 116 | - **`get_log_file_contents`** - Get contents of a specific log file (supports size limits and head/tail reading) 117 | 118 | ## 🚀 When to Use These Tools 119 | 120 | ### ✅ DO Use MCP Tools For: 121 | 122 | 1. **API Documentation Questions** 123 | ``` 124 | "What methods are available on dw.catalog.Product?" 125 | → Use: get_sfcc_class_methods with className: "dw.catalog.Product" 126 | ``` 127 | 128 | 2. **Finding the Right Class** 129 | ``` 130 | "How do I work with customer data in SFCC?" 131 | → Use: search_sfcc_classes with query: "customer" 132 | ``` 133 | 134 | 3. **Implementation Best Practices** 135 | ``` 136 | "How should I create a new cartridge?" 137 | → Use: get_best_practice_guide with guideName: "cartridge_creation" 138 | ``` 139 | 140 | 4. **Understanding System Objects** 141 | ``` 142 | "What custom attributes are on the Product object?" 143 | → Use: get_system_object_attribute_definitions with objectType: "Product" 144 | ``` 145 | 146 | 5. **Debugging Issues** 147 | ``` 148 | "Are there any recent errors in the logs?" 149 | → Use: get_latest_errors 150 | 151 | "What log files are available for today?" 152 | → Use: list_log_files 153 | 154 | "I need to see the full contents of a specific error log file" 155 | → Use: get_log_file_contents with filename: "error-2023-01-01.log" 156 | 157 | "Show me just the first 1MB of a large log file" 158 | → Use: get_log_file_contents with filename: "large.log", maxBytes: 1048576, tailOnly: false 159 | 160 | "Get the last 500KB of a log file to see recent entries" 161 | → Use: get_log_file_contents with filename: "debug.log", maxBytes: 512000, tailOnly: true 162 | ``` 163 | 164 | 6. **Code Version Management** 165 | ``` 166 | "What code versions are available on the instance?" 167 | → Use: get_code_versions 168 | 169 | "Need to do a code-switch fix for SCAPI endpoints" 170 | → Use: activate_code_version with versionId: "target_version" 171 | ``` 172 | 173 | 7. **Hook Development** 174 | ``` 175 | "What SCAPI hooks are available?" 176 | → Use: get_hook_reference with guideName: "scapi_hooks" 177 | ``` 178 | 179 | ### ❌ DON'T Guess About: 180 | 181 | - SFCC class names or method signatures 182 | - Custom attribute names or system object structures 183 | - Current best practices or security guidelines 184 | - Available hook endpoints or extension points 185 | - Recent system errors or log patterns 186 | 187 | ## 🎨 Example Usage Patterns 188 | 189 | ### Finding and Using SFCC Classes 190 | ```markdown 191 | 1. First search for relevant classes: 192 | → search_sfcc_classes with query: "order" 193 | 194 | 2. Get detailed information about the class: 195 | → get_sfcc_class_info with className: "dw.order.Order" 196 | 197 | 3. Explore specific methods or properties: 198 | → get_sfcc_class_methods with className: "dw.order.Order" 199 | ``` 200 | 201 | ### Implementing New Features 202 | ```markdown 203 | 1. Check best practices first: 204 | → get_available_best_practice_guides 205 | 206 | 2. Get specific implementation guide: 207 | → get_best_practice_guide with guideName: "sfra_controllers" 208 | 209 | 3. Search for specific guidance: 210 | → search_best_practices with query: "validation" 211 | ``` 212 | 213 | ### Debugging Problems 214 | ```markdown 215 | 1. Get log overview: 216 | → summarize_logs 217 | 218 | 2. Check recent errors: 219 | → get_latest_error 220 | 221 | 3. Search for specific issues: 222 | → search_logs with pattern: "your_error_pattern" 223 | ``` 224 | 225 | ### Working with System Objects 226 | ```markdown 227 | 1. List available system objects: 228 | → get_system_object_definitions 229 | 230 | 2. Get object structure: 231 | → get_system_object_definition with objectType: "Product" 232 | 233 | 3. Find specific attributes: 234 | → search_system_object_attribute_definitions 235 | with objectType: "Product" 236 | and searchRequest: { query: { text_query: { fields: ["id"], search_phrase: "custom" } } } 237 | ``` 238 | 239 | ## 🔐 Tool Availability 240 | 241 | Some tools require specific SFCC credentials: 242 | 243 | - **Documentation & Best Practices**: Always available 244 | - **Log Analysis**: Requires SFCC instance credentials (hostname, username, password) 245 | - **System Objects & Code Versions**: Requires OCAPI credentials (clientId, clientSecret) 246 | 247 | If a tool isn't available, the MCP server will provide clear error messages about what credentials are needed. 248 | 249 | ## 💡 Pro Tips 250 | 251 | 1. **Start Broad, Then Narrow**: Use search tools first, then get detailed information 252 | 2. **Check Best Practices Early**: Always consult best practice guides before implementing 253 | 3. **Use Real Data**: Query actual system objects rather than assuming structure 254 | 4. **Debug with Logs**: Use log analysis tools for troubleshooting real issues 255 | 5. **Stay Current**: MCP tools provide current information, not outdated documentation 256 | 257 | ## 🚨 Important Reminders 258 | 259 | - **Always prefer MCP tools** over guessing or using potentially outdated information 260 | - **Use specific tool calls** rather than making assumptions about SFCC APIs 261 | - **Check logs and system objects** from the actual SFCC instance when debugging 262 | - **Follow best practices** from the guides rather than improvising solutions 263 | - **Verify class and method names** using the documentation tools 264 | 265 | --- 266 | 267 | By following these guidelines, you'll get more accurate, current, and reliable assistance with SFCC development tasks. The MCP server is your source of truth for SFCC development information! 268 | 269 | ## 🧠 META TOOLS: AI Agent Intelligence Guide 270 | 271 | ### 🎯 Tool Selection Decision Trees 272 | 273 | #### **When User Asks About SFCC Classes/APIs** 274 | ``` 275 | Question Type: "How do I work with [X] in SFCC?" 276 | ├── Unknown what class to use 277 | │ └── → search_sfcc_classes (query: key concept) 278 | ├── Know specific class name 279 | │ └── → get_sfcc_class_info (className: "dw.namespace.Class") 280 | ├── Looking for specific method 281 | │ └── → search_sfcc_methods (methodName: "methodName") 282 | └── Need comprehensive class docs 283 | └── → get_sfcc_class_documentation (className: "dw.namespace.Class") 284 | ``` 285 | 286 | #### **When User Asks About Implementation** 287 | ``` 288 | Implementation Question 289 | ├── "How to create/build [X]?" 290 | │ ├── → get_available_best_practice_guides (discover available guides) 291 | │ └── → get_best_practice_guide (guideName: specific guide) 292 | ├── "Best way to [X]?" 293 | │ └── → search_best_practices (query: key concept) 294 | ├── "What hooks are available for [X]?" 295 | │ └── → get_hook_reference (guideName: "ocapi_hooks" or "scapi_hooks") 296 | └── "SFRA related question?" 297 | ├── → get_available_sfra_documents (discover what's available) 298 | └── → get_sfra_document (documentName: specific module) 299 | ``` 300 | 301 | #### **When User Reports Problems/Errors** 302 | ``` 303 | Problem/Error Scenario 304 | ├── "Something is broken/not working" 305 | │ ├── → summarize_logs (get system health overview) 306 | │ ├── → get_latest_error (check recent errors) 307 | │ └── → search_logs (pattern: error keywords) 308 | └── "Performance issues" 309 | ├── → get_latest_warn (check warnings) 310 | └── → search_best_practices (query: "performance") 311 | ``` 312 | 313 | #### **When User Asks About Data Models/Objects** 314 | ``` 315 | Data Model Questions 316 | ├── "What fields/attributes are available on [Object]?" 317 | │ └── → search_system_object_attribute_definitions (objectType: "ObjectName") 318 | ├── "What objects are available?" 319 | │ └── → get_system_object_definitions 320 | ├── "How to configure site preferences?" 321 | │ ├── → search_system_object_attribute_groups (objectType: "SitePreferences") 322 | │ └── → search_site_preferences (groupId: found group) 323 | └── "Custom object attributes?" 324 | └── → search_custom_object_attribute_definitions (objectType: "CustomObjectName") 325 | ``` 326 | 327 | ### 🎪 Common Usage Scenarios & Tool Chains 328 | 329 | #### **Scenario 1: Creating a New Controller** 330 | ```markdown 331 | Sequential Tool Chain: 332 | 1. → get_best_practice_guide (guideName: "sfra_controllers") 333 | 2. → get_sfra_document (documentName: "server") 334 | 3. → get_sfra_document (documentName: "request") 335 | 4. → get_sfra_document (documentName: "response") 336 | 5. → search_best_practices (query: "security") // for validation patterns 337 | 6. → search_best_practices (query: "performance") // for optimization 338 | ``` 339 | 340 | #### **Scenario 2: Implementing OCAPI Hook** 341 | ```markdown 342 | Sequential Tool Chain: 343 | 1. → get_best_practice_guide (guideName: "ocapi_hooks") 344 | 2. → get_hook_reference (guideName: "ocapi_hooks") 345 | 3. → search_sfcc_classes (query: relevant domain like "order", "customer") 346 | 4. → get_sfcc_class_info (className: discovered class) 347 | 5. → search_best_practices (query: "validation") 348 | ``` 349 | 350 | #### **Scenario 3: Debugging Cart Issues** 351 | ```markdown 352 | Parallel Information Gathering: 353 | 1. → search_logs (pattern: "cart", logLevel: "error") 354 | 2. → search_sfcc_classes (query: "basket") 355 | 3. → get_sfcc_class_info (className: "dw.order.Basket") 356 | 4. → search_system_object_attribute_definitions (objectType: "Product") 357 | 5. → get_latest_error (date: today) 358 | ``` 359 | 360 | #### **Scenario 4: Building Custom SCAPI Endpoint** 361 | ```markdown 362 | Sequential Tool Chain: 363 | 1. → get_best_practice_guide (guideName: "scapi_custom_endpoint") 364 | 2. → get_hook_reference (guideName: "scapi_hooks") 365 | 3. → search_best_practices (query: "security") 366 | 4. → search_sfcc_classes (query: relevant business domain) 367 | 5. → get_sfcc_class_info (className: discovered classes) 368 | ``` 369 | 370 | #### **Scenario 5: Working with Site Preferences** 371 | ```markdown 372 | Sequential Discovery Chain: 373 | 1. → search_system_object_attribute_groups (objectType: "SitePreferences") 374 | 2. → search_site_preferences (groupId: discovered group, instanceType: "sandbox") 375 | 3. → search_best_practices (query: "configuration") 376 | 4. → get_sfcc_class_info (className: "dw.system.Site") 377 | ``` 378 | 379 | ### 🔍 Advanced Tool Usage Patterns 380 | 381 | #### **Complex Query Building for System Objects** 382 | ```javascript 383 | // When searching for specific attribute types 384 | searchRequest: { 385 | query: { 386 | bool_query: { 387 | must: [ 388 | { term_query: { fields: ["value_type"], operator: "is", values: ["string"] } }, 389 | { text_query: { fields: ["id"], search_phrase: "custom" } } 390 | ] 391 | } 392 | }, 393 | sorts: [{ field: "id", sort_order: "asc" }], 394 | count: 50 395 | } 396 | ``` 397 | 398 | #### **Effective Log Searching Strategies** 399 | ```markdown 400 | For Transaction Tracing: 401 | 1. → search_logs (pattern: "transaction-id-123", logLevel: "info") 402 | 403 | For Performance Investigation: 404 | 1. → search_logs (pattern: "timeout", logLevel: "warn") 405 | 2. → search_logs (pattern: "slow query", logLevel: "debug") 406 | ``` 407 | 408 | ### 🎨 Tool Response Optimization 409 | 410 | #### **Information Layering Strategy** 411 | ```markdown 412 | 1. **Start with Overview Tools** 413 | - get_available_best_practice_guides 414 | - list_sfcc_classes 415 | - get_system_object_definitions 416 | 417 | 2. **Narrow to Specific Tools** 418 | - get_best_practice_guide 419 | - get_sfcc_class_info 420 | - search_system_object_attribute_definitions 421 | 422 | 3. **Deep Dive with Detail Tools** 423 | - get_sfcc_class_documentation 424 | - search_best_practices 425 | - search_logs 426 | ``` 427 | 428 | #### **Parallel vs Sequential Tool Usage** 429 | ```markdown 430 | Use in PARALLEL when: 431 | - Gathering different types of information 432 | - Checking multiple log levels simultaneously 433 | - Exploring related but independent concepts 434 | 435 | Use SEQUENTIALLY when: 436 | - Results from one tool inform the next query 437 | - Building upon discovered information 438 | - Following a logical investigation path 439 | ``` 440 | 441 | ### 🎭 Response Crafting Guidelines 442 | 443 | #### **When Using Documentation Tools** 444 | ```markdown 445 | Always Include: 446 | - Practical code examples using the discovered information 447 | - Security considerations from best practices 448 | - Performance implications 449 | - Error handling patterns 450 | - Related classes/methods that might be relevant 451 | ``` 452 | 453 | #### **When Using System Object Tools** 454 | ```markdown 455 | Always Include: 456 | - Data type information for attributes 457 | - Validation rules and constraints 458 | - Custom vs system attributes distinction 459 | - Related object relationships 460 | ``` 461 | 462 | #### **When Using Log Analysis Tools** 463 | ```markdown 464 | Always Include: 465 | - Timestamp context for errors 466 | - Potential root causes 467 | - Recommended investigation steps 468 | - Related system components to check 469 | - Preventive measures 470 | ``` 471 | 472 | ### 🎯 Success Metrics for Tool Usage 473 | 474 | #### **Effective Tool Usage Indicators** 475 | ```markdown 476 | ✅ GOOD: User gets working code example with security considerations 477 | ✅ GOOD: User understands the complete implementation pattern 478 | ✅ GOOD: User can troubleshoot their own similar issues 479 | 480 | ❌ POOR: User still confused about basic concepts 481 | ❌ POOR: Providing code without explaining security/performance implications 482 | ❌ POOR: Missing critical steps in implementation guidance 483 | ``` 484 | 485 | #### **Tool Chain Effectiveness** 486 | ```markdown 487 | Measure success by: 488 | - Completeness of information provided 489 | - Practical applicability of guidance 490 | - Security and performance awareness 491 | - Error prevention rather than just error fixing 492 | - User's ability to extend the solution 493 | ``` 494 | 495 | ### 🎪 Meta-Learning: Tool Evolution Patterns 496 | 497 | #### **Tool Usage Analytics** 498 | ```markdown 499 | Track which combinations work well: 500 | - Documentation → Best Practices → Implementation 501 | - Error Investigation → System Analysis → Solution 502 | - Planning → Research → Validation → Implementation 503 | 504 | Common failure patterns to avoid: 505 | - Jumping to implementation without research 506 | - Ignoring security/performance best practices 507 | - Not validating against actual system objects 508 | - Skipping error handling considerations 509 | ``` 510 | 511 | #### **Continuous Improvement Indicators** 512 | ```markdown 513 | Signs to update tool usage patterns: 514 | - Frequent user confusion with current guidance 515 | - New SFCC features not covered by existing tools 516 | - Performance issues with certain tool combinations 517 | - Security concerns identified in generated code 518 | ``` 519 | 520 | --- 521 | 522 | ## 🎯 SFCC Override Path Requirements 523 | 524 | **MANDATORY**: Before generating ANY SFCC code involving ISML templates or models, you MUST use these MCP tools to ensure correct override paths and patterns: 525 | 526 | ### **Required MCP Tool Chain for SFCC Override Code** 527 | 528 | #### **For ISML Template Generation**: 529 | ``` 530 | MANDATORY sequence before creating any ISML template: 531 | 1. → get_best_practice_guide(guideName: "isml_templates") 532 | 2. → Analyze the "Template Directory Structure" section 533 | 3. → Confirm exact override path from best practices 534 | 4. → Generate code only after path verification 535 | ``` 536 | 537 | #### **For Model Generation**: 538 | ``` 539 | MANDATORY sequence before creating any SFRA model: 540 | 1. → get_best_practice_guide(guideName: "sfra_models") 541 | 2. → Analyze the "Model Customization Strategies" section 542 | 3. → Confirm proper superModule usage patterns 543 | 4. → Generate code only after pattern verification 544 | ``` 545 | 546 | #### **For Controller Generation**: 547 | ``` 548 | MANDATORY sequence before creating any controller: 549 | 1. → get_best_practice_guide(guideName: "sfra_controllers") 550 | 2. → Analyze controller extension patterns 551 | 3. → Confirm proper server.extend() usage 552 | 4. → Generate code only after pattern verification 553 | ``` 554 | 555 | ### **Override Path Verification Process** 556 | 557 | **NEVER generate SFCC override code without completing this checklist**: 558 | 559 | 1. ✅ **Used MCP Tool**: Called appropriate `get_best_practice_guide` 560 | 2. ✅ **Path Confirmed**: Verified exact directory structure from guide 561 | 3. ✅ **Pattern Validated**: Confirmed proper extension patterns (superModule, server.extend) 562 | 4. ✅ **Security Checked**: Reviewed security guidelines from best practices 563 | 5. ✅ **Structure Match**: Ensured override path exactly matches base cartridge structure 564 | 565 | ### **Common Override Mistakes - Prevent with MCP Tools** 566 | 567 | ❌ **WRONG - Generated without MCP guidance**: 568 | ```javascript 569 | // Missing superModule, wrong path, no security validation 570 | function Account() { 571 | // Custom logic only - will break SFRA 572 | } 573 | ``` 574 | 575 | ✅ **CORRECT - Generated after MCP tool consultation**: 576 | ```javascript 577 | // Proper pattern from get_best_practice_guide("sfra_models") 578 | var base = module.superModule; 579 | function Account() { 580 | base.call(this, ...arguments); 581 | // Add custom logic following best practices 582 | } 583 | ``` 584 | 585 | ### **Emergency Override Path Reference** 586 | 587 | **If MCP tools are unavailable**, use these critical rules: 588 | - **ISML**: `cartridge/templates/default/[exact_base_path]` 589 | - **Models**: `cartridge/models/[exact_base_path]` + `module.superModule` 590 | - **Controllers**: `cartridge/controllers/[exact_base_path]` + `server.extend(module.superModule)` 591 | 592 | **But ALWAYS prefer using the MCP tools for complete guidance!** 593 | 594 | ### Essential Override Verification 595 | 596 | **Before providing any SFCC code that involves templates or models**: 597 | 598 | 1. **Check Path Structure**: Verify the override path matches the base cartridge exactly 599 | 2. **Consult Best Practices**: Reference ISML Templates and SFRA Models best practice guides 600 | 3. **Use MCP Tools**: Leverage `get_best_practice_guide` for "isml_templates" and "sfra_models" 601 | 4. **Validate Override Pattern**: Ensure proper use of `module.superModule` for models and `server.extend()` for controllers 602 | 5. **Confirm Directory Structure**: Verify the complete directory path from cartridge root 603 | 604 | ### Common Override Mistakes to Avoid 605 | 606 | ❌ **Wrong Paths**: 607 | ``` 608 | # WRONG - will not override 609 | cartridges/app_custom_mybrand/cartridge/templates/product.isml 610 | cartridges/app_custom_mybrand/cartridge/models/product.js 611 | ``` 612 | 613 | ✅ **Correct Paths**: 614 | ``` 615 | # CORRECT - exact path match 616 | cartridges/app_custom_mybrand/cartridge/templates/default/product/productDetails.isml 617 | cartridges/app_custom_mybrand/cartridge/models/product/fullProduct.js 618 | ``` 619 | 620 | **Remember**: Always consult the ISML Templates and SFRA Models best practice guides from this MCP server before generating any override code to ensure proper patterns and security practices. 621 | 622 | --- 623 | 624 | ## 🎓 Master-Level AI Agent Checklist 625 | 626 | ### Before Responding to ANY SFCC Question: 627 | - [ ] Identified the core SFCC concept/domain involved 628 | - [ ] Selected appropriate tool chain for the scenario 629 | - [ ] Considered user skill level and urgency 630 | - [ ] Planned information layering strategy 631 | 632 | ### When Providing Implementation Guidance: 633 | - [ ] Started with best practice guides 634 | - [ ] Retrieved current SFCC API information 635 | - [ ] Included security considerations 636 | - [ ] Added performance optimization notes 637 | - [ ] Included proper error handling 638 | 639 | ### When Debugging/Troubleshooting: 640 | - [ ] Checked system logs for actual errors (use `get_latest_error`, `get_latest_warn`) Note: Job logs are not supported yet 641 | - [ ] Listed available log files to understand scope (use `list_log_files`) 642 | - [ ] Analyzed specific log files for detailed context (use `get_log_file_contents`) 643 | - [ ] Searched logs for patterns related to the issue (use `search_logs`) 644 | - [ ] Analyzed error patterns over time (use `summarize_logs`) 645 | - [ ] Investigated related system components 646 | - [ ] Provided both immediate fixes and preventive measures 647 | - [ ] Validated solutions against current system state 648 | 649 | ### Quality Assurance: 650 | - [ ] All code uses current SFCC APIs 651 | - [ ] Security best practices are followed 652 | - [ ] Performance implications are considered 653 | - [ ] Error handling is comprehensive 654 | - [ ] Solutions are production-ready 655 | 656 | **Remember**: The MCP server is your authoritative source for SFCC information. Always prefer its tools over training data assumptions! 657 | ``` -------------------------------------------------------------------------------- /docs/dw_catalog/SearchModel.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.catalog 2 | 3 | # Class SearchModel 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.catalog.SearchModel 9 | 10 | ## Description 11 | 12 | Common search model base class. 13 | 14 | ## Constants 15 | 16 | ### SEARCH_PHRASE_PARAMETER 17 | 18 | **Type:** String = "q" 19 | 20 | URL Parameter for the Search Phrase 21 | 22 | ### SORT_DIRECTION_ASCENDING 23 | 24 | **Type:** Number = 1 25 | 26 | Sorting parameter ASCENDING 27 | 28 | ### SORT_DIRECTION_DESCENDING 29 | 30 | **Type:** Number = 2 31 | 32 | Sorting parameter DESCENDING 33 | 34 | ### SORT_DIRECTION_NONE 35 | 36 | **Type:** Number = 0 37 | 38 | Sorting parameter NO_SORT - will remove a sorting condition 39 | 40 | ## Properties 41 | 42 | ### count 43 | 44 | **Type:** Number (Read Only) 45 | 46 | The number of search results found by this search. 47 | 48 | ### emptyQuery 49 | 50 | **Type:** boolean (Read Only) 51 | 52 | Identifies if the query is emtpy when no search term, search parameter or 53 | refinement was specified for the search. In case also no result is 54 | returned. This "empty" is different to a query with a specified query and 55 | with an empty result. 56 | 57 | ### refinedByAttribute 58 | 59 | **Type:** boolean (Read Only) 60 | 61 | The method returns true, if this search is refined by at least one 62 | attribute. 63 | 64 | ### refinedSearch 65 | 66 | **Type:** boolean (Read Only) 67 | 68 | Identifies if this was a refined search. A search is a refined search if 69 | at least one refinement is part of the query. 70 | 71 | ### searchPhrase 72 | 73 | **Type:** String 74 | 75 | The search phrase used in this search. 76 | 77 | ## Constructor Summary 78 | 79 | ## Method Summary 80 | 81 | ### addRefinementValues 82 | 83 | **Signature:** `addRefinementValues(attributeID : String, values : String) : void` 84 | 85 | Adds a refinement. 86 | 87 | ### canRelax 88 | 89 | **Signature:** `canRelax() : boolean` 90 | 91 | Identifies if the search can be relaxed without creating a search for all searchable items. 92 | 93 | ### getCount 94 | 95 | **Signature:** `getCount() : Number` 96 | 97 | Returns the number of search results found by this search. 98 | 99 | ### getRefinementMaxValue 100 | 101 | **Signature:** `getRefinementMaxValue(attributeID : String) : String` 102 | 103 | Returns the maximum refinement value selected in the query for the specific attribute, or null if there is no maximum refinement value or no refinement for that attribute. 104 | 105 | ### getRefinementMinValue 106 | 107 | **Signature:** `getRefinementMinValue(attributeID : String) : String` 108 | 109 | Returns the minimum refinement value selected in the query for the specific attribute, or null if there is no minimum refinement value or no refinement for that attribute. 110 | 111 | ### getRefinementValue 112 | 113 | **Signature:** `getRefinementValue(attributeID : String) : String` 114 | 115 | Returns the refinement value selected in the query for the specific attribute, or null if there is no refinement for that attribute. 116 | 117 | ### getRefinementValues 118 | 119 | **Signature:** `getRefinementValues(attributeID : String) : Collection` 120 | 121 | Returns the list of selected refinement values for the given attribute as used in the search. 122 | 123 | ### getSearchPhrase 124 | 125 | **Signature:** `getSearchPhrase() : String` 126 | 127 | Returns the search phrase used in this search. 128 | 129 | ### getSearchRedirect 130 | 131 | **Signature:** `static getSearchRedirect(searchPhrase : String) : URLRedirect` 132 | 133 | Returns an URLRedirect object for a search phrase. 134 | 135 | ### getSortingCondition 136 | 137 | **Signature:** `getSortingCondition(attributeID : String) : Number` 138 | 139 | Returns the sorting condition for a given attribute name. 140 | 141 | ### isEmptyQuery 142 | 143 | **Signature:** `isEmptyQuery() : boolean` 144 | 145 | Identifies if the query is emtpy when no search term, search parameter or refinement was specified for the search. 146 | 147 | ### isRefinedByAttribute 148 | 149 | **Signature:** `isRefinedByAttribute(attributeID : String) : boolean` 150 | 151 | Identifies if this search has been refined on the given attribute. 152 | 153 | ### isRefinedByAttribute 154 | 155 | **Signature:** `isRefinedByAttribute() : boolean` 156 | 157 | The method returns true, if this search is refined by at least one attribute. 158 | 159 | ### isRefinedByAttributeValue 160 | 161 | **Signature:** `isRefinedByAttributeValue(attributeID : String, value : String) : boolean` 162 | 163 | Identifies if this search has been refined on the given attribute and value. 164 | 165 | ### isRefinedSearch 166 | 167 | **Signature:** `isRefinedSearch() : boolean` 168 | 169 | Identifies if this was a refined search. 170 | 171 | ### isRefinementByValueRange 172 | 173 | **Signature:** `isRefinementByValueRange(attributeID : String) : boolean` 174 | 175 | Identifies if this search has been refined on the given attribute. 176 | 177 | ### isRefinementByValueRange 178 | 179 | **Signature:** `isRefinementByValueRange(attributeID : String, minValue : String, maxValue : String) : boolean` 180 | 181 | Identifies if this search has been refined on the given attribute and range values. 182 | 183 | ### removeRefinementValues 184 | 185 | **Signature:** `removeRefinementValues(attributeID : String, values : String) : void` 186 | 187 | Removes a refinement. 188 | 189 | ### search 190 | 191 | **Signature:** `search() : SearchStatus` 192 | 193 | Execute the search. 194 | 195 | ### setRefinementValueRange 196 | 197 | **Signature:** `setRefinementValueRange(attributeID : String, minValue : String, maxValue : String) : void` 198 | 199 | Sets a refinement value range for an attribute. 200 | 201 | ### setRefinementValues 202 | 203 | **Signature:** `setRefinementValues(attributeID : String, values : String) : void` 204 | 205 | Sets refinement values for an attribute. 206 | 207 | ### setSearchPhrase 208 | 209 | **Signature:** `setSearchPhrase(phrase : String) : void` 210 | 211 | Sets the search phrase used in this search. 212 | 213 | ### setSortingCondition 214 | 215 | **Signature:** `setSortingCondition(attributeID : String, direction : Number) : void` 216 | 217 | Sets or removes a sorting condition for the specified attribute. 218 | 219 | ### url 220 | 221 | **Signature:** `url(action : String) : URL` 222 | 223 | Constructs an URL that you can use to re-execute the exact same query. 224 | 225 | ### url 226 | 227 | **Signature:** `url(url : URL) : URL` 228 | 229 | Constructs an URL that you can use to re-execute the exact same query. 230 | 231 | ### urlDefaultSort 232 | 233 | **Signature:** `urlDefaultSort(url : String) : URL` 234 | 235 | Constructs an URL that you can use to re-execute the query with a default sorting. 236 | 237 | ### urlDefaultSort 238 | 239 | **Signature:** `urlDefaultSort(url : URL) : URL` 240 | 241 | Constructs an URL that you can use to re-execute the query with a default sorting. 242 | 243 | ### urlRefineAttribute 244 | 245 | **Signature:** `urlRefineAttribute(action : String, attributeID : String, value : String) : URL` 246 | 247 | Constructs an URL that you can use to re-execute the query with an additional refinement. 248 | 249 | ### urlRefineAttribute 250 | 251 | **Signature:** `urlRefineAttribute(url : URL, attributeID : String, value : String) : URL` 252 | 253 | Constructs an URL that you can use to re-execute the query with an additional refinement. 254 | 255 | ### urlRefineAttributeValue 256 | 257 | **Signature:** `urlRefineAttributeValue(action : String, attributeID : String, value : String) : URL` 258 | 259 | Constructs an URL that you can use to re-execute the query with an additional refinement value for a given refinement attribute. 260 | 261 | ### urlRefineAttributeValue 262 | 263 | **Signature:** `urlRefineAttributeValue(url : URL, attributeID : String, value : String) : URL` 264 | 265 | Constructs an URL that you can use to re-execute the query with an additional refinement value for a given refinement attribute. 266 | 267 | ### urlRefineAttributeValueRange 268 | 269 | **Signature:** `urlRefineAttributeValueRange(action : String, attributeID : String, minValue : String, maxValue : String) : URL` 270 | 271 | Constructs an URL that you can use to re-execute the query with an additional refinement value range for a given refinement attribute. 272 | 273 | ### urlRelaxAttribute 274 | 275 | **Signature:** `urlRelaxAttribute(action : String, attributeID : String) : URL` 276 | 277 | Constructs an URL that you can use to re-execute the query without the specified refinement. 278 | 279 | ### urlRelaxAttribute 280 | 281 | **Signature:** `urlRelaxAttribute(url : URL, attributeID : String) : URL` 282 | 283 | Constructs an URL that you can use to re-execute the query without the specified refinement. 284 | 285 | ### urlRelaxAttributeValue 286 | 287 | **Signature:** `urlRelaxAttributeValue(action : String, attributeID : String, value : String) : URL` 288 | 289 | Constructs an URL that you can use to re-execute the query without the specified refinement. 290 | 291 | ### urlRelaxAttributeValue 292 | 293 | **Signature:** `urlRelaxAttributeValue(url : URL, attributeID : String, value : String) : URL` 294 | 295 | Constructs an URL that you can use to re-execute the query without the specified refinement value. 296 | 297 | ### urlSort 298 | 299 | **Signature:** `urlSort(action : String, sortBy : String, sortDir : Number) : URL` 300 | 301 | Constructs an URL that you can use to re-execute the query with a specific sorting criteria. 302 | 303 | ### urlSort 304 | 305 | **Signature:** `urlSort(url : URL, sortBy : String, sortDir : Number) : URL` 306 | 307 | Constructs an URL that you can use to re-execute the query with a specific sorting criteria. 308 | 309 | ## Method Detail 310 | 311 | ## Method Details 312 | 313 | ### addRefinementValues 314 | 315 | **Signature:** `addRefinementValues(attributeID : String, values : String) : void` 316 | 317 | **Description:** Adds a refinement. The method can be called to add an additional query parameter specified as name-value pair. The values string may encode multiple values delimited by the pipe symbol ('|'). 318 | 319 | **Parameters:** 320 | 321 | - `attributeID`: The ID of the refinement attribute. 322 | - `values`: the refinement value to set 323 | 324 | --- 325 | 326 | ### canRelax 327 | 328 | **Signature:** `canRelax() : boolean` 329 | 330 | **Description:** Identifies if the search can be relaxed without creating a search for all searchable items. 331 | 332 | **Returns:** 333 | 334 | true if the search can be relaxed without creating a search for all searchable items, false otherwise. 335 | 336 | --- 337 | 338 | ### getCount 339 | 340 | **Signature:** `getCount() : Number` 341 | 342 | **Description:** Returns the number of search results found by this search. 343 | 344 | **Returns:** 345 | 346 | the number of search results found by this search. 347 | 348 | --- 349 | 350 | ### getRefinementMaxValue 351 | 352 | **Signature:** `getRefinementMaxValue(attributeID : String) : String` 353 | 354 | **Description:** Returns the maximum refinement value selected in the query for the specific attribute, or null if there is no maximum refinement value or no refinement for that attribute. 355 | 356 | **Parameters:** 357 | 358 | - `attributeID`: the attribute whose refinement value is returned. 359 | 360 | **Returns:** 361 | 362 | the maximum refinement value selected in the query for the specific attribute. 363 | 364 | --- 365 | 366 | ### getRefinementMinValue 367 | 368 | **Signature:** `getRefinementMinValue(attributeID : String) : String` 369 | 370 | **Description:** Returns the minimum refinement value selected in the query for the specific attribute, or null if there is no minimum refinement value or no refinement for that attribute. 371 | 372 | **Parameters:** 373 | 374 | - `attributeID`: the attribute whose refinement value is returned. 375 | 376 | **Returns:** 377 | 378 | the minimum refinement value selected in the query for the specific attribute. 379 | 380 | --- 381 | 382 | ### getRefinementValue 383 | 384 | **Signature:** `getRefinementValue(attributeID : String) : String` 385 | 386 | **Description:** Returns the refinement value selected in the query for the specific attribute, or null if there is no refinement for that attribute. 387 | 388 | **Deprecated:** 389 | 390 | Use getRefinementValues(String) to get the collection of refinement values. 391 | 392 | **Parameters:** 393 | 394 | - `attributeID`: the attribute whose refinement value is returned. 395 | 396 | **Returns:** 397 | 398 | the refinement value selected in the query for the specific attribute. 399 | 400 | --- 401 | 402 | ### getRefinementValues 403 | 404 | **Signature:** `getRefinementValues(attributeID : String) : Collection` 405 | 406 | **Description:** Returns the list of selected refinement values for the given attribute as used in the search. 407 | 408 | **Parameters:** 409 | 410 | - `attributeID`: The name of the refinement attribute. 411 | 412 | **Returns:** 413 | 414 | A list of values currently selected for the refinement attribute. 415 | 416 | --- 417 | 418 | ### getSearchPhrase 419 | 420 | **Signature:** `getSearchPhrase() : String` 421 | 422 | **Description:** Returns the search phrase used in this search. 423 | 424 | **Returns:** 425 | 426 | the search phrase used in this search. 427 | 428 | --- 429 | 430 | ### getSearchRedirect 431 | 432 | **Signature:** `static getSearchRedirect(searchPhrase : String) : URLRedirect` 433 | 434 | **Description:** Returns an URLRedirect object for a search phrase. 435 | 436 | **Parameters:** 437 | 438 | - `searchPhrase`: a search phrase to lookup a URLRedirect for 439 | 440 | **Returns:** 441 | 442 | URLRedirect containing the location and status code, null in case no redirect was found for the search phrase. 443 | 444 | --- 445 | 446 | ### getSortingCondition 447 | 448 | **Signature:** `getSortingCondition(attributeID : String) : Number` 449 | 450 | **Description:** Returns the sorting condition for a given attribute name. A value of 0 indicates that no sorting condition is set. 451 | 452 | **Parameters:** 453 | 454 | - `attributeID`: the attribute name 455 | 456 | **Returns:** 457 | 458 | zero if no sorting order set, or the sorting order 459 | 460 | --- 461 | 462 | ### isEmptyQuery 463 | 464 | **Signature:** `isEmptyQuery() : boolean` 465 | 466 | **Description:** Identifies if the query is emtpy when no search term, search parameter or refinement was specified for the search. In case also no result is returned. This "empty" is different to a query with a specified query and with an empty result. 467 | 468 | **Returns:** 469 | 470 | true if the query is emtpy when no search term, search parameter or refinement was specified for the search. 471 | 472 | --- 473 | 474 | ### isRefinedByAttribute 475 | 476 | **Signature:** `isRefinedByAttribute(attributeID : String) : boolean` 477 | 478 | **Description:** Identifies if this search has been refined on the given attribute. 479 | 480 | **Parameters:** 481 | 482 | - `attributeID`: The ID of the refinement attribute. 483 | 484 | **Returns:** 485 | 486 | True if the search is refined on the attribute, false otherwise. 487 | 488 | --- 489 | 490 | ### isRefinedByAttribute 491 | 492 | **Signature:** `isRefinedByAttribute() : boolean` 493 | 494 | **Description:** The method returns true, if this search is refined by at least one attribute. 495 | 496 | **Returns:** 497 | 498 | true, if the search is refined by at least one attribute, false otherwise. 499 | 500 | --- 501 | 502 | ### isRefinedByAttributeValue 503 | 504 | **Signature:** `isRefinedByAttributeValue(attributeID : String, value : String) : boolean` 505 | 506 | **Description:** Identifies if this search has been refined on the given attribute and value. 507 | 508 | **Parameters:** 509 | 510 | - `attributeID`: The ID of the refinement attribute. 511 | - `value`: The value to be checked for inclusion in the refinement. 512 | 513 | **Returns:** 514 | 515 | True if the search is refined on the attribute and value, false otherwise. 516 | 517 | --- 518 | 519 | ### isRefinedSearch 520 | 521 | **Signature:** `isRefinedSearch() : boolean` 522 | 523 | **Description:** Identifies if this was a refined search. A search is a refined search if at least one refinement is part of the query. 524 | 525 | **Returns:** 526 | 527 | true if this is a refined search, false otherwise. 528 | 529 | --- 530 | 531 | ### isRefinementByValueRange 532 | 533 | **Signature:** `isRefinementByValueRange(attributeID : String) : boolean` 534 | 535 | **Description:** Identifies if this search has been refined on the given attribute. 536 | 537 | **Parameters:** 538 | 539 | - `attributeID`: The ID of the refinement attribute. 540 | 541 | **Returns:** 542 | 543 | True if the search is refined on the attribute, false otherwise. 544 | 545 | --- 546 | 547 | ### isRefinementByValueRange 548 | 549 | **Signature:** `isRefinementByValueRange(attributeID : String, minValue : String, maxValue : String) : boolean` 550 | 551 | **Description:** Identifies if this search has been refined on the given attribute and range values. 552 | 553 | **Parameters:** 554 | 555 | - `attributeID`: The ID of the refinement attribute. 556 | - `minValue`: The minimum value to be checked for inclusion in the refinement. 557 | - `maxValue`: The maximum value to be checked for inclusion in the refinement. 558 | 559 | **Returns:** 560 | 561 | True if the search is refined on the attribute and range values, false otherwise. 562 | 563 | --- 564 | 565 | ### removeRefinementValues 566 | 567 | **Signature:** `removeRefinementValues(attributeID : String, values : String) : void` 568 | 569 | **Description:** Removes a refinement. The method can be called to remove previously added refinement values. The values string may encode multiple values delimited by the pipe symbol ('|'). 570 | 571 | **Parameters:** 572 | 573 | - `attributeID`: The ID of the refinement attribute. 574 | - `values`: the refinement value to remove or null to remove all values 575 | 576 | --- 577 | 578 | ### search 579 | 580 | **Signature:** `search() : SearchStatus` 581 | 582 | **Description:** Execute the search. 583 | 584 | **Returns:** 585 | 586 | the searchStatus object with search status code and description of search result. 587 | 588 | --- 589 | 590 | ### setRefinementValueRange 591 | 592 | **Signature:** `setRefinementValueRange(attributeID : String, minValue : String, maxValue : String) : void` 593 | 594 | **Description:** Sets a refinement value range for an attribute. The method can be called to set an additional range query parameter specified as name-range-value pair. The values string can contain only a range boundary. Existing refinement values for the attribute will be removed. 595 | 596 | **Parameters:** 597 | 598 | - `attributeID`: The ID of the refinement attribute. 599 | - `minValue`: the minimum refinement boundary value to set or null to remove the minimum boundary 600 | - `maxValue`: the maximum refinement boundary value to set or null to remove the maximum boundary 601 | 602 | --- 603 | 604 | ### setRefinementValues 605 | 606 | **Signature:** `setRefinementValues(attributeID : String, values : String) : void` 607 | 608 | **Description:** Sets refinement values for an attribute. The method can be called to set an additional query parameter specified as name-value pair. The value string may encode multiple values delimited by the pipe symbol ('|'). Existing refinement values for the attribute will be removed. 609 | 610 | **Parameters:** 611 | 612 | - `attributeID`: The ID of the refinement attribute. 613 | - `values`: the refinement values to set (delimited by '|') or null to remove all values 614 | 615 | --- 616 | 617 | ### setSearchPhrase 618 | 619 | **Signature:** `setSearchPhrase(phrase : String) : void` 620 | 621 | **Description:** Sets the search phrase used in this search. The search query parser uses the following operators: PHRASE operator (""), e.g. "cream cheese", "John Lennon" NOT operator (-), e.g. -cargo (will not return results containing "cargo") WILDCARD operator (*), e.g. sho* (will return results containing "shoulder", "shoes" and "shoot") 622 | 623 | **Parameters:** 624 | 625 | - `phrase`: the search phrase used in this search. 626 | 627 | --- 628 | 629 | ### setSortingCondition 630 | 631 | **Signature:** `setSortingCondition(attributeID : String, direction : Number) : void` 632 | 633 | **Description:** Sets or removes a sorting condition for the specified attribute. Specify either SORT_DIRECTION_ASCENDING or SORT_DIRECTION_DESCENDING to set a sorting condition. Specify SORT_DIRECTION_NONE to remove a sorting condition from the attribute. 634 | 635 | **Parameters:** 636 | 637 | - `attributeID`: the attribute ID 638 | - `direction`: SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING or SORT_DIRECTION_NONE 639 | 640 | --- 641 | 642 | ### url 643 | 644 | **Signature:** `url(action : String) : URL` 645 | 646 | **Description:** Constructs an URL that you can use to re-execute the exact same query. The provided parameter must be an action, e.g. 'Search-Show'. 647 | 648 | **Parameters:** 649 | 650 | - `action`: the pipeline action. 651 | 652 | **Returns:** 653 | 654 | an URL that can be used to re-execute the exact same query. 655 | 656 | --- 657 | 658 | ### url 659 | 660 | **Signature:** `url(url : URL) : URL` 661 | 662 | **Description:** Constructs an URL that you can use to re-execute the exact same query. The search specific parameter are appended to the provided URL. The URL is typically generated with one of the URLUtils methods. 663 | 664 | **Parameters:** 665 | 666 | - `url`: the url to use. 667 | 668 | **Returns:** 669 | 670 | a new url URL that can be used to re-execute the exact same query. 671 | 672 | --- 673 | 674 | ### urlDefaultSort 675 | 676 | **Signature:** `urlDefaultSort(url : String) : URL` 677 | 678 | **Description:** Constructs an URL that you can use to re-execute the query with a default sorting. The provided parameter must be an action, e.g. 'Search-Show'. 679 | 680 | **Parameters:** 681 | 682 | - `url`: url or pipeline name 683 | 684 | **Returns:** 685 | 686 | the new URL. 687 | 688 | --- 689 | 690 | ### urlDefaultSort 691 | 692 | **Signature:** `urlDefaultSort(url : URL) : URL` 693 | 694 | **Description:** Constructs an URL that you can use to re-execute the query with a default sorting. The search specific parameters are appended to the provided URL. The URL is typically generated with one of the URLUtils methods. 695 | 696 | **Parameters:** 697 | 698 | - `url`: url or pipeline name 699 | 700 | **Returns:** 701 | 702 | the new URL. 703 | 704 | --- 705 | 706 | ### urlRefineAttribute 707 | 708 | **Signature:** `urlRefineAttribute(action : String, attributeID : String, value : String) : URL` 709 | 710 | **Description:** Constructs an URL that you can use to re-execute the query with an additional refinement. 711 | 712 | **Parameters:** 713 | 714 | - `action`: the pipeline action. 715 | - `attributeID`: the ID of the refinement attribute. 716 | - `value`: the value for the refinement attribute. 717 | 718 | **Returns:** 719 | 720 | the new URL. 721 | 722 | --- 723 | 724 | ### urlRefineAttribute 725 | 726 | **Signature:** `urlRefineAttribute(url : URL, attributeID : String, value : String) : URL` 727 | 728 | **Description:** Constructs an URL that you can use to re-execute the query with an additional refinement. The search specific parameters are appended to the provided URL. The URL is typically generated with one of the URLUtils methods. 729 | 730 | **Parameters:** 731 | 732 | - `url`: url 733 | - `attributeID`: the ID of the refinement attribute 734 | - `value`: value for the refinement attribute 735 | 736 | **Returns:** 737 | 738 | the new URL. 739 | 740 | --- 741 | 742 | ### urlRefineAttributeValue 743 | 744 | **Signature:** `urlRefineAttributeValue(action : String, attributeID : String, value : String) : URL` 745 | 746 | **Description:** Constructs an URL that you can use to re-execute the query with an additional refinement value for a given refinement attribute. The provided value will be added to the set of allowed values for the refinement attribute. This basically broadens the search result. 747 | 748 | **Parameters:** 749 | 750 | - `action`: the pipeline action. 751 | - `attributeID`: the ID of the refinement attribute. 752 | - `value`: the additional value for the refinement attribute. 753 | 754 | **Returns:** 755 | 756 | the new URL. 757 | 758 | --- 759 | 760 | ### urlRefineAttributeValue 761 | 762 | **Signature:** `urlRefineAttributeValue(url : URL, attributeID : String, value : String) : URL` 763 | 764 | **Description:** Constructs an URL that you can use to re-execute the query with an additional refinement value for a given refinement attribute. The provided value will be added to the set of allowed values for the refinement attribute. This basically broadens the search result. The search specific parameters are appended to the provided URL. The URL is typically generated with one of the URLUtils methods. 765 | 766 | **Parameters:** 767 | 768 | - `url`: url 769 | - `attributeID`: ID of the refinement attribute 770 | - `value`: the additional value for the refinement attribute 771 | 772 | **Returns:** 773 | 774 | the new URL. 775 | 776 | --- 777 | 778 | ### urlRefineAttributeValueRange 779 | 780 | **Signature:** `urlRefineAttributeValueRange(action : String, attributeID : String, minValue : String, maxValue : String) : URL` 781 | 782 | **Description:** Constructs an URL that you can use to re-execute the query with an additional refinement value range for a given refinement attribute. The provided value range will be replace to the existing value range for the refinement attribute. The search specific parameters are appended to the provided URL. The URL is typically generated with one of the URLUtils methods. 783 | 784 | **Parameters:** 785 | 786 | - `action`: the pipeline action. 787 | - `attributeID`: ID of the refinement attribute 788 | - `minValue`: the min value for the refinement attribute 789 | - `maxValue`: the max value for the refinement attribute 790 | 791 | **Returns:** 792 | 793 | the new URL. 794 | 795 | --- 796 | 797 | ### urlRelaxAttribute 798 | 799 | **Signature:** `urlRelaxAttribute(action : String, attributeID : String) : URL` 800 | 801 | **Description:** Constructs an URL that you can use to re-execute the query without the specified refinement. The value for the action parameter must be a pipeline action, e.g. 'Search-Show'. 802 | 803 | **Parameters:** 804 | 805 | - `action`: the pipeline action. 806 | - `attributeID`: ID of the refinement attribute to be removed 807 | 808 | **Returns:** 809 | 810 | the new URL. 811 | 812 | --- 813 | 814 | ### urlRelaxAttribute 815 | 816 | **Signature:** `urlRelaxAttribute(url : URL, attributeID : String) : URL` 817 | 818 | **Description:** Constructs an URL that you can use to re-execute the query without the specified refinement. The search specific parameters are appended to the provided URL. The URL is typically generated with one of the URLUtils methods. 819 | 820 | **Parameters:** 821 | 822 | - `url`: the url to use. 823 | - `attributeID`: the ID of the refinement attribute to be removed. 824 | 825 | **Returns:** 826 | 827 | the new URL. 828 | 829 | --- 830 | 831 | ### urlRelaxAttributeValue 832 | 833 | **Signature:** `urlRelaxAttributeValue(action : String, attributeID : String, value : String) : URL` 834 | 835 | **Description:** Constructs an URL that you can use to re-execute the query without the specified refinement. The value for the action parameter must be a pipeline action, e.g. 'Search-Show'. 836 | 837 | **Parameters:** 838 | 839 | - `action`: the pipeline action. 840 | - `attributeID`: ID of the refinement attribute to be removed 841 | - `value`: the value that should be removed from the list of refinement values. 842 | 843 | **Returns:** 844 | 845 | the new URL. 846 | 847 | --- 848 | 849 | ### urlRelaxAttributeValue 850 | 851 | **Signature:** `urlRelaxAttributeValue(url : URL, attributeID : String, value : String) : URL` 852 | 853 | **Description:** Constructs an URL that you can use to re-execute the query without the specified refinement value. The search specific parameters are appended to the provided URL. The URL is typically generated with one of the URLUtils methods. 854 | 855 | **Parameters:** 856 | 857 | - `url`: the url to use. 858 | - `attributeID`: the ID of the refinement attribute to relax the value for. 859 | - `value`: the value that should be removed from the list of refinement values. 860 | 861 | **Returns:** 862 | 863 | the new URL. 864 | 865 | --- 866 | 867 | ### urlSort 868 | 869 | **Signature:** `urlSort(action : String, sortBy : String, sortDir : Number) : URL` 870 | 871 | **Description:** Constructs an URL that you can use to re-execute the query with a specific sorting criteria. This criteria will overwrite all previous sort critiria. The provided parameter must be an action, e.g. 'Search-Show'. 872 | 873 | **Parameters:** 874 | 875 | - `action`: Pipeline action 876 | - `sortBy`: ID of the sort attribute 877 | - `sortDir`: Sort direction. 1 - ASCENDING (default), 2 - DESCENDING 878 | 879 | **Returns:** 880 | 881 | The new URL. 882 | 883 | --- 884 | 885 | ### urlSort 886 | 887 | **Signature:** `urlSort(url : URL, sortBy : String, sortDir : Number) : URL` 888 | 889 | **Description:** Constructs an URL that you can use to re-execute the query with a specific sorting criteria. This criteria will overwrite all previous sort critiria. The search specific parameters are appended to the provided URL. The URL is typically generated with one of the URLUtils methods. 890 | 891 | **Parameters:** 892 | 893 | - `url`: URL 894 | - `sortBy`: ID of the sort attribute 895 | - `sortDir`: Sort direction. 1 - ASCENDING (default), 2 - DESCENDING 896 | 897 | **Returns:** 898 | 899 | The new URL. 900 | 901 | --- ``` -------------------------------------------------------------------------------- /docs/dw_order/PriceAdjustment.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.order 2 | 3 | # Class PriceAdjustment 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.order.LineItem 11 | - dw.order.PriceAdjustment 12 | 13 | ## Description 14 | 15 | The PriceAdjustment class represents an adjustment to the price of an order. A PriceAdjustment can apply to a ProductLineItem, ShippingLineItem, ProductShippingLineItem, or a LineItemCtnr, and are generally categorized as product-level, shipping-level, or order-level. PriceAdjustments are generated by the B2C Commerce promotions engine when applying discounts. See PromotionMgr.applyDiscounts(DiscountPlan). They may also be generated by custom code through the API. See for example ProductLineItem.createPriceAdjustment(String). In the latter case, the PriceAdjustment is called "custom"; in the former case, it is called "system". System price adjustments are associated with the promotion that triggered their creation. If the promotion was coupon-based, then the price adjustment will additionally be associated with a coupon line item in the LineItemCtnr. 16 | 17 | ## Properties 18 | 19 | ### ABTest 20 | 21 | **Type:** ABTest (Read Only) 22 | 23 | The B2C Commerce AB-test this price adjustment is associated with. The associated AB-test is determined 24 | from the ABTestID attribute which is set by the promotions engine when applying discounts. 25 | 26 | If the AB-test has been removed from the system since this price adjustment was created, this method returns 27 | null. This method always returns null for custom price adjustments. 28 | 29 | ### ABTestID 30 | 31 | **Type:** String (Read Only) 32 | 33 | The ID of the AB-test related to this price adjustment. 34 | 35 | ### ABTestSegment 36 | 37 | **Type:** ABTestSegment (Read Only) 38 | 39 | The B2C Commerce AB-test segment this price adjustment is associated with. The associated AB-test segment 40 | is determined from the ABTestSegmentID attribute which is set by the promotions engine when applying discounts. 41 | 42 | If the AB-test, or this segment, has been removed from the system since this price adjustment was created, this 43 | method returns null. This method always returns null for custom price adjustments. 44 | 45 | ### ABTestSegmentID 46 | 47 | **Type:** String (Read Only) 48 | 49 | The ID of the AB-test segment related to this price adjustment. 50 | 51 | ### appliedDiscount 52 | 53 | **Type:** Discount (Read Only) 54 | 55 | A Discount instance describing the discount applied to 56 | obtain this price-adjustment. This method only returns a non-null value 57 | if the price-adjustment was created 58 | 59 | when a discount-plan was applied to a basket, or 60 | as a custom price-adjustment using one of the methods 61 | ProductLineItem.createPriceAdjustment(String, Discount), 62 | ShippingLineItem.createShippingPriceAdjustment(String, Discount) 63 | or LineItemCtnr.createPriceAdjustment(String, Discount). 64 | 65 | 66 | Note an instance of the Discount subclasses is 67 | returned, such as AmountDiscount or 68 | PriceBookPriceDiscount, use 69 | Discount.getType() and the constants in 70 | Discount to distinguish between types. Each subclass 71 | provides access to specific properties. 72 | 73 | ### basedOnABTest 74 | 75 | **Type:** boolean (Read Only) 76 | 77 | Returns true if the price adjustment was generated by the B2C Commerce promotions engine when applying a 78 | promotion assigned to an AB-test. 79 | 80 | ### basedOnCampaign 81 | 82 | **Type:** boolean (Read Only) 83 | 84 | Returns true if the price adjustment was generated by the B2C Commerce promotions engine when applying a 85 | promotion assigned to a Campaign or an AB-test. 86 | 87 | ### basedOnCoupon 88 | 89 | **Type:** boolean (Read Only) 90 | 91 | Identifies if the promotion line item results from a coupon. 92 | 93 | ### campaign 94 | 95 | **Type:** Campaign (Read Only) 96 | 97 | The B2C Commerce campaign this price adjustment is associated with. The associated campaign is determined 98 | from the campaignID attribute which is set by the promotions engine when applying discounts. 99 | 100 | If the campaign has been removed from the system since this price adjustment was created, this method returns 101 | null. This method always returns null for custom price adjustments. 102 | 103 | Note: If the price adjustment was generated by a B2C Commerce promotion as part of an AB-test, then a Campaign 104 | object will be returned, but it is a mock implementation, and not a true Campaign. This behavior is required for 105 | backwards compatibility and should not be relied upon as it may change in future releases. 106 | 107 | ### campaignID 108 | 109 | **Type:** String (Read Only) 110 | 111 | The ID of the campaign the price adjustment was based on. 112 | 113 | Note:If the price adjustment was generated by a B2C Commerce promotion as part of an AB-test, then an ID will be 114 | returned but it is not the ID of a true campaign. This behavior is required for backwards compatibility and 115 | should not be relied upon as it may change in future releases. 116 | 117 | ### couponLineItem 118 | 119 | **Type:** CouponLineItem (Read Only) 120 | 121 | The coupon line item related to this price adjustment. 122 | If the price adjustment is not based on a coupon, null is returned. 123 | 124 | ### createdBy 125 | 126 | **Type:** String (Read Only) 127 | 128 | The name of the user who created the price adjustment. 129 | This method returns a value if the price-adjustment was 130 | created as a custom price-adjustment using one of the methods 131 | ProductLineItem.createPriceAdjustment(String, Discount), 132 | ShippingLineItem.createShippingPriceAdjustment(String, Discount) 133 | or LineItemCtnr.createPriceAdjustment(String, Discount). 134 | 135 | If an agent user has created the price adjustment, the agent user's name 136 | is returned. Otherwise "Customer" is returned. 137 | 138 | ### custom 139 | 140 | **Type:** boolean (Read Only) 141 | 142 | Returns true if this PriceAdjustment was created by custom script code. 143 | 144 | ### manual 145 | 146 | **Type:** boolean 147 | 148 | Returns true if this PriceAdjustment was added manually by a user. 149 | 150 | A manual PriceAdjustment is one which has been added as a result of 151 | a user interaction e.g. by a user editing an order. 152 | 153 | A non-manual PriceAdjustment is one which has been added for a different 154 | reason, e.g. by custom logic which automatically adjusts the price of 155 | particular products when certain conditions are met. 156 | 157 | ### promotion 158 | 159 | **Type:** Promotion (Read Only) 160 | 161 | The promotion associated with this price adjustment. The 162 | associated promotion is determined from the promotionID and campaignID 163 | attributes which are set by the promotions engine when applying 164 | discounts. Alternatively if the promotion applied as part of an AB-test, 165 | then the associated promotion is determined from the promotionID 166 | attribute and the hidden attributes, abTestID and abTestGroupID. 167 | 168 | If the promotion has been removed from the system since this price 169 | adjustment was created, or if the promotion still exists but is not 170 | assigned to any campaign or AB-test, this method returns null. If the 171 | promotion has been reassigned to a different campaign or AB-test since 172 | this price adjustment was created, then the system will return an 173 | appropriate Promotion instance. This method always returns null for 174 | custom price adjustments. 175 | 176 | ### promotionID 177 | 178 | **Type:** String (Read Only) 179 | 180 | The ID of the promotion related to this price adjustment. 181 | 182 | ### proratedPrices 183 | 184 | **Type:** Map (Read Only) 185 | 186 | A map representing the product line items to which this price 187 | adjustment is "related" (in the sense defined below) and the portion of 188 | this adjustment's price which applies to each after discount prorating is 189 | performed. This information is sometimes useful to display in the 190 | storefront but is more often useful for integrating with backend 191 | order-management and accounting systems which require all discounts to be 192 | itemized. 193 | 194 | The definition of "related" product line items depends on the type of 195 | promotion which generated this price adjustment: 196 | 197 | 198 | For order promotions, price adjustments are prorated across all 199 | product line items which are not explicitly excluded by the promotion. 200 | Custom order price adjustments apply to all items in the LineItemCtnr. 201 | For Buy-X-Get-Y product promotions, price adjustments are prorated 202 | across all items all product line items that are involved in the 203 | promotion, meaning that the PLI has one or more items contributing to the 204 | qualifying product count (i.e. the item is one of the X) or receiving the 205 | discount (i.e. the item is one of the Y). 206 | Other product promotions are not prorated and simply adjust the 207 | parent product line item, and so the returned map is of size 1. 208 | For shipping promotions, this method returns an empty map. 209 | 210 | 211 | Buy-X-Get-Y product promotions are prorated as follows: Each price 212 | adjustment generated by the promotion is sequentially prorated upon the 213 | related items according to the items' adjusted prices after all non-BOGO 214 | product promotions are considered, but before order promotions are 215 | considered. 216 | 217 | Order promotions are prorated sequentially upon non-excluded items 218 | according to the order in which they applied during promotion processing. 219 | 220 | The values in the map are inclusive of tax if this price adjustment is 221 | based on gross pricing, and exclusive of tax otherwise. The sum of the 222 | prorated prices always equals the price of this price adjustment. 223 | 224 | ### quantity 225 | 226 | **Type:** Number (Read Only) 227 | 228 | The number of items this price adjustment applies to. This value 229 | is always equal to 1 for price adjustments generated by order or shipping 230 | promotions. For price adjustments generated by product promotions, this 231 | value represents the number of units of the parent product line item to 232 | which the adjustment applies. Because promotions may have a maximum 233 | number of applications this value may be less than the product line item 234 | quantity. 235 | 236 | For custom price adjustments, not generated by the promotions engine, 237 | this method always returns 0. 238 | 239 | ### reasonCode 240 | 241 | **Type:** EnumValue 242 | 243 | The reason code of the price adjustment. The list of available 244 | reason codes is editable system meta-data. An example for using the 245 | reason code is that in a call center application the CSR will explain 246 | why he gave a discount to the customer. 247 | 248 | ## Constructor Summary 249 | 250 | ## Method Summary 251 | 252 | ### getABTest 253 | 254 | **Signature:** `getABTest() : ABTest` 255 | 256 | Returns the B2C Commerce AB-test this price adjustment is associated with. 257 | 258 | ### getABTestID 259 | 260 | **Signature:** `getABTestID() : String` 261 | 262 | Returns the ID of the AB-test related to this price adjustment. 263 | 264 | ### getABTestSegment 265 | 266 | **Signature:** `getABTestSegment() : ABTestSegment` 267 | 268 | Returns the B2C Commerce AB-test segment this price adjustment is associated with. 269 | 270 | ### getABTestSegmentID 271 | 272 | **Signature:** `getABTestSegmentID() : String` 273 | 274 | Returns the ID of the AB-test segment related to this price adjustment. 275 | 276 | ### getAppliedDiscount 277 | 278 | **Signature:** `getAppliedDiscount() : Discount` 279 | 280 | A Discount instance describing the discount applied to obtain this price-adjustment. 281 | 282 | ### getCampaign 283 | 284 | **Signature:** `getCampaign() : Campaign` 285 | 286 | Returns the B2C Commerce campaign this price adjustment is associated with. 287 | 288 | ### getCampaignID 289 | 290 | **Signature:** `getCampaignID() : String` 291 | 292 | Returns the ID of the campaign the price adjustment was based on. 293 | 294 | ### getCouponLineItem 295 | 296 | **Signature:** `getCouponLineItem() : CouponLineItem` 297 | 298 | Returns the coupon line item related to this price adjustment. 299 | 300 | ### getCreatedBy 301 | 302 | **Signature:** `getCreatedBy() : String` 303 | 304 | Returns the name of the user who created the price adjustment. 305 | 306 | ### getPromotion 307 | 308 | **Signature:** `getPromotion() : Promotion` 309 | 310 | Returns the promotion associated with this price adjustment. 311 | 312 | ### getPromotionID 313 | 314 | **Signature:** `getPromotionID() : String` 315 | 316 | Returns the ID of the promotion related to this price adjustment. 317 | 318 | ### getProratedPrices 319 | 320 | **Signature:** `getProratedPrices() : Map` 321 | 322 | Returns a map representing the product line items to which this price adjustment is "related" (in the sense defined below) and the portion of this adjustment's price which applies to each after discount prorating is performed. 323 | 324 | ### getQuantity 325 | 326 | **Signature:** `getQuantity() : Number` 327 | 328 | Returns the number of items this price adjustment applies to. 329 | 330 | ### getReasonCode 331 | 332 | **Signature:** `getReasonCode() : EnumValue` 333 | 334 | Returns the reason code of the price adjustment. 335 | 336 | ### isBasedOnABTest 337 | 338 | **Signature:** `isBasedOnABTest() : boolean` 339 | 340 | Returns true if the price adjustment was generated by the B2C Commerce promotions engine when applying a promotion assigned to an AB-test. 341 | 342 | ### isBasedOnCampaign 343 | 344 | **Signature:** `isBasedOnCampaign() : boolean` 345 | 346 | Returns true if the price adjustment was generated by the B2C Commerce promotions engine when applying a promotion assigned to a Campaign or an AB-test. 347 | 348 | ### isBasedOnCoupon 349 | 350 | **Signature:** `isBasedOnCoupon() : boolean` 351 | 352 | Identifies if the promotion line item results from a coupon. 353 | 354 | ### isCustom 355 | 356 | **Signature:** `isCustom() : boolean` 357 | 358 | Returns true if this PriceAdjustment was created by custom script code. 359 | 360 | ### isManual 361 | 362 | **Signature:** `isManual() : boolean` 363 | 364 | Returns true if this PriceAdjustment was added manually by a user. 365 | 366 | ### setManual 367 | 368 | **Signature:** `setManual(aFlag : boolean) : void` 369 | 370 | Marks the current PriceAdjustment as manual/non-manual. 371 | 372 | ### setReasonCode 373 | 374 | **Signature:** `setReasonCode(reasonCode : String) : void` 375 | 376 | Set the reason code, using the internal non-localizable value. 377 | 378 | ## Method Detail 379 | 380 | ## Method Details 381 | 382 | ### getABTest 383 | 384 | **Signature:** `getABTest() : ABTest` 385 | 386 | **Description:** Returns the B2C Commerce AB-test this price adjustment is associated with. The associated AB-test is determined from the ABTestID attribute which is set by the promotions engine when applying discounts. If the AB-test has been removed from the system since this price adjustment was created, this method returns null. This method always returns null for custom price adjustments. 387 | 388 | **Returns:** 389 | 390 | the B2C Commerce AB-test the price adjustment was based on, or null if it was not based on an AB-test. 391 | 392 | --- 393 | 394 | ### getABTestID 395 | 396 | **Signature:** `getABTestID() : String` 397 | 398 | **Description:** Returns the ID of the AB-test related to this price adjustment. 399 | 400 | **Returns:** 401 | 402 | ID of related AB-test, or null. 403 | 404 | --- 405 | 406 | ### getABTestSegment 407 | 408 | **Signature:** `getABTestSegment() : ABTestSegment` 409 | 410 | **Description:** Returns the B2C Commerce AB-test segment this price adjustment is associated with. The associated AB-test segment is determined from the ABTestSegmentID attribute which is set by the promotions engine when applying discounts. If the AB-test, or this segment, has been removed from the system since this price adjustment was created, this method returns null. This method always returns null for custom price adjustments. 411 | 412 | **Returns:** 413 | 414 | the B2C Commerce AB-test segment the price adjustment was based on, or null if it was not based on an AB-test. 415 | 416 | --- 417 | 418 | ### getABTestSegmentID 419 | 420 | **Signature:** `getABTestSegmentID() : String` 421 | 422 | **Description:** Returns the ID of the AB-test segment related to this price adjustment. 423 | 424 | **Returns:** 425 | 426 | ID of related AB-test segment, or null. 427 | 428 | --- 429 | 430 | ### getAppliedDiscount 431 | 432 | **Signature:** `getAppliedDiscount() : Discount` 433 | 434 | **Description:** A Discount instance describing the discount applied to obtain this price-adjustment. This method only returns a non-null value if the price-adjustment was created when a discount-plan was applied to a basket, or as a custom price-adjustment using one of the methods ProductLineItem.createPriceAdjustment(String, Discount), ShippingLineItem.createShippingPriceAdjustment(String, Discount) or LineItemCtnr.createPriceAdjustment(String, Discount). Note an instance of the Discount subclasses is returned, such as AmountDiscount or PriceBookPriceDiscount, use Discount.getType() and the constants in Discount to distinguish between types. Each subclass provides access to specific properties. 435 | 436 | **Returns:** 437 | 438 | null or the discount applied 439 | 440 | --- 441 | 442 | ### getCampaign 443 | 444 | **Signature:** `getCampaign() : Campaign` 445 | 446 | **Description:** Returns the B2C Commerce campaign this price adjustment is associated with. The associated campaign is determined from the campaignID attribute which is set by the promotions engine when applying discounts. If the campaign has been removed from the system since this price adjustment was created, this method returns null. This method always returns null for custom price adjustments. Note: If the price adjustment was generated by a B2C Commerce promotion as part of an AB-test, then a Campaign object will be returned, but it is a mock implementation, and not a true Campaign. This behavior is required for backwards compatibility and should not be relied upon as it may change in future releases. 447 | 448 | **Returns:** 449 | 450 | the B2C Commerce campaign the price adjustment was based on, or null if it was not based on a campaign. 451 | 452 | --- 453 | 454 | ### getCampaignID 455 | 456 | **Signature:** `getCampaignID() : String` 457 | 458 | **Description:** Returns the ID of the campaign the price adjustment was based on. Note:If the price adjustment was generated by a B2C Commerce promotion as part of an AB-test, then an ID will be returned but it is not the ID of a true campaign. This behavior is required for backwards compatibility and should not be relied upon as it may change in future releases. 459 | 460 | **Returns:** 461 | 462 | the ID of the B2C Commerce campaign the price adjustment was based on, or null if it was not based on a campaign. 463 | 464 | --- 465 | 466 | ### getCouponLineItem 467 | 468 | **Signature:** `getCouponLineItem() : CouponLineItem` 469 | 470 | **Description:** Returns the coupon line item related to this price adjustment. If the price adjustment is not based on a coupon, null is returned. 471 | 472 | **Returns:** 473 | 474 | Coupon line item or null. 475 | 476 | --- 477 | 478 | ### getCreatedBy 479 | 480 | **Signature:** `getCreatedBy() : String` 481 | 482 | **Description:** Returns the name of the user who created the price adjustment. This method returns a value if the price-adjustment was created as a custom price-adjustment using one of the methods ProductLineItem.createPriceAdjustment(String, Discount), ShippingLineItem.createShippingPriceAdjustment(String, Discount) or LineItemCtnr.createPriceAdjustment(String, Discount). If an agent user has created the price adjustment, the agent user's name is returned. Otherwise "Customer" is returned. 483 | 484 | **Returns:** 485 | 486 | the name of the user who created the price adjustment 487 | 488 | --- 489 | 490 | ### getPromotion 491 | 492 | **Signature:** `getPromotion() : Promotion` 493 | 494 | **Description:** Returns the promotion associated with this price adjustment. The associated promotion is determined from the promotionID and campaignID attributes which are set by the promotions engine when applying discounts. Alternatively if the promotion applied as part of an AB-test, then the associated promotion is determined from the promotionID attribute and the hidden attributes, abTestID and abTestGroupID. If the promotion has been removed from the system since this price adjustment was created, or if the promotion still exists but is not assigned to any campaign or AB-test, this method returns null. If the promotion has been reassigned to a different campaign or AB-test since this price adjustment was created, then the system will return an appropriate Promotion instance. This method always returns null for custom price adjustments. 495 | 496 | **Returns:** 497 | 498 | the associated promotion, or null. 499 | 500 | --- 501 | 502 | ### getPromotionID 503 | 504 | **Signature:** `getPromotionID() : String` 505 | 506 | **Description:** Returns the ID of the promotion related to this price adjustment. 507 | 508 | **Returns:** 509 | 510 | ID of related promotion. 511 | 512 | --- 513 | 514 | ### getProratedPrices 515 | 516 | **Signature:** `getProratedPrices() : Map` 517 | 518 | **Description:** Returns a map representing the product line items to which this price adjustment is "related" (in the sense defined below) and the portion of this adjustment's price which applies to each after discount prorating is performed. This information is sometimes useful to display in the storefront but is more often useful for integrating with backend order-management and accounting systems which require all discounts to be itemized. The definition of "related" product line items depends on the type of promotion which generated this price adjustment: For order promotions, price adjustments are prorated across all product line items which are not explicitly excluded by the promotion. Custom order price adjustments apply to all items in the LineItemCtnr. For Buy-X-Get-Y product promotions, price adjustments are prorated across all items all product line items that are involved in the promotion, meaning that the PLI has one or more items contributing to the qualifying product count (i.e. the item is one of the X) or receiving the discount (i.e. the item is one of the Y). Other product promotions are not prorated and simply adjust the parent product line item, and so the returned map is of size 1. For shipping promotions, this method returns an empty map. Buy-X-Get-Y product promotions are prorated as follows: Each price adjustment generated by the promotion is sequentially prorated upon the related items according to the items' adjusted prices after all non-BOGO product promotions are considered, but before order promotions are considered. Order promotions are prorated sequentially upon non-excluded items according to the order in which they applied during promotion processing. The values in the map are inclusive of tax if this price adjustment is based on gross pricing, and exclusive of tax otherwise. The sum of the prorated prices always equals the price of this price adjustment. 519 | 520 | **Returns:** 521 | 522 | map of ProductLineItems to Money instances representing the product line items across which this price adjustment is prorated and the portion of this adjustment which applies towards each. 523 | 524 | --- 525 | 526 | ### getQuantity 527 | 528 | **Signature:** `getQuantity() : Number` 529 | 530 | **Description:** Returns the number of items this price adjustment applies to. This value is always equal to 1 for price adjustments generated by order or shipping promotions. For price adjustments generated by product promotions, this value represents the number of units of the parent product line item to which the adjustment applies. Because promotions may have a maximum number of applications this value may be less than the product line item quantity. For custom price adjustments, not generated by the promotions engine, this method always returns 0. 531 | 532 | **Returns:** 533 | 534 | The number of items this price adjustment applies to. 535 | 536 | --- 537 | 538 | ### getReasonCode 539 | 540 | **Signature:** `getReasonCode() : EnumValue` 541 | 542 | **Description:** Returns the reason code of the price adjustment. The list of available reason codes is editable system meta-data. An example for using the reason code is that in a call center application the CSR will explain why he gave a discount to the customer. 543 | 544 | **Returns:** 545 | 546 | reason code of the price adjustment 547 | 548 | --- 549 | 550 | ### isBasedOnABTest 551 | 552 | **Signature:** `isBasedOnABTest() : boolean` 553 | 554 | **Description:** Returns true if the price adjustment was generated by the B2C Commerce promotions engine when applying a promotion assigned to an AB-test. 555 | 556 | **Returns:** 557 | 558 | true if the price adjustment was generated by the B2C Commerce promotions engine when applying a promotion assigned to an AB-test, false otherwise. 559 | 560 | --- 561 | 562 | ### isBasedOnCampaign 563 | 564 | **Signature:** `isBasedOnCampaign() : boolean` 565 | 566 | **Description:** Returns true if the price adjustment was generated by the B2C Commerce promotions engine when applying a promotion assigned to a Campaign or an AB-test. 567 | 568 | **Deprecated:** 569 | 570 | The method has been deprecated since the name implies that there is a related Campaign, which may not be true. Use !isCustom() instead. 571 | 572 | **Returns:** 573 | 574 | true if the price adjustment was generated by the B2C Commerce promotions engine, false otherwise. 575 | 576 | --- 577 | 578 | ### isBasedOnCoupon 579 | 580 | **Signature:** `isBasedOnCoupon() : boolean` 581 | 582 | **Description:** Identifies if the promotion line item results from a coupon. 583 | 584 | **Returns:** 585 | 586 | true if the promotion line item results from a coupon, false otherwise. 587 | 588 | --- 589 | 590 | ### isCustom 591 | 592 | **Signature:** `isCustom() : boolean` 593 | 594 | **Description:** Returns true if this PriceAdjustment was created by custom script code. 595 | 596 | **Returns:** 597 | 598 | true if this PriceAdjustment was created by custom script code, or false if it was created by B2C Commerce promotions engine. 599 | 600 | --- 601 | 602 | ### isManual 603 | 604 | **Signature:** `isManual() : boolean` 605 | 606 | **Description:** Returns true if this PriceAdjustment was added manually by a user. A manual PriceAdjustment is one which has been added as a result of a user interaction e.g. by a user editing an order. A non-manual PriceAdjustment is one which has been added for a different reason, e.g. by custom logic which automatically adjusts the price of particular products when certain conditions are met. 607 | 608 | **Returns:** 609 | 610 | true if this PriceAdjustment was added manually by a user, otherwise - false 611 | 612 | --- 613 | 614 | ### setManual 615 | 616 | **Signature:** `setManual(aFlag : boolean) : void` 617 | 618 | **Description:** Marks the current PriceAdjustment as manual/non-manual. Note that only custom PriceAdjustment can be marked as manual/non-manual. A manual PriceAdjustment is one which has been added as a result of a user interaction e.g. by a user editing an order. A non-manual PriceAdjustment is one which has been added for a different reason, e.g. by custom logic which automatically adjusts the price of particular products when certain conditions are met. 619 | 620 | **Parameters:** 621 | 622 | - `aFlag`: the manual flag to set 623 | 624 | **Throws:** 625 | 626 | IllegalArgumentException - if the adjustment is not custom 627 | 628 | --- 629 | 630 | ### setReasonCode 631 | 632 | **Signature:** `setReasonCode(reasonCode : String) : void` 633 | 634 | **Description:** Set the reason code, using the internal non-localizable value. Standard values are 'PRICE_MATCH', 'BACKORDER' and 'EVEN_EXCHANGE', but the available list is editable system meta-data. 635 | 636 | **Parameters:** 637 | 638 | - `reasonCode`: reason code 639 | 640 | --- ``` -------------------------------------------------------------------------------- /tests/mcp/node/list-sfcc-classes.docs-only.programmatic.test.js: -------------------------------------------------------------------------------- ```javascript 1 | /** 2 | * Programmatic tests for list_sfcc_classes tool 3 | * 4 | * These tests provide advanced verification capabilities beyond YAML pattern matching, 5 | * including performance monitoring, dynamic validation, comprehensive content analysis, 6 | * and advanced error categorization for the SFCC class listing functionality. 7 | * 8 | * Response format discovered via aegis query: 9 | * - Succ // Core SFCC namespaces should have good coverage 10 | const coreNamespaces = ['dw.catalog', 'dw.customer', 'dw.order', 'dw.system']; 11 | coreNamespaces.forEach(namespace => { 12 | const classCount = analysis.classsByNamespace[namespace].length; 13 | assert.ok(classCount >= 5, 14 | `Core namespace ${namespace} should have at least 5 classes (got ${classCount})`); 15 | }); 16 | 17 | // TopLevel namespace should have good coverage for utility classes 18 | const topLevelCount = analysis.classsByNamespace['TopLevel'].length; 19 | assert.ok(topLevelCount >= 10, 20 | `TopLevel namespace should have substantial coverage (got ${topLevelCount})`); 21 | });t: [{ type: "text", text: "[\"class1\", \"class2\", ...]" }] } 22 | * - Always successful: No isError field or error conditions 23 | * - Ignores extra parameters: Gracefully handles unexpected parameters 24 | * - Comprehensive: Returns 500+ classes across all SFCC namespaces 25 | */ 26 | 27 | import { test, describe, before, after, beforeEach } from 'node:test'; 28 | import { strict as assert } from 'node:assert'; 29 | import { connect } from 'mcp-aegis'; 30 | 31 | /** 32 | * Performance monitoring utility class for comprehensive metrics collection 33 | */ 34 | 35 | /** 36 | * Content analysis utility for SFCC class validation 37 | */ 38 | class ContentAnalyzer { 39 | constructor() { 40 | // SFCC tools should only return actual SFCC classes (dw.* and TopLevel) 41 | // best-practices and sfra content belong to their respective specialized tools 42 | this.expectedNamespaces = [ 43 | 'TopLevel', 44 | 'dw.campaign', 45 | 'dw.catalog', 46 | 'dw.content', 47 | 'dw.crypto', 48 | 'dw.customer', 49 | 'dw.extensions', 50 | 'dw.io', 51 | 'dw.job', 52 | 'dw.net', 53 | 'dw.object', 54 | 'dw.order', 55 | 'dw.rpc', 56 | 'dw.suggest', 57 | 'dw.svc', 58 | 'dw.system', 59 | 'dw.util', 60 | 'dw.value', 61 | 'dw.web' 62 | ]; 63 | 64 | // Only include actual SFCC classes, not SFRA or best-practice content 65 | this.criticalClasses = [ 66 | 'dw.catalog.Product', 67 | 'dw.catalog.Category', 68 | 'dw.order.Order', 69 | 'dw.order.Basket', 70 | 'dw.customer.Customer', 71 | 'dw.system.Site', 72 | 'dw.system.SitePreferences', 73 | 'dw.util.ArrayList', 74 | 'dw.web.URL' 75 | ]; 76 | 77 | // Best practice guides are handled by dedicated best-practice tools, not SFCC tools 78 | this.bestPracticeGuides = []; 79 | } 80 | 81 | analyzeClassList(classArray) { 82 | const analysis = { 83 | totalClasses: classArray.length, 84 | namespacesCovered: new Set(), 85 | missingNamespaces: [], 86 | missingCriticalClasses: [], 87 | foundCriticalClasses: [], 88 | foundBestPractices: [], 89 | duplicates: [], 90 | invalidFormats: [], 91 | classsByNamespace: {} 92 | }; 93 | 94 | // Initialize namespace counters 95 | this.expectedNamespaces.forEach(ns => { 96 | analysis.classsByNamespace[ns] = []; 97 | }); 98 | 99 | // Analyze each class 100 | const seenClasses = new Set(); 101 | classArray.forEach(className => { 102 | // Check for duplicates 103 | if (seenClasses.has(className)) { 104 | analysis.duplicates.push(className); 105 | return; 106 | } 107 | seenClasses.add(className); 108 | 109 | // Validate format (should have at least one dot or be TopLevel.*) 110 | if (!className.includes('.') && !className.startsWith('TopLevel.')) { 111 | analysis.invalidFormats.push(className); 112 | } 113 | 114 | // Categorize by namespace 115 | let namespace; 116 | if (className.startsWith('dw.')) { 117 | // For dw.* classes, use first two parts (e.g., "dw.catalog" from "dw.catalog.Product") 118 | namespace = className.split('.').slice(0, 2).join('.'); 119 | } else { 120 | // For TopLevel and other classes, use first part only 121 | namespace = className.split('.')[0]; 122 | } 123 | analysis.namespacesCovered.add(namespace); 124 | 125 | if (analysis.classsByNamespace[namespace]) { 126 | analysis.classsByNamespace[namespace].push(className); 127 | } else { 128 | // Unexpected namespace 129 | if (!analysis.classsByNamespace.other) { 130 | analysis.classsByNamespace.other = []; 131 | } 132 | analysis.classsByNamespace.other.push(className); 133 | } 134 | 135 | // Check critical classes 136 | if (this.criticalClasses.includes(className)) { 137 | analysis.foundCriticalClasses.push(className); 138 | } 139 | 140 | // Check best practice guides 141 | if (this.bestPracticeGuides.includes(className)) { 142 | analysis.foundBestPractices.push(className); 143 | } 144 | }); 145 | 146 | // Find missing namespaces 147 | analysis.missingNamespaces = this.expectedNamespaces.filter( 148 | ns => !analysis.namespacesCovered.has(ns) 149 | ); 150 | 151 | // Find missing critical classes 152 | analysis.missingCriticalClasses = this.criticalClasses.filter( 153 | cls => !analysis.foundCriticalClasses.includes(cls) 154 | ); 155 | 156 | return analysis; 157 | } 158 | 159 | validateCompleteness(analysis) { 160 | const issues = []; 161 | 162 | if (analysis.totalClasses < 350) { 163 | issues.push(`Class count too low: ${analysis.totalClasses} (expected 350+)`); 164 | } 165 | 166 | if (analysis.missingNamespaces.length > 0) { 167 | issues.push(`Missing namespaces: ${analysis.missingNamespaces.join(', ')}`); 168 | } 169 | 170 | if (analysis.missingCriticalClasses.length > 0) { 171 | issues.push(`Missing critical classes: ${analysis.missingCriticalClasses.join(', ')}`); 172 | } 173 | 174 | if (analysis.duplicates.length > 0) { 175 | issues.push(`Duplicate classes found: ${analysis.duplicates.length}`); 176 | } 177 | 178 | if (analysis.invalidFormats.length > 0) { 179 | issues.push(`Invalid formats found: ${analysis.invalidFormats.length}`); 180 | } 181 | 182 | return issues; 183 | } 184 | } 185 | 186 | describe('list_sfcc_classes Programmatic Tests', () => { 187 | let client; 188 | const contentAnalyzer = new ContentAnalyzer(); 189 | 190 | before(async () => { 191 | client = await connect('./aegis.config.docs-only.json'); 192 | }); 193 | 194 | after(async () => { 195 | if (client?.connected) { 196 | await client.disconnect(); 197 | } 198 | }); 199 | 200 | beforeEach(() => { 201 | // CRITICAL: Clear all buffers to prevent test interference 202 | client.clearAllBuffers(); // Recommended - comprehensive protection 203 | }); 204 | 205 | describe('Protocol Compliance', () => { 206 | test('should be properly connected to MCP server', async () => { 207 | assert.ok(client.connected, 'Client should be connected'); 208 | }); 209 | 210 | test('should have list_sfcc_classes tool available', async () => { 211 | const tools = await client.listTools(); 212 | const listTool = tools.find(tool => tool.name === 'list_sfcc_classes'); 213 | 214 | assert.ok(listTool, 'list_sfcc_classes tool should be available'); 215 | assert.equal(listTool.name, 'list_sfcc_classes'); 216 | assert.ok(listTool.description, 'Tool should have description'); 217 | assert.ok(listTool.inputSchema, 'Tool should have input schema'); 218 | }); 219 | 220 | test('should have correct tool input schema', async () => { 221 | const tools = await client.listTools(); 222 | const listTool = tools.find(tool => tool.name === 'list_sfcc_classes'); 223 | 224 | assert.equal(listTool.inputSchema.type, 'object'); 225 | // list_sfcc_classes takes no required parameters 226 | assert.ok(!listTool.inputSchema.required || listTool.inputSchema.required.length === 0); 227 | }); 228 | }); 229 | 230 | describe('Basic Functionality', () => { 231 | test('should execute successfully with empty parameters', async () => { 232 | const result = await client.callTool('list_sfcc_classes', {}); 233 | 234 | assertValidMCPResponse(result); 235 | assert.equal(result.isError, false, 'Should not return error'); 236 | assert.equal(result.content.length, 1, 'Should return single content item'); 237 | assert.equal(result.content[0].type, 'text', 'Content type should be text'); 238 | }); 239 | 240 | test('should return valid JSON array in response', async () => { 241 | const result = await client.callTool('list_sfcc_classes', {}); 242 | 243 | assertValidMCPResponse(result); 244 | const responseText = result.content[0].text; 245 | 246 | // Should be valid JSON 247 | let classArray; 248 | assert.doesNotThrow(() => { 249 | classArray = JSON.parse(responseText); 250 | }, 'Response should be valid JSON'); 251 | 252 | assert.ok(Array.isArray(classArray), 'Response should be JSON array'); 253 | assert.ok(classArray.length > 0, 'Class array should not be empty'); 254 | }); 255 | 256 | test('should ignore additional parameters gracefully', async () => { 257 | const result = await client.callTool('list_sfcc_classes', { 258 | unexpectedParam: 'should be ignored', 259 | anotherParam: 123, 260 | objectParam: { nested: 'value' } 261 | }); 262 | 263 | assertValidMCPResponse(result); 264 | assert.equal(result.isError, false, 'Should handle extra params gracefully'); 265 | 266 | // Result should be identical to empty params call 267 | const baselineResult = await client.callTool('list_sfcc_classes', {}); 268 | assert.equal(result.content[0].text, baselineResult.content[0].text, 269 | 'Result should be identical regardless of extra params'); 270 | }); 271 | }); 272 | 273 | describe('Content Quality and Completeness', () => { 274 | test('should return comprehensive SFCC class coverage', async () => { 275 | const result = await client.callTool('list_sfcc_classes', {}); 276 | const classArray = JSON.parse(result.content[0].text); 277 | const analysis = contentAnalyzer.analyzeClassList(classArray); 278 | 279 | // Validate completeness 280 | const issues = contentAnalyzer.validateCompleteness(analysis); 281 | assert.equal(issues.length, 0, `Content issues found: ${issues.join('; ')}`); 282 | 283 | // Verify substantial class count 284 | assert.ok(analysis.totalClasses >= 400, 285 | `Should have substantial class count (got ${analysis.totalClasses})`); 286 | }); 287 | 288 | test('should include all expected SFCC namespaces', async () => { 289 | const result = await client.callTool('list_sfcc_classes', {}); 290 | const classArray = JSON.parse(result.content[0].text); 291 | const analysis = contentAnalyzer.analyzeClassList(classArray); 292 | 293 | contentAnalyzer.expectedNamespaces.forEach(namespace => { 294 | assert.ok(analysis.namespacesCovered.has(namespace), 295 | `Missing expected namespace: ${namespace}`); 296 | assert.ok(analysis.classsByNamespace[namespace].length > 0, 297 | `Namespace ${namespace} should have classes`); 298 | }); 299 | }); 300 | 301 | test('should include critical SFCC classes for development', async () => { 302 | const result = await client.callTool('list_sfcc_classes', {}); 303 | const classArray = JSON.parse(result.content[0].text); 304 | const analysis = contentAnalyzer.analyzeClassList(classArray); 305 | 306 | contentAnalyzer.criticalClasses.forEach(criticalClass => { 307 | assert.ok(classArray.includes(criticalClass), 308 | `Missing critical class: ${criticalClass}`); 309 | }); 310 | 311 | assert.ok(analysis.foundCriticalClasses.length >= contentAnalyzer.criticalClasses.length * 0.9, 312 | 'Should include at least 90% of critical classes'); 313 | }); 314 | 315 | test('should include best practice guides', async () => { 316 | const result = await client.callTool('list_sfcc_classes', {}); 317 | const classArray = JSON.parse(result.content[0].text); 318 | 319 | contentAnalyzer.bestPracticeGuides.forEach(guide => { 320 | assert.ok(classArray.includes(guide), 321 | `Missing best practice guide: ${guide}`); 322 | }); 323 | }); 324 | 325 | test('should have proper class naming format', async () => { 326 | const result = await client.callTool('list_sfcc_classes', {}); 327 | const classArray = JSON.parse(result.content[0].text); 328 | const analysis = contentAnalyzer.analyzeClassList(classArray); 329 | 330 | assert.equal(analysis.invalidFormats.length, 0, 331 | `Invalid class formats found: ${analysis.invalidFormats.join(', ')}`); 332 | 333 | assert.equal(analysis.duplicates.length, 0, 334 | `Duplicate classes found: ${analysis.duplicates.join(', ')}`); 335 | }); 336 | }); 337 | 338 | describe('Advanced Content Analysis', () => { 339 | test('should provide balanced namespace distribution', async () => { 340 | const result = await client.callTool('list_sfcc_classes', {}); 341 | const classArray = JSON.parse(result.content[0].text); 342 | const analysis = contentAnalyzer.analyzeClassList(classArray); 343 | 344 | // Core namespaces should have substantial class counts 345 | const coreNamespaces = ['dw.catalog', 'dw.order', 'dw.customer', 'dw.system']; 346 | coreNamespaces.forEach(namespace => { 347 | const classCount = analysis.classsByNamespace[namespace].length; 348 | assert.ok(classCount >= 5, 349 | `Core namespace ${namespace} should have at least 5 classes (got ${classCount})`); 350 | }); 351 | 352 | // TopLevel namespace should have good coverage for utility classes 353 | const topLevelCount = analysis.classsByNamespace['TopLevel'].length; 354 | assert.ok(topLevelCount >= 10, 355 | `TopLevel namespace should have substantial coverage (got ${topLevelCount})`); 356 | }); 357 | 358 | test('should include developer-friendly discovery content', async () => { 359 | const result = await client.callTool('list_sfcc_classes', {}); 360 | const classArray = JSON.parse(result.content[0].text); 361 | 362 | // Should include educational namespaces for new developers (SFCC classes only) 363 | const educationalClasses = [ 364 | 'TopLevel.Array', 365 | 'TopLevel.Object', 366 | 'dw.catalog.Product', 367 | 'dw.system.Site' 368 | ]; 369 | 370 | educationalClasses.forEach(educationalClass => { 371 | assert.ok(classArray.includes(educationalClass), 372 | `Missing educational class: ${educationalClass}`); 373 | }); 374 | }); 375 | 376 | test('should support API exploration workflows', async () => { 377 | const result = await client.callTool('list_sfcc_classes', {}); 378 | const classArray = JSON.parse(result.content[0].text); 379 | 380 | // Should include classes that developers commonly search for 381 | const commonSearchTargets = [ 382 | 'dw.catalog.ProductMgr', 383 | 'dw.order.BasketMgr', 384 | 'dw.customer.CustomerMgr', 385 | 'dw.system.SitePreferences', 386 | 'dw.web.URLUtils' 387 | ]; 388 | 389 | commonSearchTargets.forEach(searchTarget => { 390 | assert.ok(classArray.includes(searchTarget), 391 | `Missing common search target: ${searchTarget}`); 392 | }); 393 | }); 394 | 395 | test('should provide comprehensive extension coverage', async () => { 396 | const result = await client.callTool('list_sfcc_classes', {}); 397 | const classArray = JSON.parse(result.content[0].text); 398 | 399 | // Should include extension classes for integrations 400 | const extensionClasses = classArray.filter(cls => cls.startsWith('dw.extensions.')); 401 | assert.ok(extensionClasses.length >= 10, 402 | `Should include substantial extension classes (got ${extensionClasses.length})`); 403 | 404 | // Should cover major payment integrations 405 | const paymentClasses = classArray.filter(cls => 406 | cls.includes('payments') || cls.includes('PayPal') || cls.includes('ApplePay')); 407 | assert.ok(paymentClasses.length >= 5, 408 | `Should include payment integration classes (got ${paymentClasses.length})`); 409 | }); 410 | }); 411 | 412 | 413 | describe('Response Structure Validation', () => { 414 | test('should maintain consistent response structure', async () => { 415 | const result1 = await client.callTool('list_sfcc_classes', {}); 416 | const result2 = await client.callTool('list_sfcc_classes', {}); 417 | 418 | // Structure should be identical 419 | assert.equal(result1.content.length, result2.content.length); 420 | assert.equal(result1.content[0].type, result2.content[0].type); 421 | assert.equal(result1.content[0].text, result2.content[0].text); 422 | 423 | // Content should be deterministic 424 | const classes1 = JSON.parse(result1.content[0].text); 425 | const classes2 = JSON.parse(result2.content[0].text); 426 | assert.deepEqual(classes1, classes2, 'Class lists should be identical between calls'); 427 | }); 428 | 429 | test('should return properly formatted JSON with valid structure', async () => { 430 | const result = await client.callTool('list_sfcc_classes', {}); 431 | const responseText = result.content[0].text; 432 | 433 | // Should be properly formatted JSON 434 | assert.ok(responseText.startsWith('['), 'Should start with array bracket'); 435 | assert.ok(responseText.endsWith(']'), 'Should end with array bracket'); 436 | 437 | // Parse and validate structure 438 | const classArray = JSON.parse(responseText); 439 | assert.ok(Array.isArray(classArray), 'Should be array'); 440 | 441 | // Each item should be a string 442 | classArray.forEach((item, index) => { 443 | assert.equal(typeof item, 'string', `Item ${index} should be string`); 444 | assert.ok(item.length > 0, `Item ${index} should not be empty`); 445 | }); 446 | }); 447 | 448 | test('should provide useful content for AI agents', async () => { 449 | const result = await client.callTool('list_sfcc_classes', {}); 450 | const classArray = JSON.parse(result.content[0].text); 451 | 452 | // Should include SFCC classes that AI agents commonly need 453 | const aiUsefulClasses = [ 454 | 'dw.system.Logger', // For logging 455 | 'dw.util.StringUtils', // For text processing 456 | 'dw.web.URLUtils', // For URL generation 457 | 'dw.system.Site', // For site context 458 | 'dw.catalog.Product', // For product operations 459 | 'dw.order.Basket' // For basket operations 460 | ]; 461 | 462 | aiUsefulClasses.forEach(usefulClass => { 463 | assert.ok(classArray.includes(usefulClass), 464 | `Should include AI-useful class: ${usefulClass}`); 465 | }); 466 | }); 467 | }); 468 | 469 | describe('Educational and Discovery Value', () => { 470 | test('should serve as comprehensive SFCC reference', async () => { 471 | const result = await client.callTool('list_sfcc_classes', {}); 472 | const classArray = JSON.parse(result.content[0].text); 473 | const analysis = contentAnalyzer.analyzeClassList(classArray); 474 | 475 | // Should cover all major SFCC functional areas (SFCC classes only) 476 | const functionalAreas = { 477 | 'commerce': ['dw.catalog', 'dw.order'], 478 | 'customer': ['dw.customer'], 479 | 'system': ['dw.system'], 480 | 'web': ['dw.web'], 481 | 'utilities': ['dw.util'], 482 | 'integrations': ['dw.svc', 'dw.extensions'], 483 | 'foundations': ['TopLevel'] 484 | }; 485 | 486 | Object.entries(functionalAreas).forEach(([area, namespaces]) => { 487 | namespaces.forEach(namespace => { 488 | const classCount = analysis.classsByNamespace[namespace].length; 489 | assert.ok(classCount > 0, 490 | `Functional area '${area}' should have classes in namespace '${namespace}'`); 491 | }); 492 | }); 493 | }); 494 | 495 | test('should support progressive learning for developers', async () => { 496 | const result = await client.callTool('list_sfcc_classes', {}); 497 | const classArray = JSON.parse(result.content[0].text); 498 | 499 | // Should include beginner-friendly TopLevel classes 500 | const beginnerClasses = classArray.filter(cls => 501 | cls.startsWith('TopLevel.')); 502 | assert.ok(beginnerClasses.length >= 10, 503 | `Should include beginner-friendly TopLevel classes (got ${beginnerClasses.length})`); 504 | 505 | // Should include advanced integration classes 506 | const advancedClasses = classArray.filter(cls => 507 | cls.includes('Hook') || cls.includes('Extension') || cls.includes('Service')); 508 | assert.ok(advancedClasses.length >= 20, 509 | `Should include advanced integration classes (got ${advancedClasses.length})`); 510 | }); 511 | }); 512 | 513 | describe('Integration and Cross-Tool Validation', () => { 514 | test('should provide classes that work with search_sfcc_classes tool', async () => { 515 | const listResult = await client.callTool('list_sfcc_classes', {}); 516 | const classArray = JSON.parse(listResult.content[0].text); 517 | 518 | // Pick a few SFCC classes to test with search tool 519 | const testClasses = ['dw.catalog.Product', 'dw.system.Site', 'dw.customer.Customer']; 520 | 521 | for (const testClass of testClasses) { 522 | assert.ok(classArray.includes(testClass), 523 | `List should include searchable class: ${testClass}`); 524 | 525 | // Test that the class is discoverable via search 526 | const namespace = testClass.split('.')[1]; // Get 'catalog' from 'dw.catalog.Product' 527 | const searchResult = await client.callTool('search_sfcc_classes', { 528 | query: namespace 529 | }); 530 | 531 | assertValidMCPResponse(searchResult); 532 | if (!searchResult.isError) { 533 | const searchedClasses = JSON.parse(searchResult.content[0].text); 534 | assert.ok(Array.isArray(searchedClasses), 535 | `Search should return array for ${namespace}`); 536 | } 537 | } 538 | }); 539 | 540 | test('should include classes that have detailed documentation available', async () => { 541 | const listResult = await client.callTool('list_sfcc_classes', {}); 542 | const classArray = JSON.parse(listResult.content[0].text); 543 | 544 | // Test a few well-documented classes 545 | const documentedClasses = ['dw.catalog.Product', 'dw.system.Site', 'dw.order.Basket']; 546 | 547 | for (const docClass of documentedClasses) { 548 | assert.ok(classArray.includes(docClass), 549 | `List should include documented class: ${docClass}`); 550 | 551 | // Test that class info is available 552 | const infoResult = await client.callTool('get_sfcc_class_info', { 553 | className: docClass 554 | }); 555 | 556 | assertValidMCPResponse(infoResult); 557 | if (!infoResult.isError) { 558 | const infoText = infoResult.content[0].text; 559 | // Parse the JSON response to check for class information 560 | let classInfo; 561 | assert.doesNotThrow(() => { 562 | classInfo = JSON.parse(infoText); 563 | }, `Class info should be valid JSON for ${docClass}`); 564 | 565 | // Check that the response contains class information 566 | assert.ok(classInfo.className || classInfo.packageName, 567 | `Class info should contain class information for ${docClass}`); 568 | 569 | // For dw.catalog.Product, verify it contains "Product" and "dw.catalog" 570 | if (docClass === 'dw.catalog.Product') { 571 | assert.ok(classInfo.className === 'Product', 572 | 'Product class info should contain className "Product"'); 573 | assert.ok(classInfo.packageName === 'dw.catalog', 574 | 'Product class info should contain packageName "dw.catalog"'); 575 | } 576 | } 577 | } 578 | }); 579 | 580 | test('should maintain consistency with available tools ecosystem', async () => { 581 | const listResult = await client.callTool('list_sfcc_classes', {}); 582 | const classArray = JSON.parse(listResult.content[0].text); 583 | 584 | // Should include SFCC classes that support the SFCC toolchain 585 | const toolchainClasses = [ 586 | 'dw.catalog.Product', // For product tool integration 587 | 'dw.system.SitePreferences', // For site preference tools 588 | 'dw.order.Order', // For order management tools 589 | 'dw.util.ArrayList', // For collection operations 590 | 'dw.web.URLUtils' // For URL generation tools 591 | ]; 592 | 593 | toolchainClasses.forEach(toolClass => { 594 | assert.ok(classArray.includes(toolClass), 595 | `Should include toolchain-supporting class: ${toolClass}`); 596 | }); 597 | }); 598 | 599 | test('should enable effective AI-assisted development workflows', async () => { 600 | const listResult = await client.callTool('list_sfcc_classes', {}); 601 | const classArray = JSON.parse(listResult.content[0].text); 602 | 603 | // Should include SFCC classes that AI agents commonly recommend 604 | const aiWorkflowClasses = [ 605 | 'dw.system.Logger', // For debugging workflows 606 | 'dw.util.StringUtils', // For data manipulation 607 | 'dw.web.URLUtils', // For URL generation 608 | 'dw.catalog.ProductMgr', // For product operations 609 | 'dw.order.BasketMgr', // For cart operations 610 | 'dw.customer.CustomerMgr', // For customer operations 611 | 'dw.system.Site', // For site context 612 | 'dw.order.Order' // For order handling 613 | ]; 614 | 615 | aiWorkflowClasses.forEach(workflowClass => { 616 | assert.ok(classArray.includes(workflowClass), 617 | `Should include AI workflow class: ${workflowClass}`); 618 | }); 619 | 620 | // Should provide good coverage for common AI development tasks (SFCC classes only) 621 | const taskCoverage = { 622 | 'data_access': classArray.filter(cls => cls.includes('Mgr')).length, 623 | 'web_operations': classArray.filter(cls => cls.startsWith('dw.web.')).length, 624 | 'system_integration': classArray.filter(cls => cls.startsWith('dw.system.')).length, 625 | 'utilities': classArray.filter(cls => cls.startsWith('dw.util.')).length 626 | }; 627 | 628 | Object.entries(taskCoverage).forEach(([task, count]) => { 629 | assert.ok(count >= 3, 630 | `Should have good coverage for ${task} (got ${count} classes)`); 631 | }); 632 | }); 633 | }); 634 | }); 635 | 636 | /** 637 | * Helper function to validate basic MCP response structure 638 | */ 639 | function assertValidMCPResponse(result) { 640 | assert.ok(result.content, 'Response should have content'); 641 | assert.ok(Array.isArray(result.content), 'Content should be array'); 642 | assert.equal(typeof result.isError, 'boolean', 'isError should be boolean and always present'); 643 | } 644 | ```