This is page 21 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_order.hooks/ReturnHooks.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.order.hooks 2 | 3 | # Class ReturnHooks 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - dw.order.hooks.ReturnHooks 8 | 9 | ## Description 10 | 11 | This interface represents all script hooks that can be registered to customizing the order center return resource. It contains the extension points (hook names), and the functions that are called by each extension point. A function must be defined inside a JavaScript source and must be exported. The script with the exported hook function must be located inside a site cartridge. Inside the site cartridge a 'package.json' file with a 'hooks' entry must exist. "hooks": "./hooks.json" The hooks entry links to a json file, relative to the 'package.json' file. This file lists all registered hooks inside the hooks property: "hooks": [ {"name": "dw.order.return.createReturn", "script": "./returns.ds"}, {"name": "dw.order.return.addReturnItem", "script": "./returns.ds"}, {"name": "dw.order.return.changeStatus", "script": "./returns.ds"}, ] A hook entry has a 'name' and a 'script' property. The 'name' contains the extension point, the hook name. The 'script' contains the script relative to the hooks file, with the exported hook function. Overview Return Functionality Business objects ReturnCase All returns exist in the context of a ReturnCase, each Order can have any number of ReturnCases. A ReturnCase has ReturnCaseItems, each of which is associated with an OrderItem (an extension to either a ProductLineItem or a ShippingLineItem). Each ReturnCaseItem defines an ReturnCaseItem.getAuthorizedQuantity() representing the maximum quantity expected to be returned. A ReturnCaseItem may be associated with 0..n ReturnItems - ReturnItems are added to the ReturnCaseItem when Returns are created. Either - a ReturnCase may be used as an RMA, in which case they are created when a customer first shows a wish to return item(s). The customer then includes the RMA number with the returned item(s). The Return created as a result is then associated with the existing ReturnCase. Or - a ReturnCase is automatically created as part of the return creation, i.e. the customer returns some item(s) leading to a creation of both a Return and an associated ReturnCase. The scripting api allows access to the ReturnCases, whether the ReturnCase is an RMA or not, and the ReturnCase status. Both the ReturnCaseItems and any Returns associated with the ReturnCase can be accessed. A ReturnCase has one of these status values: New - the ReturnCase has been created and can be edited previous to its authorization CONFIRMED - the ReturnCase is CONFIRMED, can no longer be edited, no Returns have been associated with it. Only an New- ReturnCase can be CONFIRMED PARTIAL_RETURNED - the ReturnCase has been associated with at least one Return, but is not yet complete. Only an CONFIRMED- ReturnCase can be set to PARTIAL_RETURNED RETURNED - the ReturnCase has been associated with Returns which match the expected authorized quantity. Only an CONFIRMED- or PARTIAL_RETURNED- return-case can be set to RETURNED Cancelled - the ReturnCase has been cancelled (only a New- or CONFIRMED- ReturnCase can be cancelled) Return A Return represents a physical customer return, and contains 1..n ReturnItems. A Return is associated with one ReturnCase, and each ReturnItem is associated with one ReturnCaseItem and (via the ReturnCaseItem) a single OrderItem usually representing an Order ProductLineItem. A ReturnItem records the quantity returned. A Return can have one of these status values: NEW - the Return is new, i.e. needs to undergo a check before it can be marked as COMPLETED COMPLETED - the return is complete, this is a precondition for refunding the customer for a return. Credit Invoice As a result of making a Return, the customer may be refunded. The refund amount is held in a credit Invoice which may be associated either with one Return or with one ReturnCase. The Invoice is passed to the refund payment hook allowing custom code to handle the payment refund. Process overview Create ReturnCase The creation of ReturnCases is supported using the data-api. The api supports, within the context of an Order, the specification of an (optional) RMA-number and addition of ReturnCaseItems for a given order-item and quantity. Authorize ReturnCase Following its creation, a ReturnCase needs to be CONFIRMED - an CONFIRMED ReturnCase cannot be modified. Cancel ReturnCase Following its creation or authorization, a ReturnCase may be cancelled. Create Return Returns may be imported or created via the data-api. These apis specify an (optional) RMA allowing a Return to be associated with a ReturnCase, and ReturnItems with a quantity and a key allowing them to be associated with an order-item. The process is delegated to custom scripts which control the creation of the Return and the addition of the ReturnItems: Hook extensionPointCreateReturn The creation of the new Return is delegated to the custom script when this hook is called, passing the order, and details of the Return to be created to the script. Typically the script accesses the ReturnCase from the order and creates the return with the provided return-number. It may also update the Order, ReturnCase or Return using custom values passed in the Return details. exports.createReturn = function (order:Order, returnDetails) { var returnNumber=returnDetails.returnNumber; var returnCase = order.getReturnCase(returnDetails.returnCaseNumber); var newReturn = returnCase.createReturn(returnNumber); return newReturn; } Hook extensionPointAddReturnItem This call delegates the creation of individual ReturnItems to a custom script, passing the Order, returnNumber, returnCaseItemId and return-item-details. Typically the script will access the ReturnCaseItem from the order and create a new ReturnItem for it. exports.addReturnItem = function (retrn:Return, returnItemDetails) { var returnCaseItem = order.getReturnCaseItem(returnCaseItemId); var item = returnCaseItem.createReturnItem(returnNr); Hook extensionPointChangeStatus This call delegates the update of the return-status to a custom script, passing the Order, returnNumber and new status. The custom script is responsible for setting the status and taking any other actions necessary, including the possibility of creating a credit invoice: changeStatus = function (retrn:Return, status) { retrn.status=status; Hook extensionPointAfterStatusChange This call delegates the update of the return-status to a custom script, passing the Order, returnNumber and new status. The custom script is responsible for setting the status and taking any other actions necessary, including the possibility of creating a credit invoice: changeStatus = function (retrn:Return, status) { retrn.status=status; Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice. 12 | 13 | ## Constants 14 | 15 | ## Properties 16 | 17 | ## Constructor Summary 18 | 19 | ## Method Summary 20 | 21 | ### addReturnItem 22 | 23 | **Signature:** `addReturnItem(retrn : Return, inputData : ReturnItem) : Status` 24 | 25 | The hook provides customization in the process of assigning the returned amount, quantity etc. 26 | 27 | ### afterStatusChange 28 | 29 | **Signature:** `afterStatusChange(retrn : Return) : Status` 30 | 31 | Called after method changeStatus(Return, ReturnWO) returns Status.OK. 32 | 33 | ### changeStatus 34 | 35 | **Signature:** `changeStatus(retrn : Return, inputData : Return) : Status` 36 | 37 | Responsible to change the status of a Return: the custom script is responsible for setting the new status using Return.setStatus(String). 38 | 39 | ### createReturn 40 | 41 | **Signature:** `createReturn(inputData : Return) : Return` 42 | 43 | This hook is responsible for creating a new Return, based on a ReturnCase. 44 | 45 | ### notifyStatusChange 46 | 47 | **Signature:** `notifyStatusChange(retrn : Return) : Status` 48 | 49 | Called after method changeStatus(Return, ReturnWO) returns Status.OK (and after method afterStatusChange(Return)) to inform of a successful status change. 50 | 51 | ## Method Detail 52 | 53 | ## Method Details 54 | 55 | ### addReturnItem 56 | 57 | **Signature:** `addReturnItem(retrn : Return, inputData : ReturnItem) : Status` 58 | 59 | **Description:** The hook provides customization in the process of assigning the returned amount, quantity etc. Here it is possible to refund differently based on the return reason code for example. Also one could correct the inventory based on the return information. Utilize ReturnCaseItem.createReturnItem(String) to create a new ReturnItem. 60 | 61 | **Parameters:** 62 | 63 | - `retrn`: the return for which an return item should be created 64 | - `inputData`: the return item 65 | 66 | **Returns:** 67 | 68 | Status.OK return item is successfully added Status.ERROR return item addition failed. 69 | 70 | --- 71 | 72 | ### afterStatusChange 73 | 74 | **Signature:** `afterStatusChange(retrn : Return) : Status` 75 | 76 | **Description:** Called after method changeStatus(Return, ReturnWO) returns Status.OK. The call is made in a separate database transaction allowing the script implementation to make an independent remote call if desired. 77 | 78 | **Parameters:** 79 | 80 | - `retrn`: the return 81 | 82 | **Returns:** 83 | 84 | Status.OK status successful Status.ERROR on failure 85 | 86 | --- 87 | 88 | ### changeStatus 89 | 90 | **Signature:** `changeStatus(retrn : Return, inputData : Return) : Status` 91 | 92 | **Description:** Responsible to change the status of a Return: the custom script is responsible for setting the new status using Return.setStatus(String). The invoice handling should be implemented here using Return.createInvoice(String) or ReturnCase.createInvoice(String). For example create an Invoice for a Return moving to status Return.STATUS_COMPLETED. 93 | 94 | **Parameters:** 95 | 96 | - `retrn`: the return which status should change 97 | - `inputData`: the data in which the new status is included 98 | 99 | **Returns:** 100 | 101 | Status.OK status successfully changed Status.ERROR status not changed. 102 | 103 | --- 104 | 105 | ### createReturn 106 | 107 | **Signature:** `createReturn(inputData : Return) : Return` 108 | 109 | **Description:** This hook is responsible for creating a new Return, based on a ReturnCase. 2 basic workflows are supported: On-the-fly return: create the parent ReturnCase using Order.createReturnCase(String, Boolean). Return-merchandise-authorization (RMA) workflow: resolve an existing ReturnCase using Order.getReturnCase(String). In both cases use this method to create the Return based on the inputData. Additional functionality like creating history entry, handling the return fees or the shipping cost credit can be implemented in the hook after the Return is created. 110 | 111 | **Parameters:** 112 | 113 | - `inputData`: the return 114 | 115 | **Returns:** 116 | 117 | the created return 118 | 119 | --- 120 | 121 | ### notifyStatusChange 122 | 123 | **Signature:** `notifyStatusChange(retrn : Return) : Status` 124 | 125 | **Description:** Called after method changeStatus(Return, ReturnWO) returns Status.OK (and after method afterStatusChange(Return)) to inform of a successful status change. The call is made outside any database transaction. This is the best hook in which to send customer notifications as the status change has already been successfully written to the database 126 | 127 | **Parameters:** 128 | 129 | - `retrn`: the return 130 | 131 | **Returns:** 132 | 133 | Status.OK status successful Status.ERROR on failure 134 | 135 | --- ``` -------------------------------------------------------------------------------- /docs/dw_customer/ProductListMgr.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.customer 2 | 3 | # Class ProductListMgr 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.customer.ProductListMgr 9 | 10 | ## Description 11 | 12 | ProductListMgr provides methods for retrieving, creating, searching for, and removing product lists. 13 | 14 | ## Constructor Summary 15 | 16 | ## Method Summary 17 | 18 | ### createProductList 19 | 20 | **Signature:** `static createProductList(customer : Customer, type : Number) : ProductList` 21 | 22 | Creates a new instance of a product list, of the specified type. 23 | 24 | ### getProductList 25 | 26 | **Signature:** `static getProductList(ID : String) : ProductList` 27 | 28 | Gets the product list by its ID. 29 | 30 | ### getProductList 31 | 32 | **Signature:** `static getProductList(profile : Profile, type : Number) : ProductList` 33 | 34 | Returns the first product list belonging to the customer with the specified profile. 35 | 36 | ### getProductLists 37 | 38 | **Signature:** `static getProductLists(customer : Customer, type : Number) : Collection` 39 | 40 | Retrieve all product lists of the specified type owned by the specified customer. 41 | 42 | ### getProductLists 43 | 44 | **Signature:** `static getProductLists(customer : Customer, type : Number, eventType : String) : Collection` 45 | 46 | Retrieve all the product lists of the specified type and event type belonging to the specified customer. 47 | 48 | ### getProductLists 49 | 50 | **Signature:** `static getProductLists(customerAddress : CustomerAddress) : Collection` 51 | 52 | Returns the collection of product lists that have the specified address as the shipping address. 53 | 54 | ### queryProductLists 55 | 56 | **Signature:** `static queryProductLists(queryAttributes : Map, sortString : String) : SeekableIterator` 57 | 58 | Searches for product list instances. 59 | 60 | ### queryProductLists 61 | 62 | **Signature:** `static queryProductLists(queryString : String, sortString : String, args : Object...) : SeekableIterator` 63 | 64 | Searches for product list instances. 65 | 66 | ### removeProductList 67 | 68 | **Signature:** `static removeProductList(productList : ProductList) : void` 69 | 70 | Removes the specified product list from the system. 71 | 72 | ## Method Detail 73 | 74 | ## Method Details 75 | 76 | ### createProductList 77 | 78 | **Signature:** `static createProductList(customer : Customer, type : Number) : ProductList` 79 | 80 | **Description:** Creates a new instance of a product list, of the specified type. 81 | 82 | **Parameters:** 83 | 84 | - `customer`: The customer owning the product list, must not be null. 85 | - `type`: The type of list (e.g. wish list, gift registry). The types are defined as constants within ProductList. 86 | 87 | **Returns:** 88 | 89 | the new list instance. 90 | 91 | --- 92 | 93 | ### getProductList 94 | 95 | **Signature:** `static getProductList(ID : String) : ProductList` 96 | 97 | **Description:** Gets the product list by its ID. 98 | 99 | **Parameters:** 100 | 101 | - `ID`: The product list ID. 102 | 103 | **Returns:** 104 | 105 | the ProductList instance, or null if a list with the specified UUID doesn't exist. 106 | 107 | --- 108 | 109 | ### getProductList 110 | 111 | **Signature:** `static getProductList(profile : Profile, type : Number) : ProductList` 112 | 113 | **Description:** Returns the first product list belonging to the customer with the specified profile. 114 | 115 | **Deprecated:** 116 | 117 | Use getProductLists(Customer, Number) or getProductLists(Customer, Number, String) instead. 118 | 119 | **Parameters:** 120 | 121 | - `profile`: The profile of the customer whose product list is to be retrieved. 122 | - `type`: The type of list (e.g. wish list, gift registry). The types are defined as constants within ProductList. 123 | 124 | **Returns:** 125 | 126 | the product list, or null if none exists. 127 | 128 | --- 129 | 130 | ### getProductLists 131 | 132 | **Signature:** `static getProductLists(customer : Customer, type : Number) : Collection` 133 | 134 | **Description:** Retrieve all product lists of the specified type owned by the specified customer. 135 | 136 | **Parameters:** 137 | 138 | - `customer`: The customer used for the query, must not be null. 139 | - `type`: The type of list used for the query. The types are defined as constants within ProductList. 140 | 141 | **Returns:** 142 | 143 | the unsorted collection of ProductList instances of the specified type belonging to the specified customer. 144 | 145 | --- 146 | 147 | ### getProductLists 148 | 149 | **Signature:** `static getProductLists(customer : Customer, type : Number, eventType : String) : Collection` 150 | 151 | **Description:** Retrieve all the product lists of the specified type and event type belonging to the specified customer. 152 | 153 | **Parameters:** 154 | 155 | - `customer`: The customer used for the query, must not be null. 156 | - `type`: The type of list used for the query. The types are defined as constants within ProductList. 157 | - `eventType`: The event type used for the query, must not be null. 158 | 159 | **Returns:** 160 | 161 | the unsorted collection of ProductList instances of the specified type and event type belonging to the specified customer. 162 | 163 | --- 164 | 165 | ### getProductLists 166 | 167 | **Signature:** `static getProductLists(customerAddress : CustomerAddress) : Collection` 168 | 169 | **Description:** Returns the collection of product lists that have the specified address as the shipping address. 170 | 171 | **Parameters:** 172 | 173 | - `customerAddress`: the address to test, must not be null. 174 | 175 | **Returns:** 176 | 177 | the unsorted collection of ProductList instances using this address. 178 | 179 | --- 180 | 181 | ### queryProductLists 182 | 183 | **Signature:** `static queryProductLists(queryAttributes : Map, sortString : String) : SeekableIterator` 184 | 185 | **Description:** Searches for product list instances. The search can be configured with a map, which key-value pairs are converted into a query expression. The key-value pairs are turned into a sequence of '=' or 'like' conditions, which are combined with AND statements. Example: A map with the key/value pairs: 'name'/'tom*', 'age'/66 will be converted as follows: "name like 'tom*' and age = 66" The identifier for an attribute to use in a query condition is always the ID of the attribute as defined in the type definition. For custom defined attributes the prefix custom is required in the search term (e.g. custom.color = {1}), while for system attributes no prefix is used (e.g. name = {4}). Supported attribute value types with sample expression values: String 'String', 'Str*', 'Strin?' Integer 1, 3E4 Number 1.0, 3.99E5 Date yyyy-MM-dd e.g. 2007-05-31 (Default TimeZone = UTC) DateTime yyyy-MM-dd'T'hh:mm:ss+Z e.g. 2007-05-31T00:00+Z (Z TimeZone = UTC) or 2007-05-31T00:00:00 Boolean true, false Email '[email protected]', '*@demandware.com' Set of String 'String', 'Str*', 'Strin?' Set of Integer 1, 3E4 Set of Number 1.0, 3.99E5 Enum of String 'String', 'Str*', 'Strin?' Enum of Integer 1, 3E4 The following types of attributes are not queryable: Image HTML Text Quantity Password Note, that some system attributes are not queryable by default regardless of the actual value type code. The sorting parameter is optional and may contain a comma separated list of attribute names to sort by. Each sort attribute name may be followed by an optional sort direction specifier ('asc' | 'desc'). Default sorting directions is ascending, if no direction was specified. Example: age desc, name Please note that specifying a localized custom attribute as the sorting attribute is currently not supported. It is strongly recommended to call close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. 186 | 187 | **Parameters:** 188 | 189 | - `queryAttributes`: key-value pairs, which define the query. 190 | - `sortString`: an optional sorting or null if no sorting is necessary. 191 | 192 | **Returns:** 193 | 194 | SeekableIterator containing the result set of the query. 195 | 196 | **See Also:** 197 | 198 | SeekableIterator.close() 199 | 200 | --- 201 | 202 | ### queryProductLists 203 | 204 | **Signature:** `static queryProductLists(queryString : String, sortString : String, args : Object...) : SeekableIterator` 205 | 206 | **Description:** Searches for product list instances. The search can be configured using a simple query language, which provides most common filter and operator functionality. The identifier for an attribute to use in a query condition is always the ID of the attribute as defined in the type definition. For custom defined attributes the prefix custom is required in the search term (e.g. custom.color = {1}), while for system attributes no prefix is used (e.g. name = {4}). Supported attribute value types with sample expression values: String 'String', 'Str*', 'Strin?' Integer 1, 3E4 Number 1.0, 3.99E5 Date yyyy-MM-dd e.g. 2007-05-31 (Default TimeZone = UTC) DateTime yyyy-MM-dd'T'hh:mm:ss+Z e.g. 2007-05-31T00:00+Z (Z TimeZone = UTC) or 2007-05-31T00:00:00 Boolean true, false Email '[email protected]', '*@demandware.com' Set of String 'String', 'Str*', 'Strin?' Set of Integer 1, 3E4 Set of Number 1.0, 3.99E5 Enum of String 'String', 'Str*', 'Strin?' Enum of Integer 1, 3E4 The following types of attributes are not queryable: Image HTML Text Quantity Password Note, that some system attributes are not queryable by default regardless of the actual value type code. The following operators are supported in a condition: = Equals - All types; supports NULL value (thumbnail = NULL) != Not equals - All types; supports NULL value (thumbnail != NULL) < Less than - Integer, Number and Date types only > Greater than - Integer, Number and Date types only <= Less or equals than - Integer, Number and Date types only >= Greater or equals than - Integer, Number and Date types only LIKE Like - String types and Email only; use if leading or trailing wildcards will be used to support substring search(custom.country LIKE 'US*') ILIKE Caseindependent Like - String types and Email only, use to support case insensitive query (custom.country ILIKE 'usa'), does also support wildcards for substring matching Conditions can be combined using logical expressions 'AND', 'OR' and 'NOT' and nested using parenthesis e.g. gender = {1} AND (age >= {2} OR (NOT profession LIKE {3})). The query language provides a placeholder syntax to pass objects as additional search parameters. Each passed object is related to a placeholder in the query string. The placeholder must be an Integer that is surrounded by braces. The first Integer value must be '0', the second '1' and so on, e.g. querySystemObjects("sample", "age = {0} or creationDate >= {1}", 18, date) The sorting parameter is optional and may contain a comma separated list of attribute names to sort by. Each sort attribute name may be followed by an optional sort direction specifier ('asc' | 'desc'). Default sorting directions is ascending, if no direction was specified. Example: age desc, name Please note that specifying a localized custom attribute as the sorting attribute is currently not supported. Sometimes it is desired to get all instances with a special sorting condition. This can be easily done by providing the 'sortString' in combination with an empty 'queryString', e.g. querySystemObjects("sample", "", "ID asc") It is strongly recommended to call close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. 207 | 208 | **Parameters:** 209 | 210 | - `queryString`: the actual query. 211 | - `sortString`: an optional sorting or null if no sorting is necessary. 212 | - `args`: optional parameters for the queryString. 213 | 214 | **Returns:** 215 | 216 | SeekableIterator containing the result set of the query. 217 | 218 | **See Also:** 219 | 220 | SeekableIterator.close() 221 | 222 | --- 223 | 224 | ### removeProductList 225 | 226 | **Signature:** `static removeProductList(productList : ProductList) : void` 227 | 228 | **Description:** Removes the specified product list from the system. 229 | 230 | **Parameters:** 231 | 232 | - `productList`: The list to remove, must not be null. 233 | 234 | --- ``` -------------------------------------------------------------------------------- /docs/dw_campaign/DiscountPlan.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.campaign 2 | 3 | # Class DiscountPlan 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.campaign.DiscountPlan 9 | 10 | ## Description 11 | 12 | DiscountPlan represents a set of Discounts. Instances of the class are returned by the PromotionMgr when requesting applicable discounts for a specified set of promotions and a line item container (see PromotionMgr.getDiscounts(LineItemCtnr, PromotionPlan)). DiscountPlan provides methods to access the discounts contained in the plan, add additional discounts to the plan (future) or remove discounts from the plan. 13 | 14 | ## Properties 15 | 16 | ### approachingOrderDiscounts 17 | 18 | **Type:** Collection (Read Only) 19 | 20 | Get the collection of order discounts that the LineItemCtnr "almost" 21 | qualifies for based on the merchandise total in the cart. Nearness is 22 | controlled by thresholds configured at the promotion level. 23 | 24 | The collection of returned approaching discounts is ordered by the 25 | condition threshold of the promotion (e.g. "spend $100 and get 10% off" 26 | discount is returned before "spend $150 and get 15% off" discount.) A 27 | discount is not returned if it would be prevented from applying (because 28 | of compatibility rules) by an order discount already in the DiscountPlan 29 | or an approaching order discount with a lower condition threshold. 30 | 31 | ### bonusDiscounts 32 | 33 | **Type:** Collection (Read Only) 34 | 35 | All bonus discounts contained in the discount plan. 36 | 37 | ### lineItemCtnr 38 | 39 | **Type:** LineItemCtnr (Read Only) 40 | 41 | Returns line item container associated with discount plan. 42 | A discount plan is created from and associated with a line item container. 43 | This method returns the line item container associated with this discount plan. 44 | 45 | ### orderDiscounts 46 | 47 | **Type:** Collection (Read Only) 48 | 49 | The percentage and amount order discounts contained in the 50 | discount plan. 51 | 52 | ## Constructor Summary 53 | 54 | ## Method Summary 55 | 56 | ### getApproachingOrderDiscounts 57 | 58 | **Signature:** `getApproachingOrderDiscounts() : Collection` 59 | 60 | Get the collection of order discounts that the LineItemCtnr "almost" qualifies for based on the merchandise total in the cart. 61 | 62 | ### getApproachingShippingDiscounts 63 | 64 | **Signature:** `getApproachingShippingDiscounts(shipment : Shipment) : Collection` 65 | 66 | Get the collection of shipping discounts that the passed shipment "almost" qualifies for based on the merchandise total in the shipment. 67 | 68 | ### getApproachingShippingDiscounts 69 | 70 | **Signature:** `getApproachingShippingDiscounts(shipment : Shipment, shippingMethod : ShippingMethod) : Collection` 71 | 72 | Get the collection of shipping discounts that the passed shipment "almost" qualifies for based on the merchandise total in the shipment. 73 | 74 | ### getApproachingShippingDiscounts 75 | 76 | **Signature:** `getApproachingShippingDiscounts(shipment : Shipment, shippingMethods : Collection) : Collection` 77 | 78 | Get the collection of shipping discounts that the passed shipment "almost" qualifies for based on the merchandise total in the shipment. 79 | 80 | ### getBonusDiscounts 81 | 82 | **Signature:** `getBonusDiscounts() : Collection` 83 | 84 | Returns all bonus discounts contained in the discount plan. 85 | 86 | ### getLineItemCtnr 87 | 88 | **Signature:** `getLineItemCtnr() : LineItemCtnr` 89 | 90 | Returns line item container associated with discount plan. 91 | 92 | ### getOrderDiscounts 93 | 94 | **Signature:** `getOrderDiscounts() : Collection` 95 | 96 | Returns the percentage and amount order discounts contained in the discount plan. 97 | 98 | ### getProductDiscounts 99 | 100 | **Signature:** `getProductDiscounts(productLineItem : ProductLineItem) : Collection` 101 | 102 | Returns the percentage, amount and fix price discounts associated with the specified product line item. 103 | 104 | ### getProductShippingDiscounts 105 | 106 | **Signature:** `getProductShippingDiscounts(productLineItem : ProductLineItem) : Collection` 107 | 108 | Returns the product-shipping discounts associated with the specified product line item. 109 | 110 | ### getShippingDiscounts 111 | 112 | **Signature:** `getShippingDiscounts(shipment : Shipment) : Collection` 113 | 114 | Returns the percentage, amount and fix price discounts associated with the specified shipment. 115 | 116 | ### removeDiscount 117 | 118 | **Signature:** `removeDiscount(discount : Discount) : void` 119 | 120 | Removes the specified discount from the discount plan. 121 | 122 | ## Method Detail 123 | 124 | ## Method Details 125 | 126 | ### getApproachingOrderDiscounts 127 | 128 | **Signature:** `getApproachingOrderDiscounts() : Collection` 129 | 130 | **Description:** Get the collection of order discounts that the LineItemCtnr "almost" qualifies for based on the merchandise total in the cart. Nearness is controlled by thresholds configured at the promotion level. The collection of returned approaching discounts is ordered by the condition threshold of the promotion (e.g. "spend $100 and get 10% off" discount is returned before "spend $150 and get 15% off" discount.) A discount is not returned if it would be prevented from applying (because of compatibility rules) by an order discount already in the DiscountPlan or an approaching order discount with a lower condition threshold. 131 | 132 | **Returns:** 133 | 134 | Collection of approaching order discounts ordered by the condition threshold of the promotion ascending. 135 | 136 | --- 137 | 138 | ### getApproachingShippingDiscounts 139 | 140 | **Signature:** `getApproachingShippingDiscounts(shipment : Shipment) : Collection` 141 | 142 | **Description:** Get the collection of shipping discounts that the passed shipment "almost" qualifies for based on the merchandise total in the shipment. Nearness is controlled by thresholds configured at the promotion level. The collection of returned approaching discounts is ordered by the condition threshold of the promotion (e.g. "spend $100 and get free standard shipping" discount is returned before "spend $150 and get free standard shipping" discount.) A discount is not returned if it would be prevented from applying (because of compatibility rules) by a shipping discount already in the DiscountPlan or an approaching shipping discount with a lower condition threshold. 143 | 144 | **Parameters:** 145 | 146 | - `shipment`: The shipment to calculate approaching discounts for. 147 | 148 | **Returns:** 149 | 150 | Collection of approaching shipping discounts ordered by the condition threshold of the promotion ascending. 151 | 152 | --- 153 | 154 | ### getApproachingShippingDiscounts 155 | 156 | **Signature:** `getApproachingShippingDiscounts(shipment : Shipment, shippingMethod : ShippingMethod) : Collection` 157 | 158 | **Description:** Get the collection of shipping discounts that the passed shipment "almost" qualifies for based on the merchandise total in the shipment. Here "almost" is controlled by thresholds configured at the promotion level. This method only returns discounts for shipping promotions which apply to the passed shipping method. The collection of returned approaching discounts is ordered by the condition threshold of the promotion (e.g. "spend $100 and get free standard shipping" discount is returned before "spend $150 and get free standard shipping" discount.) A discount is not returned if it would be prevented from applying (because of compatibility rules) by a shipping discount already in the DiscountPlan or an approaching shipping discount with a lower condition threshold. 159 | 160 | **Parameters:** 161 | 162 | - `shipment`: The shipment to calculate approaching discounts for. 163 | - `shippingMethod`: The shipping method to filter by. 164 | 165 | **Returns:** 166 | 167 | Collection of approaching shipping discounts ordered by the condition threshold of the promotion, ascending. 168 | 169 | --- 170 | 171 | ### getApproachingShippingDiscounts 172 | 173 | **Signature:** `getApproachingShippingDiscounts(shipment : Shipment, shippingMethods : Collection) : Collection` 174 | 175 | **Description:** Get the collection of shipping discounts that the passed shipment "almost" qualifies for based on the merchandise total in the shipment. Here "almost" is controlled by thresholds configured at the promotion level. This method only returns discounts for shipping promotions which apply to one of the passed shipping methods. The collection of returned approaching discounts is ordered by the condition threshold of the promotion (e.g. "spend $100 and get free standard shipping" discount is returned before "spend $150 and get free standard shipping" discount.) A discount is not returned if it would be prevented from applying (because of compatibility rules) by a shipping discount already in the DiscountPlan or an approaching shipping discount with a lower condition threshold. 176 | 177 | **Parameters:** 178 | 179 | - `shipment`: The shipment to calculate approaching discounts for. 180 | - `shippingMethods`: The shipping methods to filter by. 181 | 182 | **Returns:** 183 | 184 | Collection of approaching shipping discounts ordered by the condition threshold of the promotion ascending. 185 | 186 | --- 187 | 188 | ### getBonusDiscounts 189 | 190 | **Signature:** `getBonusDiscounts() : Collection` 191 | 192 | **Description:** Returns all bonus discounts contained in the discount plan. 193 | 194 | **Returns:** 195 | 196 | All bonus discounts contained in discount plan 197 | 198 | --- 199 | 200 | ### getLineItemCtnr 201 | 202 | **Signature:** `getLineItemCtnr() : LineItemCtnr` 203 | 204 | **Description:** Returns line item container associated with discount plan. A discount plan is created from and associated with a line item container. This method returns the line item container associated with this discount plan. 205 | 206 | **Returns:** 207 | 208 | Line item container associated with plan 209 | 210 | --- 211 | 212 | ### getOrderDiscounts 213 | 214 | **Signature:** `getOrderDiscounts() : Collection` 215 | 216 | **Description:** Returns the percentage and amount order discounts contained in the discount plan. 217 | 218 | **Returns:** 219 | 220 | Order discounts contained in the discount plan 221 | 222 | --- 223 | 224 | ### getProductDiscounts 225 | 226 | **Signature:** `getProductDiscounts(productLineItem : ProductLineItem) : Collection` 227 | 228 | **Description:** Returns the percentage, amount and fix price discounts associated with the specified product line item. If the specified product line item is not contained in the discount plan, an empty collection is returned. 229 | 230 | **Parameters:** 231 | 232 | - `productLineItem`: Product line item 233 | 234 | **Returns:** 235 | 236 | Discounts associated with specified product line item 237 | 238 | --- 239 | 240 | ### getProductShippingDiscounts 241 | 242 | **Signature:** `getProductShippingDiscounts(productLineItem : ProductLineItem) : Collection` 243 | 244 | **Description:** Returns the product-shipping discounts associated with the specified product line item. If the specified product line item is not contained in the discount plan, an empty collection is returned. 245 | 246 | **Parameters:** 247 | 248 | - `productLineItem`: Product line item 249 | 250 | **Returns:** 251 | 252 | Product-shipping discounts associated with specified product line item 253 | 254 | --- 255 | 256 | ### getShippingDiscounts 257 | 258 | **Signature:** `getShippingDiscounts(shipment : Shipment) : Collection` 259 | 260 | **Description:** Returns the percentage, amount and fix price discounts associated with the specified shipment. If the specified shipment is not contained in the discount plan, an empty collection is returned. 261 | 262 | **Parameters:** 263 | 264 | - `shipment`: the shipment for which to fetch discounts. 265 | 266 | **Returns:** 267 | 268 | Discounts associated with specified shipment 269 | 270 | --- 271 | 272 | ### removeDiscount 273 | 274 | **Signature:** `removeDiscount(discount : Discount) : void` 275 | 276 | **Description:** Removes the specified discount from the discount plan. This method should only be used for very simple discount scenarios. It is not recommended to use the method in case of stacked discounts (i.e. multiple order or product discounts), or complex discount types like Buy X Get Y or Total Fixed Price, since correct re-calculation of the discount plan based on the promotion rules is not guaranteed. 277 | 278 | **Parameters:** 279 | 280 | - `discount`: Discount to be removed 281 | 282 | --- ``` -------------------------------------------------------------------------------- /docs/dw_customer/ProductListItem.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.customer 2 | 3 | # Class ProductListItem 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.customer.ProductListItem 11 | 12 | ## Description 13 | 14 | An item in a product list. Types of items are: An item that references a product via the product's SKU. An item that represents a gift certificate. 15 | 16 | ## Constants 17 | 18 | ### TYPE_GIFT_CERTIFICATE 19 | 20 | **Type:** Number = 2 21 | 22 | Constant representing a gift certificate list item type. 23 | 24 | ### TYPE_PRODUCT 25 | 26 | **Type:** Number = 1 27 | 28 | Constant representing a product list item type. 29 | 30 | ## Properties 31 | 32 | ### ID 33 | 34 | **Type:** String (Read Only) 35 | 36 | The unique system generated ID of the object. 37 | 38 | ### list 39 | 40 | **Type:** ProductList (Read Only) 41 | 42 | The product list that this item belongs to. 43 | 44 | ### priority 45 | 46 | **Type:** Number 47 | 48 | Specify the priority level for the item. Typically the lower the 49 | number, the higher the priority. This can be used by the owner of the product list 50 | to express which items he/she likes to get purchased first. 51 | 52 | ### product 53 | 54 | **Type:** Product 55 | 56 | The referenced product for this item. The reference is made 57 | via the product ID attribute. This method returns null if there is 58 | no such product in the system or if the product exists but is not 59 | assigned to the site catalog. 60 | 61 | ### productID 62 | 63 | **Type:** String (Read Only) 64 | 65 | The ID of the product referenced by this item. 66 | This attribute is set when a product is assigned via setProduct(). 67 | It is possible for the ID to reference a product that doesn't exist 68 | anymore. In this case getProduct() would return null. 69 | 70 | ### productOptionModel 71 | 72 | **Type:** ProductOptionModel 73 | 74 | The ProductOptionModel for the product associated with this item, 75 | or null if there is no valid product associated with this item. 76 | 77 | ### public 78 | 79 | **Type:** boolean 80 | 81 | A flag, typically used to determine whether the item should display 82 | in a customer's view of the list (as opposed to the list owner's view). 83 | 84 | ### purchasedQuantity 85 | 86 | **Type:** Quantity (Read Only) 87 | 88 | The sum of the quantities of all the individual purchase records 89 | for this item. 90 | 91 | ### purchasedQuantityValue 92 | 93 | **Type:** Number (Read Only) 94 | 95 | The value part of the underlying purchased quantity object, as distinct 96 | from the unit. 97 | 98 | ### purchases 99 | 100 | **Type:** Collection (Read Only) 101 | 102 | All purchases made for this item. 103 | 104 | ### quantity 105 | 106 | **Type:** Quantity 107 | 108 | The quantity of the item. 109 | The quantity is the number of products or gift certificates 110 | that get shipped when purchasing this product list item. 111 | 112 | ### quantityValue 113 | 114 | **Type:** Number 115 | 116 | The value part of the underlying quantity object, as distinct 117 | from the unit. 118 | 119 | ### type 120 | 121 | **Type:** Number (Read Only) 122 | 123 | The type of this product list item. 124 | 125 | ## Constructor Summary 126 | 127 | ## Method Summary 128 | 129 | ### createPurchase 130 | 131 | **Signature:** `createPurchase(quantity : Number, purchaserName : String) : ProductListItemPurchase` 132 | 133 | Create a purchase record for this item. 134 | 135 | ### getID 136 | 137 | **Signature:** `getID() : String` 138 | 139 | Returns the unique system generated ID of the object. 140 | 141 | ### getList 142 | 143 | **Signature:** `getList() : ProductList` 144 | 145 | Returns the product list that this item belongs to. 146 | 147 | ### getPriority 148 | 149 | **Signature:** `getPriority() : Number` 150 | 151 | Specify the priority level for the item. 152 | 153 | ### getProduct 154 | 155 | **Signature:** `getProduct() : Product` 156 | 157 | Returns the referenced product for this item. 158 | 159 | ### getProductID 160 | 161 | **Signature:** `getProductID() : String` 162 | 163 | Returns the ID of the product referenced by this item. 164 | 165 | ### getProductOptionModel 166 | 167 | **Signature:** `getProductOptionModel() : ProductOptionModel` 168 | 169 | Returns the ProductOptionModel for the product associated with this item, or null if there is no valid product associated with this item. 170 | 171 | ### getPurchasedQuantity 172 | 173 | **Signature:** `getPurchasedQuantity() : Quantity` 174 | 175 | Returns the sum of the quantities of all the individual purchase records for this item. 176 | 177 | ### getPurchasedQuantityValue 178 | 179 | **Signature:** `getPurchasedQuantityValue() : Number` 180 | 181 | Returns the value part of the underlying purchased quantity object, as distinct from the unit. 182 | 183 | ### getPurchases 184 | 185 | **Signature:** `getPurchases() : Collection` 186 | 187 | Returns all purchases made for this item. 188 | 189 | ### getQuantity 190 | 191 | **Signature:** `getQuantity() : Quantity` 192 | 193 | Returns the quantity of the item. 194 | 195 | ### getQuantityValue 196 | 197 | **Signature:** `getQuantityValue() : Number` 198 | 199 | Returns the value part of the underlying quantity object, as distinct from the unit. 200 | 201 | ### getType 202 | 203 | **Signature:** `getType() : Number` 204 | 205 | Returns the type of this product list item. 206 | 207 | ### isPublic 208 | 209 | **Signature:** `isPublic() : boolean` 210 | 211 | A flag, typically used to determine whether the item should display in a customer's view of the list (as opposed to the list owner's view). 212 | 213 | ### setPriority 214 | 215 | **Signature:** `setPriority(priority : Number) : void` 216 | 217 | Specify the priority level for the item. 218 | 219 | ### setProduct 220 | 221 | **Signature:** `setProduct(product : Product) : void` 222 | 223 | Sets the referenced product for this item by storing the product's id. 224 | 225 | ### setProductOptionModel 226 | 227 | **Signature:** `setProductOptionModel(productOptionModel : ProductOptionModel) : void` 228 | 229 | Store a product option model with this object. 230 | 231 | ### setPublic 232 | 233 | **Signature:** `setPublic(flag : boolean) : void` 234 | 235 | Typically used to determine if the item is visible to other customers. 236 | 237 | ### setQuantity 238 | 239 | **Signature:** `setQuantity(value : Quantity) : void` 240 | 241 | Sets the quantity of the item. 242 | 243 | ### setQuantityValue 244 | 245 | **Signature:** `setQuantityValue(value : Number) : void` 246 | 247 | Set the value part of the underlying quantity object, as distinct from the unit. 248 | 249 | ## Method Detail 250 | 251 | ## Method Details 252 | 253 | ### createPurchase 254 | 255 | **Signature:** `createPurchase(quantity : Number, purchaserName : String) : ProductListItemPurchase` 256 | 257 | **Description:** Create a purchase record for this item. 258 | 259 | **Parameters:** 260 | 261 | - `quantity`: The number of items purchased. 262 | - `purchaserName`: The name of the purchaser. 263 | 264 | **Returns:** 265 | 266 | the purchase record. 267 | 268 | --- 269 | 270 | ### getID 271 | 272 | **Signature:** `getID() : String` 273 | 274 | **Description:** Returns the unique system generated ID of the object. 275 | 276 | **Returns:** 277 | 278 | the ID of object. 279 | 280 | --- 281 | 282 | ### getList 283 | 284 | **Signature:** `getList() : ProductList` 285 | 286 | **Description:** Returns the product list that this item belongs to. 287 | 288 | **Returns:** 289 | 290 | the list. 291 | 292 | --- 293 | 294 | ### getPriority 295 | 296 | **Signature:** `getPriority() : Number` 297 | 298 | **Description:** Specify the priority level for the item. Typically the lower the number, the higher the priority. This can be used by the owner of the product list to express which items he/she likes to get purchased first. 299 | 300 | **Returns:** 301 | 302 | the specified priority level. 303 | 304 | --- 305 | 306 | ### getProduct 307 | 308 | **Signature:** `getProduct() : Product` 309 | 310 | **Description:** Returns the referenced product for this item. The reference is made via the product ID attribute. This method returns null if there is no such product in the system or if the product exists but is not assigned to the site catalog. 311 | 312 | **Returns:** 313 | 314 | the product referenced by this item, or null. 315 | 316 | --- 317 | 318 | ### getProductID 319 | 320 | **Signature:** `getProductID() : String` 321 | 322 | **Description:** Returns the ID of the product referenced by this item. This attribute is set when a product is assigned via setProduct(). It is possible for the ID to reference a product that doesn't exist anymore. In this case getProduct() would return null. 323 | 324 | **Returns:** 325 | 326 | the product ID, or null if none exists. 327 | 328 | --- 329 | 330 | ### getProductOptionModel 331 | 332 | **Signature:** `getProductOptionModel() : ProductOptionModel` 333 | 334 | **Description:** Returns the ProductOptionModel for the product associated with this item, or null if there is no valid product associated with this item. 335 | 336 | **Returns:** 337 | 338 | the associated ProductOptionModel or null. 339 | 340 | --- 341 | 342 | ### getPurchasedQuantity 343 | 344 | **Signature:** `getPurchasedQuantity() : Quantity` 345 | 346 | **Description:** Returns the sum of the quantities of all the individual purchase records for this item. 347 | 348 | **Returns:** 349 | 350 | the sum of the quantities of all the individual purchase records for this item. 351 | 352 | --- 353 | 354 | ### getPurchasedQuantityValue 355 | 356 | **Signature:** `getPurchasedQuantityValue() : Number` 357 | 358 | **Description:** Returns the value part of the underlying purchased quantity object, as distinct from the unit. 359 | 360 | **Returns:** 361 | 362 | the value part of the underlying purchased quantity object, as distinct from the unit. 363 | 364 | --- 365 | 366 | ### getPurchases 367 | 368 | **Signature:** `getPurchases() : Collection` 369 | 370 | **Description:** Returns all purchases made for this item. 371 | 372 | **Returns:** 373 | 374 | the collection of purchase records for this item. Returns an empty list if this item has not been purchased yet. 375 | 376 | --- 377 | 378 | ### getQuantity 379 | 380 | **Signature:** `getQuantity() : Quantity` 381 | 382 | **Description:** Returns the quantity of the item. The quantity is the number of products or gift certificates that get shipped when purchasing this product list item. 383 | 384 | **Returns:** 385 | 386 | the quantity of the item. 387 | 388 | --- 389 | 390 | ### getQuantityValue 391 | 392 | **Signature:** `getQuantityValue() : Number` 393 | 394 | **Description:** Returns the value part of the underlying quantity object, as distinct from the unit. 395 | 396 | **Returns:** 397 | 398 | the value part of the underlying quantity object, as distinct from the unit. 399 | 400 | --- 401 | 402 | ### getType 403 | 404 | **Signature:** `getType() : Number` 405 | 406 | **Description:** Returns the type of this product list item. 407 | 408 | **Returns:** 409 | 410 | a code that specifies the type of item (i.e. product or gift certificate). 411 | 412 | --- 413 | 414 | ### isPublic 415 | 416 | **Signature:** `isPublic() : boolean` 417 | 418 | **Description:** A flag, typically used to determine whether the item should display in a customer's view of the list (as opposed to the list owner's view). 419 | 420 | **Returns:** 421 | 422 | true if the item is public. 423 | 424 | --- 425 | 426 | ### setPriority 427 | 428 | **Signature:** `setPriority(priority : Number) : void` 429 | 430 | **Description:** Specify the priority level for the item. Typically the lower the number, the higher the priority. This can be used by the owner of the product list to express which items he/she likes to get purchased first. 431 | 432 | **Parameters:** 433 | 434 | - `priority`: The new priority level. 435 | 436 | --- 437 | 438 | ### setProduct 439 | 440 | **Signature:** `setProduct(product : Product) : void` 441 | 442 | **Description:** Sets the referenced product for this item by storing the product's id. If null is specified, then the id is set to null. 443 | 444 | **Deprecated:** 445 | 446 | Use ProductList.createProductItem(Product) instead. 447 | 448 | **Parameters:** 449 | 450 | - `product`: The referenced product for this item. 451 | 452 | --- 453 | 454 | ### setProductOptionModel 455 | 456 | **Signature:** `setProductOptionModel(productOptionModel : ProductOptionModel) : void` 457 | 458 | **Description:** Store a product option model with this object. This stores a copy of the specified model, rather than an assocation to the same instance. 459 | 460 | **Parameters:** 461 | 462 | - `productOptionModel`: The object to store. 463 | 464 | --- 465 | 466 | ### setPublic 467 | 468 | **Signature:** `setPublic(flag : boolean) : void` 469 | 470 | **Description:** Typically used to determine if the item is visible to other customers. 471 | 472 | **Parameters:** 473 | 474 | - `flag`: If true, this product list becomes visible to other customers. If false, this product list can only be seen by the owner of the product list. 475 | 476 | --- 477 | 478 | ### setQuantity 479 | 480 | **Signature:** `setQuantity(value : Quantity) : void` 481 | 482 | **Description:** Sets the quantity of the item. 483 | 484 | **Deprecated:** 485 | 486 | Use setQuantityValue(Number) instead. 487 | 488 | **Parameters:** 489 | 490 | - `value`: the new quantity of the item. 491 | 492 | --- 493 | 494 | ### setQuantityValue 495 | 496 | **Signature:** `setQuantityValue(value : Number) : void` 497 | 498 | **Description:** Set the value part of the underlying quantity object, as distinct from the unit. 499 | 500 | **Parameters:** 501 | 502 | - `value`: the value to use. 503 | 504 | --- ``` -------------------------------------------------------------------------------- /docs/dw_order.hooks/PaymentHooks.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.order.hooks 2 | 3 | # Class PaymentHooks 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - dw.order.hooks.PaymentHooks 8 | 9 | ## Description 10 | 11 | This interface represents all script hooks that can be registered to customize the order center payment functionality. It contains the extension points (hook names), and the functions that are called by each extension point. A function must be defined inside a JavaScript source and must be exported. The script with the exported hook function must be located inside a site cartridge. Inside the site cartridge a 'package.json' file with a 'hooks' entry must exist. "hooks": "./hooks.json" The hooks entry links to a json file, relative to the 'package.json' file. This file lists all registered hooks inside the hooks property: "hooks": [ {"name": "dw.order.payment.authorize", "script": "./authorize.js"}, {"name": "dw.order.payment.validateAuthorization", "script": "./validateAuthorization.js"}, ] A hook entry has a 'name' and a 'script' property. The 'name' contains the extension point, the hook name. The 'script' contains the script relative to the hooks file, with the exported hook function. 12 | 13 | ## Constants 14 | 15 | ## Properties 16 | 17 | ## Constructor Summary 18 | 19 | ## Method Summary 20 | 21 | ### authorize 22 | 23 | **Signature:** `authorize(order : Order, paymentDetails : OrderPaymentInstrument) : Status` 24 | 25 | The function is called by extension point extensionPointAuthorize. 26 | 27 | ### authorizeCreditCard 28 | 29 | **Signature:** `authorizeCreditCard(order : Order, paymentDetails : OrderPaymentInstrument, cvn : String) : Status` 30 | 31 | The function is called by extension point extensionPointAuthorizeCreditCard. 32 | 33 | ### capture 34 | 35 | **Signature:** `capture(invoice : Invoice) : Status` 36 | 37 | The function is called by extension point extensionPointCapture. 38 | 39 | ### reauthorize 40 | 41 | **Signature:** `reauthorize(order : Order) : Status` 42 | 43 | The function is called by extension point extensionPointReauthorize. 44 | 45 | ### refund 46 | 47 | **Signature:** `refund(invoice : Invoice) : Status` 48 | 49 | The function is called by extension point extensionPointRefund. 50 | 51 | ### releaseAuthorization 52 | 53 | **Signature:** `releaseAuthorization(order : Order) : Status` 54 | 55 | The function is called by extension point extensionPointReleaseAuthorization. 56 | 57 | ### validateAuthorization 58 | 59 | **Signature:** `validateAuthorization(order : Order) : Status` 60 | 61 | The function is called by extension point extensionPointValidateAuthorization. 62 | 63 | ## Method Detail 64 | 65 | ## Method Details 66 | 67 | ### authorize 68 | 69 | **Signature:** `authorize(order : Order, paymentDetails : OrderPaymentInstrument) : Status` 70 | 71 | **Description:** The function is called by extension point extensionPointAuthorize. Custom payment authorization - modify the order as needed. Prerequisite: An order has been created using the data api or via the storefront. Return Status.OK: Corresponding payment transaction is marked as authorized (usually a custom property is used for this). Return Status.ERROR: Order is held, authorization needs to be repeated. 72 | 73 | **Parameters:** 74 | 75 | - `order`: the order 76 | - `paymentDetails`: specified payment details 77 | 78 | **Returns:** 79 | 80 | Status.OK successful authorization. Status.ERROR authorization failed. 81 | 82 | --- 83 | 84 | ### authorizeCreditCard 85 | 86 | **Signature:** `authorizeCreditCard(order : Order, paymentDetails : OrderPaymentInstrument, cvn : String) : Status` 87 | 88 | **Description:** The function is called by extension point extensionPointAuthorizeCreditCard. Custom payment authorization of a credit card - modify the order as needed. Prerequisite: An order has been created using the data api or via the storefront. Return Status.OK: Corresponding payment transaction is marked as authorized (usually a custom property is used for this). Return Status.ERROR: Order is held, authorization needs to be repeated. 89 | 90 | **Parameters:** 91 | 92 | - `order`: the order 93 | - `paymentDetails`: specified payment details 94 | - `cvn`: the credit card verification number 95 | 96 | **Returns:** 97 | 98 | Status.OK successful authorization. Status.ERROR authorization failed. 99 | 100 | --- 101 | 102 | ### capture 103 | 104 | **Signature:** `capture(invoice : Invoice) : Status` 105 | 106 | **Description:** The function is called by extension point extensionPointCapture. Custom payment capture - modify the order as needed. Prerequisite: [ either ] As a result of shipping (or part-shipping) a shipping -order the warehouse updates the status of the shipping-order resulting in the creation of an unpaid debit invoice (the creation of the invoice is usually handled in ShippingOrderHooks.changeStatus(ShippingOrder, ShippingOrderWO)). [ or ] A unpaid debit invoice has been created using the data api. Context: An unpaid debit invoice is passed to the payment system for capture. The capture attempt can either succeed (complete invoice amount captured) or fail. As a result the invoice status is updated by ordercenter for further processing. See Invoice. Hook responsibility: The hook should attempt to capture the amount located in invoice.grandTotal.grossPrice. When successful, the capture hook should also update the invoice by calling Invoice.addCaptureTransaction(OrderPaymentInstrument, Money) which serves to record the capturedAmount and associate the invoice with the payment-transaction. Return Status.OK: Indicates capture succeeded: Order Center sets the Invoice status to Invoice.STATUS_PAID. Return Status.ERROR: Indicates capture failed: Order Center sets the Invoice status to Invoice.STATUS_FAILED for further processing. Post processing: When the capture hook returns with success, order center not only sets the relevant invoice status, but also sets the relevant capturedAmount values on the invoice item. Returning success means the entire invoice total has been captured, so each item within the invoice can also be marked as completely captured. Note the script implementing the hook can take responsibility for this if desired order center will not overwrite existing values, but normally the standard implementation fits. As a result each invoice item and the related order item can return a capturedAmount, useful for calculating possible refunds. 107 | 108 | **Parameters:** 109 | 110 | - `invoice`: the invoice 111 | 112 | **Returns:** 113 | 114 | Status.OK for successful capture of entire invoice amount. Status.ERROR capture failed 115 | 116 | --- 117 | 118 | ### reauthorize 119 | 120 | **Signature:** `reauthorize(order : Order) : Status` 121 | 122 | **Description:** The function is called by extension point extensionPointReauthorize. Custom payment authorization - modify the order as needed. Prerequisite: [ either ] Based on a selected Order, a ShippingOrder (which represents the whole or part of the order which can be shipped) is to be created ready for export to the warehouse system. [ or ] A ShippingOrder is to be directly created using the data api. Context: The related order is passed to the payment hook to check its authorization has not become invalid. Two hooks are called: a. validateAuthorization(Order) is used to check the orders authorization is still valid b. reauthorize(Order) is called if step a. returns Error Return Status.OK: Corresponding payment transaction is marked as authorized (usually a custom property is used for this). If the order had been previously authorized, the custom property values may be overwritten during reauthorization. Return Status.ERROR: Order is held, authorization needs to be repeated. 123 | 124 | **Parameters:** 125 | 126 | - `order`: the order 127 | 128 | **Returns:** 129 | 130 | Status.OK successful authorization. Status.ERROR authorization failed 131 | 132 | --- 133 | 134 | ### refund 135 | 136 | **Signature:** `refund(invoice : Invoice) : Status` 137 | 138 | **Description:** The function is called by extension point extensionPointRefund. Custom payment refund - modify the order as needed. Prerequisite: [ either ] Goods returned by the customer result in the creation of one or more return documents, resulting in the creation of an unpaid customer credit invoice (the creation of the invoice is usually handled in ReturnHooks.changeStatus(Return, ReturnWO)). [ or ] An unpaid customer credit invoice is created using the data api (perhaps as a result of the creation of a customer appeasement). Context: An unpaid credit invoice is passed to the payment system for refund. The refund attempt can either succeed (complete invoice amount refunded) or fail. As a result the invoice status is updated by ordercenter for further processing. See Invoice. Hook responsibility: The hook should attempt to refund the amount located in invoice.grandTotal.grossPrice. When successful, the refund hook should also update the invoice by calling Invoice.addRefundTransaction(OrderPaymentInstrument, Money) which serves to record the refundedAmount and associate the invoice with the payment-transaction. Return Status.OK: Indicates refund succeeded: Order Center sets the Invoice status to Invoice.STATUS_PAID. Return Status.ERROR: Indicates refund failed: Order Center sets the Invoice status to Invoice.STATUS_FAILED for further processing. Post processing: When the refund hook returns with success, order center not only sets the relevant invoice status, but also sets the relevant refundAmount values on the invoice item. Returning success means the entire invoice total has been refunded, so each item within the invoice can also be marked as completely refunded. Note the script implementing the hook can take responsibility for this if desired order center will not overwrite existing values, but normally the standard implementation fits. As a result each invoice item and the related order item can return a refundedAmount, useful for calculating further possible refunds. 139 | 140 | **Parameters:** 141 | 142 | - `invoice`: the invoice 143 | 144 | **Returns:** 145 | 146 | Status.OK for successful refund of entire invoice amount. Status.ERROR refund failed 147 | 148 | --- 149 | 150 | ### releaseAuthorization 151 | 152 | **Signature:** `releaseAuthorization(order : Order) : Status` 153 | 154 | **Description:** The function is called by extension point extensionPointReleaseAuthorization. Custom payment release authorization - modify the order as needed. Prerequisite: an authorized order is updated resulting in a need to release the remaining authorization. This happens when: - order is cancelled - order is complete after remaining order items are cancelled. Return Status.OK - successful release authorization Return Status.ERROR - failed release authorization 155 | 156 | **Parameters:** 157 | 158 | - `order`: the order 159 | 160 | **Returns:** 161 | 162 | Status.OK for successful release authorization. Status.ERROR failed release authorization 163 | 164 | --- 165 | 166 | ### validateAuthorization 167 | 168 | **Signature:** `validateAuthorization(order : Order) : Status` 169 | 170 | **Description:** The function is called by extension point extensionPointValidateAuthorization. Custom payment authorization - modify the order as needed. Context: This hook is called to validate whether a payment authorization exists for the order. It should usually check: - Whether the authorize or reauthorize hook was previously successfully executed for the order, e.g. by checking whether custom property has been previously set. - Whether an existing authorization has expired e.g. by comparing a timestamp set on authorization with the current time. Return Status.OK: indicates the order has a valid payment authorization. Return Status.ERROR: indicates reauthorize(Order) should be called. See reauthorize(Order) for more details. 171 | 172 | **Parameters:** 173 | 174 | - `order`: the order 175 | 176 | **Returns:** 177 | 178 | Status.OK order has a valid payment authorization. Status.ERROR order has no valid payment authorization, reauthorize(Order) should be called 179 | 180 | --- ``` -------------------------------------------------------------------------------- /docs-site/pages/ToolsPage.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | import SEO from '../components/SEO'; 4 | import BreadcrumbSchema from '../components/BreadcrumbSchema'; 5 | import StructuredData from '../components/StructuredData'; 6 | import { H1, PageSubtitle } from '../components/Typography'; 7 | import ToolFilters from '../components/ToolFilters'; 8 | import ToolCard from '../components/ToolCard'; 9 | import { tools, popularTools } from '../utils/toolsData'; 10 | import { SITE_DATES } from '../constants'; 11 | 12 | const ToolsPage: React.FC = () => { 13 | const [activeCategory, setActiveCategory] = React.useState('All'); 14 | const [search, setSearch] = React.useState(''); 15 | const [showPopularExpanded, setShowPopularExpanded] = React.useState(true); 16 | 17 | const filtered = tools.filter(t => { 18 | const inCat = activeCategory === 'All' || t.category === activeCategory; 19 | if (!inCat) return false; 20 | if (!search) return true; 21 | const q = search.toLowerCase(); 22 | return ( 23 | t.name.toLowerCase().includes(q) || 24 | t.description.toLowerCase().includes(q) || 25 | t.examples?.some(e => e.toLowerCase().includes(q)) || 26 | t.params?.some(p => p.name.toLowerCase().includes(q)) || 27 | t.tags?.some(tag => tag.toLowerCase().includes(q)) 28 | ); 29 | }); 30 | 31 | const toolsStructuredData = { 32 | "@context": "https://schema.org", 33 | "@type": "TechArticle", 34 | "headline": "Available Tools & APIs - SFCC Development MCP Server", 35 | "description": "Interactive reference of SFCC Development MCP Server tools with filtering, search, examples, and quick start actions.", 36 | "author": { 37 | "@type": "Person", 38 | "name": "Thomas Theunen" 39 | }, 40 | "publisher": { 41 | "@type": "Person", 42 | "name": "Thomas Theunen" 43 | }, 44 | "datePublished": SITE_DATES.PUBLISHED, 45 | "dateModified": SITE_DATES.MODIFIED, 46 | "url": "https://sfcc-mcp-dev.rhino-inquisitor.com/tools/", 47 | "mainEntity": { 48 | "@type": "SoftwareApplication", 49 | "name": "SFCC Development MCP Server", 50 | "applicationCategory": "DeveloperApplication", 51 | "operatingSystem": "Node.js", 52 | "description": "Interactive API reference and tools catalog", 53 | "offers": { 54 | "@type": "Offer", 55 | "price": "0", 56 | "priceCurrency": "USD", 57 | "availability": "https://schema.org/InStock" 58 | } 59 | } 60 | }; 61 | 62 | return ( 63 | <div className="max-w-7xl mx-auto px-6 py-10"> 64 | <SEO 65 | title="Available Tools & APIs" 66 | description="Interactive reference of SFCC Development MCP Server tools with filtering, search, examples, and quick start actions." 67 | keywords="SFCC MCP tools, Commerce Cloud APIs, log analysis, system objects, cartridge generation, best practices" 68 | canonical="/tools/" 69 | ogType="article" 70 | /> 71 | <BreadcrumbSchema items={[ 72 | { name: "Home", url: "/" }, 73 | { name: "Tools", url: "/tools/" } 74 | ]} /> 75 | <StructuredData data={toolsStructuredData} /> 76 | 77 | {/* Hero */} 78 | <div className="text-center mb-14"> 79 | <div className="inline-flex items-center gap-2 bg-gradient-to-r from-blue-600 to-purple-600 text-white px-4 py-2 rounded-full text-sm font-medium mb-6"> 80 | <span>🛠️</span> Available Tools 81 | </div> 82 | <H1 id="available-tools" className="text-4xl md:text-5xl font-extrabold bg-gradient-to-r from-gray-900 via-blue-900 to-purple-900 bg-clip-text text-transparent mb-6">Interactive Tool Explorer</H1> 83 | <PageSubtitle className="text-lg md:text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed"> 84 | 36+ specialized tools. Filter by category, search prompts, copy examples, and get productive in seconds. 85 | </PageSubtitle> 86 | </div> 87 | 88 | {/* Quick Actions / Popular */} 89 | <section className="mb-16 bg-gradient-to-r from-blue-50 via-indigo-50 to-purple-50 rounded-2xl p-6 md:p-8 shadow-xl border border-blue-100"> 90 | <div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6 mb-6"> 91 | <div> 92 | <h2 id="quick-actions" className="text-2xl font-bold text-gray-900 mb-1">🚀 Quick Actions</h2> 93 | <p className="text-sm text-gray-600">Most common starting points – copy and ask your AI now.</p> 94 | </div> 95 | <button onClick={() => setShowPopularExpanded(e=>!e)} className="text-xs font-medium text-blue-700 hover:text-blue-900 bg-blue-50 border border-blue-200 px-3 py-1.5 rounded-lg transition"> 96 | {showPopularExpanded ? 'Collapse' : 'Expand'} 97 | </button> 98 | </div> 99 | {showPopularExpanded && ( 100 | <div className="grid sm:grid-cols-2 lg:grid-cols-2 gap-4"> 101 | {popularTools.map(tool => ( 102 | <div key={tool.id} className="relative rounded-xl border border-gray-200 bg-white/90 p-4 shadow-sm group"> 103 | <div className="flex items-center justify-between mb-2"> 104 | <p className="font-mono text-xs font-semibold text-gray-800">{tool.name}</p> 105 | <button 106 | onClick={() => { 107 | const ex = tool.examples?.[0]; 108 | if (ex) navigator.clipboard.writeText(ex); 109 | }} 110 | className="text-[10px] bg-gray-100 hover:bg-gray-200 text-gray-700 px-2 py-0.5 rounded transition" 111 | >Copy</button> 112 | </div> 113 | <p className="text-[11px] text-gray-600 line-clamp-2 group-hover:line-clamp-none transition-all">{tool.description}</p> 114 | {tool.examples && tool.examples.length > 0 && ( 115 | <p className="mt-2 text-[11px] text-gray-500 italic">{tool.examples[0]}</p> 116 | )} 117 | <a href={`#${tool.id}`} className="absolute inset-0" aria-label={`Jump to ${tool.name}`}></a> 118 | </div> 119 | ))} 120 | {/* Added Hook Reference Quick Action */} 121 | <div className="relative rounded-xl border border-amber-300 bg-gradient-to-br from-amber-50 to-orange-50 p-4 shadow-sm group"> 122 | <div className="flex items-center justify-between mb-2"> 123 | <p className="font-mono text-xs font-semibold text-amber-700">get_hook_reference</p> 124 | <button 125 | onClick={() => navigator.clipboard.writeText('Ask: "List available SCAPI hook extension points for product search"')} 126 | className="text-[10px] bg-amber-100 hover:bg-amber-200 text-amber-700 px-2 py-0.5 rounded transition" 127 | >Copy</button> 128 | </div> 129 | <p className="text-[11px] text-amber-800 line-clamp-2 group-hover:line-clamp-none transition-all"> 130 | Discover all available OCAPI or SCAPI hook extension points to select the correct customization surface. 131 | </p> 132 | <p className="mt-2 text-[11px] text-amber-600 italic">Ask: "List available SCAPI hook extension points for product search"</p> 133 | <a href="#get_hook_reference" className="absolute inset-0" aria-label="Jump to get_hook_reference"></a> 134 | </div> 135 | </div> 136 | )} 137 | </section> 138 | 139 | {/* Filters */} 140 | <div className="mb-10"> 141 | <ToolFilters activeCategory={activeCategory} setActiveCategory={setActiveCategory} search={search} setSearch={setSearch} /> 142 | </div> 143 | 144 | {/* Legend */} 145 | <div className="flex flex-wrap gap-3 items-center text-[11px] text-gray-600 mb-6"> 146 | <span className="flex items-center gap-1"><span className="bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full font-semibold">Full</span> Requires credentials</span> 147 | <span className="flex items-center gap-1"><span className="bg-gradient-to-r from-blue-600 to-purple-600 text-white px-2 py-0.5 rounded-full font-semibold">Docs + Full</span> Both modes</span> 148 | </div> 149 | 150 | {/* Results */} 151 | <div className="space-y-14"> 152 | {filtered.length === 0 && ( 153 | <div className="text-center py-16 border border-dashed border-gray-300 rounded-xl"> 154 | <p className="text-sm text-gray-600 mb-4">No tools match that search.</p> 155 | <p className="text-xs text-gray-500">Tip: Try a simpler keyword like <strong>log</strong>, <strong>class</strong>, or <strong>version</strong>.</p> 156 | </div> 157 | )} 158 | {filtered.length > 0 && ( 159 | <div className="grid md:grid-cols-2 xl:grid-cols-2 gap-5"> 160 | {filtered.map(tool => <ToolCard key={tool.id} tool={tool} />)} 161 | </div> 162 | )} 163 | </div> 164 | 165 | {/* Getting Started Hint */} 166 | <div className="mt-20 bg-blue-50 border border-blue-200 rounded-xl p-6"> 167 | <h3 className="text-sm font-semibold text-blue-800 mb-2">💡 Mode Recommendation</h3> 168 | <p className="text-xs text-blue-800 leading-relaxed">Explore freely in Documentation Mode first. Add <code className="font-mono bg-blue-100 px-1 py-0.5 rounded">--dw-json</code> later to unlock log analysis, system & custom object exploration, job log insights, and code version management without changing any other configuration.</p> 169 | </div> 170 | 171 | {/* Next Steps */} 172 | <section className="mt-24 mb-12 text-center" aria-labelledby="next-steps"> 173 | <h2 id="next-steps" className="text-2xl md:text-3xl font-bold text-gray-900 mb-4">🔗 Next Steps</h2> 174 | <p className="text-sm md:text-base text-gray-600 max-w-2xl mx-auto mb-8">Move from raw tool surface into practical flows or reinforce secure patterns before enabling full-mode capabilities.</p> 175 | <div className="flex flex-col sm:flex-row gap-4 justify-center mb-10"> 176 | <NavLink 177 | to="/examples/" 178 | className="group bg-gradient-to-r from-blue-600 to-purple-600 text-white px-8 py-4 rounded-xl font-semibold text-lg shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 no-underline hover:no-underline focus:no-underline" 179 | > 180 | Examples & Use Cases 181 | <span className="ml-2 group-hover:translate-x-1 inline-block transition-transform">→</span> 182 | </NavLink> 183 | <NavLink 184 | to="/security/" 185 | className="border-2 border-gray-300 text-gray-700 px-8 py-4 rounded-xl font-semibold text-lg hover:border-blue-500 hover:text-blue-600 transition-all duration-300 no-underline hover:no-underline focus:no-underline" 186 | > 187 | Security Guidance 188 | </NavLink> 189 | </div> 190 | <div className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto mb-4 text-left"> 191 | <div className="rounded-xl border border-gray-200 bg-white p-5"> 192 | <h3 className="font-semibold text-sm mb-2">Why start with examples?</h3> 193 | <p className="text-xs text-gray-600">Shortens prompt iteration by showing multi-step sequences that combine docs lookup, log inspection, and object introspection.</p> 194 | </div> 195 | <div className="rounded-xl border border-blue-200 bg-blue-50 p-5"> 196 | <h3 className="font-semibold text-sm mb-2 text-blue-800">Why review security now?</h3> 197 | <p className="text-xs text-blue-700">Prevents leaking credentials, encourages safe log handling, and sets a baseline for principle-of-least-privilege tokens before automation.</p> 198 | </div> 199 | </div> 200 | </section> 201 | </div> 202 | ); 203 | }; 204 | 205 | export default ToolsPage; 206 | ``` -------------------------------------------------------------------------------- /docs/dw_order/GiftCertificate.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.order 2 | 3 | # Class GiftCertificate 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.order.GiftCertificate 11 | 12 | ## Description 13 | 14 | Represents a Gift Certificate that can be used to purchase products. 15 | 16 | ## Constants 17 | 18 | ### STATUS_ISSUED 19 | 20 | **Type:** Number = 1 21 | 22 | Represents a status of 'issued', which indicates that the Gift Certificate has been created and that it can be used to purchase products. 23 | 24 | ### STATUS_PARTIALLY_REDEEMED 25 | 26 | **Type:** Number = 2 27 | 28 | Represents a status of 'partially redeemed', which indicates that the Gift Certificate has been used to purchase products, but that there is still a balance on the gift certificate. 29 | 30 | ### STATUS_PENDING 31 | 32 | **Type:** Number = 0 33 | 34 | Represents a status of 'pending', which indicates that the Gift Certificate has been created but that it cannot be used yet. 35 | 36 | ### STATUS_REDEEMED 37 | 38 | **Type:** Number = 3 39 | 40 | Represents a status of 'redeemed', which indicates that the Gift Certificate has been used and no longer contains a balance. 41 | 42 | ## Properties 43 | 44 | ### amount 45 | 46 | **Type:** Money (Read Only) 47 | 48 | The original amount on the gift certificate. 49 | 50 | ### balance 51 | 52 | **Type:** Money (Read Only) 53 | 54 | The balance on the gift certificate. 55 | 56 | ### description 57 | 58 | **Type:** String 59 | 60 | The description string. 61 | 62 | ### enabled 63 | 64 | **Type:** boolean 65 | 66 | Returns true if the Gift Certificate is enabled, false otherwise. 67 | 68 | ### giftCertificateCode 69 | 70 | **Type:** String (Read Only) 71 | 72 | The code of the gift certificate. This redemption code is send to 73 | gift certificate recipient. 74 | 75 | ### ID 76 | 77 | **Type:** String (Read Only) 78 | 79 | The code of the gift certificate. This redemption code is send to 80 | gift certificate recipient. 81 | 82 | ### maskedGiftCertificateCode 83 | 84 | **Type:** String (Read Only) 85 | 86 | The masked gift certificate code with 87 | all but the last 4 characters replaced with a '*' character. 88 | 89 | ### merchantID 90 | 91 | **Type:** String (Read Only) 92 | 93 | The merchant ID of the gift certificate. 94 | 95 | ### message 96 | 97 | **Type:** String 98 | 99 | The message to include in the email of the person receiving 100 | the gift certificate. 101 | 102 | ### orderNo 103 | 104 | **Type:** String 105 | 106 | The order number 107 | 108 | ### recipientEmail 109 | 110 | **Type:** String 111 | 112 | The email address of the person receiving 113 | the gift certificate. 114 | 115 | ### recipientName 116 | 117 | **Type:** String 118 | 119 | The name of the person receiving 120 | the gift certificate. 121 | 122 | ### senderName 123 | 124 | **Type:** String 125 | 126 | The name of the person or organization that 127 | sent the gift certificate or null if undefined. 128 | 129 | ### status 130 | 131 | **Type:** Number 132 | 133 | The status where the possible values are 134 | STATUS_PENDING, STATUS_ISSUED, STATUS_PARTIALLY_REDEEMED 135 | or STATUS_REDEEMED. 136 | 137 | ## Constructor Summary 138 | 139 | ## Method Summary 140 | 141 | ### getAmount 142 | 143 | **Signature:** `getAmount() : Money` 144 | 145 | Returns the original amount on the gift certificate. 146 | 147 | ### getBalance 148 | 149 | **Signature:** `getBalance() : Money` 150 | 151 | Returns the balance on the gift certificate. 152 | 153 | ### getDescription 154 | 155 | **Signature:** `getDescription() : String` 156 | 157 | Returns the description string. 158 | 159 | ### getGiftCertificateCode 160 | 161 | **Signature:** `getGiftCertificateCode() : String` 162 | 163 | Returns the code of the gift certificate. 164 | 165 | ### getID 166 | 167 | **Signature:** `getID() : String` 168 | 169 | Returns the code of the gift certificate. 170 | 171 | ### getMaskedGiftCertificateCode 172 | 173 | **Signature:** `getMaskedGiftCertificateCode() : String` 174 | 175 | Returns the masked gift certificate code with all but the last 4 characters replaced with a '*' character. 176 | 177 | ### getMaskedGiftCertificateCode 178 | 179 | **Signature:** `getMaskedGiftCertificateCode(ignore : Number) : String` 180 | 181 | Returns the masked gift certificate code with all but the specified number of characters replaced with a '*' character. 182 | 183 | ### getMerchantID 184 | 185 | **Signature:** `getMerchantID() : String` 186 | 187 | Returns the merchant ID of the gift certificate. 188 | 189 | ### getMessage 190 | 191 | **Signature:** `getMessage() : String` 192 | 193 | Returns the message to include in the email of the person receiving the gift certificate. 194 | 195 | ### getOrderNo 196 | 197 | **Signature:** `getOrderNo() : String` 198 | 199 | Returns the order number 200 | 201 | ### getRecipientEmail 202 | 203 | **Signature:** `getRecipientEmail() : String` 204 | 205 | Returns the email address of the person receiving the gift certificate. 206 | 207 | ### getRecipientName 208 | 209 | **Signature:** `getRecipientName() : String` 210 | 211 | Returns the name of the person receiving the gift certificate. 212 | 213 | ### getSenderName 214 | 215 | **Signature:** `getSenderName() : String` 216 | 217 | Returns the name of the person or organization that sent the gift certificate or null if undefined. 218 | 219 | ### getStatus 220 | 221 | **Signature:** `getStatus() : Number` 222 | 223 | Returns the status where the possible values are STATUS_PENDING, STATUS_ISSUED, STATUS_PARTIALLY_REDEEMED or STATUS_REDEEMED. 224 | 225 | ### isEnabled 226 | 227 | **Signature:** `isEnabled() : boolean` 228 | 229 | Returns true if the Gift Certificate is enabled, false otherwise. 230 | 231 | ### setDescription 232 | 233 | **Signature:** `setDescription(description : String) : void` 234 | 235 | An optional description that you can use to categorize the gift certificate. 236 | 237 | ### setEnabled 238 | 239 | **Signature:** `setEnabled(enabled : boolean) : void` 240 | 241 | Controls if the Gift Certificate is enabled. 242 | 243 | ### setMessage 244 | 245 | **Signature:** `setMessage(message : String) : void` 246 | 247 | Sets the message to include in the email of the person receiving the gift certificate. 248 | 249 | ### setOrderNo 250 | 251 | **Signature:** `setOrderNo(orderNo : String) : void` 252 | 253 | Sets the order number 254 | 255 | ### setRecipientEmail 256 | 257 | **Signature:** `setRecipientEmail(recipientEmail : String) : void` 258 | 259 | Sets the email address of the person receiving the gift certificate. 260 | 261 | ### setRecipientName 262 | 263 | **Signature:** `setRecipientName(recipient : String) : void` 264 | 265 | Sets the name of the person receiving the gift certificate. 266 | 267 | ### setSenderName 268 | 269 | **Signature:** `setSenderName(sender : String) : void` 270 | 271 | Sets the name of the person or organization that sent the gift certificate. 272 | 273 | ### setStatus 274 | 275 | **Signature:** `setStatus(status : Number) : void` 276 | 277 | Sets the status of the gift certificate. 278 | 279 | ## Method Detail 280 | 281 | ## Method Details 282 | 283 | ### getAmount 284 | 285 | **Signature:** `getAmount() : Money` 286 | 287 | **Description:** Returns the original amount on the gift certificate. 288 | 289 | **Returns:** 290 | 291 | the original amount on the gift certificate. 292 | 293 | --- 294 | 295 | ### getBalance 296 | 297 | **Signature:** `getBalance() : Money` 298 | 299 | **Description:** Returns the balance on the gift certificate. 300 | 301 | **Returns:** 302 | 303 | the balance on the gift certificate. 304 | 305 | --- 306 | 307 | ### getDescription 308 | 309 | **Signature:** `getDescription() : String` 310 | 311 | **Description:** Returns the description string. 312 | 313 | **Returns:** 314 | 315 | the description. 316 | 317 | --- 318 | 319 | ### getGiftCertificateCode 320 | 321 | **Signature:** `getGiftCertificateCode() : String` 322 | 323 | **Description:** Returns the code of the gift certificate. This redemption code is send to gift certificate recipient. 324 | 325 | **Returns:** 326 | 327 | the code of the gift certificate. 328 | 329 | --- 330 | 331 | ### getID 332 | 333 | **Signature:** `getID() : String` 334 | 335 | **Description:** Returns the code of the gift certificate. This redemption code is send to gift certificate recipient. 336 | 337 | **Deprecated:** 338 | 339 | Use getGiftCertificateCode() 340 | 341 | **Returns:** 342 | 343 | the code of the gift certificate. 344 | 345 | --- 346 | 347 | ### getMaskedGiftCertificateCode 348 | 349 | **Signature:** `getMaskedGiftCertificateCode() : String` 350 | 351 | **Description:** Returns the masked gift certificate code with all but the last 4 characters replaced with a '*' character. 352 | 353 | **Returns:** 354 | 355 | the masked gift certificate code. 356 | 357 | --- 358 | 359 | ### getMaskedGiftCertificateCode 360 | 361 | **Signature:** `getMaskedGiftCertificateCode(ignore : Number) : String` 362 | 363 | **Description:** Returns the masked gift certificate code with all but the specified number of characters replaced with a '*' character. 364 | 365 | **Parameters:** 366 | 367 | - `ignore`: the number of characters to leave unmasked. 368 | 369 | **Returns:** 370 | 371 | the masked gift certificate code. 372 | 373 | **Throws:** 374 | 375 | IllegalArgumentException - if ignore is negative. 376 | 377 | --- 378 | 379 | ### getMerchantID 380 | 381 | **Signature:** `getMerchantID() : String` 382 | 383 | **Description:** Returns the merchant ID of the gift certificate. 384 | 385 | **Returns:** 386 | 387 | the merchant ID of the gift certificate. 388 | 389 | --- 390 | 391 | ### getMessage 392 | 393 | **Signature:** `getMessage() : String` 394 | 395 | **Description:** Returns the message to include in the email of the person receiving the gift certificate. 396 | 397 | **Returns:** 398 | 399 | the message to include in the email of the person receiving the gift certificate. 400 | 401 | --- 402 | 403 | ### getOrderNo 404 | 405 | **Signature:** `getOrderNo() : String` 406 | 407 | **Description:** Returns the order number 408 | 409 | **Returns:** 410 | 411 | the order number 412 | 413 | --- 414 | 415 | ### getRecipientEmail 416 | 417 | **Signature:** `getRecipientEmail() : String` 418 | 419 | **Description:** Returns the email address of the person receiving the gift certificate. 420 | 421 | **Returns:** 422 | 423 | the email address of the person receiving the gift certificate. 424 | 425 | --- 426 | 427 | ### getRecipientName 428 | 429 | **Signature:** `getRecipientName() : String` 430 | 431 | **Description:** Returns the name of the person receiving the gift certificate. 432 | 433 | **Returns:** 434 | 435 | the name of the person receiving the gift certificate. 436 | 437 | --- 438 | 439 | ### getSenderName 440 | 441 | **Signature:** `getSenderName() : String` 442 | 443 | **Description:** Returns the name of the person or organization that sent the gift certificate or null if undefined. 444 | 445 | **Returns:** 446 | 447 | the name of the person or organization that sent the gift certificate or null if undefined. 448 | 449 | --- 450 | 451 | ### getStatus 452 | 453 | **Signature:** `getStatus() : Number` 454 | 455 | **Description:** Returns the status where the possible values are STATUS_PENDING, STATUS_ISSUED, STATUS_PARTIALLY_REDEEMED or STATUS_REDEEMED. 456 | 457 | **Returns:** 458 | 459 | the status. 460 | 461 | --- 462 | 463 | ### isEnabled 464 | 465 | **Signature:** `isEnabled() : boolean` 466 | 467 | **Description:** Returns true if the Gift Certificate is enabled, false otherwise. 468 | 469 | **Returns:** 470 | 471 | true if the Gift Certificate is enabled, false otherwise. 472 | 473 | --- 474 | 475 | ### setDescription 476 | 477 | **Signature:** `setDescription(description : String) : void` 478 | 479 | **Description:** An optional description that you can use to categorize the gift certificate. 480 | 481 | **Parameters:** 482 | 483 | - `description`: additional description. 484 | 485 | --- 486 | 487 | ### setEnabled 488 | 489 | **Signature:** `setEnabled(enabled : boolean) : void` 490 | 491 | **Description:** Controls if the Gift Certificate is enabled. 492 | 493 | **Parameters:** 494 | 495 | - `enabled`: if true, enables the Gift Certificate. 496 | 497 | --- 498 | 499 | ### setMessage 500 | 501 | **Signature:** `setMessage(message : String) : void` 502 | 503 | **Description:** Sets the message to include in the email of the person receiving the gift certificate. 504 | 505 | **Parameters:** 506 | 507 | - `message`: the message to include in the email of the person receiving the gift certificate. 508 | 509 | --- 510 | 511 | ### setOrderNo 512 | 513 | **Signature:** `setOrderNo(orderNo : String) : void` 514 | 515 | **Description:** Sets the order number 516 | 517 | **Parameters:** 518 | 519 | - `orderNo`: the order number to be set 520 | 521 | --- 522 | 523 | ### setRecipientEmail 524 | 525 | **Signature:** `setRecipientEmail(recipientEmail : String) : void` 526 | 527 | **Description:** Sets the email address of the person receiving the gift certificate. 528 | 529 | **Parameters:** 530 | 531 | - `recipientEmail`: the email address of the person receiving the gift certificate. 532 | 533 | --- 534 | 535 | ### setRecipientName 536 | 537 | **Signature:** `setRecipientName(recipient : String) : void` 538 | 539 | **Description:** Sets the name of the person receiving the gift certificate. 540 | 541 | **Parameters:** 542 | 543 | - `recipient`: the name of the person receiving the gift certificate. 544 | 545 | --- 546 | 547 | ### setSenderName 548 | 549 | **Signature:** `setSenderName(sender : String) : void` 550 | 551 | **Description:** Sets the name of the person or organization that sent the gift certificate. 552 | 553 | **Parameters:** 554 | 555 | - `sender`: the name of the person or organization that sent the gift certificate. 556 | 557 | --- 558 | 559 | ### setStatus 560 | 561 | **Signature:** `setStatus(status : Number) : void` 562 | 563 | **Description:** Sets the status of the gift certificate. Possible values are: STATUS_ISSUED, STATUS_PENDING, STATUS_PARTIALLY_REDEEMED and STATUS_REDEEMED. 564 | 565 | **Parameters:** 566 | 567 | - `status`: Gift certificate status 568 | 569 | --- ``` -------------------------------------------------------------------------------- /src/clients/docs-client.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * SFCC Documentation Client (Refactored) 3 | * 4 | * This module provides functionality to query and retrieve SFCC class documentation 5 | * from converted Markdown files. It orchestrates specialized modules to handle 6 | * different aspects of documentation processing. 7 | * 8 | * Responsibilities: 9 | * - Orchestrating specialized modules 10 | * - Managing initialization and caching 11 | * - Providing public API for documentation access 12 | * - Applying filters and search functionality 13 | */ 14 | 15 | import { PathResolver } from '../utils/path-resolver.js'; 16 | import { CacheManager } from '../utils/cache.js'; 17 | import { Logger } from '../utils/logger.js'; 18 | import { DocumentationScanner, SFCCClassInfo } from './docs/documentation-scanner.js'; 19 | import { ClassContentParser, SFCCClassDetails, SFCCMethod } from './docs/class-content-parser.js'; 20 | import { ClassNameResolver } from './docs/class-name-resolver.js'; 21 | import { ReferencedTypesExtractor } from './docs/referenced-types-extractor.js'; 22 | 23 | // Re-export types for backward compatibility 24 | export { SFCCClassInfo, SFCCMethod, SFCCClassDetails }; 25 | export type { SFCCProperty, SFCCConstant } from './docs/class-content-parser.js'; 26 | 27 | export interface ClassDetailsFilterOptions { 28 | includeDescription?: boolean; 29 | includeConstants?: boolean; 30 | includeProperties?: boolean; 31 | includeMethods?: boolean; 32 | includeInheritance?: boolean; 33 | search?: string; 34 | } 35 | 36 | export class SFCCDocumentationClient { 37 | private docsPath: string; 38 | private classCache: Map<string, SFCCClassInfo> = new Map(); 39 | private cacheManager: CacheManager; 40 | private initialized = false; 41 | private logger: Logger; 42 | private documentationScanner: DocumentationScanner; 43 | private classContentParser: ClassContentParser; 44 | 45 | constructor() { 46 | this.docsPath = PathResolver.getDocsPath(); 47 | this.cacheManager = new CacheManager(); 48 | this.logger = Logger.getChildLogger('DocsClient'); 49 | this.documentationScanner = new DocumentationScanner(); 50 | this.classContentParser = new ClassContentParser(); 51 | } 52 | 53 | /** 54 | * Initialize the documentation client by scanning all available classes 55 | */ 56 | async initialize(): Promise<void> { 57 | if (this.initialized) { 58 | return; 59 | } 60 | 61 | try { 62 | this.classCache = await this.documentationScanner.scanDocumentation(this.docsPath); 63 | this.initialized = true; 64 | } catch (error) { 65 | throw new Error(`Failed to initialize SFCC documentation: ${error}`); 66 | } 67 | } 68 | 69 | /** 70 | * Get a list of all available SFCC classes 71 | */ 72 | async getAvailableClasses(): Promise<string[]> { 73 | await this.initialize(); 74 | return Array.from(this.classCache.keys()) 75 | .sort() 76 | .map(className => ClassNameResolver.toOfficialFormat(className)); 77 | } 78 | 79 | /** 80 | * Search for classes by name (partial matching) 81 | */ 82 | async searchClasses(query: string): Promise<string[]> { 83 | await this.initialize(); 84 | 85 | // Check cache first 86 | const cacheKey = `search:classes:${query.toLowerCase()}`; 87 | const cachedResult = this.cacheManager.getSearchResults(cacheKey); 88 | if (cachedResult) { 89 | return cachedResult; 90 | } 91 | 92 | const lowercaseQuery = query.toLowerCase(); 93 | const results = Array.from(this.classCache.keys()) 94 | .filter(className => className.toLowerCase().includes(lowercaseQuery)) 95 | .sort() 96 | .map(className => ClassNameResolver.toOfficialFormat(className)); 97 | 98 | // Cache the results 99 | this.cacheManager.setSearchResults(cacheKey, results); 100 | return results; 101 | } 102 | 103 | /** 104 | * Get the raw documentation content for a class 105 | */ 106 | async getClassDocumentation(className: string): Promise<string | null> { 107 | await this.initialize(); 108 | 109 | // Check cache first 110 | const normalizedClassName = ClassNameResolver.normalizeClassName(className); 111 | const cacheKey = `content:${normalizedClassName}`; 112 | const cachedContent = this.cacheManager.getFileContent(cacheKey); 113 | if (cachedContent !== undefined) { 114 | return cachedContent || null; 115 | } 116 | 117 | // Resolve class name with fallback logic 118 | const resolved = ClassNameResolver.resolveClassName(normalizedClassName, this.classCache); 119 | const content = resolved ? resolved.info.content : null; 120 | 121 | // Cache the result (including null results to avoid repeated lookups) 122 | this.cacheManager.setFileContent(cacheKey, content ?? ''); 123 | 124 | return content; 125 | } 126 | 127 | /** 128 | * Parse class documentation and extract structured information 129 | */ 130 | async getClassDetails(className: string): Promise<SFCCClassDetails | null> { 131 | // Check cache first 132 | const cacheKey = `details:${className}`; 133 | const cachedDetails = this.cacheManager.getClassDetails(cacheKey); 134 | if (cachedDetails !== undefined) { 135 | return cachedDetails; 136 | } 137 | 138 | const content = await this.getClassDocumentation(className); 139 | if (!content) { 140 | // Cache null results to avoid repeated parsing attempts 141 | this.cacheManager.setClassDetails(cacheKey, null); 142 | return null; 143 | } 144 | 145 | const details = this.classContentParser.parseClassContent(content); 146 | 147 | // Cache the parsed details 148 | this.cacheManager.setClassDetails(cacheKey, details); 149 | 150 | return details; 151 | } 152 | 153 | /** 154 | * Get class details with optional expansion of referenced types and filtering 155 | */ 156 | async getClassDetailsExpanded( 157 | className: string, 158 | expand: boolean = false, 159 | filterOptions?: ClassDetailsFilterOptions, 160 | ): Promise<SFCCClassDetails & { referencedTypes?: SFCCClassDetails[] } | null> { 161 | // Set default filter options 162 | const filters = { 163 | includeDescription: filterOptions?.includeDescription ?? true, 164 | includeConstants: filterOptions?.includeConstants ?? true, 165 | includeProperties: filterOptions?.includeProperties ?? true, 166 | includeMethods: filterOptions?.includeMethods ?? true, 167 | includeInheritance: filterOptions?.includeInheritance ?? true, 168 | search: filterOptions?.search, 169 | }; 170 | 171 | // Check cache first for expanded details with filter options 172 | const cacheKey = `details-expanded:${className}:${expand}:${JSON.stringify(filters)}`; 173 | const cachedResult = this.cacheManager.getClassDetails(cacheKey); 174 | if (cachedResult !== undefined) { 175 | return cachedResult; 176 | } 177 | 178 | const classDetails = await this.getClassDetails(className); 179 | if (!classDetails) { 180 | this.cacheManager.setClassDetails(cacheKey, null); 181 | return null; 182 | } 183 | 184 | // Apply filtering and search to the class details 185 | const filteredDetails = this.applyFiltersAndSearch(classDetails, filters); 186 | 187 | if (!expand) { 188 | this.cacheManager.setClassDetails(cacheKey, filteredDetails); 189 | return filteredDetails; 190 | } 191 | 192 | // Get the raw content to extract referenced types 193 | const content = await this.getClassDocumentation(className); 194 | if (!content) { 195 | this.cacheManager.setClassDetails(cacheKey, filteredDetails); 196 | return filteredDetails; 197 | } 198 | 199 | const referencedTypeNames = ReferencedTypesExtractor.extractFilteredReferencedTypes(content, className); 200 | const referencedTypes: SFCCClassDetails[] = []; 201 | 202 | // Get details for each referenced type 203 | for (const typeName of referencedTypeNames) { 204 | try { 205 | const typeDetails = await this.getClassDetails(typeName); 206 | if (typeDetails) { 207 | referencedTypes.push(typeDetails); 208 | } 209 | } catch { 210 | // Silently skip types that can't be found 211 | this.logger.warn(`Could not find details for referenced type: ${typeName}`); 212 | } 213 | } 214 | 215 | const result = { 216 | ...filteredDetails, 217 | referencedTypes: referencedTypes.length > 0 ? referencedTypes : undefined, 218 | }; 219 | 220 | // Cache the result 221 | this.cacheManager.setClassDetails(cacheKey, result); 222 | 223 | return result; 224 | } 225 | 226 | /** 227 | * Apply filters and search to class details 228 | */ 229 | private applyFiltersAndSearch( 230 | classDetails: SFCCClassDetails, 231 | filters: { 232 | includeDescription: boolean; 233 | includeConstants: boolean; 234 | includeProperties: boolean; 235 | includeMethods: boolean; 236 | includeInheritance: boolean; 237 | search?: string; 238 | }, 239 | ): SFCCClassDetails { 240 | const result: SFCCClassDetails = { 241 | className: classDetails.className, 242 | packageName: classDetails.packageName, 243 | description: filters.includeDescription ? classDetails.description : '', 244 | constants: [], 245 | properties: [], 246 | methods: [], 247 | inheritance: filters.includeInheritance ? classDetails.inheritance : undefined, 248 | constructorInfo: classDetails.constructorInfo, 249 | }; 250 | 251 | // Apply search filter to constants 252 | if (filters.includeConstants) { 253 | result.constants = filters.search 254 | ? classDetails.constants.filter(constant => 255 | this.matchesSearch(constant.name, constant.description, filters.search!), 256 | ) 257 | : classDetails.constants; 258 | } 259 | 260 | // Apply search filter to properties 261 | if (filters.includeProperties) { 262 | result.properties = filters.search 263 | ? classDetails.properties.filter(property => 264 | this.matchesSearch(property.name, property.description, filters.search!), 265 | ) 266 | : classDetails.properties; 267 | } 268 | 269 | // Apply search filter to methods 270 | if (filters.includeMethods) { 271 | result.methods = filters.search 272 | ? classDetails.methods.filter(method => 273 | this.matchesSearch(method.name, method.description, filters.search!) || 274 | this.matchesSearch(method.signature, '', filters.search!), 275 | ) 276 | : classDetails.methods; 277 | } 278 | 279 | // Apply search filter to inheritance 280 | if (filters.includeInheritance && filters.search && result.inheritance) { 281 | result.inheritance = result.inheritance.filter(inheritanceItem => 282 | this.matchesSearch(inheritanceItem, '', filters.search!), 283 | ); 284 | } 285 | 286 | return result; 287 | } 288 | 289 | /** 290 | * Check if a name or description matches the search term (case-insensitive) 291 | */ 292 | private matchesSearch(name: string, description: string, searchTerm: string): boolean { 293 | const search = searchTerm.toLowerCase(); 294 | return ( 295 | name.toLowerCase().includes(search) || 296 | description.toLowerCase().includes(search) 297 | ); 298 | } 299 | 300 | /** 301 | * Search for methods across all classes 302 | */ 303 | async searchMethods(methodName: string): Promise<{ className: string; method: SFCCMethod }[]> { 304 | await this.initialize(); 305 | 306 | // Check cache first 307 | const cacheKey = `search:methods:${methodName.toLowerCase()}`; 308 | const cachedResult = this.cacheManager.getMethodSearch(cacheKey); 309 | if (cachedResult) { 310 | return cachedResult; 311 | } 312 | 313 | const results: { className: string; method: SFCCMethod }[] = []; 314 | 315 | for (const [fullClassName] of this.classCache) { 316 | const details = await this.getClassDetails(fullClassName); 317 | const methods = details?.methods ?? []; 318 | for (const method of methods) { 319 | if (method.name.toLowerCase().includes(methodName.toLowerCase())) { 320 | results.push({ 321 | className: fullClassName, 322 | method, 323 | }); 324 | } 325 | } 326 | } 327 | 328 | // Cache the search results 329 | this.cacheManager.setMethodSearch(cacheKey, results); 330 | return results; 331 | } 332 | 333 | /** 334 | * Get cache statistics for monitoring performance 335 | */ 336 | getCacheStats(): ReturnType<CacheManager['getAllStats']> { 337 | return this.cacheManager.getAllStats(); 338 | } 339 | 340 | /** 341 | * Clear all caches 342 | */ 343 | clearCache(): void { 344 | this.cacheManager.clearAll(); 345 | } 346 | 347 | /** 348 | * Cleanup resources and destroy caches 349 | */ 350 | destroy(): void { 351 | this.cacheManager.destroy(); 352 | } 353 | } 354 | ``` -------------------------------------------------------------------------------- /docs/dw_campaign/PromotionPlan.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.campaign 2 | 3 | # Class PromotionPlan 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.campaign.PromotionPlan 9 | 10 | ## Description 11 | 12 | PromotionPlan represents a set of Promotion instances and is used to display active or upcoming promotions on storefront pages, or to pass it to the PromotionMgr to calculate a DiscountPlan and subsequently apply discounts to a line item container. Instances of the class are returned by the PromotionMgr.getActivePromotions(), PromotionMgr.getActiveCustomerPromotions() and PromotionMgr.getUpcomingPromotions(Number). PromotionPlan provides methods to access the promotions in the plan and to remove promotions from the plan. All methods which return a collection of promotions sort them by the following ordered criteria: Exclusivity: GLOBAL exclusive promotions first, followed by CLASS exclusive promotions, and NO exclusive promotions last. Rank: sorted ascending Promotion Class: PRODUCT promotions first, followed by ORDER promotions, and SHIPPING promotions last. Discount type: Fixed price promotions first, followed by free, amount-off, percentage-off, and bonus product promotions last. Best discount: Sorted descending. For example, 30% off comes before 20% off. ID: alphanumeric ascending. 13 | 14 | ## Constants 15 | 16 | ### SORT_BY_EXCLUSIVITY 17 | 18 | **Type:** Number = 1 19 | 20 | Constant indicating that a collection of promotions should be sorted first by exclusivity, then rank, promotion class, etc. See class-level javadoc for details. This is the default sort order for methods that return a collection of promotions. 21 | 22 | ### SORT_BY_START_DATE 23 | 24 | **Type:** Number = 2 25 | 26 | Constant indicating that a collection of promotions should be sorted by start date ascending. If there is no explicit start date for a promotion the start date of its containing Campaign or AB-test is used instead. Promotions without a start date are sorted before promotions with a start date in the future and after promotions with a start date in the past. In case two promotion assignments have the same start date, they are sorted by their ID. 27 | 28 | ## Properties 29 | 30 | ### orderPromotions 31 | 32 | **Type:** Collection (Read Only) 33 | 34 | All order promotions contained in this plan. 35 | 36 | ### productPromotions 37 | 38 | **Type:** Collection (Read Only) 39 | 40 | All product promotions contained in this plan. 41 | 42 | ### promotions 43 | 44 | **Type:** Collection (Read Only) 45 | 46 | All promotions contained in this plan sorted by exclusivity. 47 | 48 | ### shippingPromotions 49 | 50 | **Type:** Collection (Read Only) 51 | 52 | All shipping promotions contained in this plan. 53 | 54 | ## Constructor Summary 55 | 56 | ## Method Summary 57 | 58 | ### getOrderPromotions 59 | 60 | **Signature:** `getOrderPromotions() : Collection` 61 | 62 | Returns all order promotions contained in this plan. 63 | 64 | ### getPaymentCardPromotions 65 | 66 | **Signature:** `getPaymentCardPromotions(paymentCard : PaymentCard) : Collection` 67 | 68 | Returns the order promotions explicitly associated to the specified discounted payment card. 69 | 70 | ### getPaymentMethodPromotions 71 | 72 | **Signature:** `getPaymentMethodPromotions(paymentMethod : PaymentMethod) : Collection` 73 | 74 | Returns the order promotions explicitly associated to the specified discounted payment method. 75 | 76 | ### getProductPromotions 77 | 78 | **Signature:** `getProductPromotions() : Collection` 79 | 80 | Returns all product promotions contained in this plan. 81 | 82 | ### getProductPromotions 83 | 84 | **Signature:** `getProductPromotions(product : Product) : Collection` 85 | 86 | Returns the promotions related to the specified product. 87 | 88 | ### getProductPromotionsForDiscountedProduct 89 | 90 | **Signature:** `getProductPromotionsForDiscountedProduct(product : Product) : Collection` 91 | 92 | Returns the product promotions for which the specified product is a discounted (and possibly also a qualifying) product. 93 | 94 | ### getProductPromotionsForQualifyingProduct 95 | 96 | **Signature:** `getProductPromotionsForQualifyingProduct(product : Product) : Collection` 97 | 98 | Returns the product promotions for which the specified product is a qualifying but NOT a discounted product. 99 | 100 | ### getPromotions 101 | 102 | **Signature:** `getPromotions() : Collection` 103 | 104 | Returns all promotions contained in this plan sorted by exclusivity. 105 | 106 | ### getPromotions 107 | 108 | **Signature:** `getPromotions(sortOrder : Number) : Collection` 109 | 110 | Returns all promotions contained in this plan sorted according to the specified sort order. 111 | 112 | ### getPromotions 113 | 114 | **Signature:** `getPromotions(product : Product) : Collection` 115 | 116 | Returns the promotions related to the specified product. 117 | 118 | ### getShippingPromotions 119 | 120 | **Signature:** `getShippingPromotions() : Collection` 121 | 122 | Returns all shipping promotions contained in this plan. 123 | 124 | ### getShippingPromotions 125 | 126 | **Signature:** `getShippingPromotions(shippingMethod : ShippingMethod) : Collection` 127 | 128 | Returns the shipping promotions related to the specified discounted shipping method, i.e. 129 | 130 | ### removePromotion 131 | 132 | **Signature:** `removePromotion(promotion : Promotion) : void` 133 | 134 | Remove promotion from promotion plan. 135 | 136 | ## Method Detail 137 | 138 | ## Method Details 139 | 140 | ### getOrderPromotions 141 | 142 | **Signature:** `getOrderPromotions() : Collection` 143 | 144 | **Description:** Returns all order promotions contained in this plan. 145 | 146 | **Returns:** 147 | 148 | The sorted collection of order promotions contained in the promotion plan. 149 | 150 | --- 151 | 152 | ### getPaymentCardPromotions 153 | 154 | **Signature:** `getPaymentCardPromotions(paymentCard : PaymentCard) : Collection` 155 | 156 | **Description:** Returns the order promotions explicitly associated to the specified discounted payment card. This method is usually used to display order promotions along with payment card choices. 157 | 158 | **Parameters:** 159 | 160 | - `paymentCard`: Discounted payment card 161 | 162 | **Returns:** 163 | 164 | The sorted collection of order promotions associated with the specified payment card. 165 | 166 | --- 167 | 168 | ### getPaymentMethodPromotions 169 | 170 | **Signature:** `getPaymentMethodPromotions(paymentMethod : PaymentMethod) : Collection` 171 | 172 | **Description:** Returns the order promotions explicitly associated to the specified discounted payment method. This method is usually used to display order promotions along with payment method choices. 173 | 174 | **Parameters:** 175 | 176 | - `paymentMethod`: Discounted payment method 177 | 178 | **Returns:** 179 | 180 | The sorted collection of order promotions associated with the specified payment method. 181 | 182 | --- 183 | 184 | ### getProductPromotions 185 | 186 | **Signature:** `getProductPromotions() : Collection` 187 | 188 | **Description:** Returns all product promotions contained in this plan. 189 | 190 | **Returns:** 191 | 192 | The sorted collection of product promotions contained in the promotion plan. 193 | 194 | --- 195 | 196 | ### getProductPromotions 197 | 198 | **Signature:** `getProductPromotions(product : Product) : Collection` 199 | 200 | **Description:** Returns the promotions related to the specified product. The method returns all promotions where the product is either a qualifying product, or a discounted product, or both. It also returns promotions where the specified product is a bonus product. This method is usually used to display product promotions on a product details page. If a master product is passed, then this method will return promotions which are relevant for the master itself or at least one of its variants. 201 | 202 | **Parameters:** 203 | 204 | - `product`: Product associated with promotion 205 | 206 | **Returns:** 207 | 208 | The sorted collection of promotions related to specified discounted product. 209 | 210 | --- 211 | 212 | ### getProductPromotionsForDiscountedProduct 213 | 214 | **Signature:** `getProductPromotionsForDiscountedProduct(product : Product) : Collection` 215 | 216 | **Description:** Returns the product promotions for which the specified product is a discounted (and possibly also a qualifying) product. It also returns promotions where the specified product is a bonus product. This method is usually used to display product promotions on a product details page when separate callout messages are defined depending on if the product is a qualifying or discounted product for the promotion. If a master product is passed, then this method will return promotions for which the master product itself or at least one of its product's variants is a discounted product. 217 | 218 | **Parameters:** 219 | 220 | - `product`: The discounted product. 221 | 222 | **Returns:** 223 | 224 | Product promotions related to the specified discounted product. 225 | 226 | --- 227 | 228 | ### getProductPromotionsForQualifyingProduct 229 | 230 | **Signature:** `getProductPromotionsForQualifyingProduct(product : Product) : Collection` 231 | 232 | **Description:** Returns the product promotions for which the specified product is a qualifying but NOT a discounted product. This method is usually used to display product promotions on a product details page when separate callout messages are defined depending on if the product is a qualifying or discounted product for the promotion. If a master product is passed, then this method will return promotions for which the master product itself or at least one of its product's variants is a qualifying product. 233 | 234 | **Parameters:** 235 | 236 | - `product`: The qualifying product. 237 | 238 | **Returns:** 239 | 240 | Product promotions related to the specified qualifying product. 241 | 242 | --- 243 | 244 | ### getPromotions 245 | 246 | **Signature:** `getPromotions() : Collection` 247 | 248 | **Description:** Returns all promotions contained in this plan sorted by exclusivity. 249 | 250 | **Returns:** 251 | 252 | The sorted collection of promotions contained in the promotion plan. 253 | 254 | --- 255 | 256 | ### getPromotions 257 | 258 | **Signature:** `getPromotions(sortOrder : Number) : Collection` 259 | 260 | **Description:** Returns all promotions contained in this plan sorted according to the specified sort order. If the passed sort order is invalid, then the returned promotions will be sorted by exclusivity. 261 | 262 | **Parameters:** 263 | 264 | - `sortOrder`: the sort order to use. Must be SORT_BY_EXCLUSIVITY or SORT_BY_START_DATE. If an invalid value is passed, SORT_BY_EXCLUSIVITY is used. 265 | 266 | **Returns:** 267 | 268 | The sorted collection of promotions contained in the promotion plan. 269 | 270 | --- 271 | 272 | ### getPromotions 273 | 274 | **Signature:** `getPromotions(product : Product) : Collection` 275 | 276 | **Description:** Returns the promotions related to the specified product. The method returns all promotions where the product is either a qualifying product, or a discounted product, or both. It also returns promotions where the specified product is a bonus product. This method is usually used to display product promotions on a product details page. 277 | 278 | **Deprecated:** 279 | 280 | Use getProductPromotions(Product) 281 | 282 | **Parameters:** 283 | 284 | - `product`: Product associated with promotion 285 | 286 | **Returns:** 287 | 288 | The sorted collection of promotions related to the specified discounted product. 289 | 290 | --- 291 | 292 | ### getShippingPromotions 293 | 294 | **Signature:** `getShippingPromotions() : Collection` 295 | 296 | **Description:** Returns all shipping promotions contained in this plan. 297 | 298 | **Returns:** 299 | 300 | The sorted collection of shipping promotions contained in promotion plan. 301 | 302 | --- 303 | 304 | ### getShippingPromotions 305 | 306 | **Signature:** `getShippingPromotions(shippingMethod : ShippingMethod) : Collection` 307 | 308 | **Description:** Returns the shipping promotions related to the specified discounted shipping method, i.e. the returned promotions apply a discount on the specified shipping method. This method is usually used to display shipping promotions along with shipping methods. 309 | 310 | **Parameters:** 311 | 312 | - `shippingMethod`: Discounted shipping method 313 | 314 | **Returns:** 315 | 316 | The sorted collection of shipping promotions with specified method as discounted method. 317 | 318 | --- 319 | 320 | ### removePromotion 321 | 322 | **Signature:** `removePromotion(promotion : Promotion) : void` 323 | 324 | **Description:** Remove promotion from promotion plan. Please note that this is the only way to remove promotions from the plan, i.e. removing promotions from the collections returned by methods such as getProductPromotions() has no effect on the promotion plan. 325 | 326 | **Parameters:** 327 | 328 | - `promotion`: Promotion to remove from promotion plan 329 | 330 | --- ``` -------------------------------------------------------------------------------- /tests/mcp/node/get-available-sfra-documents.programmatic.test.js: -------------------------------------------------------------------------------- ```javascript 1 | import { test, describe, before, after, beforeEach } from 'node:test'; 2 | import { strict as assert } from 'node:assert'; 3 | import { connect } from 'mcp-aegis'; 4 | 5 | /** 6 | * Programmatic tests for the MCP tool: get_available_sfra_documents 7 | * Mirrors YAML tests but adds deeper JSON parsing & resilience checks. 8 | * 9 | * Validates: 10 | * 1. Tool is registered 11 | * 2. Basic successful invocation returns non-empty array JSON string 12 | * 3. JSON parse yields objects with required keys (id, name, category, filename) 13 | * 4. Category coverage (expects at least: core, product, order, customer, pricing, store, other) 14 | * 5. Extraneous argument tolerance (should ignore unexpected params) 15 | * 6. Negative invalid method error path via raw JSON-RPC call 16 | */ 17 | 18 | describe('get_available_sfra_documents (programmatic)', () => { 19 | let client; 20 | const CONFIG = './aegis.config.docs-only.json'; 21 | 22 | before(async () => { 23 | client = await connect(CONFIG); 24 | }); 25 | 26 | after(async () => { 27 | if (client?.connected) await client.disconnect(); 28 | }); 29 | 30 | beforeEach(() => { 31 | client.clearAllBuffers(); // Recommended - comprehensive protection 32 | }); 33 | 34 | const TOOL_NAME = 'get_available_sfra_documents'; 35 | const REQUIRED_CATEGORIES = ['core', 'product', 'order', 'customer', 'pricing', 'store', 'other']; 36 | // Actual response objects do NOT include an explicit 'id' field; we validate name/category/filename 37 | const REQUIRED_KEYS = ['name', 'category', 'filename']; 38 | 39 | test('tool should be registered', async () => { 40 | const tools = await client.listTools(); 41 | const names = tools.map(t => t.name); 42 | assert.ok(names.includes(TOOL_NAME), 'Tool not found in listTools'); 43 | }); 44 | 45 | async function invoke(extraArgs = {}) { 46 | const result = await client.callTool(TOOL_NAME, { ...extraArgs }); 47 | assert.equal(result.isError, false, 'Tool invocation should not be error'); 48 | assert.ok(result.content && Array.isArray(result.content) && result.content.length > 0, 'Content array expected'); 49 | const textBlocks = result.content.filter(c => c.type === 'text'); 50 | assert.ok(textBlocks.length > 0, 'At least one text block expected'); 51 | const raw = textBlocks.map(t => t.text).join('\n'); 52 | return { raw, result }; 53 | } 54 | 55 | function safeParseDocuments(raw) { 56 | let docs; 57 | try { 58 | docs = JSON.parse(raw); 59 | } catch { 60 | // Attempt to extract JSON array via regex fallback if wrapping noise present 61 | const match = raw.match(/\[[\s\S]*\]/); 62 | assert.ok(match, 'Could not locate JSON array in content'); 63 | docs = JSON.parse(match[0]); 64 | } 65 | assert.ok(Array.isArray(docs), 'Parsed docs should be array'); 66 | assert.ok(docs.length >= 15, 'Expect at least 15 SFRA documents'); 67 | return docs; 68 | } 69 | 70 | test('basic invocation returns valid JSON array with required keys', async () => { 71 | const { raw } = await invoke(); 72 | const docs = safeParseDocuments(raw); 73 | // Validate keys & reasonable string values 74 | for (const d of docs.slice(0, 25)) { // sample first 25 to keep runtime low 75 | for (const key of REQUIRED_KEYS) { 76 | assert.ok(Object.prototype.hasOwnProperty.call(d, key), `Missing key ${key}`); 77 | assert.equal(typeof d[key], 'string', `${key} should be string`); 78 | assert.ok(d[key].length > 0, `${key} should be non-empty`); 79 | } 80 | } 81 | }); 82 | 83 | test('category coverage present', async () => { 84 | const { raw } = await invoke(); 85 | const docs = safeParseDocuments(raw); 86 | const categories = new Set(docs.map(d => d.category)); 87 | for (const c of REQUIRED_CATEGORIES) { 88 | assert.ok(categories.has(c), `Expected category ${c}`); 89 | } 90 | }); 91 | 92 | test('extraneous argument is ignored', async () => { 93 | const { raw } = await invoke({ unused: 'value' }); 94 | const docs = safeParseDocuments(raw); 95 | assert.ok(docs.length >= 15, 'At least 15 docs expected with extraneous argument'); 96 | }); 97 | 98 | test('multiple product-* occurrences and filename pattern sanity', async () => { 99 | const { raw } = await invoke(); 100 | assert.match(raw, /(product-).*(product-).*(product-)/s, 'Expect at least three product- occurrences'); 101 | assert.match(raw, /"filename"\s*:\s*"[a-z0-9-]+\.md"/, 'Expect filename .md entries'); 102 | }); 103 | 104 | test('negative path: invalid method should return JSON-RPC error', async () => { 105 | // Raw JSON-RPC send with invalid method name returns error response (not throw) 106 | const response = await client.sendMessage({ 107 | jsonrpc: '2.0', 108 | id: 'bad-method-1', 109 | method: 'tools/call_WRONG', 110 | params: { name: TOOL_NAME, arguments: {} } 111 | }); 112 | assert.ok(response.error, 'Expected JSON-RPC error object'); 113 | assert.match(response.error.message || '', /method/i, 'Error message should mention method'); 114 | }); 115 | 116 | test('argument validation error path (null arguments object)', async () => { 117 | let caught = null; 118 | try { 119 | await client.callTool(TOOL_NAME, null); 120 | } catch (err) { 121 | caught = err; 122 | } 123 | assert.ok(caught, 'Expected callTool to reject with invalid_type'); 124 | assert.match(caught.message, /invalid_type|Expected object/i, 'Error should mention invalid type'); 125 | }); 126 | 127 | test('filenames unique and kebab-case', async () => { 128 | const { raw } = await invoke(); 129 | const docs = safeParseDocuments(raw); 130 | const filenames = docs.map(d => d.filename); 131 | const fileSet = new Set(filenames); 132 | assert.equal(fileSet.size, filenames.length, 'Filenames must be unique'); 133 | for (const f of filenames.slice(0, 50)) { 134 | assert.match(f, /^[a-z0-9-]+\.md$/, 'filename should be kebab-case .md'); 135 | } 136 | }); 137 | 138 | test('filename semantic correlation with name (first token appears in name)', async () => { 139 | const { raw } = await invoke(); 140 | const docs = safeParseDocuments(raw); 141 | for (const d of docs.slice(0, 40)) { 142 | assert.match(d.filename, /^[a-z0-9-]+\.md$/, 'filename must be kebab-case .md'); 143 | const firstSegment = d.filename.replace(/\.md$/, '').split('-')[0]; 144 | assert.ok(d.name.toLowerCase().includes(firstSegment), 'name should contain filename first segment'); 145 | } 146 | }); 147 | 148 | test('category distribution sanity (no category monopolizes >70%)', async () => { 149 | const { raw } = await invoke(); 150 | const docs = safeParseDocuments(raw); 151 | const counts = docs.reduce((acc, d) => { acc[d.category] = (acc[d.category]||0)+1; return acc; }, {}); 152 | const total = docs.length; 153 | for (const [cat, cnt] of Object.entries(counts)) { 154 | assert.ok(cnt / total <= 0.7, `Category ${cat} dominates with ${(cnt/total*100).toFixed(1)}%`); 155 | } 156 | }); 157 | 158 | test('deterministic ordering (two calls produce identical first 10 IDs)', async () => { 159 | const first = safeParseDocuments((await invoke()).raw).slice(0,10).map(d=>d.id); 160 | const second = safeParseDocuments((await invoke()).raw).slice(0,10).map(d=>d.id); 161 | assert.deepEqual(second, first, 'First 10 IDs should be stable across calls'); 162 | }); 163 | 164 | test('sample documents contain expected semantic keywords', async () => { 165 | const { raw } = await invoke(); 166 | const docs = safeParseDocuments(raw); 167 | const textBlob = JSON.stringify(docs.slice(0, 30)).toLowerCase(); 168 | const expected = ['server', 'request', 'response', 'product', 'price', 'cart']; 169 | for (const token of expected) { 170 | assert.ok(textBlob.includes(token), `Expected token ${token} in first 30 docs blob`); 171 | } 172 | }); 173 | 174 | test('no empty or placeholder names', async () => { 175 | const { raw } = await invoke(); 176 | const docs = safeParseDocuments(raw); 177 | for (const d of docs) { 178 | assert.ok(d.name.trim().length > 2, 'name should be >2 chars'); 179 | assert.ok(!/^(todo|tbd|placeholder)$/i.test(d.name), 'name should not be placeholder'); 180 | } 181 | }); 182 | 183 | test('round-trip JSON parse reproducibility (stringify->parse equality for first 5)', async () => { 184 | const { raw } = await invoke(); 185 | const docs = safeParseDocuments(raw).slice(0,5); 186 | const roundTrip = JSON.parse(JSON.stringify(docs)); 187 | assert.deepEqual(roundTrip, docs, 'Round trip serialization must preserve first 5 docs'); 188 | }); 189 | 190 | test('at least one document per required category and each category has <= 50% of docs', async () => { 191 | const { raw } = await invoke(); 192 | const docs = safeParseDocuments(raw); 193 | const counts = docs.reduce((acc, d) => { acc[d.category] = (acc[d.category]||0)+1; return acc; }, {}); 194 | const total = docs.length; 195 | for (const c of REQUIRED_CATEGORIES) { 196 | assert.ok(counts[c] > 0, `Category ${c} missing`); 197 | assert.ok((counts[c] / total) <= 0.5, `Category ${c} exceeds 50% threshold`); 198 | } 199 | }); 200 | 201 | test('filename/id pairing uniqueness (no duplicate filename)', async () => { 202 | const { raw } = await invoke(); 203 | const docs = safeParseDocuments(raw); 204 | const filenames = docs.map(d => d.filename); 205 | const fileSet = new Set(filenames); 206 | assert.equal(fileSet.size, filenames.length, 'Filenames must be unique'); 207 | }); 208 | 209 | test('sorted by name alpha? (non-fatal heuristic) warn if not mostly sorted', async () => { 210 | const { raw } = await invoke(); 211 | const docs = safeParseDocuments(raw).slice(0,30); 212 | const names = docs.map(d=>d.name.toLowerCase()); 213 | const sorted = [...names].sort(); 214 | let outOfOrder = 0; 215 | names.forEach((n,i)=>{ if(n!==sorted[i]) outOfOrder++; }); 216 | if (outOfOrder > Math.ceil(names.length * 0.4)) { 217 | console.warn(`Heuristic: more than 40% (${outOfOrder}/${names.length}) of first 30 names out of alpha order`); 218 | } 219 | }); 220 | 221 | // Removed category/id correlation test (no id field in response) 222 | 223 | // Removed id/filename prefix correlation (id not present) 224 | 225 | test('document count stable across two invocations (delta <= 2)', async () => { 226 | const firstLen = safeParseDocuments((await invoke()).raw).length; 227 | const secondLen = safeParseDocuments((await invoke()).raw).length; 228 | const delta = Math.abs(firstLen - secondLen); 229 | assert.ok(delta <= 2, `Doc count changed unexpectedly by ${delta}`); 230 | }); 231 | 232 | test('category histogram logged for diagnostic insight (non-asserting)', async () => { 233 | const { raw } = await invoke(); 234 | const docs = safeParseDocuments(raw); 235 | const counts = docs.reduce((acc, d) => { acc[d.category] = (acc[d.category]||0)+1; return acc; }, {}); 236 | console.info('SFRA docs category histogram:', counts); 237 | }); 238 | 239 | test('ensure presence of canonical core documents', async () => { 240 | const { raw } = await invoke(); 241 | const docs = safeParseDocuments(raw); 242 | const names = docs.map(d=>d.name.toLowerCase()); 243 | const required = ['server', 'request', 'response', 'querystring', 'render']; 244 | for (const r of required) { 245 | assert.ok(names.some(n=>n.includes(r)), `Missing canonical core doc: ${r}`); 246 | } 247 | }); 248 | 249 | test('ensure presence of key product/order/customer domain docs', async () => { 250 | const { raw } = await invoke(); 251 | const docs = safeParseDocuments(raw); 252 | const blob = JSON.stringify(docs).toLowerCase(); 253 | const expected = ['product', 'cart', 'shipping', 'billing', 'account', 'customer', 'price']; 254 | for (const token of expected) { 255 | assert.ok(blob.includes(token), `Missing expected domain token: ${token}`); 256 | } 257 | }); 258 | 259 | test('no obvious placeholder filenames', async () => { 260 | const { raw } = await invoke(); 261 | const docs = safeParseDocuments(raw); 262 | for (const d of docs) { 263 | assert.ok(!/(placeholder|temp|dummy)/i.test(d.filename), 'Filename should not look placeholder'); 264 | } 265 | }); 266 | 267 | test('filename extension enforcement (.md only)', async () => { 268 | const { raw } = await invoke(); 269 | const docs = safeParseDocuments(raw); 270 | for (const d of docs) { 271 | assert.ok(d.filename.endsWith('.md'), 'All filenames must end with .md'); 272 | } 273 | }); 274 | }); 275 | ``` -------------------------------------------------------------------------------- /tests/validation-helpers.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ValidationHelpers, ValidationRule, CommonValidations } from '../src/core/handlers/validation-helpers.js'; 2 | import { HandlerError } from '../src/core/handlers/base-handler.js'; 3 | 4 | describe('ValidationHelpers', () => { 5 | describe('validateArguments', () => { 6 | it('should pass when all required fields are present and valid', () => { 7 | const args = { 8 | stringField: 'value1', 9 | numberField: 42, 10 | booleanField: true, 11 | objectField: { prop: 'value' }, 12 | }; 13 | 14 | const rules: ValidationRule[] = [ 15 | { field: 'stringField', required: true, type: 'string' }, 16 | { field: 'numberField', required: true, type: 'number' }, 17 | { field: 'booleanField', required: true, type: 'boolean' }, 18 | { field: 'objectField', required: true, type: 'object' }, 19 | ]; 20 | 21 | expect(() => { 22 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 23 | }).not.toThrow(); 24 | }); 25 | 26 | it('should throw HandlerError when required field is missing', () => { 27 | const args = { 28 | field1: 'value1', 29 | }; 30 | 31 | const rules: ValidationRule[] = [ 32 | { field: 'field1', required: true, type: 'string' }, 33 | { field: 'field2', required: true, type: 'string' }, 34 | ]; 35 | 36 | expect(() => { 37 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 38 | }).toThrow(HandlerError); 39 | 40 | try { 41 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 42 | } catch (error) { 43 | expect(error).toBeInstanceOf(HandlerError); 44 | expect((error as HandlerError).message).toBe('field2 is required'); 45 | expect((error as HandlerError).toolName).toBe('test_tool'); 46 | expect((error as HandlerError).code).toBe('MISSING_ARGUMENT'); 47 | } 48 | }); 49 | 50 | it('should throw when required field is null', () => { 51 | const args = { 52 | field1: 'value1', 53 | field2: null, 54 | }; 55 | 56 | const rules: ValidationRule[] = [ 57 | { field: 'field1', required: true, type: 'string' }, 58 | { field: 'field2', required: true, type: 'string' }, 59 | ]; 60 | 61 | expect(() => { 62 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 63 | }).toThrow('field2 is required'); 64 | }); 65 | 66 | it('should throw when required field is empty string', () => { 67 | const args = { 68 | field1: 'value1', 69 | field2: '', 70 | }; 71 | 72 | const rules: ValidationRule[] = [ 73 | { field: 'field1', required: true, type: 'string' }, 74 | { field: 'field2', required: true, type: 'string' }, 75 | ]; 76 | 77 | expect(() => { 78 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 79 | }).toThrow('field2 is required'); 80 | }); 81 | 82 | it('should throw when field type is incorrect', () => { 83 | const args = { 84 | stringField: 123, // Should be string 85 | }; 86 | 87 | const rules: ValidationRule[] = [ 88 | { field: 'stringField', required: true, type: 'string' }, 89 | ]; 90 | 91 | expect(() => { 92 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 93 | }).toThrow(HandlerError); 94 | 95 | try { 96 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 97 | } catch (error) { 98 | expect(error).toBeInstanceOf(HandlerError); 99 | expect((error as HandlerError).message).toBe('stringField must be of type string'); 100 | expect((error as HandlerError).code).toBe('INVALID_TYPE'); 101 | } 102 | }); 103 | 104 | it('should pass when optional field is missing', () => { 105 | const args = { 106 | required: 'value', 107 | }; 108 | 109 | const rules: ValidationRule[] = [ 110 | { field: 'required', required: true, type: 'string' }, 111 | { field: 'optional', required: false, type: 'string' }, 112 | ]; 113 | 114 | expect(() => { 115 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 116 | }).not.toThrow(); 117 | }); 118 | 119 | it('should validate custom validator function', () => { 120 | const args = { 121 | email: 'invalid-email', 122 | }; 123 | 124 | const rules: ValidationRule[] = [ 125 | { 126 | field: 'email', 127 | required: true, 128 | type: 'string', 129 | validator: (value: string) => value.includes('@'), 130 | errorMessage: 'email must be a valid email address', 131 | }, 132 | ]; 133 | 134 | expect(() => { 135 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 136 | }).toThrow('email must be a valid email address'); 137 | }); 138 | 139 | it('should pass custom validator when valid', () => { 140 | const args = { 141 | email: '[email protected]', 142 | }; 143 | 144 | const rules: ValidationRule[] = [ 145 | { 146 | field: 'email', 147 | required: true, 148 | type: 'string', 149 | validator: (value: string) => value.includes('@'), 150 | errorMessage: 'email must be a valid email address', 151 | }, 152 | ]; 153 | 154 | expect(() => { 155 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 156 | }).not.toThrow(); 157 | }); 158 | }); 159 | 160 | describe('requireStrings', () => { 161 | it('should pass when all required string fields are present', () => { 162 | const args = { 163 | field1: 'value1', 164 | field2: 'value2', 165 | }; 166 | 167 | expect(() => { 168 | ValidationHelpers.requireStrings(args, ['field1', 'field2'], 'test_tool'); 169 | }).not.toThrow(); 170 | }); 171 | 172 | it('should throw when required string field is missing', () => { 173 | const args = { 174 | field1: 'value1', 175 | }; 176 | 177 | expect(() => { 178 | ValidationHelpers.requireStrings(args, ['field1', 'field2'], 'test_tool'); 179 | }).toThrow('field2 is required for test_tool'); 180 | }); 181 | 182 | it('should pass when no fields are required', () => { 183 | const args = { field1: 'value1' }; 184 | 185 | expect(() => { 186 | ValidationHelpers.requireStrings(args, [], 'test_tool'); 187 | }).not.toThrow(); 188 | }); 189 | }); 190 | 191 | describe('type validation', () => { 192 | it('should validate string type correctly', () => { 193 | const args = { field: 'string value' }; 194 | const rules: ValidationRule[] = [{ field: 'field', type: 'string' }]; 195 | 196 | expect(() => { 197 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 198 | }).not.toThrow(); 199 | }); 200 | 201 | it('should validate number type correctly', () => { 202 | const args = { field: 42 }; 203 | const rules: ValidationRule[] = [{ field: 'field', type: 'number' }]; 204 | 205 | expect(() => { 206 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 207 | }).not.toThrow(); 208 | }); 209 | 210 | it('should validate boolean type correctly', () => { 211 | const args = { field: true }; 212 | const rules: ValidationRule[] = [{ field: 'field', type: 'boolean' }]; 213 | 214 | expect(() => { 215 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 216 | }).not.toThrow(); 217 | }); 218 | 219 | it('should validate object type correctly', () => { 220 | const args = { field: { prop: 'value' } }; 221 | const rules: ValidationRule[] = [{ field: 'field', type: 'object' }]; 222 | 223 | expect(() => { 224 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 225 | }).not.toThrow(); 226 | }); 227 | 228 | it('should validate array type correctly', () => { 229 | const args = { field: ['item1', 'item2'] }; 230 | const rules: ValidationRule[] = [{ field: 'field', type: 'array' }]; 231 | 232 | expect(() => { 233 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 234 | }).not.toThrow(); 235 | }); 236 | 237 | it('should reject array when object expected', () => { 238 | const args = { field: ['item1', 'item2'] }; 239 | const rules: ValidationRule[] = [{ field: 'field', type: 'object' }]; 240 | 241 | expect(() => { 242 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 243 | }).toThrow('field must be of type object'); 244 | }); 245 | 246 | it('should reject null when object expected', () => { 247 | const args = { field: null }; 248 | const rules: ValidationRule[] = [{ field: 'field', required: true, type: 'object' }]; 249 | 250 | expect(() => { 251 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 252 | }).toThrow('field is required'); 253 | }); 254 | }); 255 | 256 | describe('CommonValidations', () => { 257 | describe('Generic validation methods', () => { 258 | it('should support generic requiredString validation', () => { 259 | const rules = CommonValidations.requiredString('customField'); 260 | expect(rules).toEqual([{ 261 | field: 'customField', 262 | required: true, 263 | type: 'string', 264 | validator: expect.any(Function), 265 | errorMessage: 'customField must be a non-empty string', 266 | }]); 267 | }); 268 | 269 | it('should support custom error messages in requiredString', () => { 270 | const rules = CommonValidations.requiredString('customField', 'Custom error message'); 271 | expect(rules[0].errorMessage).toBe('Custom error message'); 272 | }); 273 | 274 | it('should validate with generic requiredString', () => { 275 | const args = { customField: 'valid-value' }; 276 | const rules = CommonValidations.requiredString('customField'); 277 | 278 | expect(() => { 279 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 280 | }).not.toThrow(); 281 | }); 282 | 283 | it('should fail with empty string in requiredString', () => { 284 | const args = { customField: '' }; 285 | const rules = CommonValidations.requiredString('customField'); 286 | 287 | expect(() => { 288 | ValidationHelpers.validateArguments(args, rules, 'test_tool'); 289 | }).toThrow('customField must be a non-empty string'); 290 | }); 291 | 292 | it('should support generic requiredField validation', () => { 293 | const rules = CommonValidations.requiredField( 294 | 'myField', 295 | 'number', 296 | (value: number) => value > 0, 297 | 'myField must be positive', 298 | ); 299 | expect(rules).toEqual([{ 300 | field: 'myField', 301 | required: true, 302 | type: 'number', 303 | validator: expect.any(Function), 304 | errorMessage: 'myField must be positive', 305 | }]); 306 | }); 307 | 308 | it('should support optionalField validation', () => { 309 | const rules = CommonValidations.optionalField( 310 | 'optionalField', 311 | 'boolean', 312 | (value: boolean) => typeof value === 'boolean', 313 | 'optionalField must be boolean', 314 | ); 315 | expect(rules[0].required).toBe(false); 316 | }); 317 | 318 | it('should validate cartridge name with custom pattern', () => { 319 | const rules = CommonValidations.requiredField( 320 | 'cartridgeName', 321 | 'string', 322 | (value: string) => /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(value), 323 | 'cartridgeName must be a valid identifier (letters, numbers, underscore, hyphen)', 324 | ); 325 | 326 | // Test valid cartridge name 327 | const validArgs = { cartridgeName: 'plugin_example' }; 328 | expect(() => { 329 | ValidationHelpers.validateArguments(validArgs, rules, 'test_tool'); 330 | }).not.toThrow(); 331 | 332 | // Test invalid cartridge name 333 | const invalidArgs = { cartridgeName: '123invalid' }; 334 | expect(() => { 335 | ValidationHelpers.validateArguments(invalidArgs, rules, 'test_tool'); 336 | }).toThrow('cartridgeName must be a valid identifier'); 337 | }); 338 | 339 | it('should validate className pattern', () => { 340 | const rules = CommonValidations.requiredString('className'); 341 | 342 | // Test valid className 343 | const validArgs = { className: 'Product' }; 344 | expect(() => { 345 | ValidationHelpers.validateArguments(validArgs, rules, 'test_tool'); 346 | }).not.toThrow(); 347 | 348 | // Test empty className 349 | const invalidArgs = { className: '' }; 350 | expect(() => { 351 | ValidationHelpers.validateArguments(invalidArgs, rules, 'test_tool'); 352 | }).toThrow('className must be a non-empty string'); 353 | }); 354 | }); 355 | }); 356 | }); 357 | ```