This is page 14 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 -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server/src/routes/ocapi/code-versions-handler.js: -------------------------------------------------------------------------------- ```javascript 1 | /** 2 | * Code Versions Handler 3 | * 4 | * Handles code version listing and activation operations for OCAPI endpoints. 5 | */ 6 | 7 | const express = require('express'); 8 | 9 | class CodeVersionsHandler { 10 | constructor(config, dataLoader) { 11 | this.config = config; 12 | this.ocapiConfig = config.getOcapiConfig(); 13 | this.dataLoader = dataLoader; 14 | this.router = express.Router(); 15 | this.setupRoutes(); 16 | } 17 | 18 | setupRoutes() { 19 | // Code Versions API 20 | this.router.get(`/s/-/dw/data/${this.ocapiConfig.version}/code_versions`, 21 | this.handleGetCodeVersions.bind(this) 22 | ); 23 | 24 | this.router.patch(`/s/-/dw/data/${this.ocapiConfig.version}/code_versions/:versionId`, 25 | this.handleActivateCodeVersion.bind(this) 26 | ); 27 | } 28 | 29 | /** 30 | * Handle get code versions 31 | */ 32 | async handleGetCodeVersions(req, res) { 33 | let mockData = this.dataLoader.loadOcapiData('code-versions.json'); 34 | 35 | if (!mockData) { 36 | // Create fallback data with proper SFCC format 37 | mockData = { 38 | "_v": "23.2", 39 | "_type": "code_version_result", 40 | "count": 1, 41 | "data": [ 42 | { 43 | "_type": "code_version", 44 | "id": "SFRA_FALLBACK_VERSION", 45 | "active": true, 46 | "activation_time": new Date().toISOString(), 47 | "last_modification_time": new Date().toISOString(), 48 | "rollback": false, 49 | "compatibility_mode": "22.7", 50 | "cartridges": [ 51 | "app_storefront_base", 52 | "bm_app_storefront_base", 53 | "modules" 54 | ], 55 | "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/SFRA_FALLBACK_VERSION" 56 | } 57 | ], 58 | "total": 1 59 | }; 60 | } 61 | 62 | res.json(mockData); 63 | } 64 | 65 | /** 66 | * Handle activate code version 67 | */ 68 | async handleActivateCodeVersion(req, res) { 69 | const { versionId } = req.params; 70 | 71 | // Load current mock data to find the version being activated 72 | let mockData = this.dataLoader.loadOcapiData('code-versions.json'); 73 | 74 | if (!mockData) { 75 | return res.status(404).json({ 76 | "_v": "23.2", 77 | "_type": "fault", 78 | "fault": { 79 | "type": "InvalidParameterException", 80 | "message": `Code version '${versionId}' not found` 81 | } 82 | }); 83 | } 84 | 85 | // Find the code version being activated 86 | const versionToActivate = mockData.data.find(cv => cv.id === versionId); 87 | if (!versionToActivate) { 88 | return res.status(404).json({ 89 | "_v": "23.2", 90 | "_type": "fault", 91 | "fault": { 92 | "type": "InvalidParameterException", 93 | "message": `Code version '${versionId}' not found` 94 | } 95 | }); 96 | } 97 | 98 | if (versionToActivate.active) { 99 | // For the reset version, return the current version data instead of an error 100 | // This allows tests to "activate" it reliably regardless of current state 101 | if (versionId === 'reset_version') { 102 | const activationTime = versionToActivate.activation_time || new Date().toISOString(); 103 | const activatedVersion = { 104 | "_v": "23.2", 105 | "_type": "code_version", 106 | "_resource_state": this.generateResourceState(), 107 | "activation_time": activationTime, 108 | "active": true, 109 | "cartridges": versionToActivate.cartridges || [], 110 | "compatibility_mode": versionToActivate.compatibility_mode || "22.7", 111 | "id": versionId, 112 | "last_modification_time": versionToActivate.last_modification_time || activationTime, 113 | "rollback": false, 114 | "web_dav_url": versionToActivate.web_dav_url || `https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/${versionId}` 115 | }; 116 | return res.json(activatedVersion); 117 | } 118 | 119 | return res.status(400).json({ 120 | "_v": "23.2", 121 | "_type": "fault", 122 | "fault": { 123 | "type": "InvalidParameterException", 124 | "message": `Code version '${versionId}' is already active` 125 | } 126 | }); 127 | } 128 | 129 | // Create response matching real SFCC API behavior 130 | const activationTime = new Date().toISOString(); 131 | const activatedVersion = { 132 | "_v": "23.2", 133 | "_type": "code_version", 134 | "_resource_state": this.generateResourceState(), 135 | "activation_time": activationTime, 136 | "active": true, 137 | "cartridges": versionToActivate.cartridges || [], 138 | "compatibility_mode": versionToActivate.compatibility_mode || "22.7", 139 | "id": versionId, 140 | "last_modification_time": versionToActivate.last_modification_time || activationTime, 141 | "rollback": false, // Real API sets this to false when activating 142 | "web_dav_url": versionToActivate.web_dav_url || `https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/${versionId}` 143 | }; 144 | 145 | // Update the mock data state (deactivate others, activate target) 146 | mockData.data.forEach(cv => { 147 | if (cv.id === versionId) { 148 | cv.active = true; 149 | cv.activation_time = activationTime; 150 | cv.rollback = false; 151 | } else if (cv.active) { 152 | cv.active = false; 153 | delete cv.activation_time; // Remove activation_time from deactivated versions 154 | } 155 | }); 156 | 157 | // Save updated state back to file (in a real scenario) 158 | // For now, just return the response 159 | 160 | res.json(activatedVersion); 161 | } 162 | 163 | /** 164 | * Generate a mock resource state hash 165 | */ 166 | generateResourceState() { 167 | // Generate a 64-character hex string similar to SFCC's resource state 168 | const chars = '0123456789abcdef'; 169 | let result = ''; 170 | for (let i = 0; i < 64; i++) { 171 | result += chars[Math.floor(Math.random() * chars.length)]; 172 | } 173 | return result; 174 | } 175 | 176 | /** 177 | * Get the configured router 178 | */ 179 | getRouter() { 180 | return this.router; 181 | } 182 | } 183 | 184 | module.exports = CodeVersionsHandler; ``` -------------------------------------------------------------------------------- /docs/sfra/product-bundle.md: -------------------------------------------------------------------------------- ```markdown 1 | # SFRA Product Bundle Model 2 | 3 | ## Overview 4 | 5 | The Product Bundle model represents bundled products in SFRA applications, where multiple products are sold together as a single unit. It provides comprehensive bundle information including individual bundled products, pricing, and bundle-specific functionality. 6 | 7 | ## Module Function 8 | 9 | ```javascript 10 | module.exports = function bundleProduct(product, apiProduct, options, factory) 11 | ``` 12 | 13 | Decorates a product object with bundle-specific information using various decorators. 14 | 15 | ### Parameters 16 | 17 | - `product` (Object) - Product Model to be decorated 18 | - `apiProduct` (dw.catalog.Product) - Product information returned by the script API 19 | - `options` (Object) - Options object containing: 20 | - `productType` (string) - Product type information 21 | - `variationModel` (dw.catalog.ProductVariationModel) - Variation model returned by the API 22 | - `options` (Object) - Options provided on the query string 23 | - `optionModel` (dw.catalog.ProductOptionModel) - Options model returned by the API 24 | - `promotions` (dw.util.Collection) - Active promotions for the product 25 | - `quantity` (number) - Current selected quantity 26 | - `variables` (Object) - Variables passed in on the query string 27 | - `factory` (Object) - Reference to product factory for creating bundled products 28 | 29 | ### Returns 30 | 31 | Object - Decorated product model with bundle-specific information 32 | 33 | ## Applied Decorators 34 | 35 | The product bundle model applies the following decorators in sequence: 36 | 37 | ### Standard Product Decorators 38 | - **base** - Fundamental product information: `uuid`, `id`, `productName`, `productType`, `brand` 39 | - **price** - Bundle pricing: `price` and `renderedPrice` with promotions 40 | - **images** - Product images: `images` with large and small sizes 41 | - **quantity** - Quantity constraints: `selectedQuantity`, `minOrderQuantity`, `maxOrderQuantity` 42 | - **description** - Product descriptions: `longDescription` and `shortDescription` markup 43 | - **ratings** - Product rating: `rating` value calculated from product ID 44 | - **promotions** - Active promotions: `promotions` array with promotion details 45 | - **attributes** - Product attributes: `attributes` array with grouped attribute data 46 | - **availability** - Availability status: `availability` object with messages and stock status 47 | - **options** - Product options: `options` array with option configurations 48 | - **quantitySelector** - Quantity selection: `quantities` array with step quantity options 49 | 50 | ### Bundle-Specific Decorators 51 | - **sizeChart** (conditional) - Size chart: `sizeChartId` if category has custom sizeChartID 52 | - **currentUrl** - Product URL: `selectedProductUrl` with variation and option parameters 53 | - **bundledProducts** - Bundle contents: `bundledProducts` array with individual product models 54 | - **bundleReadyToOrder** - Bundle readiness: `readyToOrder` boolean validation for entire bundle 55 | - **raw** - Raw API data: `raw` property (non-enumerable) with original API product 56 | - **pageMetaData** - SEO metadata: `pageTitle`, `pageDescription`, `pageKeywords`, `pageMetaTags` 57 | - **template** - Template info: `template` property for rendering 58 | 59 | ## Bundle-Specific Features 60 | 61 | ### Bundled Products Information 62 | The `bundledProducts` decorator adds: 63 | - List of individual products in the bundle 64 | - Quantities of each bundled product 65 | - Individual product pricing and availability 66 | - Product configuration options for each item 67 | 68 | ### Bundle Readiness Validation 69 | The `bundleReadyToOrder` decorator provides: 70 | - Overall bundle availability status 71 | - Validation that all required bundled products are available 72 | - Bundle-specific ordering constraints 73 | 74 | ### Bundle Pricing 75 | Bundle pricing includes: 76 | - Combined pricing of all bundled products 77 | - Bundle-level discounts and promotions 78 | - Individual product price contributions 79 | - Savings compared to individual purchases 80 | 81 | ## Usage Example 82 | 83 | ```javascript 84 | var bundleProductDecorator = require('*/cartridge/models/product/productBundle'); 85 | var productFactory = require('*/cartridge/scripts/factories/product'); 86 | 87 | // Prepare options for bundle 88 | var options = { 89 | productType: 'bundle', 90 | variationModel: variationModel, 91 | optionModel: optionModel, 92 | promotions: activePromotions, 93 | quantity: 1, 94 | variables: req.querystring, 95 | options: req.httpParameterMap 96 | }; 97 | 98 | // Create and decorate bundle product 99 | var product = {}; 100 | var bundleProduct = bundleProductDecorator(product, apiProduct, options, productFactory); 101 | 102 | // Access bundle-specific information 103 | console.log(bundleProduct.bundledProducts.length); 104 | console.log(bundleProduct.price.sales.formatted); 105 | console.log(bundleProduct.readyToOrder); 106 | 107 | // Check individual bundled products 108 | bundleProduct.bundledProducts.forEach(function(bundledProduct) { 109 | console.log(bundledProduct.productName + ' x ' + bundledProduct.quantity); 110 | }); 111 | ``` 112 | 113 | ## Bundle Structure 114 | 115 | After decoration, the bundle product contains: 116 | 117 | ### Standard Product Properties 118 | - **uuid** - Product UUID 119 | - **id** - Product ID 120 | - **productName** - Product name 121 | - **productType** - Product type 122 | - **brand** - Product brand 123 | - **price** - Comprehensive pricing with bundle discounts 124 | - **renderedPrice** - HTML-rendered price for display 125 | - **images** - Product images (large and small sizes) 126 | - **selectedQuantity** - Currently selected quantity 127 | - **minOrderQuantity** - Minimum order quantity 128 | - **maxOrderQuantity** - Maximum order quantity 129 | - **longDescription** - Product long description markup 130 | - **shortDescription** - Product short description markup 131 | - **rating** - Product rating value 132 | - **promotions** - Active promotion information 133 | - **attributes** - Product attribute groups and values 134 | - **availability** - Availability status and messages 135 | - **options** - Product option configurations 136 | - **quantities** - Available quantity selection options 137 | - **selectedProductUrl** - Current product URL with parameters 138 | 139 | ### Bundle-Specific Properties 140 | - **bundledProducts** - Array of individual product models in the bundle 141 | - **readyToOrder** - Boolean indicating if the entire bundle can be ordered 142 | - **sizeChartId** - Size chart identifier (if applicable) 143 | - **pageTitle** - SEO page title 144 | - **pageDescription** - SEO page description 145 | - **pageKeywords** - SEO page keywords 146 | - **pageMetaTags** - SEO meta tags array 147 | - **template** - Template identifier for rendering 148 | 149 | ## Notes 150 | 151 | - Handles complex bundle pricing calculations 152 | - Validates availability for all bundled products 153 | - Supports bundle-specific promotions and discounts 154 | - Maintains individual product information within the bundle 155 | - Provides comprehensive bundle readiness validation 156 | - Includes size chart support if any bundled products have size charts 157 | - Optimized for bundle product detail pages 158 | 159 | ## Related Models 160 | 161 | - **Product Factory** - Creates individual bundled product models 162 | - **Full Product Model** - Similar comprehensive decoration approach 163 | - **Product Set Model** - Alternative product grouping model 164 | - **Product Decorators** - Individual decoration functions 165 | ``` -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server-manager.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { spawn, ChildProcess } from 'child_process'; 2 | import path from 'path'; 3 | 4 | /** 5 | * Manager for the unified SFCC Mock Server for testing purposes 6 | * 7 | * This class provides utilities to start, stop, and interact with the unified 8 | * SFCC mock server that combines WebDAV and OCAPI functionality. 9 | */ 10 | export class SFCCMockServerManager { 11 | private serverProcess: ChildProcess | null = null; 12 | private readonly config: SFCCMockServerConfig; 13 | private readonly serverPath: string; 14 | private startupPromise: Promise<void> | null = null; 15 | 16 | constructor(config: Partial<SFCCMockServerConfig> = {}) { 17 | this.config = { 18 | port: 3000, 19 | host: 'localhost', 20 | dev: false, 21 | autoSetup: true, 22 | timeout: 10000, 23 | webdav: true, 24 | ocapi: true, 25 | cors: true, 26 | ...config, 27 | }; 28 | 29 | this.serverPath = path.join(__dirname, 'sfcc-mock-server'); 30 | } 31 | 32 | /** 33 | * Check if the SFCC mock server is available for testing 34 | */ 35 | async isServerAvailable(): Promise<boolean> { 36 | try { 37 | const fs = await import('fs'); 38 | const serverJs = path.join(this.serverPath, 'server.js'); 39 | const packageJson = path.join(this.serverPath, 'package.json'); 40 | 41 | return fs.existsSync(serverJs) && fs.existsSync(packageJson); 42 | } catch { 43 | return false; 44 | } 45 | } 46 | 47 | /** 48 | * Start the SFCC mock server 49 | */ 50 | async start(): Promise<void> { 51 | if (this.startupPromise) { 52 | return this.startupPromise; 53 | } 54 | 55 | this.startupPromise = this._startServer(); 56 | return this.startupPromise; 57 | } 58 | 59 | private async _startServer(): Promise<void> { 60 | if (this.serverProcess) { 61 | throw new Error('Server is already running'); 62 | } 63 | 64 | const isAvailable = await this.isServerAvailable(); 65 | if (!isAvailable) { 66 | throw new Error('SFCC mock server is not available. Run setup first.'); 67 | } 68 | 69 | // Setup logs if auto-setup is enabled 70 | if (this.config.autoSetup) { 71 | await this._runSetup(); 72 | } 73 | 74 | const args = this._buildServerArgs(); 75 | 76 | return new Promise((resolve, reject) => { 77 | const timeout = setTimeout(() => { 78 | this.stop(); 79 | reject(new Error(`Server failed to start within ${this.config.timeout}ms`)); 80 | }, this.config.timeout); 81 | 82 | this.serverProcess = spawn('node', ['server.js', ...args], { 83 | cwd: this.serverPath, 84 | stdio: this.config.dev ? 'inherit' : 'pipe', 85 | }); 86 | 87 | this.serverProcess.on('error', (error) => { 88 | clearTimeout(timeout); 89 | reject(error); 90 | }); 91 | 92 | this.serverProcess.on('exit', (code, signal) => { 93 | if (code !== null && code !== 0) { 94 | clearTimeout(timeout); 95 | reject(new Error(`Server exited with code ${code}`)); 96 | } 97 | }); 98 | 99 | // Wait a bit for the server to start up 100 | setTimeout(async () => { 101 | try { 102 | // Test if server is responding using the health endpoint 103 | const response = await fetch(`${this.getServerUrl()}/health`); 104 | if (response.ok) { 105 | clearTimeout(timeout); 106 | resolve(); 107 | } else { 108 | clearTimeout(timeout); 109 | reject(new Error(`Server health check failed with status ${response.status}`)); 110 | } 111 | } catch (error) { 112 | clearTimeout(timeout); 113 | reject(new Error(`Server health check failed: ${error}`)); 114 | } 115 | }, 2000); // Give server 2 seconds to start 116 | }); 117 | } 118 | 119 | private async _runSetup(): Promise<void> { 120 | return new Promise((resolve, reject) => { 121 | const setupProcess = spawn('npm', ['run', 'setup:logs'], { 122 | cwd: this.serverPath, 123 | stdio: 'pipe', 124 | }); 125 | 126 | setupProcess.on('error', reject); 127 | setupProcess.on('exit', (code) => { 128 | if (code === 0) { 129 | resolve(); 130 | } else { 131 | reject(new Error(`Setup failed with code ${code}`)); 132 | } 133 | }); 134 | }); 135 | } 136 | 137 | private _buildServerArgs(): string[] { 138 | const args: string[] = []; 139 | 140 | args.push('--port', this.config.port.toString()); 141 | args.push('--host', this.config.host); 142 | 143 | if (this.config.dev) { 144 | args.push('--dev'); 145 | } 146 | 147 | if (!this.config.webdav) { 148 | args.push('--no-webdav'); 149 | } 150 | 151 | if (!this.config.ocapi) { 152 | args.push('--no-ocapi'); 153 | } 154 | 155 | if (!this.config.cors) { 156 | args.push('--no-cors'); 157 | } 158 | 159 | return args; 160 | } 161 | 162 | /** 163 | * Stop the SFCC mock server 164 | */ 165 | async stop(): Promise<void> { 166 | return new Promise((resolve) => { 167 | if (!this.serverProcess) { 168 | resolve(); 169 | return; 170 | } 171 | 172 | const process = this.serverProcess; 173 | this.serverProcess = null; 174 | this.startupPromise = null; 175 | 176 | const timeout = setTimeout(() => { 177 | process.kill('SIGKILL'); 178 | resolve(); 179 | }, 5000); 180 | 181 | process.on('exit', () => { 182 | clearTimeout(timeout); 183 | resolve(); 184 | }); 185 | 186 | process.kill('SIGTERM'); 187 | }); 188 | } 189 | 190 | /** 191 | * Check if the server is currently running 192 | */ 193 | isRunning(): boolean { 194 | return this.serverProcess !== null && this.serverProcess.exitCode === null; 195 | } 196 | 197 | /** 198 | * Get the base server URL 199 | */ 200 | getServerUrl(): string { 201 | return `http://${this.config.host}:${this.config.port}`; 202 | } 203 | 204 | /** 205 | * Get the WebDAV logs URL (SFCC path) 206 | */ 207 | getWebDAVLogsUrl(): string { 208 | return `${this.getServerUrl()}/on/demandware.servlet/webdav/Sites/Logs/`; 209 | } 210 | 211 | /** 212 | * Get the direct logs URL 213 | */ 214 | getDirectLogsUrl(): string { 215 | return `${this.getServerUrl()}/Logs/`; 216 | } 217 | 218 | /** 219 | * Get the OCAPI base URL 220 | */ 221 | getOCAPIUrl(): string { 222 | return `${this.getServerUrl()}/s/-/dw/data/v23_2`; 223 | } 224 | 225 | /** 226 | * Get the OCAPI OAuth URL 227 | */ 228 | getOAuthUrl(): string { 229 | return `${this.getServerUrl()}/dw/oauth2/access_token`; 230 | } 231 | } 232 | 233 | /** 234 | * Configuration interface for the SFCC mock server manager 235 | */ 236 | export interface SFCCMockServerConfig { 237 | port: number; 238 | host: string; 239 | dev: boolean; 240 | autoSetup: boolean; 241 | timeout: number; 242 | webdav: boolean; 243 | ocapi: boolean; 244 | cors: boolean; 245 | } 246 | 247 | /** 248 | * Utility function to run a test with the SFCC mock server 249 | * 250 | * @param testFn Function to run with server URLs 251 | * @param config Optional server configuration 252 | * @returns Promise resolving to the test function result 253 | */ 254 | export async function withSFCCMockServer<T>( 255 | testFn: (serverUrl: string, webdavLogsUrl: string, directLogsUrl: string, ocapiUrl: string, oauthUrl: string) => Promise<T>, 256 | config: Partial<SFCCMockServerConfig> = {} 257 | ): Promise<T> { 258 | const manager = new SFCCMockServerManager({ 259 | port: 3004, // Use different port for utility function 260 | ...config, 261 | }); 262 | 263 | if (!await manager.isServerAvailable()) { 264 | throw new Error('SFCC mock server is not available'); 265 | } 266 | 267 | try { 268 | await manager.start(); 269 | 270 | return await testFn( 271 | manager.getServerUrl(), 272 | manager.getWebDAVLogsUrl(), 273 | manager.getDirectLogsUrl(), 274 | manager.getOCAPIUrl(), 275 | manager.getOAuthUrl() 276 | ); 277 | } finally { 278 | await manager.stop(); 279 | } 280 | } ``` -------------------------------------------------------------------------------- /docs/dw_rpc/Stub.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.rpc 2 | 3 | # Class Stub 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.rpc.Stub 9 | 10 | ## Description 11 | 12 | This is the base class for all service stubs accessible through a WebReference object. The Stub provides access to the WSDL operations. Demandware recommends a low timeout to ensure responsiveness of the site and to avoid thread exhaustion. Use the Services module in Business Manager to set timeout values, not the methods for this class. The Services module provides better analytics and timeout management. The default timeout, if not set, is 15 minutes when the web service is used in a job, and 2 minutes otherwise. If the timeout of the calling script is lower, the script timeout is used. // get WebReference var webref : WebReference = webreferences.myWSDLname; // get service stub var stub : Stub = webref.defaultService; 13 | 14 | ## Constants 15 | 16 | ### CONNECTION_TIMEOUT 17 | 18 | **Type:** String 19 | 20 | This property allows the user to set the web service connection timeout value in milliseconds. By default, the web service connection timeout is 5000 milliseconds (5 seconds). The minimum allowed value is 100 milliseconds and the maximum allowed value is 15000 milliseconds (15 seconds). Demandware recommends setting timeout values in Business Manager Services module as it provides better analytics and timeout management. 21 | 22 | ### ENDPOINT_ADDRESS_PROPERTY 23 | 24 | **Type:** String 25 | 26 | Standard property: target service endpoint address. The URI scheme for the endpoint address specification must correspond to the protocol/transport binding for this stub class. 27 | 28 | ### PASSWORD_PROPERTY 29 | 30 | **Type:** String 31 | 32 | Standard property: password for authentication. 33 | 34 | ### SESSION_MAINTAIN_PROPERTY 35 | 36 | **Type:** String 37 | 38 | Standard property: this boolean property is used by a service client to indicate whether or not it wants to participate in a session with a service endpoint. If this property is set to true, the service client indicates that it wants the session to be maintained. If set to false, the session is not maintained. The default value for this property is false. 39 | 40 | ### USERNAME_PROPERTY 41 | 42 | **Type:** String 43 | 44 | Standard property: user name for authentication. 45 | 46 | ## Properties 47 | 48 | ### password 49 | 50 | **Type:** String 51 | 52 | The password. 53 | 54 | ### timeout 55 | 56 | **Type:** Number 57 | 58 | The current read timeout value in milliseconds for this Stub. 59 | 60 | ### username 61 | 62 | **Type:** String 63 | 64 | The user name. 65 | 66 | Note: this method handles sensitive security-related data. 67 | Pay special attention to PCI DSS v3. requirements 2, 4, and 12. 68 | 69 | ## Constructor Summary 70 | 71 | Stub() 72 | 73 | ## Method Summary 74 | 75 | ### _getProperty 76 | 77 | **Signature:** `_getProperty(name : String) : Object` 78 | 79 | Gets the value of a specific configuration property. 80 | 81 | ### _setProperty 82 | 83 | **Signature:** `_setProperty(name : String, value : Object) : void` 84 | 85 | Sets the name and value of a configuration property for this Stub instance. 86 | 87 | ### getPassword 88 | 89 | **Signature:** `getPassword() : String` 90 | 91 | Returns the password. 92 | 93 | ### getTimeout 94 | 95 | **Signature:** `getTimeout() : Number` 96 | 97 | Returns the current read timeout value in milliseconds for this Stub. 98 | 99 | ### getUsername 100 | 101 | **Signature:** `getUsername() : String` 102 | 103 | Returns the user name. 104 | 105 | ### setHeader 106 | 107 | **Signature:** `setHeader(namespace : String, name : String, value : Object) : void` 108 | 109 | Sets an additional SOAP header value for the next operation. 110 | 111 | ### setPassword 112 | 113 | **Signature:** `setPassword(password : String) : void` 114 | 115 | Sets the password. 116 | 117 | ### setTimeout 118 | 119 | **Signature:** `setTimeout(timeout : Number) : void` 120 | 121 | Sets the timeout in milliseconds for the next call through this Stub. 122 | 123 | ### setUsername 124 | 125 | **Signature:** `setUsername(username : String) : void` 126 | 127 | Sets the user name. 128 | 129 | ## Constructor Detail 130 | 131 | ## Method Detail 132 | 133 | ## Method Details 134 | 135 | ### _getProperty 136 | 137 | **Signature:** `_getProperty(name : String) : Object` 138 | 139 | **Description:** Gets the value of a specific configuration property. 140 | 141 | **Deprecated:** 142 | 143 | use webreferences2 instead 144 | 145 | **Parameters:** 146 | 147 | - `name`: Name of the property whose value is to be retrieved 148 | 149 | **Returns:** 150 | 151 | Value of the configuration property 152 | 153 | --- 154 | 155 | ### _setProperty 156 | 157 | **Signature:** `_setProperty(name : String, value : Object) : void` 158 | 159 | **Description:** Sets the name and value of a configuration property for this Stub instance. If the Stub instance contains a value for the same property, the old value is replaced. Note: the _setProperty method may not perform a validity check on a configured property value. An example is the standard property for the target service endpoint address, which is not checked for validity in the _setProperty method. In this case, stub configuration errors are detected at the remote method invocation. 160 | 161 | **Deprecated:** 162 | 163 | use webreferences2 instead 164 | 165 | **Parameters:** 166 | 167 | - `name`: Name of the configuration property 168 | - `value`: Value of the property 169 | 170 | --- 171 | 172 | ### getPassword 173 | 174 | **Signature:** `getPassword() : String` 175 | 176 | **Description:** Returns the password. 177 | 178 | **Deprecated:** 179 | 180 | use webreferences2 instead 181 | 182 | **Returns:** 183 | 184 | the password. Note: this method handles sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, and 12. 185 | 186 | --- 187 | 188 | ### getTimeout 189 | 190 | **Signature:** `getTimeout() : Number` 191 | 192 | **Description:** Returns the current read timeout value in milliseconds for this Stub. 193 | 194 | **Deprecated:** 195 | 196 | use webreferences2 instead 197 | 198 | **Returns:** 199 | 200 | the current timeout value for this Stub. 201 | 202 | --- 203 | 204 | ### getUsername 205 | 206 | **Signature:** `getUsername() : String` 207 | 208 | **Description:** Returns the user name. Note: this method handles sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, and 12. 209 | 210 | **Deprecated:** 211 | 212 | use webreferences2 instead 213 | 214 | **Returns:** 215 | 216 | the user name. 217 | 218 | --- 219 | 220 | ### setHeader 221 | 222 | **Signature:** `setHeader(namespace : String, name : String, value : Object) : void` 223 | 224 | **Description:** Sets an additional SOAP header value for the next operation. 225 | 226 | **Deprecated:** 227 | 228 | use webreferences2 instead 229 | 230 | **Parameters:** 231 | 232 | - `namespace`: the namespace to use. 233 | - `name`: the name of the header item. 234 | - `value`: the value for the header item. 235 | 236 | --- 237 | 238 | ### setPassword 239 | 240 | **Signature:** `setPassword(password : String) : void` 241 | 242 | **Description:** Sets the password. 243 | 244 | **Deprecated:** 245 | 246 | use webreferences2 instead 247 | 248 | **Parameters:** 249 | 250 | - `password`: the password to set. 251 | 252 | --- 253 | 254 | ### setTimeout 255 | 256 | **Signature:** `setTimeout(timeout : Number) : void` 257 | 258 | **Description:** Sets the timeout in milliseconds for the next call through this Stub. This timeout value controls "read timeout" (how long, after connecting, it will wait without any data being read). To control "connection timeout" you use the _setProperty(String, Object) method where the name parameter is CONNECTION_TIMEOUT. 259 | 260 | **Deprecated:** 261 | 262 | use webreferences2 instead 263 | 264 | **Parameters:** 265 | 266 | - `timeout`: the timeout for the next call through this stub. 267 | 268 | **See Also:** 269 | 270 | _setProperty(String, Object) 271 | CONNECTION_TIMEOUT 272 | 273 | --- 274 | 275 | ### setUsername 276 | 277 | **Signature:** `setUsername(username : String) : void` 278 | 279 | **Description:** Sets the user name. 280 | 281 | **Deprecated:** 282 | 283 | use webreferences2 instead 284 | 285 | **Parameters:** 286 | 287 | - `username`: the user name to set. 288 | 289 | --- ``` -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server/mock-data/ocapi/site-preferences-sfra.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "_v": "23.2", 3 | "_type": "preference_value_search_result", 4 | "count": 4, 5 | "hits": [ 6 | { 7 | "_type": "preference_value", 8 | "attribute_definition": { 9 | "_type": "object_attribute_definition", 10 | "_resource_state": "sfra1-resource-state", 11 | "creation_date": "2024-01-01T00:00:00.000Z", 12 | "description": { 13 | "default": "Enable SFRA debug mode for development" 14 | }, 15 | "display_name": { 16 | "default": "SFRA Debug Mode" 17 | }, 18 | "effective_id": "c_sfraDebugMode", 19 | "externally_defined": false, 20 | "externally_managed": false, 21 | "id": "sfraDebugMode", 22 | "key": false, 23 | "last_modified": "2024-01-01T00:00:00.000Z", 24 | "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/sfraDebugMode", 25 | "localizable": false, 26 | "mandatory": false, 27 | "multi_value_type": false, 28 | "order_required": false, 29 | "queryable": false, 30 | "read_only": false, 31 | "requires_encoding": false, 32 | "searchable": false, 33 | "set_value_type": false, 34 | "site_specific": false, 35 | "system": false, 36 | "value_type": "boolean", 37 | "visible": true 38 | }, 39 | "description": { 40 | "default": "Enable SFRA debug mode for development" 41 | }, 42 | "display_name": { 43 | "default": "SFRA Debug Mode" 44 | }, 45 | "id": "sfraDebugMode", 46 | "site_values": { 47 | "RefArch": false, 48 | "RefArchGlobal": false, 49 | "pxl_1": true, 50 | "pxl_2": false, 51 | "pxl_3": null, 52 | "pxl_4": null, 53 | "pxl_5": null, 54 | "pxl_6": null 55 | }, 56 | "value_type": "boolean" 57 | }, 58 | { 59 | "_type": "preference_value", 60 | "attribute_definition": { 61 | "_type": "object_attribute_definition", 62 | "_resource_state": "sfra2-resource-state", 63 | "creation_date": "2024-01-01T00:00:00.000Z", 64 | "description": { 65 | "default": "Custom middleware configurations for SFRA" 66 | }, 67 | "display_name": { 68 | "default": "SFRA Middleware Config" 69 | }, 70 | "effective_id": "c_sfraMiddlewareConfig", 71 | "externally_defined": false, 72 | "externally_managed": false, 73 | "field_height": 8, 74 | "id": "sfraMiddlewareConfig", 75 | "key": false, 76 | "last_modified": "2024-01-01T00:00:00.000Z", 77 | "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/sfraMiddlewareConfig", 78 | "localizable": false, 79 | "mandatory": false, 80 | "multi_value_type": false, 81 | "order_required": false, 82 | "queryable": false, 83 | "read_only": false, 84 | "requires_encoding": false, 85 | "searchable": false, 86 | "set_value_type": false, 87 | "site_specific": true, 88 | "system": false, 89 | "value_type": "text", 90 | "visible": true 91 | }, 92 | "description": { 93 | "default": "Custom middleware configurations for SFRA" 94 | }, 95 | "display_name": { 96 | "default": "SFRA Middleware Config" 97 | }, 98 | "id": "sfraMiddlewareConfig", 99 | "site_values": { 100 | "RefArch": "{\"cache\": true, \"csrf\": true, \"compression\": false}", 101 | "RefArchGlobal": "{\"cache\": true, \"csrf\": true, \"compression\": true, \"logging\": \"debug\"}", 102 | "pxl_1": "", 103 | "pxl_2": "{\"cache\": false, \"csrf\": false}", 104 | "pxl_3": null, 105 | "pxl_4": null, 106 | "pxl_5": null, 107 | "pxl_6": null 108 | }, 109 | "value_type": "text" 110 | }, 111 | { 112 | "_type": "preference_value", 113 | "attribute_definition": { 114 | "_type": "object_attribute_definition", 115 | "_resource_state": "sfra3-resource-state", 116 | "creation_date": "2024-01-01T00:00:00.000Z", 117 | "description": { 118 | "default": "Maximum SFRA cache timeout in seconds" 119 | }, 120 | "display_name": { 121 | "default": "SFRA Cache Timeout" 122 | }, 123 | "effective_id": "c_sfraCacheTimeout", 124 | "externally_defined": false, 125 | "externally_managed": false, 126 | "id": "sfraCacheTimeout", 127 | "key": false, 128 | "last_modified": "2024-01-01T00:00:00.000Z", 129 | "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/sfraCacheTimeout", 130 | "localizable": false, 131 | "mandatory": false, 132 | "multi_value_type": false, 133 | "order_required": false, 134 | "queryable": false, 135 | "read_only": false, 136 | "requires_encoding": false, 137 | "searchable": false, 138 | "set_value_type": false, 139 | "site_specific": true, 140 | "system": false, 141 | "value_type": "int", 142 | "visible": true 143 | }, 144 | "description": { 145 | "default": "Maximum SFRA cache timeout in seconds" 146 | }, 147 | "display_name": { 148 | "default": "SFRA Cache Timeout" 149 | }, 150 | "id": "sfraCacheTimeout", 151 | "site_values": { 152 | "RefArch": 3600, 153 | "RefArchGlobal": 7200, 154 | "pxl_1": 0, 155 | "pxl_2": 1800, 156 | "pxl_3": null, 157 | "pxl_4": null, 158 | "pxl_5": null, 159 | "pxl_6": null 160 | }, 161 | "value_type": "int" 162 | }, 163 | { 164 | "_type": "preference_value", 165 | "attribute_definition": { 166 | "_type": "object_attribute_definition", 167 | "_resource_state": "sfra4-resource-state", 168 | "creation_date": "2024-01-01T00:00:00.000Z", 169 | "description": { 170 | "default": "Configuration with null values to test edge cases" 171 | }, 172 | "display_name": { 173 | "default": "Edge Case Config" 174 | }, 175 | "effective_id": "c_edgeCaseConfig", 176 | "externally_defined": false, 177 | "externally_managed": false, 178 | "id": "edgeCaseConfig", 179 | "key": false, 180 | "last_modified": "2024-01-01T00:00:00.000Z", 181 | "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/edgeCaseConfig", 182 | "localizable": false, 183 | "mandatory": false, 184 | "multi_value_type": false, 185 | "order_required": false, 186 | "queryable": false, 187 | "read_only": false, 188 | "requires_encoding": false, 189 | "searchable": false, 190 | "set_value_type": false, 191 | "site_specific": true, 192 | "system": false, 193 | "value_type": "text", 194 | "visible": false 195 | }, 196 | "description": { 197 | "default": "Configuration with null values to test edge cases" 198 | }, 199 | "display_name": { 200 | "default": "Edge Case Config" 201 | }, 202 | "id": "edgeCaseConfig", 203 | "site_values": { 204 | "RefArch": null, 205 | "RefArchGlobal": null, 206 | "pxl_1": null, 207 | "pxl_2": null, 208 | "pxl_3": null, 209 | "pxl_4": null, 210 | "pxl_5": null, 211 | "pxl_6": null 212 | }, 213 | "value_type": "text" 214 | } 215 | ], 216 | "query": { 217 | "match_all_query": { 218 | "_type": "match_all_query" 219 | } 220 | }, 221 | "select": "(**)", 222 | "start": 0, 223 | "total": 4 224 | } ``` -------------------------------------------------------------------------------- /src/core/server.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * MCP Server for SFCC Development 3 | * 4 | * This module implements the Model Context Protocol (MCP) server for accessing 5 | * Salesforce B2C Commerce Cloud development features. It provides a standardized interface 6 | * for AI assistants to interact with SFCC development tools and data. 7 | */ 8 | 9 | import { Server } from '@modelcontextprotocol/sdk/server/index.js'; 10 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 11 | import { 12 | CallToolRequestSchema, 13 | ListToolsRequestSchema, 14 | } from '@modelcontextprotocol/sdk/types.js'; 15 | import { SFCCConfig } from '../types/types.js'; 16 | import { Logger } from '../utils/logger.js'; 17 | import { ConfigurationFactory } from '../config/configuration-factory.js'; 18 | import { 19 | SFCC_DOCUMENTATION_TOOLS, 20 | BEST_PRACTICES_TOOLS, 21 | SFRA_DOCUMENTATION_TOOLS, 22 | LOG_TOOLS, 23 | JOB_LOG_TOOLS, 24 | SYSTEM_OBJECT_TOOLS, 25 | CARTRIDGE_GENERATION_TOOLS, 26 | CODE_VERSION_TOOLS, 27 | } from './tool-definitions.js'; 28 | 29 | // Modular tool handlers 30 | import { BaseToolHandler, HandlerContext } from './handlers/base-handler.js'; 31 | import { LogToolHandler } from './handlers/log-handler.js'; 32 | import { JobLogToolHandler } from './handlers/job-log-handler.js'; 33 | import { DocsToolHandler } from './handlers/docs-handler.js'; 34 | import { BestPracticesToolHandler } from './handlers/best-practices-handler.js'; 35 | import { SFRAToolHandler } from './handlers/sfra-handler.js'; 36 | import { SystemObjectToolHandler } from './handlers/system-object-handler.js'; 37 | import { CodeVersionToolHandler } from './handlers/code-version-handler.js'; 38 | import { CartridgeToolHandler } from './handlers/cartridge-handler.js'; 39 | /** 40 | * MCP Server implementation for SFCC development assistance 41 | * 42 | * This class sets up the MCP server, defines available tools, and handles 43 | * requests from MCP clients (like AI assistants) to interact with SFCC development features. 44 | */ 45 | export class SFCCDevServer { 46 | private server!: Server; 47 | private logger: Logger; 48 | private config: SFCCConfig; 49 | private capabilities: ReturnType<typeof ConfigurationFactory.getCapabilities>; 50 | private handlers: BaseToolHandler[] = []; 51 | 52 | /** 53 | * Initialize the SFCC Development MCP Server 54 | * 55 | * @param config - SFCC configuration for connecting to the logging system 56 | */ 57 | constructor(config: SFCCConfig) { 58 | this.logger = Logger.getChildLogger('Server'); 59 | this.config = config; 60 | this.logMethodEntry('constructor', { hostname: config.hostname }); 61 | this.capabilities = ConfigurationFactory.getCapabilities(config); 62 | this.initializeServer(); 63 | this.registerHandlers(); 64 | this.setupToolHandlers(); 65 | 66 | this.logMethodExit('constructor'); 67 | } 68 | 69 | private initializeServer(): void { 70 | this.server = new Server( 71 | { 72 | name: 'SFCC Development MCP Server', 73 | version: '1.0.14', // synced with package.json 74 | }, 75 | { 76 | capabilities: { 77 | tools: {}, 78 | }, 79 | }, 80 | ); 81 | } 82 | 83 | private logMethodEntry(methodName: string, params?: any): void { 84 | this.logger.methodEntry(methodName, params); 85 | } 86 | 87 | private logMethodExit(methodName: string, result?: any): void { 88 | this.logger.methodExit(methodName, result); 89 | } 90 | 91 | // Register modular handlers (each encapsulates its own responsibility) 92 | private registerHandlers(): void { 93 | const context: HandlerContext = { 94 | logger: this.logger, 95 | config: this.config, 96 | capabilities: this.capabilities, 97 | }; 98 | this.handlers = [ 99 | new LogToolHandler(context, 'Log'), 100 | new JobLogToolHandler(context, 'JobLog'), 101 | new DocsToolHandler(context, 'Docs'), 102 | new BestPracticesToolHandler(context, 'BestPractices'), 103 | new SFRAToolHandler(context, 'SFRA'), 104 | new SystemObjectToolHandler(context, 'SystemObjects'), 105 | new CodeVersionToolHandler(context, 'CodeVersions'), 106 | new CartridgeToolHandler(context, 'Cartridge'), 107 | ]; 108 | } 109 | 110 | /** 111 | * Set up MCP tool handlers for SFCC operations 112 | */ 113 | private setupToolHandlers(): void { 114 | this.server.setRequestHandler(ListToolsRequestSchema, async () => { 115 | const tools = []; 116 | 117 | // Always available tools 118 | tools.push(...SFCC_DOCUMENTATION_TOOLS); 119 | tools.push(...BEST_PRACTICES_TOOLS); 120 | tools.push(...SFRA_DOCUMENTATION_TOOLS); 121 | tools.push(...CARTRIDGE_GENERATION_TOOLS); 122 | 123 | // Conditional tools based on available capabilities 124 | if (this.capabilities.canAccessLogs) { 125 | tools.push(...LOG_TOOLS); 126 | tools.push(...JOB_LOG_TOOLS); 127 | } 128 | 129 | if (this.capabilities.canAccessOCAPI) { 130 | tools.push(...SYSTEM_OBJECT_TOOLS); 131 | tools.push(...CODE_VERSION_TOOLS); 132 | } 133 | 134 | return { tools }; 135 | }); 136 | 137 | this.server.setRequestHandler(CallToolRequestSchema, async (request): Promise<any> => { 138 | const { name, arguments: args } = request.params; 139 | const startTime = Date.now(); 140 | 141 | this.logger.methodEntry(`handleToolRequest:${name}`, args); 142 | 143 | try { 144 | const handler = this.handlers.find((h) => h.canHandle(name)); 145 | if (!handler) { 146 | this.logger.error(`Unknown tool requested: ${name}`); 147 | throw new Error(`Unknown tool: ${name}`); 148 | } 149 | const result = await handler.handle(name, args ?? {}, startTime); 150 | 151 | // Log the full response in debug mode 152 | this.logger.debug(`Full response for ${name}:`, { 153 | contentType: result.content?.[0]?.type, 154 | contentLength: result.content?.[0]?.text?.length ?? 0, 155 | responsePreview: result.content?.[0]?.text?.substring(0, 200) + (result.content?.[0]?.text?.length > 200 ? '...' : ''), 156 | fullResponse: result.content?.[0]?.text, 157 | }); 158 | 159 | return result as any; 160 | } catch (error) { 161 | this.logger.error(`Error handling tool "${name}":`, error); 162 | this.logger.timing(`${name}_error`, startTime); 163 | const errorResult = { 164 | content: [ 165 | { 166 | type: 'text', 167 | text: `Error: ${error instanceof Error ? error.message : String(error)}`, 168 | }, 169 | ], 170 | isError: true, 171 | }; 172 | 173 | // Log error response in debug mode 174 | this.logger.debug(`Error response for ${name}:`, errorResult); 175 | 176 | return errorResult as any; 177 | } finally { 178 | this.logger.methodExit(`handleToolRequest:${name}`); 179 | } 180 | }); 181 | } 182 | 183 | /** 184 | * Start the MCP server 185 | */ 186 | async run(): Promise<void> { 187 | const transport = new StdioServerTransport(); 188 | 189 | // Set up graceful shutdown 190 | process.on('SIGINT', () => this.shutdown()); 191 | process.on('SIGTERM', () => this.shutdown()); 192 | 193 | await this.server.connect(transport); 194 | this.logger.log('SFCC Development MCP server running on stdio'); 195 | } 196 | 197 | /** 198 | * Gracefully shutdown the server and dispose of resources 199 | */ 200 | private async shutdown(): Promise<void> { 201 | this.logger.log('Shutting down SFCC Development MCP server...'); 202 | 203 | // Dispose of all handlers 204 | await Promise.all(this.handlers.map(handler => handler.dispose())); 205 | 206 | this.logger.log('SFCC Development MCP server shutdown complete'); 207 | process.exit(0); 208 | } 209 | } 210 | ``` -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server/src/routes/ocapi/ocapi-error-utils.js: -------------------------------------------------------------------------------- ```javascript 1 | /** 2 | * OCAPI Error Utilities 3 | * 4 | * Utilities for generating SFCC-compliant error responses that match 5 | * the real OCAPI Data API error format and behavior. 6 | */ 7 | 8 | class OCAPIErrorUtils { 9 | /** 10 | * Create a standard SFCC fault response 11 | */ 12 | static createFaultResponse(type, message, args = {}, statusCode = 400, version = "23.2") { 13 | return { 14 | response: { 15 | "_v": version, 16 | "fault": { 17 | "arguments": args, 18 | "type": type, 19 | "message": message 20 | } 21 | }, 22 | statusCode: statusCode 23 | }; 24 | } 25 | 26 | /** 27 | * Create ObjectTypeNotFoundException (404) 28 | */ 29 | static createObjectTypeNotFound(objectType) { 30 | return this.createFaultResponse( 31 | "ObjectTypeNotFoundException", 32 | `No object type with ID '${objectType}' could be found.`, 33 | { objectType }, 34 | 404 35 | ); 36 | } 37 | 38 | /** 39 | * Create PropertyConstraintViolationException (400) 40 | */ 41 | static createPropertyConstraintViolation(path, document = "search_request") { 42 | return this.createFaultResponse( 43 | "PropertyConstraintViolationException", 44 | `An error occurred while decoding the request. There's a value constraint violation of property '${path}' in document '${document}'.`, 45 | { path, document }, 46 | 400 47 | ); 48 | } 49 | 50 | /** 51 | * Create EnumConstraintViolationException (400) 52 | */ 53 | static createEnumConstraintViolation(enumValue, document = "search_request") { 54 | return this.createFaultResponse( 55 | "EnumConstraintViolationException", 56 | `An error occurred while decoding the request. There's an unknown enum value '${enumValue}' in document '${document}'.`, 57 | { document, enumValue }, 58 | 400 59 | ); 60 | } 61 | 62 | /** 63 | * Create InvalidRequestException (400) 64 | */ 65 | static createInvalidRequest(message, field = null) { 66 | const args = field ? { field } : {}; 67 | return this.createFaultResponse( 68 | "InvalidRequestException", 69 | message, 70 | args, 71 | 400 72 | ); 73 | } 74 | 75 | /** 76 | * Create AuthenticationFailedException (401) 77 | */ 78 | static createAuthenticationFailed() { 79 | return this.createFaultResponse( 80 | "AuthenticationFailedException", 81 | "Authentication failed. Please check your credentials.", 82 | {}, 83 | 401 84 | ); 85 | } 86 | 87 | /** 88 | * Create InsufficientPermissionsException (403) 89 | */ 90 | static createInsufficientPermissions(permission = "access") { 91 | return this.createFaultResponse( 92 | "InsufficientPermissionsException", 93 | `You are not allowed to ${permission} the requested resource.`, 94 | { permission }, 95 | 403 96 | ); 97 | } 98 | 99 | /** 100 | * Create InternalServerException (500) 101 | */ 102 | static createInternalServerError(details = "An unexpected error occurred.") { 103 | return this.createFaultResponse( 104 | "InternalServerException", 105 | details, 106 | {}, 107 | 500 108 | ); 109 | } 110 | 111 | /** 112 | * Validate object type against known system objects 113 | */ 114 | static validateObjectType(objectType) { 115 | const validObjectTypes = [ 116 | 'Product', 'Customer', 'Order', 'Category', 'Site', 'SitePreferences', 117 | 'CustomerGroup', 'CustomerAddress', 'Profile', 'Basket', 'OrderPaymentInstrument', 118 | 'ProductOption', 'ProductVariationAttribute', 'PriceBook', 'Campaign', 119 | 'Promotion', 'Content', 'ContentSlot', 'Folder', 'Library' 120 | ]; 121 | 122 | if (!validObjectTypes.includes(objectType)) { 123 | return this.createObjectTypeNotFound(objectType); 124 | } 125 | return null; 126 | } 127 | 128 | /** 129 | * Validate search request structure 130 | */ 131 | static validateSearchRequest(searchRequest) { 132 | // Check if searchRequest exists 133 | if (!searchRequest || typeof searchRequest !== 'object') { 134 | return this.createPropertyConstraintViolation("$", "search_request"); 135 | } 136 | 137 | // Check if query exists 138 | if (!searchRequest.query) { 139 | return this.createPropertyConstraintViolation("$.query", "search_request"); 140 | } 141 | 142 | // Validate query type 143 | const validQueryTypes = ['text_query', 'term_query', 'filtered_query', 'bool_query', 'match_all_query']; 144 | const hasValidQuery = validQueryTypes.some(type => searchRequest.query[type]); 145 | 146 | if (!hasValidQuery) { 147 | return this.createInvalidRequest( 148 | "Search query must contain at least one of: text_query, term_query, filtered_query, bool_query, match_all_query" 149 | ); 150 | } 151 | 152 | return null; 153 | } 154 | 155 | /** 156 | * Validate pagination parameters 157 | */ 158 | static validatePagination(start, count) { 159 | // Validate start parameter 160 | if (start !== undefined && start !== null) { 161 | if (typeof start !== 'number' || start < 0) { 162 | return this.createInvalidRequest("start must be a positive number"); 163 | } 164 | } 165 | 166 | // Validate count parameter 167 | if (count !== undefined && count !== null) { 168 | if (typeof count !== 'number' || count < 0) { 169 | return this.createPropertyConstraintViolation("$.count", "search_request"); 170 | } 171 | if (count > 200) { 172 | return this.createPropertyConstraintViolation("$.count", "search_request"); 173 | } 174 | } 175 | 176 | return null; 177 | } 178 | 179 | /** 180 | * Validate text_query structure 181 | */ 182 | static validateTextQuery(textQuery) { 183 | if (!textQuery.fields || !Array.isArray(textQuery.fields) || textQuery.fields.length === 0) { 184 | return this.createPropertyConstraintViolation("$.query.text_query.fields", "search_request"); 185 | } 186 | 187 | if (!textQuery.search_phrase || typeof textQuery.search_phrase !== 'string') { 188 | return this.createPropertyConstraintViolation("$.query.text_query.search_phrase", "search_request"); 189 | } 190 | 191 | return null; 192 | } 193 | 194 | /** 195 | * Validate term_query structure 196 | */ 197 | static validateTermQuery(termQuery) { 198 | if (!termQuery.fields || !Array.isArray(termQuery.fields) || termQuery.fields.length === 0) { 199 | return this.createPropertyConstraintViolation("$.query.term_query.fields", "search_request"); 200 | } 201 | 202 | if (!termQuery.operator || typeof termQuery.operator !== 'string') { 203 | return this.createPropertyConstraintViolation("$.query.term_query.operator", "search_request"); 204 | } 205 | 206 | if (!termQuery.values || !Array.isArray(termQuery.values)) { 207 | return this.createPropertyConstraintViolation("$.query.term_query.values", "search_request"); 208 | } 209 | 210 | return null; 211 | } 212 | 213 | /** 214 | * Send error response 215 | */ 216 | static sendErrorResponse(res, errorInfo) { 217 | res.status(errorInfo.statusCode).json(errorInfo.response); 218 | } 219 | } 220 | 221 | module.exports = OCAPIErrorUtils; ``` -------------------------------------------------------------------------------- /tests/mcp/yaml/get-log-file-contents.full-mode.test.mcp.yml: -------------------------------------------------------------------------------- ```yaml 1 | --- 2 | description: "Test get_log_file_contents tool in full mode - optimized for YAML/aegis testing" 3 | tests: 4 | # === Basic Functionality === 5 | - it: "should get log file contents with proper structure" 6 | request: 7 | jsonrpc: "2.0" 8 | id: "get-contents-basic" 9 | method: "tools/call" 10 | params: 11 | name: "get_log_file_contents" 12 | arguments: 13 | filename: "jobs/ImportCatalog/Job-ImportCatalog-0987654321.log" 14 | expect: 15 | response: 16 | jsonrpc: "2.0" 17 | id: "get-contents-basic" 18 | result: 19 | content: "match:arrayLength:1" 20 | isError: false 21 | stderr: "toBeEmpty" 22 | performance: 23 | maxResponseTime: "1500ms" 24 | 25 | - it: "should include essential metadata in response" 26 | request: 27 | jsonrpc: "2.0" 28 | id: "get-contents-metadata" 29 | method: "tools/call" 30 | params: 31 | name: "get_log_file_contents" 32 | arguments: 33 | filename: "jobs/ImportCatalog/Job-ImportCatalog-0987654321.log" 34 | expect: 35 | response: 36 | jsonrpc: "2.0" 37 | id: "get-contents-metadata" 38 | result: 39 | content: 40 | match:arrayElements: 41 | type: "text" 42 | text: "match:contains:Log File Contents:" 43 | isError: false 44 | stderr: "toBeEmpty" 45 | performance: 46 | maxResponseTime: "1500ms" 47 | 48 | - it: "should include timestamps in SFCC format" 49 | request: 50 | jsonrpc: "2.0" 51 | id: "get-contents-timestamps" 52 | method: "tools/call" 53 | params: 54 | name: "get_log_file_contents" 55 | arguments: 56 | filename: "jobs/ImportCatalog/Job-ImportCatalog-0987654321.log" 57 | expect: 58 | response: 59 | jsonrpc: "2.0" 60 | id: "get-contents-timestamps" 61 | result: 62 | content: 63 | match:arrayElements: 64 | match:partial: 65 | text: "match:regex:\\[\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3} GMT\\]" 66 | isError: false 67 | stderr: "toBeEmpty" 68 | performance: 69 | maxResponseTime: "1500ms" 70 | 71 | # === Parameter Validation === 72 | - it: "should work with maxBytes parameter" 73 | request: 74 | jsonrpc: "2.0" 75 | id: "get-contents-maxbytes" 76 | method: "tools/call" 77 | params: 78 | name: "get_log_file_contents" 79 | arguments: 80 | filename: "jobs/ImportCatalog/Job-ImportCatalog-0987654321.log" 81 | maxBytes: 500 82 | expect: 83 | response: 84 | jsonrpc: "2.0" 85 | id: "get-contents-maxbytes" 86 | result: 87 | content: 88 | match:arrayElements: 89 | type: "text" 90 | text: "match:contains:Content size:" 91 | isError: false 92 | stderr: "toBeEmpty" 93 | performance: 94 | maxResponseTime: "1000ms" 95 | 96 | - it: "should work with tailOnly parameter" 97 | request: 98 | jsonrpc: "2.0" 99 | id: "get-contents-tailonly" 100 | method: "tools/call" 101 | params: 102 | name: "get_log_file_contents" 103 | arguments: 104 | filename: "jobs/ImportCatalog/Job-ImportCatalog-0987654321.log" 105 | tailOnly: true 106 | maxBytes: 200 107 | expect: 108 | response: 109 | jsonrpc: "2.0" 110 | id: "get-contents-tailonly" 111 | result: 112 | content: 113 | match:arrayElements: 114 | match:partial: 115 | text: "match:contains:tail read" 116 | isError: false 117 | stderr: "toBeEmpty" 118 | performance: 119 | maxResponseTime: "1000ms" 120 | 121 | # === Error Handling === 122 | - it: "should handle non-existent file gracefully" 123 | request: 124 | jsonrpc: "2.0" 125 | id: "get-contents-notfound" 126 | method: "tools/call" 127 | params: 128 | name: "get_log_file_contents" 129 | arguments: 130 | filename: "nonexistent-file.log" 131 | expect: 132 | response: 133 | jsonrpc: "2.0" 134 | id: "get-contents-notfound" 135 | result: 136 | content: 137 | match:arrayElements: 138 | match:partial: 139 | text: "match:contains:Failed to get_log_file_contents" 140 | isError: false 141 | stderr: "toBeEmpty" 142 | performance: 143 | maxResponseTime: "1500ms" 144 | 145 | - it: "should handle missing filename parameter" 146 | request: 147 | jsonrpc: "2.0" 148 | id: "get-contents-missing-filename" 149 | method: "tools/call" 150 | params: 151 | name: "get_log_file_contents" 152 | arguments: {} 153 | expect: 154 | response: 155 | jsonrpc: "2.0" 156 | id: "get-contents-missing-filename" 157 | result: 158 | content: 159 | match:arrayElements: 160 | match:partial: 161 | text: "match:contains:filename must be a non-empty string" 162 | isError: true 163 | stderr: "toBeEmpty" 164 | performance: 165 | maxResponseTime: "800ms" 166 | 167 | - it: "should handle empty filename parameter" 168 | request: 169 | jsonrpc: "2.0" 170 | id: "get-contents-empty-filename" 171 | method: "tools/call" 172 | params: 173 | name: "get_log_file_contents" 174 | arguments: 175 | filename: "" 176 | expect: 177 | response: 178 | jsonrpc: "2.0" 179 | id: "get-contents-empty-filename" 180 | result: 181 | content: 182 | match:arrayElements: 183 | match:partial: 184 | text: "match:contains:filename must be a non-empty string" 185 | isError: true 186 | stderr: "toBeEmpty" 187 | performance: 188 | maxResponseTime: "800ms" 189 | 190 | - it: "should handle invalid maxBytes parameter" 191 | request: 192 | jsonrpc: "2.0" 193 | id: "get-contents-invalid-maxbytes" 194 | method: "tools/call" 195 | params: 196 | name: "get_log_file_contents" 197 | arguments: 198 | filename: "jobs/ImportCatalog/Job-ImportCatalog-0987654321.log" 199 | maxBytes: 0 200 | expect: 201 | response: 202 | jsonrpc: "2.0" 203 | id: "get-contents-invalid-maxbytes" 204 | result: 205 | content: 206 | match:arrayElements: 207 | match:partial: 208 | text: "match:contains:Invalid maxBytes" 209 | isError: true 210 | stderr: "toBeEmpty" 211 | performance: 212 | maxResponseTime: "800ms" 213 | 214 | # === Content Structure Validation === 215 | - it: "should return exactly one content element" 216 | request: 217 | jsonrpc: "2.0" 218 | id: "get-contents-single-element" 219 | method: "tools/call" 220 | params: 221 | name: "get_log_file_contents" 222 | arguments: 223 | filename: "jobs/ImportCatalog/Job-ImportCatalog-0987654321.log" 224 | expect: 225 | response: 226 | jsonrpc: "2.0" 227 | id: "get-contents-single-element" 228 | result: 229 | content: "match:arrayLength:1" 230 | isError: "match:type:boolean" 231 | stderr: "toBeEmpty" 232 | performance: 233 | maxResponseTime: "1500ms" 234 | 235 | - it: "should return proper content types" 236 | request: 237 | jsonrpc: "2.0" 238 | id: "get-contents-types" 239 | method: "tools/call" 240 | params: 241 | name: "get_log_file_contents" 242 | arguments: 243 | filename: "jobs/ImportCatalog/Job-ImportCatalog-0987654321.log" 244 | expect: 245 | response: 246 | jsonrpc: "2.0" 247 | id: "get-contents-types" 248 | result: 249 | content: 250 | match:arrayElements: 251 | type: "text" 252 | text: "match:type:string" 253 | isError: "match:type:boolean" 254 | stderr: "toBeEmpty" 255 | performance: 256 | maxResponseTime: "1500ms" 257 | ``` -------------------------------------------------------------------------------- /src/clients/docs/documentation-scanner.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * Documentation Scanner 3 | * 4 | * Responsible for scanning the documentation directory structure and 5 | * discovering SFCC class documentation files with security validation. 6 | * 7 | * Single Responsibility: File system operations and documentation discovery 8 | */ 9 | 10 | import fs from 'fs/promises'; 11 | import path from 'path'; 12 | import { Logger } from '../../utils/logger.js'; 13 | 14 | export interface SFCCClassInfo { 15 | className: string; 16 | packageName: string; 17 | filePath: string; 18 | content: string; 19 | } 20 | 21 | export class DocumentationScanner { 22 | private logger: Logger; 23 | 24 | constructor() { 25 | this.logger = Logger.getChildLogger('DocumentationScanner'); 26 | } 27 | 28 | /** 29 | * Check if a directory name represents an SFCC-specific directory 30 | * SFCC directories include dw_ prefixed namespaces and TopLevel 31 | * Excludes best-practices and sfra directories 32 | */ 33 | private isSFCCDirectory(directoryName: string): boolean { 34 | // Include dw_ prefixed directories (SFCC namespaces) 35 | if (directoryName.startsWith('dw_')) { 36 | return true; 37 | } 38 | 39 | // Include TopLevel directory (contains core JavaScript classes) 40 | if (directoryName === 'TopLevel') { 41 | return true; 42 | } 43 | 44 | // Exclude best-practices directory (handled by best practices tools) 45 | if (directoryName === 'best-practices') { 46 | return false; 47 | } 48 | 49 | // Exclude sfra directory (handled by SFRA tools) 50 | if (directoryName === 'sfra') { 51 | return false; 52 | } 53 | 54 | // Exclude any other non-SFCC directories 55 | return false; 56 | } 57 | 58 | /** 59 | * Validate file name for security concerns 60 | */ 61 | private validateFileName(fileName: string): boolean { 62 | // Enhanced security validation - validate file name before path operations 63 | if (!fileName || typeof fileName !== 'string') { 64 | this.logger.warn(`Warning: Invalid file name type: ${fileName}`); 65 | return false; 66 | } 67 | 68 | // Prevent null bytes and dangerous characters in the file name itself 69 | if (fileName.includes('\0') || fileName.includes('\x00')) { 70 | this.logger.warn(`Warning: File name contains null bytes: ${fileName}`); 71 | return false; 72 | } 73 | 74 | // Prevent path traversal sequences in the file name 75 | if (fileName.includes('..') || fileName.includes('/') || fileName.includes('\\')) { 76 | this.logger.warn(`Warning: File name contains path traversal sequences: ${fileName}`); 77 | return false; 78 | } 79 | 80 | // Only allow alphanumeric characters, underscores, hyphens, and dots for file names 81 | if (!/^[a-zA-Z0-9_.-]+$/.test(fileName)) { 82 | this.logger.warn(`Warning: File name contains invalid characters: ${fileName}`); 83 | return false; 84 | } 85 | 86 | return true; 87 | } 88 | 89 | /** 90 | * Validate file path for security concerns 91 | */ 92 | private validateFilePath(filePath: string, packagePath: string, docsPath: string): boolean { 93 | // Additional security validation - ensure the resolved path is within the package directory 94 | const resolvedPath = path.resolve(filePath); 95 | const resolvedPackagePath = path.resolve(packagePath); 96 | const resolvedDocsPath = path.resolve(docsPath); 97 | 98 | // Ensure the file is within the package directory and docs directory 99 | if (!resolvedPath.startsWith(resolvedPackagePath) || !resolvedPath.startsWith(resolvedDocsPath)) { 100 | this.logger.warn(`Warning: File path outside allowed directory: ${filePath}`); 101 | return false; 102 | } 103 | 104 | // Ensure the file still ends with .md after path resolution 105 | if (!resolvedPath.toLowerCase().endsWith('.md')) { 106 | this.logger.warn(`Warning: File does not reference a markdown file: ${filePath}`); 107 | return false; 108 | } 109 | 110 | return true; 111 | } 112 | 113 | /** 114 | * Validate file content for security concerns 115 | */ 116 | private validateFileContent(content: string, fileName: string): boolean { 117 | // Basic content validation 118 | if (!content.trim()) { 119 | this.logger.warn(`Warning: Empty documentation file: ${fileName}`); 120 | return false; 121 | } 122 | 123 | // Check for binary content 124 | if (content.includes('\0')) { 125 | this.logger.warn(`Warning: Binary content detected in: ${fileName}`); 126 | return false; 127 | } 128 | 129 | return true; 130 | } 131 | 132 | /** 133 | * Read and validate a single documentation file 134 | */ 135 | private async readDocumentationFile( 136 | fileName: string, 137 | packagePath: string, 138 | packageName: string, 139 | docsPath: string, 140 | ): Promise<SFCCClassInfo | null> { 141 | if (!this.validateFileName(fileName)) { 142 | return null; 143 | } 144 | 145 | const className = fileName.replace('.md', ''); 146 | const filePath = path.join(packagePath, fileName); 147 | 148 | if (!this.validateFilePath(filePath, packagePath, docsPath)) { 149 | return null; 150 | } 151 | 152 | try { 153 | const resolvedPath = path.resolve(filePath); 154 | const content = await fs.readFile(resolvedPath, 'utf-8'); 155 | 156 | if (!this.validateFileContent(content, fileName)) { 157 | return null; 158 | } 159 | 160 | return { 161 | className, 162 | packageName, 163 | filePath, 164 | content, 165 | }; 166 | } catch (fileError) { 167 | this.logger.warn(`Warning: Could not read file ${fileName}: ${fileError}`); 168 | return null; 169 | } 170 | } 171 | 172 | /** 173 | * Scan a single package directory for documentation files 174 | */ 175 | private async scanPackageDirectory( 176 | packageName: string, 177 | packagePath: string, 178 | docsPath: string, 179 | ): Promise<SFCCClassInfo[]> { 180 | const classInfos: SFCCClassInfo[] = []; 181 | 182 | try { 183 | const files = await fs.readdir(packagePath); 184 | 185 | for (const file of files) { 186 | // Validate file name type and basic content before processing 187 | if (!file || typeof file !== 'string') { 188 | this.logger.warn(`Warning: Invalid file name type: ${file}`); 189 | continue; 190 | } 191 | 192 | if (file.endsWith('.md')) { 193 | const classInfo = await this.readDocumentationFile(file, packagePath, packageName, docsPath); 194 | if (classInfo) { 195 | classInfos.push(classInfo); 196 | } 197 | } 198 | } 199 | } catch (error) { 200 | this.logger.warn(`Warning: Could not read package ${packageName}: ${error}`); 201 | } 202 | 203 | return classInfos; 204 | } 205 | 206 | /** 207 | * Scan the docs directory and index all SFCC classes 208 | * Only scans SFCC-specific directories, excluding best-practices and sfra 209 | */ 210 | async scanDocumentation(docsPath: string): Promise<Map<string, SFCCClassInfo>> { 211 | const classCache = new Map<string, SFCCClassInfo>(); 212 | const packages = await fs.readdir(docsPath, { withFileTypes: true }); 213 | 214 | for (const packageDir of packages) { 215 | if (!packageDir.isDirectory()) { 216 | continue; 217 | } 218 | 219 | const packageName = packageDir.name; 220 | 221 | // Only scan SFCC-specific directories (dw_ prefixed and TopLevel) 222 | // Exclude best-practices and sfra directories which are handled by other tools 223 | if (!this.isSFCCDirectory(packageName)) { 224 | continue; 225 | } 226 | 227 | const packagePath = path.join(docsPath, packageName); 228 | const classInfos = await this.scanPackageDirectory(packageName, packagePath, docsPath); 229 | 230 | // Add to cache with normalized keys 231 | for (const classInfo of classInfos) { 232 | const cacheKey = `${packageName}.${classInfo.className}`; 233 | classCache.set(cacheKey, classInfo); 234 | } 235 | } 236 | 237 | return classCache; 238 | } 239 | } 240 | ``` -------------------------------------------------------------------------------- /docs/dw_catalog/SearchRefinements.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.catalog 2 | 3 | # Class SearchRefinements 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.catalog.SearchRefinements 9 | 10 | ## Description 11 | 12 | Common search refinements base class. 13 | 14 | ## Constants 15 | 16 | ### ASCENDING 17 | 18 | **Type:** Number = 0 19 | 20 | Flag for an ascending sort. 21 | 22 | ### DESCENDING 23 | 24 | **Type:** Number = 1 25 | 26 | Flag for a descending sort. 27 | 28 | ### SORT_VALUE_COUNT 29 | 30 | **Type:** Number = 1 31 | 32 | Flag for sorting on value count. 33 | 34 | ### SORT_VALUE_NAME 35 | 36 | **Type:** Number = 0 37 | 38 | Flag for sorting on value name. 39 | 40 | ## Properties 41 | 42 | ### allRefinementDefinitions 43 | 44 | **Type:** Collection (Read Only) 45 | 46 | A sorted list of refinement definitions that are appropriate for 47 | the deepest common category (or deepest common folder) of the search 48 | result. The method concatenates the sorted refinement definitions per 49 | category starting at the root category until reaching the deepest common 50 | category. 51 | 52 | The method does not filter out refinement definitions that do 53 | not provide values for the current search result and can therefore also 54 | be used on empty search results. 55 | 56 | ### refinementDefinitions 57 | 58 | **Type:** Collection (Read Only) 59 | 60 | A sorted list of refinement definitions that are appropriate for 61 | the deepest common category (or deepest common folder) of the search 62 | result. The method concatenates the sorted refinement definitions per category 63 | starting at the root category until reaching the deepest common category. 64 | 65 | The method also filters out refinement definitions that do not provide 66 | any values for the current search result. 67 | 68 | ## Constructor Summary 69 | 70 | ## Method Summary 71 | 72 | ### getAllRefinementDefinitions 73 | 74 | **Signature:** `getAllRefinementDefinitions() : Collection` 75 | 76 | Returns a sorted list of refinement definitions that are appropriate for the deepest common category (or deepest common folder) of the search result. 77 | 78 | ### getAllRefinementValues 79 | 80 | **Signature:** `getAllRefinementValues(attributeName : String) : Collection` 81 | 82 | Returns a sorted collection of refinement values for the given refinement attribute. 83 | 84 | ### getAllRefinementValues 85 | 86 | **Signature:** `getAllRefinementValues(attributeName : String, sortMode : Number, sortDirection : Number) : Collection` 87 | 88 | Returns a sorted collection of refinement values for the given refinement attribute. 89 | 90 | ### getRefinementDefinitions 91 | 92 | **Signature:** `getRefinementDefinitions() : Collection` 93 | 94 | Returns a sorted list of refinement definitions that are appropriate for the deepest common category (or deepest common folder) of the search result. 95 | 96 | ### getRefinementValues 97 | 98 | **Signature:** `getRefinementValues(attributeName : String, sortMode : Number, sortDirection : Number) : Collection` 99 | 100 | Returns a collection of refinement values for the given refinement attribute, sorting mode and sorting direction. 101 | 102 | ## Method Detail 103 | 104 | ## Method Details 105 | 106 | ### getAllRefinementDefinitions 107 | 108 | **Signature:** `getAllRefinementDefinitions() : Collection` 109 | 110 | **Description:** Returns a sorted list of refinement definitions that are appropriate for the deepest common category (or deepest common folder) of the search result. The method concatenates the sorted refinement definitions per category starting at the root category until reaching the deepest common category. The method does not filter out refinement definitions that do not provide values for the current search result and can therefore also be used on empty search results. 111 | 112 | **Returns:** 113 | 114 | A sorted list of refinement definitions appropriate for the search result (based on its deepest common category) 115 | 116 | --- 117 | 118 | ### getAllRefinementValues 119 | 120 | **Signature:** `getAllRefinementValues(attributeName : String) : Collection` 121 | 122 | **Description:** Returns a sorted collection of refinement values for the given refinement attribute. The returned collection includes all refinement values for which the hit count is greater than 0 within the search result when the passed attribute is excluded from filtering the search hits but all other refinement filters are still applied. This method is useful for rendering broadening options for attributes that the search is currently refined by. This method does NOT return refinement values independent of the search result. For product search refinements, this method may return slightly different results based on the "value set" property of the refinement definition. See ProductSearchRefinements.getAllRefinementValues(ProductSearchRefinementDefinition) for details. 123 | 124 | **Parameters:** 125 | 126 | - `attributeName`: The name of the attribute to return refinement values for. 127 | 128 | **Returns:** 129 | 130 | The collection of SearchRefinementValue instances, sorted according to the settings of the refinement definition, or null if there is no refinement definition for the passed attribute name. 131 | 132 | --- 133 | 134 | ### getAllRefinementValues 135 | 136 | **Signature:** `getAllRefinementValues(attributeName : String, sortMode : Number, sortDirection : Number) : Collection` 137 | 138 | **Description:** Returns a sorted collection of refinement values for the given refinement attribute. In general, the returned collection includes all refinement values for which hit count is greater than 0 within the search result assuming that: The passed refinement attribute is NOT used to filter the search hits. All other refinements are still applied. This is useful for rendering broadening options for the refinement definitions that the search is already refined by. It is important to note that this method does NOT return refinement values independent of the search result. For product search refinements, this method may return slightly different results based on the "value set" of the refinement definition. See ProductSearchRefinements.getAllRefinementValues(ProductSearchRefinementDefinition) for details. 139 | 140 | **Parameters:** 141 | 142 | - `attributeName`: The name of the attribute to return refinement values for. 143 | - `sortMode`: The sort mode to use to control how the collection is sorted. 144 | - `sortDirection`: The sort direction to use. 145 | 146 | **Returns:** 147 | 148 | The collection of SearchRefinementValue instances, sorted according to the passed parameters. 149 | 150 | --- 151 | 152 | ### getRefinementDefinitions 153 | 154 | **Signature:** `getRefinementDefinitions() : Collection` 155 | 156 | **Description:** Returns a sorted list of refinement definitions that are appropriate for the deepest common category (or deepest common folder) of the search result. The method concatenates the sorted refinement definitions per category starting at the root category until reaching the deepest common category. The method also filters out refinement definitions that do not provide any values for the current search result. 157 | 158 | **Returns:** 159 | 160 | A sorted list of refinement definitions appropriate for the search result (based on its deepest common category) 161 | 162 | --- 163 | 164 | ### getRefinementValues 165 | 166 | **Signature:** `getRefinementValues(attributeName : String, sortMode : Number, sortDirection : Number) : Collection` 167 | 168 | **Description:** Returns a collection of refinement values for the given refinement attribute, sorting mode and sorting direction. 169 | 170 | **Parameters:** 171 | 172 | - `attributeName`: The attribute name to use when collection refinement values. 173 | - `sortMode`: The sort mode to use to control how the collection is sorted. 174 | - `sortDirection`: The sort direction to use. 175 | 176 | **Returns:** 177 | 178 | The collection of refinement values. 179 | 180 | --- ``` -------------------------------------------------------------------------------- /docs/dw_campaign/Campaign.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.campaign 2 | 3 | # Class Campaign 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.campaign.Campaign 11 | 12 | ## Description 13 | 14 | A Campaign is a set of experiences (or site configurations) which may be deployed as a single unit for a given time frame. The system currently supports 3 types of experience that may be assigned to a campaign: Promotions Slot Configurations Sorting Rules This list may be extended in the future. A campaign can have a start and end date or be open-ended. It may also have "qualifiers" which determine which customers the campaign applies to. The currently supported qualifiers are: Customer groups (where "Everyone" is a possible customer group) Source codes Coupons A campaign can have list of stores or store groups where it can be applicable to. 15 | 16 | ## Properties 17 | 18 | ### active 19 | 20 | **Type:** boolean (Read Only) 21 | 22 | Returns 'true' if the campaign is currently active, otherwise 23 | 'false'. 24 | A campaign is active if it is enabled and scheduled for now. 25 | 26 | ### applicableInStore 27 | 28 | **Type:** boolean (Read Only) 29 | 30 | Returns true if campaign is applicable to store, otherwise false. 31 | 32 | ### applicableOnline 33 | 34 | **Type:** boolean (Read Only) 35 | 36 | Returns true if campaign is applicable to online site, otherwise false. 37 | 38 | ### coupons 39 | 40 | **Type:** Collection (Read Only) 41 | 42 | The coupons assigned to the campaign. 43 | 44 | ### customerGroups 45 | 46 | **Type:** Collection (Read Only) 47 | 48 | The customer groups assigned to the campaign. 49 | 50 | ### description 51 | 52 | **Type:** String (Read Only) 53 | 54 | The internal description of the campaign. 55 | 56 | ### enabled 57 | 58 | **Type:** boolean (Read Only) 59 | 60 | Returns true if campaign is enabled, otherwise false. 61 | 62 | ### endDate 63 | 64 | **Type:** Date (Read Only) 65 | 66 | The end date of the campaign. If no end date is defined for the 67 | campaign, null is returned. A campaign w/o end date will run forever. 68 | 69 | ### ID 70 | 71 | **Type:** String (Read Only) 72 | 73 | The unique campaign ID. 74 | 75 | ### promotions 76 | 77 | **Type:** Collection (Read Only) 78 | 79 | Returns promotions defined in this campaign in no particular order. 80 | 81 | ### sourceCodeGroups 82 | 83 | **Type:** Collection (Read Only) 84 | 85 | The source codes assigned to the campaign. 86 | 87 | ### startDate 88 | 89 | **Type:** Date (Read Only) 90 | 91 | The start date of the campaign. If no start date is defined for the 92 | campaign, null is returned. A campaign w/o start date is immediately 93 | effective. 94 | 95 | ### storeGroups 96 | 97 | **Type:** Collection (Read Only) 98 | 99 | Returns store groups assigned to the campaign. 100 | 101 | ### stores 102 | 103 | **Type:** Collection (Read Only) 104 | 105 | Returns stores assigned to the campaign. 106 | 107 | ## Constructor Summary 108 | 109 | ## Method Summary 110 | 111 | ### getCoupons 112 | 113 | **Signature:** `getCoupons() : Collection` 114 | 115 | Returns the coupons assigned to the campaign. 116 | 117 | ### getCustomerGroups 118 | 119 | **Signature:** `getCustomerGroups() : Collection` 120 | 121 | Returns the customer groups assigned to the campaign. 122 | 123 | ### getDescription 124 | 125 | **Signature:** `getDescription() : String` 126 | 127 | Returns the internal description of the campaign. 128 | 129 | ### getEndDate 130 | 131 | **Signature:** `getEndDate() : Date` 132 | 133 | Returns the end date of the campaign. 134 | 135 | ### getID 136 | 137 | **Signature:** `getID() : String` 138 | 139 | Returns the unique campaign ID. 140 | 141 | ### getPromotions 142 | 143 | **Signature:** `getPromotions() : Collection` 144 | 145 | Returns promotions defined in this campaign in no particular order. 146 | 147 | ### getSourceCodeGroups 148 | 149 | **Signature:** `getSourceCodeGroups() : Collection` 150 | 151 | Returns the source codes assigned to the campaign. 152 | 153 | ### getStartDate 154 | 155 | **Signature:** `getStartDate() : Date` 156 | 157 | Returns the start date of the campaign. 158 | 159 | ### getStoreGroups 160 | 161 | **Signature:** `getStoreGroups() : Collection` 162 | 163 | Returns store groups assigned to the campaign. 164 | 165 | ### getStores 166 | 167 | **Signature:** `getStores() : Collection` 168 | 169 | Returns stores assigned to the campaign. 170 | 171 | ### isActive 172 | 173 | **Signature:** `isActive() : boolean` 174 | 175 | Returns 'true' if the campaign is currently active, otherwise 'false'. 176 | 177 | ### isApplicableInStore 178 | 179 | **Signature:** `isApplicableInStore() : boolean` 180 | 181 | Returns true if campaign is applicable to store, otherwise false. 182 | 183 | ### isApplicableOnline 184 | 185 | **Signature:** `isApplicableOnline() : boolean` 186 | 187 | Returns true if campaign is applicable to online site, otherwise false. 188 | 189 | ### isEnabled 190 | 191 | **Signature:** `isEnabled() : boolean` 192 | 193 | Returns true if campaign is enabled, otherwise false. 194 | 195 | ## Method Detail 196 | 197 | ## Method Details 198 | 199 | ### getCoupons 200 | 201 | **Signature:** `getCoupons() : Collection` 202 | 203 | **Description:** Returns the coupons assigned to the campaign. 204 | 205 | **Returns:** 206 | 207 | All coupons assigned to the campaign. 208 | 209 | --- 210 | 211 | ### getCustomerGroups 212 | 213 | **Signature:** `getCustomerGroups() : Collection` 214 | 215 | **Description:** Returns the customer groups assigned to the campaign. 216 | 217 | **Returns:** 218 | 219 | Customer groups assigned to campaign. 220 | 221 | --- 222 | 223 | ### getDescription 224 | 225 | **Signature:** `getDescription() : String` 226 | 227 | **Description:** Returns the internal description of the campaign. 228 | 229 | **Returns:** 230 | 231 | Internal description of campaign. 232 | 233 | --- 234 | 235 | ### getEndDate 236 | 237 | **Signature:** `getEndDate() : Date` 238 | 239 | **Description:** Returns the end date of the campaign. If no end date is defined for the campaign, null is returned. A campaign w/o end date will run forever. 240 | 241 | **Returns:** 242 | 243 | End date of campaign. 244 | 245 | --- 246 | 247 | ### getID 248 | 249 | **Signature:** `getID() : String` 250 | 251 | **Description:** Returns the unique campaign ID. 252 | 253 | **Returns:** 254 | 255 | ID of the campaign. 256 | 257 | --- 258 | 259 | ### getPromotions 260 | 261 | **Signature:** `getPromotions() : Collection` 262 | 263 | **Description:** Returns promotions defined in this campaign in no particular order. 264 | 265 | **Returns:** 266 | 267 | All promotions defined in campaign. 268 | 269 | --- 270 | 271 | ### getSourceCodeGroups 272 | 273 | **Signature:** `getSourceCodeGroups() : Collection` 274 | 275 | **Description:** Returns the source codes assigned to the campaign. 276 | 277 | **Returns:** 278 | 279 | All source code groups assigned to campaign. 280 | 281 | --- 282 | 283 | ### getStartDate 284 | 285 | **Signature:** `getStartDate() : Date` 286 | 287 | **Description:** Returns the start date of the campaign. If no start date is defined for the campaign, null is returned. A campaign w/o start date is immediately effective. 288 | 289 | **Returns:** 290 | 291 | Start date of campaign. 292 | 293 | --- 294 | 295 | ### getStoreGroups 296 | 297 | **Signature:** `getStoreGroups() : Collection` 298 | 299 | **Description:** Returns store groups assigned to the campaign. 300 | 301 | **Returns:** 302 | 303 | All store groups assigned to the campaign. 304 | 305 | --- 306 | 307 | ### getStores 308 | 309 | **Signature:** `getStores() : Collection` 310 | 311 | **Description:** Returns stores assigned to the campaign. 312 | 313 | **Returns:** 314 | 315 | All stores assigned to the campaign. 316 | 317 | --- 318 | 319 | ### isActive 320 | 321 | **Signature:** `isActive() : boolean` 322 | 323 | **Description:** Returns 'true' if the campaign is currently active, otherwise 'false'. A campaign is active if it is enabled and scheduled for now. 324 | 325 | **Returns:** 326 | 327 | true of campaign is active, otherwise false. 328 | 329 | --- 330 | 331 | ### isApplicableInStore 332 | 333 | **Signature:** `isApplicableInStore() : boolean` 334 | 335 | **Description:** Returns true if campaign is applicable to store, otherwise false. 336 | 337 | **Returns:** 338 | 339 | true if campaign is applicable to store, otherwise false. 340 | 341 | --- 342 | 343 | ### isApplicableOnline 344 | 345 | **Signature:** `isApplicableOnline() : boolean` 346 | 347 | **Description:** Returns true if campaign is applicable to online site, otherwise false. 348 | 349 | **Returns:** 350 | 351 | true if campaign is applicable to online site, otherwise false. 352 | 353 | --- 354 | 355 | ### isEnabled 356 | 357 | **Signature:** `isEnabled() : boolean` 358 | 359 | **Description:** Returns true if campaign is enabled, otherwise false. 360 | 361 | **Returns:** 362 | 363 | true if campaign is enabled, otherwise false. 364 | 365 | --- ``` -------------------------------------------------------------------------------- /docs/dw_order/ProductShippingLineItem.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.order 2 | 3 | # Class ProductShippingLineItem 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.order.LineItem 11 | - dw.order.ProductShippingLineItem 12 | 13 | ## Description 14 | 15 | Represents a specific line item in a shipment. A ProductShippingLineItem defines lineitem-specific shipping costs. 16 | 17 | ## Constants 18 | 19 | ### PRODUCT_SHIPPING_ID 20 | 21 | **Type:** String = "PRODUCT_SHIPPING" 22 | 23 | Reserved constant. 24 | 25 | ## Properties 26 | 27 | ### adjustedGrossPrice 28 | 29 | **Type:** Money (Read Only) 30 | 31 | The gross price of the product shipping line item after applying 32 | all product-shipping-level adjustments. 33 | 34 | ### adjustedNetPrice 35 | 36 | **Type:** Money (Read Only) 37 | 38 | The net price of the product shipping line item after applying 39 | all product-shipping-level adjustments. 40 | 41 | ### adjustedPrice 42 | 43 | **Type:** Money (Read Only) 44 | 45 | The price of the product shipping line item after applying all 46 | pproduct-shipping-level adjustments. For net pricing the adjusted net 47 | price is returned (see getAdjustedNetPrice()). For gross 48 | pricing, the adjusted gross price is returned (see 49 | getAdjustedGrossPrice()). 50 | 51 | ### adjustedTax 52 | 53 | **Type:** Money (Read Only) 54 | 55 | The tax of the unit after applying adjustments, in the purchase 56 | currency. 57 | 58 | ### priceAdjustments 59 | 60 | **Type:** Collection (Read Only) 61 | 62 | An iterator of price adjustments that have been applied to this 63 | product shipping line item. 64 | 65 | ### productLineItem 66 | 67 | **Type:** ProductLineItem (Read Only) 68 | 69 | The parent product line item this shipping line item belongs to. 70 | 71 | ### quantity 72 | 73 | **Type:** Quantity 74 | 75 | The quantity of the shipping cost. 76 | 77 | ### shipment 78 | 79 | **Type:** Shipment (Read Only) 80 | 81 | The shipment this shipping line item belongs to. 82 | 83 | ### surcharge 84 | 85 | **Type:** boolean 86 | 87 | The 'surcharge' flag. 88 | 89 | ## Constructor Summary 90 | 91 | ## Method Summary 92 | 93 | ### getAdjustedGrossPrice 94 | 95 | **Signature:** `getAdjustedGrossPrice() : Money` 96 | 97 | Returns the gross price of the product shipping line item after applying all product-shipping-level adjustments. 98 | 99 | ### getAdjustedNetPrice 100 | 101 | **Signature:** `getAdjustedNetPrice() : Money` 102 | 103 | Returns the net price of the product shipping line item after applying all product-shipping-level adjustments. 104 | 105 | ### getAdjustedPrice 106 | 107 | **Signature:** `getAdjustedPrice() : Money` 108 | 109 | Returns the price of the product shipping line item after applying all pproduct-shipping-level adjustments. 110 | 111 | ### getAdjustedTax 112 | 113 | **Signature:** `getAdjustedTax() : Money` 114 | 115 | Returns the tax of the unit after applying adjustments, in the purchase currency. 116 | 117 | ### getPriceAdjustments 118 | 119 | **Signature:** `getPriceAdjustments() : Collection` 120 | 121 | Returns an iterator of price adjustments that have been applied to this product shipping line item. 122 | 123 | ### getProductLineItem 124 | 125 | **Signature:** `getProductLineItem() : ProductLineItem` 126 | 127 | Returns the parent product line item this shipping line item belongs to. 128 | 129 | ### getQuantity 130 | 131 | **Signature:** `getQuantity() : Quantity` 132 | 133 | Returns the quantity of the shipping cost. 134 | 135 | ### getShipment 136 | 137 | **Signature:** `getShipment() : Shipment` 138 | 139 | Returns the shipment this shipping line item belongs to. 140 | 141 | ### isSurcharge 142 | 143 | **Signature:** `isSurcharge() : boolean` 144 | 145 | Returns the 'surcharge' flag. 146 | 147 | ### setPriceValue 148 | 149 | **Signature:** `setPriceValue(value : Number) : void` 150 | 151 | Sets price attributes of the line item based on the purchase currency, taxation policy and line item quantity. The method sets the 'basePrice' attribute of the line item. 152 | 153 | ### setQuantity 154 | 155 | **Signature:** `setQuantity(quantity : Quantity) : void` 156 | 157 | Sets the quantity of the shipping cost. 158 | 159 | ### setSurcharge 160 | 161 | **Signature:** `setSurcharge(flag : boolean) : void` 162 | 163 | Sets the 'surcharge' flag. 164 | 165 | ## Method Detail 166 | 167 | ## Method Details 168 | 169 | ### getAdjustedGrossPrice 170 | 171 | **Signature:** `getAdjustedGrossPrice() : Money` 172 | 173 | **Description:** Returns the gross price of the product shipping line item after applying all product-shipping-level adjustments. 174 | 175 | **Returns:** 176 | 177 | gross price after applying product-shipping-level adjustments 178 | 179 | **See Also:** 180 | 181 | getAdjustedNetPrice() 182 | getAdjustedPrice() 183 | 184 | --- 185 | 186 | ### getAdjustedNetPrice 187 | 188 | **Signature:** `getAdjustedNetPrice() : Money` 189 | 190 | **Description:** Returns the net price of the product shipping line item after applying all product-shipping-level adjustments. 191 | 192 | **Returns:** 193 | 194 | net price after applying product-shipping-level adjustments 195 | 196 | **See Also:** 197 | 198 | getAdjustedGrossPrice() 199 | getAdjustedPrice() 200 | 201 | --- 202 | 203 | ### getAdjustedPrice 204 | 205 | **Signature:** `getAdjustedPrice() : Money` 206 | 207 | **Description:** Returns the price of the product shipping line item after applying all pproduct-shipping-level adjustments. For net pricing the adjusted net price is returned (see getAdjustedNetPrice()). For gross pricing, the adjusted gross price is returned (see getAdjustedGrossPrice()). 208 | 209 | **Returns:** 210 | 211 | Adjusted net or gross price 212 | 213 | **See Also:** 214 | 215 | getAdjustedGrossPrice() 216 | getAdjustedNetPrice() 217 | 218 | --- 219 | 220 | ### getAdjustedTax 221 | 222 | **Signature:** `getAdjustedTax() : Money` 223 | 224 | **Description:** Returns the tax of the unit after applying adjustments, in the purchase currency. 225 | 226 | **Returns:** 227 | 228 | the tax of the unit after applying adjustments, in the purchase currency. 229 | 230 | --- 231 | 232 | ### getPriceAdjustments 233 | 234 | **Signature:** `getPriceAdjustments() : Collection` 235 | 236 | **Description:** Returns an iterator of price adjustments that have been applied to this product shipping line item. 237 | 238 | **Returns:** 239 | 240 | a collection of price adjustments that have been applied to this product shipping line item. 241 | 242 | --- 243 | 244 | ### getProductLineItem 245 | 246 | **Signature:** `getProductLineItem() : ProductLineItem` 247 | 248 | **Description:** Returns the parent product line item this shipping line item belongs to. 249 | 250 | **Returns:** 251 | 252 | the product line item 253 | 254 | --- 255 | 256 | ### getQuantity 257 | 258 | **Signature:** `getQuantity() : Quantity` 259 | 260 | **Description:** Returns the quantity of the shipping cost. 261 | 262 | **Returns:** 263 | 264 | the shipping quantity 265 | 266 | --- 267 | 268 | ### getShipment 269 | 270 | **Signature:** `getShipment() : Shipment` 271 | 272 | **Description:** Returns the shipment this shipping line item belongs to. 273 | 274 | **Returns:** 275 | 276 | the shipment 277 | 278 | --- 279 | 280 | ### isSurcharge 281 | 282 | **Signature:** `isSurcharge() : boolean` 283 | 284 | **Description:** Returns the 'surcharge' flag. 285 | 286 | **Returns:** 287 | 288 | true if this is a surcharge shipping cost, false if fixed shipping cost 289 | 290 | --- 291 | 292 | ### setPriceValue 293 | 294 | **Signature:** `setPriceValue(value : Number) : void` 295 | 296 | **Description:** Sets price attributes of the line item based on the purchase currency, taxation policy and line item quantity. The method sets the 'basePrice' attribute of the line item. Additionally, it sets the 'netPrice' attribute of the line item if the current taxation policy is 'net', and the 'grossPrice' attribute, if the current taxation policy is 'gross'. The 'netPrice'/'grossPrice' attributes are set by multiplying the specified price value with the line item quantity. If null is specified as value, the price attributes are reset to Money.NA. 297 | 298 | **Parameters:** 299 | 300 | - `value`: Price value or null 301 | 302 | --- 303 | 304 | ### setQuantity 305 | 306 | **Signature:** `setQuantity(quantity : Quantity) : void` 307 | 308 | **Description:** Sets the quantity of the shipping cost. 309 | 310 | **Parameters:** 311 | 312 | - `quantity`: the shipping quantity 313 | 314 | --- 315 | 316 | ### setSurcharge 317 | 318 | **Signature:** `setSurcharge(flag : boolean) : void` 319 | 320 | **Description:** Sets the 'surcharge' flag. 321 | 322 | **Parameters:** 323 | 324 | - `flag`: true if this is a surcharge shipping cost, false if this is a fixed shipping cost. 325 | 326 | --- ``` -------------------------------------------------------------------------------- /docs/sfra/product-search.md: -------------------------------------------------------------------------------- ```markdown 1 | # SFRA Product Search Model 2 | 3 | ## Overview 4 | 5 | The Product Search model represents search functionality in SFRA applications, providing comprehensive search results, refinements, sorting options, and pagination for product discovery and catalog browsing. 6 | 7 | ## Constructor 8 | 9 | ```javascript 10 | function ProductSearch(productSearch, httpParams, sortingRule, sortingOptions, rootCategory) 11 | ``` 12 | 13 | Creates a Product Search model instance with search results and related functionality. 14 | 15 | ### Parameters 16 | 17 | - `productSearch` (dw.catalog.ProductSearchModel) - Product search object from the API 18 | - `httpParams` (Object) - HTTP query parameters including search terms and refinements 19 | - `sortingRule` (string) - Current sorting rule being applied 20 | - `sortingOptions` (dw.util.ArrayList<dw.catalog.SortingOption>) - Options to sort search results 21 | - `rootCategory` (dw.catalog.Category) - Search result's root category if applicable 22 | 23 | ## Properties 24 | 25 | ### searchKeywords 26 | **Type:** string 27 | 28 | The search keywords entered by the user. 29 | 30 | ### category 31 | **Type:** Object | null 32 | 33 | Current category information if this is a category-based search. 34 | 35 | ### productIds 36 | **Type:** Array<Object> 37 | 38 | Array of product information objects with `productID` and `productSearchHit` properties for search results. 39 | 40 | ### products 41 | **Type:** Array<Object> 42 | 43 | Array of product tile models for the search results. **Note:** This property is not directly set by the constructor but would be populated by calling code using the `productIds` array. 44 | 45 | ### pageNumber 46 | **Type:** number 47 | 48 | Current page number for pagination (1-based). 49 | 50 | ### pageSize 51 | **Type:** number 52 | 53 | Number of products displayed per page (from httpParams.sz or default). 54 | 55 | ### count 56 | **Type:** number 57 | 58 | Total number of products found matching the search criteria. 59 | 60 | ### isCategorySearch 61 | **Type:** boolean 62 | 63 | Indicates whether this is a category-based search. 64 | 65 | ### isRefinedCategorySearch 66 | **Type:** boolean 67 | 68 | Indicates whether this is a refined category search. 69 | 70 | ### resetLink 71 | **Type:** string 72 | 73 | URL to reset all search refinements (AJAX endpoint). 74 | 75 | ### resetLinkSeo 76 | **Type:** string 77 | 78 | SEO-friendly URL to reset all search refinements. 79 | 80 | ### refinements 81 | **Type:** Array<Object> (computed property) 82 | 83 | Array of available search refinements including: 84 | - `displayName` - Human-readable refinement name 85 | - `isCategoryRefinement` - Boolean flag for category refinements 86 | - `isAttributeRefinement` - Boolean flag for attribute refinements 87 | - `isPriceRefinement` - Boolean flag for price refinements 88 | - `isPromotionRefinement` - Boolean flag for promotion refinements 89 | - `values` - Array of refinement value objects 90 | 91 | ### selectedFilters 92 | **Type:** Array<Object> (computed property) 93 | 94 | Array of currently applied refinements extracted from the refinements. 95 | 96 | ### productSort 97 | **Type:** ProductSortOptions 98 | 99 | Available sorting options for the search results (ProductSortOptions model instance). 100 | 101 | ### bannerImageUrl 102 | **Type:** string | null 103 | 104 | URL for category banner image (null if not available). 105 | 106 | ### showMoreUrl 107 | **Type:** string 108 | 109 | URL to load more search results (for infinite scroll or "show more" functionality). 110 | 111 | ### permalink 112 | **Type:** string 113 | 114 | Permalink URL for the current search with filters and pagination preserved. 115 | 116 | ### isSearchSuggestionsAvailable 117 | **Type:** boolean 118 | 119 | Indicates whether search suggestions are available for the current search. 120 | 121 | ### suggestionPhrases 122 | **Type:** Array<Object> 123 | 124 | Array of suggested search phrases with `value` and `url` properties (only available when `isSearchSuggestionsAvailable` is true). 125 | 126 | ### category 127 | **Type:** Object | null 128 | 129 | Current category information if this is a category-based search, including: 130 | - `name` - Category display name 131 | - `id` - Category ID 132 | - `pageTitle` - Category page title 133 | - `pageDescription` - Category page description 134 | - `pageKeywords` - Category page keywords 135 | 136 | ### pageMetaTags 137 | **Type:** Object 138 | 139 | Page meta tags for SEO purposes. 140 | 141 | ## Helper Functions 142 | 143 | ### getResetLink(search, httpParams, actionEndpoint) 144 | Generates URL to remove all refinements and reset search criteria. 145 | 146 | **Parameters:** 147 | - `search` (dw.catalog.ProductSearchModel) - Product search object 148 | - `httpParams` (Object) - Query parameters 149 | - `actionEndpoint` (string) - Action endpoint to use 150 | 151 | **Returns:** string - URL to reset search refinements 152 | 153 | ### getRefinements(productSearch, refinements, refinementDefinitions) 154 | Retrieves and formats search refinements for display. 155 | 156 | **Parameters:** 157 | - `productSearch` (dw.catalog.ProductSearchModel) - Product search object 158 | - `refinements` (dw.catalog.ProductSearchRefinements) - Search refinements 159 | - `refinementDefinitions` (ArrayList) - Refinement definitions 160 | 161 | **Returns:** Array<Object> - Formatted refinement objects 162 | 163 | ## Usage Example 164 | 165 | ```javascript 166 | var ProductSearch = require('*/cartridge/models/search/productSearch'); 167 | 168 | // From search controller 169 | var productSearch = new ProductSearch( 170 | apiProductSearch, 171 | req.querystring, 172 | sortingRule, 173 | sortingOptions, 174 | rootCategory 175 | ); 176 | 177 | // Access search results 178 | console.log('Found ' + productSearch.count + ' products'); 179 | console.log('Search term: ' + productSearch.searchKeywords); 180 | 181 | // Display product IDs (products would be populated separately) 182 | productSearch.productIds.forEach(function(item) { 183 | console.log('Product ID: ' + item.productID); 184 | }); 185 | 186 | // Handle refinements 187 | productSearch.refinements.forEach(function(refinement) { 188 | console.log(refinement.displayName + ':'); 189 | refinement.values.forEach(function(value) { 190 | console.log(' ' + value.displayValue + ' (' + value.hitCount + ')'); 191 | }); 192 | }); 193 | 194 | // Pagination 195 | console.log('Page ' + productSearch.pageNumber + ', showing ' + productSearch.pageSize + ' items'); 196 | 197 | // Check for search suggestions 198 | if (productSearch.isSearchSuggestionsAvailable) { 199 | productSearch.suggestionPhrases.forEach(function(phrase) { 200 | console.log('Suggested: ' + phrase.value); 201 | }); 202 | } 203 | ``` 204 | 205 | ## Search Types 206 | 207 | ### Keyword Search 208 | - Based on search terms entered by users 209 | - Full-text search across product attributes 210 | - Includes search suggestions and spell correction 211 | 212 | ### Category Search 213 | - Browse products within specific categories 214 | - Maintains category hierarchy navigation 215 | - Category-specific refinements and sorting 216 | 217 | ### Refined Search 218 | - Apply filters to narrow results 219 | - Multiple refinement types (price, attributes, categories) 220 | - Combinable refinements with removal options 221 | 222 | ## Refinement Types 223 | 224 | The model supports various refinement types: 225 | - **Category Refinements** - Filter by product categories 226 | - **Attribute Refinements** - Filter by product attributes (color, size, brand) 227 | - **Price Refinements** - Filter by price ranges 228 | - **Promotion Refinements** - Filter by promotional offers 229 | 230 | ## Notes 231 | 232 | - Provides comprehensive search functionality for SFRA storefronts 233 | - Handles both keyword and category-based searches 234 | - Includes pagination support with configurable page sizes 235 | - Supports multiple refinement types with hit counts 236 | - Integrates with product tile models for consistent display 237 | - Includes sorting options and search result optimization 238 | - Provides URLs for search refinement and reset functionality 239 | 240 | ## Related Models 241 | 242 | - **ProductSortOptions** - Used for search result sorting 243 | - **Product Tile Model** - Used for individual product display 244 | - **Categories Model** - Used for category-based searches 245 | - **Refinement Models** - Used for search filtering 246 | ``` -------------------------------------------------------------------------------- /docs/dw_util/Locale.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.util 2 | 3 | # Class Locale 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.util.Locale 9 | 10 | ## Description 11 | 12 | Represents a Locale supported by the system. 13 | 14 | ## Properties 15 | 16 | ### country 17 | 18 | **Type:** String (Read Only) 19 | 20 | The uppercase ISO 3166 2-letter country/region code for this Locale. 21 | If no country has been specified for this Locale, this value is an empty string. 22 | 23 | ### displayCountry 24 | 25 | **Type:** String (Read Only) 26 | 27 | The display name of this Locale's country, in this Locale's language, 28 | not in the session locale's language. 29 | If no country has been specified for this Locale, this value is an empty string. 30 | 31 | ### displayLanguage 32 | 33 | **Type:** String (Read Only) 34 | 35 | The display name of this Locale's language, in this Locale's language, 36 | not in the session locale's language. 37 | If no country has been specified for this Locale, this value is an empty string. 38 | 39 | ### displayName 40 | 41 | **Type:** String (Read Only) 42 | 43 | The display name of this Locale, in this Locale's language, 44 | not in the session locale's language. 45 | If no display name has been specified for this Locale, this value is an empty string. 46 | 47 | ### ID 48 | 49 | **Type:** String (Read Only) 50 | 51 | The String representation of the localeID. 52 | 53 | Combines the language and the country key, concatenated with "_". 54 | For example: "en_US". This attribute is the primary key of the class. 55 | 56 | ### ISO3Country 57 | 58 | **Type:** String (Read Only) 59 | 60 | The uppercase ISO 3166 3-letter country/region code for this Locale. 61 | If no country has been specified for this Locale, this value is an empty string. 62 | 63 | ### ISO3Language 64 | 65 | **Type:** String (Read Only) 66 | 67 | The 3-letter ISO 639 language code for this Locale. 68 | If no language has been specified for this Locale, this value is an empty string. 69 | 70 | ### language 71 | 72 | **Type:** String (Read Only) 73 | 74 | The lowercase ISO 639 language code for this Locale. 75 | If no language has been specified for this Locale, this value is an empty string. 76 | 77 | ## Constructor Summary 78 | 79 | ## Method Summary 80 | 81 | ### getCountry 82 | 83 | **Signature:** `getCountry() : String` 84 | 85 | Returns the uppercase ISO 3166 2-letter country/region code for this Locale. 86 | 87 | ### getDisplayCountry 88 | 89 | **Signature:** `getDisplayCountry() : String` 90 | 91 | Returns the display name of this Locale's country, in this Locale's language, not in the session locale's language. 92 | 93 | ### getDisplayLanguage 94 | 95 | **Signature:** `getDisplayLanguage() : String` 96 | 97 | Returns the display name of this Locale's language, in this Locale's language, not in the session locale's language. 98 | 99 | ### getDisplayName 100 | 101 | **Signature:** `getDisplayName() : String` 102 | 103 | Returns the display name of this Locale, in this Locale's language, not in the session locale's language. 104 | 105 | ### getID 106 | 107 | **Signature:** `getID() : String` 108 | 109 | Returns the String representation of the localeID. 110 | 111 | ### getISO3Country 112 | 113 | **Signature:** `getISO3Country() : String` 114 | 115 | Returns the uppercase ISO 3166 3-letter country/region code for this Locale. 116 | 117 | ### getISO3Language 118 | 119 | **Signature:** `getISO3Language() : String` 120 | 121 | Returns the 3-letter ISO 639 language code for this Locale. 122 | 123 | ### getLanguage 124 | 125 | **Signature:** `getLanguage() : String` 126 | 127 | Returns the lowercase ISO 639 language code for this Locale. 128 | 129 | ### getLocale 130 | 131 | **Signature:** `static getLocale(localeId : String) : Locale` 132 | 133 | Returns a Locale instance for the given localeId, or null if no such Locale could be found. 134 | 135 | ### toString 136 | 137 | **Signature:** `toString() : String` 138 | 139 | Returns the String representation of the localeID. 140 | 141 | ## Method Detail 142 | 143 | ## Method Details 144 | 145 | ### getCountry 146 | 147 | **Signature:** `getCountry() : String` 148 | 149 | **Description:** Returns the uppercase ISO 3166 2-letter country/region code for this Locale. If no country has been specified for this Locale, this value is an empty string. 150 | 151 | **Returns:** 152 | 153 | the uppercase ISO 3166 2-letter country/region code for this Locale. If no country has been specified for this Locale, this value is an empty string. 154 | 155 | --- 156 | 157 | ### getDisplayCountry 158 | 159 | **Signature:** `getDisplayCountry() : String` 160 | 161 | **Description:** Returns the display name of this Locale's country, in this Locale's language, not in the session locale's language. If no country has been specified for this Locale, this value is an empty string. 162 | 163 | **Returns:** 164 | 165 | the display name of this Locale's country, in this Locale's language. If no country has been specified for this Locale, this value is an empty string. 166 | 167 | --- 168 | 169 | ### getDisplayLanguage 170 | 171 | **Signature:** `getDisplayLanguage() : String` 172 | 173 | **Description:** Returns the display name of this Locale's language, in this Locale's language, not in the session locale's language. If no country has been specified for this Locale, this value is an empty string. 174 | 175 | **Returns:** 176 | 177 | the display name of this Locale's language, in this Locale's language. If no language has been specified for this Locale, this value is an empty string. 178 | 179 | --- 180 | 181 | ### getDisplayName 182 | 183 | **Signature:** `getDisplayName() : String` 184 | 185 | **Description:** Returns the display name of this Locale, in this Locale's language, not in the session locale's language. If no display name has been specified for this Locale, this value is an empty string. 186 | 187 | **Returns:** 188 | 189 | the display name of this Locale, in this Locale's language. If no display name has been specified for this Locale, this value is an empty string. 190 | 191 | --- 192 | 193 | ### getID 194 | 195 | **Signature:** `getID() : String` 196 | 197 | **Description:** Returns the String representation of the localeID. Combines the language and the country key, concatenated with "_". For example: "en_US". This attribute is the primary key of the class. 198 | 199 | **Returns:** 200 | 201 | the String representation of the localeID. 202 | 203 | --- 204 | 205 | ### getISO3Country 206 | 207 | **Signature:** `getISO3Country() : String` 208 | 209 | **Description:** Returns the uppercase ISO 3166 3-letter country/region code for this Locale. If no country has been specified for this Locale, this value is an empty string. 210 | 211 | **Returns:** 212 | 213 | the uppercase ISO 3166 3-letter country/region code for this Locale. If no country has been specified for this Locale, this value is an empty string. 214 | 215 | --- 216 | 217 | ### getISO3Language 218 | 219 | **Signature:** `getISO3Language() : String` 220 | 221 | **Description:** Returns the 3-letter ISO 639 language code for this Locale. If no language has been specified for this Locale, this value is an empty string. 222 | 223 | **Returns:** 224 | 225 | the 3-letter ISO 639 language code for this Locale. If no language has been specified for this Locale, this value is an empty string. 226 | 227 | --- 228 | 229 | ### getLanguage 230 | 231 | **Signature:** `getLanguage() : String` 232 | 233 | **Description:** Returns the lowercase ISO 639 language code for this Locale. If no language has been specified for this Locale, this value is an empty string. 234 | 235 | **Returns:** 236 | 237 | the lowercase ISO 639 language code for this Locale. If no language has been specified for this Locale, this value is an empty string. 238 | 239 | --- 240 | 241 | ### getLocale 242 | 243 | **Signature:** `static getLocale(localeId : String) : Locale` 244 | 245 | **Description:** Returns a Locale instance for the given localeId, or null if no such Locale could be found. 246 | 247 | **Parameters:** 248 | 249 | - `localeId`: the localeID of the desired Locale 250 | 251 | **Returns:** 252 | 253 | the Locale instance for the given localeId, or null if no such Locale could be found. 254 | 255 | --- 256 | 257 | ### toString 258 | 259 | **Signature:** `toString() : String` 260 | 261 | **Description:** Returns the String representation of the localeID. Combines the language and the country key, concatenated with "_". For example: "en_US". This attribute is the primary key of the class. 262 | 263 | **Returns:** 264 | 265 | the String representation of the localeID. 266 | 267 | --- ``` -------------------------------------------------------------------------------- /docs/dw_system/Status.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.system 2 | 3 | # Class Status 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.system.Status 9 | 10 | ## Description 11 | 12 | A Status is used for communicating an API status code back to a client. A status consists of multiple StatusItem. Most often a Status contains only one StatusItem. For convenience, a message with parameters is formatted using standard formatting patterns. If you want to display locale-specific messages in your application, you should use the Status.getCode() as key for a resource bundle. 13 | 14 | ## Properties 15 | 16 | ### code 17 | 18 | **Type:** String (Read Only) 19 | 20 | The status code either of the first ERROR StatusItem or when there 21 | is no ERROR StatusITEM, the first StatusItem in the overall list. 22 | 23 | The status code is the unique identifier for the message and can be used by 24 | client programs to check for a specific status and to generate a localized 25 | message. 26 | 27 | ### details 28 | 29 | **Type:** Map (Read Only) 30 | 31 | The details either of the first ERROR StatusItem or when there 32 | is no ERROR StatusItem, the first StatusItem in the overall list. 33 | 34 | ### error 35 | 36 | **Type:** boolean (Read Only) 37 | 38 | Checks if the status is an ERROR. The Status is an ERROR if one of the 39 | contained StatusItems is an ERROR. 40 | 41 | ### items 42 | 43 | **Type:** List (Read Only) 44 | 45 | All status items. 46 | 47 | ### message 48 | 49 | **Type:** String (Read Only) 50 | 51 | The message either of the first ERROR StatusItem or when there 52 | is no ERROR StatusItem, the first StatusItem in the overall list. 53 | 54 | Note: Custom code and client programs must not use this message to identify 55 | a specific status. The getCode() must be used for that purpose. The actual 56 | message can change from release to release. 57 | 58 | ### parameters 59 | 60 | **Type:** List (Read Only) 61 | 62 | The parameters either of the first ERROR StatusItem or when there 63 | is no ERROR StatusItem, the first StatusItem in the overall list. 64 | 65 | ### status 66 | 67 | **Type:** Number (Read Only) 68 | 69 | The overall status. If all StatusItems are OK, the method returns 70 | OK. If one StatusItem is an ERROR it returns ERROR. 71 | 72 | ## Constructor Summary 73 | 74 | Status() Creates a Status object with no StatusItems. 75 | 76 | Status(status : Number) Creates a Status with a single StatusItem. 77 | 78 | Status(status : Number, code : String) Creates a Status with a single StatusItem. 79 | 80 | Status(status : Number, code : String, message : String, parameters : Object...) Creates a Status with a single StatusItem. 81 | 82 | ## Method Summary 83 | 84 | ### addDetail 85 | 86 | **Signature:** `addDetail(key : String, value : Object) : void` 87 | 88 | Add detail information for the given key of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 89 | 90 | ### addItem 91 | 92 | **Signature:** `addItem(item : StatusItem) : void` 93 | 94 | Adds an additional status item to this status instance. 95 | 96 | ### getCode 97 | 98 | **Signature:** `getCode() : String` 99 | 100 | Returns the status code either of the first ERROR StatusItem or when there is no ERROR StatusITEM, the first StatusItem in the overall list. 101 | 102 | ### getDetail 103 | 104 | **Signature:** `getDetail(key : String) : Object` 105 | 106 | Returns the detail value for the given key of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 107 | 108 | ### getDetails 109 | 110 | **Signature:** `getDetails() : Map` 111 | 112 | Returns the details either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 113 | 114 | ### getItems 115 | 116 | **Signature:** `getItems() : List` 117 | 118 | Returns all status items. 119 | 120 | ### getMessage 121 | 122 | **Signature:** `getMessage() : String` 123 | 124 | Returns the message either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 125 | 126 | ### getParameters 127 | 128 | **Signature:** `getParameters() : List` 129 | 130 | Returns the parameters either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 131 | 132 | ### getStatus 133 | 134 | **Signature:** `getStatus() : Number` 135 | 136 | Returns the overall status. 137 | 138 | ### isError 139 | 140 | **Signature:** `isError() : boolean` 141 | 142 | Checks if the status is an ERROR. 143 | 144 | ## Constructor Detail 145 | 146 | ## Method Detail 147 | 148 | ## Method Details 149 | 150 | ### addDetail 151 | 152 | **Signature:** `addDetail(key : String, value : Object) : void` 153 | 154 | **Description:** Add detail information for the given key of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 155 | 156 | **Parameters:** 157 | 158 | - `key`: the key of the first ERROR StatusItem or the first StatusItem in the list. 159 | - `value`: the detail value. 160 | 161 | --- 162 | 163 | ### addItem 164 | 165 | **Signature:** `addItem(item : StatusItem) : void` 166 | 167 | **Description:** Adds an additional status item to this status instance. 168 | 169 | **Parameters:** 170 | 171 | - `item`: the status item to add. 172 | 173 | --- 174 | 175 | ### getCode 176 | 177 | **Signature:** `getCode() : String` 178 | 179 | **Description:** Returns the status code either of the first ERROR StatusItem or when there is no ERROR StatusITEM, the first StatusItem in the overall list. The status code is the unique identifier for the message and can be used by client programs to check for a specific status and to generate a localized message. 180 | 181 | **Returns:** 182 | 183 | the status code 184 | 185 | --- 186 | 187 | ### getDetail 188 | 189 | **Signature:** `getDetail(key : String) : Object` 190 | 191 | **Description:** Returns the detail value for the given key of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 192 | 193 | **Parameters:** 194 | 195 | - `key`: the key for the detail to return. 196 | 197 | **Returns:** 198 | 199 | the detail value for the given key of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 200 | 201 | --- 202 | 203 | ### getDetails 204 | 205 | **Signature:** `getDetails() : Map` 206 | 207 | **Description:** Returns the details either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 208 | 209 | **Returns:** 210 | 211 | the details either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 212 | 213 | --- 214 | 215 | ### getItems 216 | 217 | **Signature:** `getItems() : List` 218 | 219 | **Description:** Returns all status items. 220 | 221 | **Returns:** 222 | 223 | all status items. 224 | 225 | --- 226 | 227 | ### getMessage 228 | 229 | **Signature:** `getMessage() : String` 230 | 231 | **Description:** Returns the message either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. Note: Custom code and client programs must not use this message to identify a specific status. The getCode() must be used for that purpose. The actual message can change from release to release. 232 | 233 | **Returns:** 234 | 235 | the message either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 236 | 237 | --- 238 | 239 | ### getParameters 240 | 241 | **Signature:** `getParameters() : List` 242 | 243 | **Description:** Returns the parameters either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 244 | 245 | **Returns:** 246 | 247 | the parameters either of the first ERROR StatusItem or when there is no ERROR StatusItem, the first StatusItem in the overall list. 248 | 249 | --- 250 | 251 | ### getStatus 252 | 253 | **Signature:** `getStatus() : Number` 254 | 255 | **Description:** Returns the overall status. If all StatusItems are OK, the method returns OK. If one StatusItem is an ERROR it returns ERROR. 256 | 257 | **Returns:** 258 | 259 | either OK or ERROR 260 | 261 | --- 262 | 263 | ### isError 264 | 265 | **Signature:** `isError() : boolean` 266 | 267 | **Description:** Checks if the status is an ERROR. The Status is an ERROR if one of the contained StatusItems is an ERROR. 268 | 269 | **Returns:** 270 | 271 | true if status is an ERROR, false otherwise. 272 | 273 | --- ``` -------------------------------------------------------------------------------- /docs/dw_crypto/Signature.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.crypto 2 | 3 | # Class Signature 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.crypto.Signature 9 | 10 | ## Description 11 | 12 | This class allows access to signature services offered through the Java Cryptography Architecture (JCA). At this time the signature/verification implementation of the methods is based on the default RSA JCE provider of the JDK - sun.security.rsa.SunRsaSign dw.crypto.Signature is an adapter to the security provider implementation and covers several digest algorithms: SHA1withRSA (deprecated) SHA256withRSA SHA384withRSA SHA512withRSA SHA256withRSA/PSS SHA384withRSA/PSS SHA512withRSA/PSS SHA256withECDSA SHA384withECDSA SHA512withECDSA Key size generally ranges between 512 and 65536 bits (the latter of which is unnecessarily large). Default key size for RSA is 1024. SHA384withRSA and SHA512withRSA require a key with length of at least 1024 bits. For ECDSA, the following key sizes are supported: SHA256withECDSA: 256-bit key (NIST P-256) SHA384withECDSA: 384-bit key (NIST P-384) SHA512withECDSA: 521-bit key (NIST P-521) When choosing a key size - beware of the tradeoff between security and processing time: The longer the key, the harder to break it but also it takes more time for the two sides to sign and verify the signature. An exception will be thrown for keys shorter than 2048 bits in this version of the API. Note: this class handles sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, 12, and other relevant requirements. 13 | 14 | ## Constants 15 | 16 | ### SUPPORTED_DIGEST_ALGORITHMS_AS_ARRAY 17 | 18 | **Type:** String 19 | 20 | Supported digest algorithms exposed as a string array 21 | 22 | ## Properties 23 | 24 | ## Constructor Summary 25 | 26 | Signature() 27 | 28 | ## Method Summary 29 | 30 | ### isDigestAlgorithmSupported 31 | 32 | **Signature:** `isDigestAlgorithmSupported(digestAlgorithm : String) : boolean` 33 | 34 | Checks to see if a digest algorithm is supported 35 | 36 | ### sign 37 | 38 | **Signature:** `sign(contentToSign : String, privateKey : String, digestAlgorithm : String) : String` 39 | 40 | Signs a string and returns a string 41 | 42 | ### sign 43 | 44 | **Signature:** `sign(contentToSign : String, privateKey : KeyRef, digestAlgorithm : String) : String` 45 | 46 | Signs a string and returns a string 47 | 48 | ### signBytes 49 | 50 | **Signature:** `signBytes(contentToSign : Bytes, privateKey : String, digestAlgorithm : String) : Bytes` 51 | 52 | Signs bytes and returns bytes 53 | 54 | ### signBytes 55 | 56 | **Signature:** `signBytes(contentToSign : Bytes, privateKey : KeyRef, digestAlgorithm : String) : Bytes` 57 | 58 | Signs bytes and returns bytes 59 | 60 | ### verifyBytesSignature 61 | 62 | **Signature:** `verifyBytesSignature(signature : Bytes, contentToVerify : Bytes, publicKey : String, digestAlgorithm : String) : boolean` 63 | 64 | Verifies a signature supplied as bytes 65 | 66 | ### verifyBytesSignature 67 | 68 | **Signature:** `verifyBytesSignature(signature : Bytes, contentToVerify : Bytes, certificate : CertificateRef, digestAlgorithm : String) : boolean` 69 | 70 | Verifies a signature supplied as bytes 71 | 72 | ### verifySignature 73 | 74 | **Signature:** `verifySignature(signature : String, contentToVerify : String, publicKey : String, digestAlgorithm : String) : boolean` 75 | 76 | Verifies a signature supplied as string 77 | 78 | ### verifySignature 79 | 80 | **Signature:** `verifySignature(signature : String, contentToVerify : String, certificate : CertificateRef, digestAlgorithm : String) : boolean` 81 | 82 | Verifies a signature supplied as string 83 | 84 | ## Constructor Detail 85 | 86 | ## Method Detail 87 | 88 | ## Method Details 89 | 90 | ### isDigestAlgorithmSupported 91 | 92 | **Signature:** `isDigestAlgorithmSupported(digestAlgorithm : String) : boolean` 93 | 94 | **Description:** Checks to see if a digest algorithm is supported 95 | 96 | **Parameters:** 97 | 98 | - `digestAlgorithm`: the digest algorithm name 99 | 100 | **Returns:** 101 | 102 | a boolean indicating success (true) or failure (false) 103 | 104 | --- 105 | 106 | ### sign 107 | 108 | **Signature:** `sign(contentToSign : String, privateKey : String, digestAlgorithm : String) : String` 109 | 110 | **Description:** Signs a string and returns a string 111 | 112 | **Parameters:** 113 | 114 | - `contentToSign`: base64 encoded content to sign 115 | - `privateKey`: base64 encoded private key 116 | - `digestAlgorithm`: must be one of the currently supported ones 117 | 118 | **Returns:** 119 | 120 | the base64 encoded signature 121 | 122 | --- 123 | 124 | ### sign 125 | 126 | **Signature:** `sign(contentToSign : String, privateKey : KeyRef, digestAlgorithm : String) : String` 127 | 128 | **Description:** Signs a string and returns a string 129 | 130 | **Parameters:** 131 | 132 | - `contentToSign`: base64 encoded content to sign 133 | - `privateKey`: a reference to a private key entry in the keystore 134 | - `digestAlgorithm`: must be one of the currently supported ones 135 | 136 | **Returns:** 137 | 138 | the base64 encoded signature 139 | 140 | --- 141 | 142 | ### signBytes 143 | 144 | **Signature:** `signBytes(contentToSign : Bytes, privateKey : String, digestAlgorithm : String) : Bytes` 145 | 146 | **Description:** Signs bytes and returns bytes 147 | 148 | **Parameters:** 149 | 150 | - `contentToSign`: transformed with UTF-8 encoding into a byte stream 151 | - `privateKey`: base64 encoded private key 152 | - `digestAlgorithm`: must be one of the currently supported ones 153 | 154 | **Returns:** 155 | 156 | signature 157 | 158 | --- 159 | 160 | ### signBytes 161 | 162 | **Signature:** `signBytes(contentToSign : Bytes, privateKey : KeyRef, digestAlgorithm : String) : Bytes` 163 | 164 | **Description:** Signs bytes and returns bytes 165 | 166 | **Parameters:** 167 | 168 | - `contentToSign`: transformed with UTF-8 encoding into a byte stream 169 | - `privateKey`: a reference to a private key entry in the keystore 170 | - `digestAlgorithm`: must be one of the currently supported ones 171 | 172 | **Returns:** 173 | 174 | signature 175 | 176 | --- 177 | 178 | ### verifyBytesSignature 179 | 180 | **Signature:** `verifyBytesSignature(signature : Bytes, contentToVerify : Bytes, publicKey : String, digestAlgorithm : String) : boolean` 181 | 182 | **Description:** Verifies a signature supplied as bytes 183 | 184 | **Parameters:** 185 | 186 | - `signature`: signature to check as bytes 187 | - `contentToVerify`: as bytes 188 | - `publicKey`: base64 encoded public key 189 | - `digestAlgorithm`: must be one of the currently supported ones 190 | 191 | **Returns:** 192 | 193 | a boolean indicating success (true) or failure (false) 194 | 195 | --- 196 | 197 | ### verifyBytesSignature 198 | 199 | **Signature:** `verifyBytesSignature(signature : Bytes, contentToVerify : Bytes, certificate : CertificateRef, digestAlgorithm : String) : boolean` 200 | 201 | **Description:** Verifies a signature supplied as bytes 202 | 203 | **Parameters:** 204 | 205 | - `signature`: signature to check as bytes 206 | - `contentToVerify`: as bytes 207 | - `certificate`: a reference to a trusted certificate 208 | - `digestAlgorithm`: must be one of the currently supported ones 209 | 210 | **Returns:** 211 | 212 | a boolean indicating success (true) or failure (false) 213 | 214 | --- 215 | 216 | ### verifySignature 217 | 218 | **Signature:** `verifySignature(signature : String, contentToVerify : String, publicKey : String, digestAlgorithm : String) : boolean` 219 | 220 | **Description:** Verifies a signature supplied as string 221 | 222 | **Parameters:** 223 | 224 | - `signature`: base64 encoded signature 225 | - `contentToVerify`: base64 encoded content to verify 226 | - `publicKey`: base64 encoded public key 227 | - `digestAlgorithm`: must be one of the currently supported ones 228 | 229 | **Returns:** 230 | 231 | a boolean indicating success (true) or failure (false) 232 | 233 | --- 234 | 235 | ### verifySignature 236 | 237 | **Signature:** `verifySignature(signature : String, contentToVerify : String, certificate : CertificateRef, digestAlgorithm : String) : boolean` 238 | 239 | **Description:** Verifies a signature supplied as string 240 | 241 | **Parameters:** 242 | 243 | - `signature`: base64 encoded signature 244 | - `contentToVerify`: base64 encoded content to verify 245 | - `certificate`: a reference to a trusted certificate 246 | - `digestAlgorithm`: must be one of the currently supported ones 247 | 248 | **Returns:** 249 | 250 | a boolean indicating success (true) or failure (false) 251 | 252 | --- ``` -------------------------------------------------------------------------------- /docs/dw_order/GiftCertificateLineItem.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.order 2 | 3 | # Class GiftCertificateLineItem 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.order.LineItem 11 | - dw.order.GiftCertificateLineItem 12 | 13 | ## Description 14 | 15 | Represents a Gift Certificate line item in the cart. When an order is processed, a Gift Certificate is created based on the information in the Gift Certificate line item. 16 | 17 | ## Properties 18 | 19 | ### giftCertificateID 20 | 21 | **Type:** String 22 | 23 | The ID of the gift certificate that this line item 24 | was used to create. If this line item has not been used to create 25 | a Gift Certificate, this method returns null. 26 | 27 | ### message 28 | 29 | **Type:** String 30 | 31 | The message to include in the email of the person receiving 32 | the gift certificate line item. 33 | 34 | ### productListItem 35 | 36 | **Type:** ProductListItem 37 | 38 | The associated ProductListItem. 39 | 40 | ### recipientEmail 41 | 42 | **Type:** String 43 | 44 | The email address of the person receiving 45 | the gift certificate line item. 46 | 47 | ### recipientName 48 | 49 | **Type:** String 50 | 51 | The name of the person receiving the gift certificate line item. 52 | 53 | ### senderName 54 | 55 | **Type:** String 56 | 57 | The name of the person or organization that 58 | sent the gift certificate line item or null if undefined. 59 | 60 | ### shipment 61 | 62 | **Type:** Shipment 63 | 64 | The associated Shipment. 65 | 66 | ## Constructor Summary 67 | 68 | ## Method Summary 69 | 70 | ### getGiftCertificateID 71 | 72 | **Signature:** `getGiftCertificateID() : String` 73 | 74 | Returns the ID of the gift certificate that this line item was used to create. 75 | 76 | ### getMessage 77 | 78 | **Signature:** `getMessage() : String` 79 | 80 | Returns the message to include in the email of the person receiving the gift certificate line item. 81 | 82 | ### getProductListItem 83 | 84 | **Signature:** `getProductListItem() : ProductListItem` 85 | 86 | Returns the associated ProductListItem. 87 | 88 | ### getRecipientEmail 89 | 90 | **Signature:** `getRecipientEmail() : String` 91 | 92 | Returns the email address of the person receiving the gift certificate line item. 93 | 94 | ### getRecipientName 95 | 96 | **Signature:** `getRecipientName() : String` 97 | 98 | Returns the name of the person receiving the gift certificate line item. 99 | 100 | ### getSenderName 101 | 102 | **Signature:** `getSenderName() : String` 103 | 104 | Returns the name of the person or organization that sent the gift certificate line item or null if undefined. 105 | 106 | ### getShipment 107 | 108 | **Signature:** `getShipment() : Shipment` 109 | 110 | Returns the associated Shipment. 111 | 112 | ### setGiftCertificateID 113 | 114 | **Signature:** `setGiftCertificateID(id : String) : void` 115 | 116 | Sets the ID of the gift certificate associated with this line item. 117 | 118 | ### setMessage 119 | 120 | **Signature:** `setMessage(message : String) : void` 121 | 122 | Sets the message to include in the email of the person receiving the gift certificate line item. 123 | 124 | ### setProductListItem 125 | 126 | **Signature:** `setProductListItem(productListItem : ProductListItem) : void` 127 | 128 | Sets the associated ProductListItem. 129 | 130 | ### setRecipientEmail 131 | 132 | **Signature:** `setRecipientEmail(recipientEmail : String) : void` 133 | 134 | Sets the email address of the person receiving the gift certificate line item. 135 | 136 | ### setRecipientName 137 | 138 | **Signature:** `setRecipientName(recipient : String) : void` 139 | 140 | Sets the name of the person receiving the gift certificate line item. 141 | 142 | ### setSenderName 143 | 144 | **Signature:** `setSenderName(sender : String) : void` 145 | 146 | Sets the name of the person or organization that sent the gift certificate line item. 147 | 148 | ### setShipment 149 | 150 | **Signature:** `setShipment(shipment : Shipment) : void` 151 | 152 | Associates the gift certificate line item with the specified shipment. 153 | 154 | ## Method Detail 155 | 156 | ## Method Details 157 | 158 | ### getGiftCertificateID 159 | 160 | **Signature:** `getGiftCertificateID() : String` 161 | 162 | **Description:** Returns the ID of the gift certificate that this line item was used to create. If this line item has not been used to create a Gift Certificate, this method returns null. 163 | 164 | **Returns:** 165 | 166 | the ID of the gift certificate or null if undefined. 167 | 168 | --- 169 | 170 | ### getMessage 171 | 172 | **Signature:** `getMessage() : String` 173 | 174 | **Description:** Returns the message to include in the email of the person receiving the gift certificate line item. 175 | 176 | **Returns:** 177 | 178 | the message to include in the email of the person receiving the gift certificate line item. 179 | 180 | --- 181 | 182 | ### getProductListItem 183 | 184 | **Signature:** `getProductListItem() : ProductListItem` 185 | 186 | **Description:** Returns the associated ProductListItem. 187 | 188 | **Returns:** 189 | 190 | item or null. 191 | 192 | --- 193 | 194 | ### getRecipientEmail 195 | 196 | **Signature:** `getRecipientEmail() : String` 197 | 198 | **Description:** Returns the email address of the person receiving the gift certificate line item. 199 | 200 | **Returns:** 201 | 202 | the email address of the person receiving the gift certificate line item. 203 | 204 | --- 205 | 206 | ### getRecipientName 207 | 208 | **Signature:** `getRecipientName() : String` 209 | 210 | **Description:** Returns the name of the person receiving the gift certificate line item. 211 | 212 | **Returns:** 213 | 214 | the name of the person receiving the gift certificate line item. 215 | 216 | --- 217 | 218 | ### getSenderName 219 | 220 | **Signature:** `getSenderName() : String` 221 | 222 | **Description:** Returns the name of the person or organization that sent the gift certificate line item or null if undefined. 223 | 224 | **Returns:** 225 | 226 | the name of the person or organization that sent the gift certificate line item or null if undefined. 227 | 228 | --- 229 | 230 | ### getShipment 231 | 232 | **Signature:** `getShipment() : Shipment` 233 | 234 | **Description:** Returns the associated Shipment. 235 | 236 | **Returns:** 237 | 238 | The shipment of the gift certificate line item 239 | 240 | --- 241 | 242 | ### setGiftCertificateID 243 | 244 | **Signature:** `setGiftCertificateID(id : String) : void` 245 | 246 | **Description:** Sets the ID of the gift certificate associated with this line item. 247 | 248 | **Parameters:** 249 | 250 | - `id`: the ID of the gift certificate associated with this line item. 251 | 252 | --- 253 | 254 | ### setMessage 255 | 256 | **Signature:** `setMessage(message : String) : void` 257 | 258 | **Description:** Sets the message to include in the email of the person receiving the gift certificate line item. 259 | 260 | **Parameters:** 261 | 262 | - `message`: the message to include in the email of the person receiving the gift certificate line item. 263 | 264 | --- 265 | 266 | ### setProductListItem 267 | 268 | **Signature:** `setProductListItem(productListItem : ProductListItem) : void` 269 | 270 | **Description:** Sets the associated ProductListItem. The product list item to be set must be of type gift certificate otherwise an exception is thrown. 271 | 272 | **Parameters:** 273 | 274 | - `productListItem`: the product list item to be associated 275 | 276 | --- 277 | 278 | ### setRecipientEmail 279 | 280 | **Signature:** `setRecipientEmail(recipientEmail : String) : void` 281 | 282 | **Description:** Sets the email address of the person receiving the gift certificate line item. 283 | 284 | **Parameters:** 285 | 286 | - `recipientEmail`: the email address of the person receiving the gift certificate line item. 287 | 288 | --- 289 | 290 | ### setRecipientName 291 | 292 | **Signature:** `setRecipientName(recipient : String) : void` 293 | 294 | **Description:** Sets the name of the person receiving the gift certificate line item. 295 | 296 | **Parameters:** 297 | 298 | - `recipient`: the name of the person receiving the gift certificate line item. 299 | 300 | --- 301 | 302 | ### setSenderName 303 | 304 | **Signature:** `setSenderName(sender : String) : void` 305 | 306 | **Description:** Sets the name of the person or organization that sent the gift certificate line item. 307 | 308 | **Parameters:** 309 | 310 | - `sender`: the name of the person or organization that sent the gift certificate line item. 311 | 312 | --- 313 | 314 | ### setShipment 315 | 316 | **Signature:** `setShipment(shipment : Shipment) : void` 317 | 318 | **Description:** Associates the gift certificate line item with the specified shipment. Gift certificate line item and shipment must belong to the same line item ctnr. 319 | 320 | **Parameters:** 321 | 322 | - `shipment`: The new shipment of the gift certificate line item 323 | 324 | --- ```