This is page 37 of 43. Use http://codebase.md/taurgis/sfcc-dev-mcp?page={x} to view the full context. # Directory Structure ``` ├── .DS_Store ├── .github │ ├── dependabot.yml │ ├── instructions │ │ ├── mcp-node-tests.instructions.md │ │ └── mcp-yml-tests.instructions.md │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── documentation.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── PULL_REQUEST_TEMPLATE │ │ ├── bug_fix.md │ │ ├── documentation.md │ │ └── new_tool.md │ ├── pull_request_template.md │ └── workflows │ ├── ci.yml │ ├── deploy-pages.yml │ ├── publish.yml │ └── update-docs.yml ├── .gitignore ├── .husky │ └── pre-commit ├── aegis.config.docs-only.json ├── aegis.config.json ├── aegis.config.with-dw.json ├── AGENTS.md ├── ai-instructions │ ├── claude-desktop │ │ └── claude_custom_instructions.md │ ├── cursor │ │ └── .cursor │ │ └── rules │ │ ├── debugging-workflows.mdc │ │ ├── hooks-development.mdc │ │ ├── isml-templates.mdc │ │ ├── job-framework.mdc │ │ ├── performance-optimization.mdc │ │ ├── scapi-endpoints.mdc │ │ ├── security-patterns.mdc │ │ ├── sfcc-development.mdc │ │ ├── sfra-controllers.mdc │ │ ├── sfra-models.mdc │ │ ├── system-objects.mdc │ │ └── testing-patterns.mdc │ └── github-copilot │ └── copilot-instructions.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── docs │ ├── best-practices │ │ ├── cartridge_creation.md │ │ ├── isml_templates.md │ │ ├── job_framework.md │ │ ├── localserviceregistry.md │ │ ├── ocapi_hooks.md │ │ ├── performance.md │ │ ├── scapi_custom_endpoint.md │ │ ├── scapi_hooks.md │ │ ├── security.md │ │ ├── sfra_client_side_js.md │ │ ├── sfra_controllers.md │ │ ├── sfra_models.md │ │ └── sfra_scss.md │ ├── dw_campaign │ │ ├── ABTest.md │ │ ├── ABTestMgr.md │ │ ├── ABTestSegment.md │ │ ├── AmountDiscount.md │ │ ├── ApproachingDiscount.md │ │ ├── BonusChoiceDiscount.md │ │ ├── BonusDiscount.md │ │ ├── Campaign.md │ │ ├── CampaignMgr.md │ │ ├── CampaignStatusCodes.md │ │ ├── Coupon.md │ │ ├── CouponMgr.md │ │ ├── CouponRedemption.md │ │ ├── CouponStatusCodes.md │ │ ├── Discount.md │ │ ├── DiscountPlan.md │ │ ├── FixedPriceDiscount.md │ │ ├── FixedPriceShippingDiscount.md │ │ ├── FreeDiscount.md │ │ ├── FreeShippingDiscount.md │ │ ├── PercentageDiscount.md │ │ ├── PercentageOptionDiscount.md │ │ ├── PriceBookPriceDiscount.md │ │ ├── Promotion.md │ │ ├── PromotionMgr.md │ │ ├── PromotionPlan.md │ │ ├── SlotContent.md │ │ ├── SourceCodeGroup.md │ │ ├── SourceCodeInfo.md │ │ ├── SourceCodeStatusCodes.md │ │ └── TotalFixedPriceDiscount.md │ ├── dw_catalog │ │ ├── Catalog.md │ │ ├── CatalogMgr.md │ │ ├── Category.md │ │ ├── CategoryAssignment.md │ │ ├── CategoryLink.md │ │ ├── PriceBook.md │ │ ├── PriceBookMgr.md │ │ ├── Product.md │ │ ├── ProductActiveData.md │ │ ├── ProductAttributeModel.md │ │ ├── ProductAvailabilityLevels.md │ │ ├── ProductAvailabilityModel.md │ │ ├── ProductInventoryList.md │ │ ├── ProductInventoryMgr.md │ │ ├── ProductInventoryRecord.md │ │ ├── ProductLink.md │ │ ├── ProductMgr.md │ │ ├── ProductOption.md │ │ ├── ProductOptionModel.md │ │ ├── ProductOptionValue.md │ │ ├── ProductPriceInfo.md │ │ ├── ProductPriceModel.md │ │ ├── ProductPriceTable.md │ │ ├── ProductSearchHit.md │ │ ├── ProductSearchModel.md │ │ ├── ProductSearchRefinementDefinition.md │ │ ├── ProductSearchRefinements.md │ │ ├── ProductSearchRefinementValue.md │ │ ├── ProductVariationAttribute.md │ │ ├── ProductVariationAttributeValue.md │ │ ├── ProductVariationModel.md │ │ ├── Recommendation.md │ │ ├── SearchModel.md │ │ ├── SearchRefinementDefinition.md │ │ ├── SearchRefinements.md │ │ ├── SearchRefinementValue.md │ │ ├── SortingOption.md │ │ ├── SortingRule.md │ │ ├── Store.md │ │ ├── StoreGroup.md │ │ ├── StoreInventoryFilter.md │ │ ├── StoreInventoryFilterValue.md │ │ ├── StoreMgr.md │ │ ├── Variant.md │ │ └── VariationGroup.md │ ├── dw_content │ │ ├── Content.md │ │ ├── ContentMgr.md │ │ ├── ContentSearchModel.md │ │ ├── ContentSearchRefinementDefinition.md │ │ ├── ContentSearchRefinements.md │ │ ├── ContentSearchRefinementValue.md │ │ ├── Folder.md │ │ ├── Library.md │ │ ├── MarkupText.md │ │ └── MediaFile.md │ ├── dw_crypto │ │ ├── CertificateRef.md │ │ ├── CertificateUtils.md │ │ ├── Cipher.md │ │ ├── Encoding.md │ │ ├── JWE.md │ │ ├── JWEHeader.md │ │ ├── JWS.md │ │ ├── JWSHeader.md │ │ ├── KeyRef.md │ │ ├── Mac.md │ │ ├── MessageDigest.md │ │ ├── SecureRandom.md │ │ ├── Signature.md │ │ ├── WeakCipher.md │ │ ├── WeakMac.md │ │ ├── WeakMessageDigest.md │ │ ├── WeakSignature.md │ │ └── X509Certificate.md │ ├── dw_customer │ │ ├── AddressBook.md │ │ ├── AgentUserMgr.md │ │ ├── AgentUserStatusCodes.md │ │ ├── AuthenticationStatus.md │ │ ├── Credentials.md │ │ ├── Customer.md │ │ ├── CustomerActiveData.md │ │ ├── CustomerAddress.md │ │ ├── CustomerCDPData.md │ │ ├── CustomerContextMgr.md │ │ ├── CustomerGroup.md │ │ ├── CustomerList.md │ │ ├── CustomerMgr.md │ │ ├── CustomerPasswordConstraints.md │ │ ├── CustomerPaymentInstrument.md │ │ ├── CustomerStatusCodes.md │ │ ├── EncryptedObject.md │ │ ├── ExternalProfile.md │ │ ├── OrderHistory.md │ │ ├── ProductList.md │ │ ├── ProductListItem.md │ │ ├── ProductListItemPurchase.md │ │ ├── ProductListMgr.md │ │ ├── ProductListRegistrant.md │ │ ├── Profile.md │ │ └── Wallet.md │ ├── dw_extensions.applepay │ │ ├── ApplePayHookResult.md │ │ └── ApplePayHooks.md │ ├── dw_extensions.facebook │ │ ├── FacebookFeedHooks.md │ │ └── FacebookProduct.md │ ├── dw_extensions.paymentrequest │ │ ├── PaymentRequestHookResult.md │ │ └── PaymentRequestHooks.md │ ├── dw_extensions.payments │ │ ├── SalesforceBancontactPaymentDetails.md │ │ ├── SalesforceCardPaymentDetails.md │ │ ├── SalesforceEpsPaymentDetails.md │ │ ├── SalesforceIdealPaymentDetails.md │ │ ├── SalesforceKlarnaPaymentDetails.md │ │ ├── SalesforcePaymentDetails.md │ │ ├── SalesforcePaymentIntent.md │ │ ├── SalesforcePaymentMethod.md │ │ ├── SalesforcePaymentRequest.md │ │ ├── SalesforcePaymentsHooks.md │ │ ├── SalesforcePaymentsMgr.md │ │ ├── SalesforcePaymentsSiteConfiguration.md │ │ ├── SalesforcePayPalOrder.md │ │ ├── SalesforcePayPalOrderAddress.md │ │ ├── SalesforcePayPalOrderPayer.md │ │ ├── SalesforcePayPalPaymentDetails.md │ │ ├── SalesforceSepaDebitPaymentDetails.md │ │ └── SalesforceVenmoPaymentDetails.md │ ├── dw_extensions.pinterest │ │ ├── PinterestAvailability.md │ │ ├── PinterestFeedHooks.md │ │ ├── PinterestOrder.md │ │ ├── PinterestOrderHooks.md │ │ └── PinterestProduct.md │ ├── dw_io │ │ ├── CSVStreamReader.md │ │ ├── CSVStreamWriter.md │ │ ├── File.md │ │ ├── FileReader.md │ │ ├── FileWriter.md │ │ ├── InputStream.md │ │ ├── OutputStream.md │ │ ├── PrintWriter.md │ │ ├── RandomAccessFileReader.md │ │ ├── Reader.md │ │ ├── StringWriter.md │ │ ├── Writer.md │ │ ├── XMLIndentingStreamWriter.md │ │ ├── XMLStreamConstants.md │ │ ├── XMLStreamReader.md │ │ └── XMLStreamWriter.md │ ├── dw_job │ │ ├── JobExecution.md │ │ └── JobStepExecution.md │ ├── dw_net │ │ ├── FTPClient.md │ │ ├── FTPFileInfo.md │ │ ├── HTTPClient.md │ │ ├── HTTPRequestPart.md │ │ ├── Mail.md │ │ ├── SFTPClient.md │ │ ├── SFTPFileInfo.md │ │ ├── WebDAVClient.md │ │ └── WebDAVFileInfo.md │ ├── dw_object │ │ ├── ActiveData.md │ │ ├── CustomAttributes.md │ │ ├── CustomObject.md │ │ ├── CustomObjectMgr.md │ │ ├── Extensible.md │ │ ├── ExtensibleObject.md │ │ ├── Note.md │ │ ├── ObjectAttributeDefinition.md │ │ ├── ObjectAttributeGroup.md │ │ ├── ObjectAttributeValueDefinition.md │ │ ├── ObjectTypeDefinition.md │ │ ├── PersistentObject.md │ │ ├── SimpleExtensible.md │ │ └── SystemObjectMgr.md │ ├── dw_order │ │ ├── AbstractItem.md │ │ ├── AbstractItemCtnr.md │ │ ├── Appeasement.md │ │ ├── AppeasementItem.md │ │ ├── Basket.md │ │ ├── BasketMgr.md │ │ ├── BonusDiscountLineItem.md │ │ ├── CouponLineItem.md │ │ ├── CreateAgentBasketLimitExceededException.md │ │ ├── CreateBasketFromOrderException.md │ │ ├── CreateCouponLineItemException.md │ │ ├── CreateOrderException.md │ │ ├── CreateTemporaryBasketLimitExceededException.md │ │ ├── GiftCertificate.md │ │ ├── GiftCertificateLineItem.md │ │ ├── GiftCertificateMgr.md │ │ ├── GiftCertificateStatusCodes.md │ │ ├── Invoice.md │ │ ├── InvoiceItem.md │ │ ├── LineItem.md │ │ ├── LineItemCtnr.md │ │ ├── Order.md │ │ ├── OrderAddress.md │ │ ├── OrderItem.md │ │ ├── OrderMgr.md │ │ ├── OrderPaymentInstrument.md │ │ ├── OrderProcessStatusCodes.md │ │ ├── PaymentCard.md │ │ ├── PaymentInstrument.md │ │ ├── PaymentMethod.md │ │ ├── PaymentMgr.md │ │ ├── PaymentProcessor.md │ │ ├── PaymentStatusCodes.md │ │ ├── PaymentTransaction.md │ │ ├── PriceAdjustment.md │ │ ├── PriceAdjustmentLimitTypes.md │ │ ├── ProductLineItem.md │ │ ├── ProductShippingCost.md │ │ ├── ProductShippingLineItem.md │ │ ├── ProductShippingModel.md │ │ ├── Return.md │ │ ├── ReturnCase.md │ │ ├── ReturnCaseItem.md │ │ ├── ReturnItem.md │ │ ├── Shipment.md │ │ ├── ShipmentShippingCost.md │ │ ├── ShipmentShippingModel.md │ │ ├── ShippingLineItem.md │ │ ├── ShippingLocation.md │ │ ├── ShippingMethod.md │ │ ├── ShippingMgr.md │ │ ├── ShippingOrder.md │ │ ├── ShippingOrderItem.md │ │ ├── SumItem.md │ │ ├── TaxGroup.md │ │ ├── TaxItem.md │ │ ├── TaxMgr.md │ │ ├── TrackingInfo.md │ │ └── TrackingRef.md │ ├── dw_order.hooks │ │ ├── CalculateHooks.md │ │ ├── OrderHooks.md │ │ ├── PaymentHooks.md │ │ ├── ReturnHooks.md │ │ └── ShippingOrderHooks.md │ ├── dw_rpc │ │ ├── SOAPUtil.md │ │ ├── Stub.md │ │ └── WebReference.md │ ├── dw_suggest │ │ ├── BrandSuggestions.md │ │ ├── CategorySuggestions.md │ │ ├── ContentSuggestions.md │ │ ├── CustomSuggestions.md │ │ ├── ProductSuggestions.md │ │ ├── SearchPhraseSuggestions.md │ │ ├── SuggestedCategory.md │ │ ├── SuggestedContent.md │ │ ├── SuggestedPhrase.md │ │ ├── SuggestedProduct.md │ │ ├── SuggestedTerm.md │ │ ├── SuggestedTerms.md │ │ ├── Suggestions.md │ │ └── SuggestModel.md │ ├── dw_svc │ │ ├── FTPService.md │ │ ├── FTPServiceDefinition.md │ │ ├── HTTPFormService.md │ │ ├── HTTPFormServiceDefinition.md │ │ ├── HTTPService.md │ │ ├── HTTPServiceDefinition.md │ │ ├── LocalServiceRegistry.md │ │ ├── Result.md │ │ ├── Service.md │ │ ├── ServiceCallback.md │ │ ├── ServiceConfig.md │ │ ├── ServiceCredential.md │ │ ├── ServiceDefinition.md │ │ ├── ServiceProfile.md │ │ ├── ServiceRegistry.md │ │ ├── SOAPService.md │ │ └── SOAPServiceDefinition.md │ ├── dw_system │ │ ├── AgentUserStatusCodes.md │ │ ├── Cache.md │ │ ├── CacheMgr.md │ │ ├── HookMgr.md │ │ ├── InternalObject.md │ │ ├── JobProcessMonitor.md │ │ ├── Log.md │ │ ├── Logger.md │ │ ├── LogNDC.md │ │ ├── OrganizationPreferences.md │ │ ├── Pipeline.md │ │ ├── PipelineDictionary.md │ │ ├── RemoteInclude.md │ │ ├── Request.md │ │ ├── RequestHooks.md │ │ ├── Response.md │ │ ├── RESTErrorResponse.md │ │ ├── RESTResponseMgr.md │ │ ├── RESTSuccessResponse.md │ │ ├── SearchStatus.md │ │ ├── Session.md │ │ ├── Site.md │ │ ├── SitePreferences.md │ │ ├── Status.md │ │ ├── StatusItem.md │ │ ├── System.md │ │ └── Transaction.md │ ├── dw_util │ │ ├── ArrayList.md │ │ ├── Assert.md │ │ ├── BigInteger.md │ │ ├── Bytes.md │ │ ├── Calendar.md │ │ ├── Collection.md │ │ ├── Currency.md │ │ ├── DateUtils.md │ │ ├── Decimal.md │ │ ├── FilteringCollection.md │ │ ├── Geolocation.md │ │ ├── HashMap.md │ │ ├── HashSet.md │ │ ├── Iterator.md │ │ ├── LinkedHashMap.md │ │ ├── LinkedHashSet.md │ │ ├── List.md │ │ ├── Locale.md │ │ ├── Map.md │ │ ├── MapEntry.md │ │ ├── MappingKey.md │ │ ├── MappingMgr.md │ │ ├── PropertyComparator.md │ │ ├── SecureEncoder.md │ │ ├── SecureFilter.md │ │ ├── SeekableIterator.md │ │ ├── Set.md │ │ ├── SortedMap.md │ │ ├── SortedSet.md │ │ ├── StringUtils.md │ │ ├── Template.md │ │ └── UUIDUtils.md │ ├── dw_value │ │ ├── EnumValue.md │ │ ├── MimeEncodedText.md │ │ ├── Money.md │ │ └── Quantity.md │ ├── dw_web │ │ ├── ClickStream.md │ │ ├── ClickStreamEntry.md │ │ ├── Cookie.md │ │ ├── Cookies.md │ │ ├── CSRFProtection.md │ │ ├── Form.md │ │ ├── FormAction.md │ │ ├── FormElement.md │ │ ├── FormElementValidationResult.md │ │ ├── FormField.md │ │ ├── FormFieldOption.md │ │ ├── FormFieldOptions.md │ │ ├── FormGroup.md │ │ ├── FormList.md │ │ ├── FormListItem.md │ │ ├── Forms.md │ │ ├── HttpParameter.md │ │ ├── HttpParameterMap.md │ │ ├── LoopIterator.md │ │ ├── PageMetaData.md │ │ ├── PageMetaTag.md │ │ ├── PagingModel.md │ │ ├── Resource.md │ │ ├── URL.md │ │ ├── URLAction.md │ │ ├── URLParameter.md │ │ ├── URLRedirect.md │ │ ├── URLRedirectMgr.md │ │ └── URLUtils.md │ ├── sfra │ │ ├── account.md │ │ ├── address.md │ │ ├── billing.md │ │ ├── cart.md │ │ ├── categories.md │ │ ├── content.md │ │ ├── locale.md │ │ ├── order.md │ │ ├── payment.md │ │ ├── price-default.md │ │ ├── price-range.md │ │ ├── price-tiered.md │ │ ├── product-bundle.md │ │ ├── product-full.md │ │ ├── product-line-items.md │ │ ├── product-search.md │ │ ├── product-tile.md │ │ ├── querystring.md │ │ ├── render.md │ │ ├── request.md │ │ ├── response.md │ │ ├── server.md │ │ ├── shipping.md │ │ ├── store.md │ │ ├── stores.md │ │ └── totals.md │ └── TopLevel │ ├── APIException.md │ ├── arguments.md │ ├── Array.md │ ├── ArrayBuffer.md │ ├── BigInt.md │ ├── Boolean.md │ ├── ConversionError.md │ ├── DataView.md │ ├── Date.md │ ├── Error.md │ ├── ES6Iterator.md │ ├── EvalError.md │ ├── Fault.md │ ├── Float32Array.md │ ├── Float64Array.md │ ├── Function.md │ ├── Generator.md │ ├── global.md │ ├── Int16Array.md │ ├── Int32Array.md │ ├── Int8Array.md │ ├── InternalError.md │ ├── IOError.md │ ├── Iterable.md │ ├── Iterator.md │ ├── JSON.md │ ├── Map.md │ ├── Math.md │ ├── Module.md │ ├── Namespace.md │ ├── Number.md │ ├── Object.md │ ├── QName.md │ ├── RangeError.md │ ├── ReferenceError.md │ ├── RegExp.md │ ├── Set.md │ ├── StopIteration.md │ ├── String.md │ ├── Symbol.md │ ├── SyntaxError.md │ ├── SystemError.md │ ├── TypeError.md │ ├── Uint16Array.md │ ├── Uint32Array.md │ ├── Uint8Array.md │ ├── Uint8ClampedArray.md │ ├── URIError.md │ ├── WeakMap.md │ ├── WeakSet.md │ ├── XML.md │ ├── XMLList.md │ └── XMLStreamError.md ├── docs-site │ ├── .gitignore │ ├── App.tsx │ ├── components │ │ ├── Badge.tsx │ │ ├── BreadcrumbSchema.tsx │ │ ├── CodeBlock.tsx │ │ ├── Collapsible.tsx │ │ ├── ConfigBuilder.tsx │ │ ├── ConfigHero.tsx │ │ ├── ConfigModeTabs.tsx │ │ ├── icons.tsx │ │ ├── Layout.tsx │ │ ├── LightCodeContainer.tsx │ │ ├── NewcomerCTA.tsx │ │ ├── NextStepsStrip.tsx │ │ ├── OnThisPage.tsx │ │ ├── Search.tsx │ │ ├── SEO.tsx │ │ ├── Sidebar.tsx │ │ ├── StructuredData.tsx │ │ ├── ToolCard.tsx │ │ ├── ToolFilters.tsx │ │ ├── Typography.tsx │ │ └── VersionBadge.tsx │ ├── constants.tsx │ ├── index.html │ ├── main.tsx │ ├── metadata.json │ ├── package-lock.json │ ├── package.json │ ├── pages │ │ ├── AIInterfacesPage.tsx │ │ ├── ConfigurationPage.tsx │ │ ├── DevelopmentPage.tsx │ │ ├── ExamplesPage.tsx │ │ ├── FeaturesPage.tsx │ │ ├── HomePage.tsx │ │ ├── SecurityPage.tsx │ │ ├── ToolsPage.tsx │ │ └── TroubleshootingPage.tsx │ ├── postcss.config.js │ ├── public │ │ ├── .well-known │ │ │ └── security.txt │ │ ├── 404.html │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── explain-product-pricing-methods-no-mcp.png │ │ ├── explain-product-pricing-methods.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── llms.txt │ │ ├── robots.txt │ │ ├── site.webmanifest │ │ └── sitemap.xml │ ├── README.md │ ├── scripts │ │ ├── generate-search-index.js │ │ ├── generate-sitemap.js │ │ └── search-dev.js │ ├── src │ │ └── styles │ │ ├── input.css │ │ └── prism-theme.css │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── types.ts │ ├── utils │ │ ├── search.ts │ │ └── toolsData.ts │ └── vite.config.ts ├── eslint.config.js ├── jest.config.js ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── scripts │ └── convert-docs.js ├── SECURITY.md ├── server.json ├── src │ ├── clients │ │ ├── base │ │ │ ├── http-client.ts │ │ │ ├── oauth-token.ts │ │ │ └── ocapi-auth-client.ts │ │ ├── best-practices-client.ts │ │ ├── cartridge-generation-client.ts │ │ ├── docs │ │ │ ├── class-content-parser.ts │ │ │ ├── class-name-resolver.ts │ │ │ ├── documentation-scanner.ts │ │ │ ├── index.ts │ │ │ └── referenced-types-extractor.ts │ │ ├── docs-client.ts │ │ ├── log-client.ts │ │ ├── logs │ │ │ ├── index.ts │ │ │ ├── log-analyzer.ts │ │ │ ├── log-client.ts │ │ │ ├── log-constants.ts │ │ │ ├── log-file-discovery.ts │ │ │ ├── log-file-reader.ts │ │ │ ├── log-formatter.ts │ │ │ ├── log-processor.ts │ │ │ ├── log-types.ts │ │ │ └── webdav-client-manager.ts │ │ ├── ocapi │ │ │ ├── code-versions-client.ts │ │ │ ├── site-preferences-client.ts │ │ │ └── system-objects-client.ts │ │ ├── ocapi-client.ts │ │ └── sfra-client.ts │ ├── config │ │ ├── configuration-factory.ts │ │ └── dw-json-loader.ts │ ├── core │ │ ├── handlers │ │ │ ├── abstract-log-tool-handler.ts │ │ │ ├── base-handler.ts │ │ │ ├── best-practices-handler.ts │ │ │ ├── cartridge-handler.ts │ │ │ ├── client-factory.ts │ │ │ ├── code-version-handler.ts │ │ │ ├── docs-handler.ts │ │ │ ├── job-log-handler.ts │ │ │ ├── job-log-tool-config.ts │ │ │ ├── log-handler.ts │ │ │ ├── log-tool-config.ts │ │ │ ├── sfra-handler.ts │ │ │ ├── system-object-handler.ts │ │ │ └── validation-helpers.ts │ │ ├── server.ts │ │ └── tool-definitions.ts │ ├── index.ts │ ├── main.ts │ ├── services │ │ ├── file-system-service.ts │ │ ├── index.ts │ │ └── path-service.ts │ ├── tool-configs │ │ ├── best-practices-tool-config.ts │ │ ├── cartridge-tool-config.ts │ │ ├── code-version-tool-config.ts │ │ ├── docs-tool-config.ts │ │ ├── job-log-tool-config.ts │ │ ├── log-tool-config.ts │ │ ├── sfra-tool-config.ts │ │ └── system-object-tool-config.ts │ ├── types │ │ └── types.ts │ └── utils │ ├── cache.ts │ ├── job-log-tool-config.ts │ ├── job-log-utils.ts │ ├── log-cache.ts │ ├── log-tool-config.ts │ ├── log-tool-constants.ts │ ├── log-tool-utils.ts │ ├── logger.ts │ ├── ocapi-url-builder.ts │ ├── path-resolver.ts │ ├── query-builder.ts │ ├── utils.ts │ └── validator.ts ├── tests │ ├── __mocks__ │ │ ├── docs-client.ts │ │ ├── src │ │ │ └── clients │ │ │ └── base │ │ │ └── http-client.js │ │ └── webdav.js │ ├── base-handler.test.ts │ ├── base-http-client.test.ts │ ├── best-practices-handler.test.ts │ ├── cache.test.ts │ ├── cartridge-handler.test.ts │ ├── class-content-parser.test.ts │ ├── class-name-resolver.test.ts │ ├── client-factory.test.ts │ ├── code-version-handler.test.ts │ ├── code-versions-client.test.ts │ ├── config.test.ts │ ├── configuration-factory.test.ts │ ├── docs-handler.test.ts │ ├── documentation-scanner.test.ts │ ├── file-system-service.test.ts │ ├── job-log-handler.test.ts │ ├── job-log-utils.test.ts │ ├── log-client.test.ts │ ├── log-handler.test.ts │ ├── log-processor.test.ts │ ├── logger.test.ts │ ├── mcp │ │ ├── AGENTS.md │ │ ├── node │ │ │ ├── activate-code-version-advanced.full-mode.programmatic.test.js │ │ │ ├── code-versions.full-mode.programmatic.test.js │ │ │ ├── generate-cartridge-structure.docs-only.programmatic.test.js │ │ │ ├── get-available-best-practice-guides.docs-only.programmatic.test.js │ │ │ ├── get-available-sfra-documents.programmatic.test.js │ │ │ ├── get-best-practice-guide.docs-only.programmatic.test.js │ │ │ ├── get-hook-reference.docs-only.programmatic.test.js │ │ │ ├── get-job-execution-summary.full-mode.programmatic.test.js │ │ │ ├── get-job-log-entries.full-mode.programmatic.test.js │ │ │ ├── get-latest-debug.full-mode.programmatic.test.js │ │ │ ├── get-latest-error.full-mode.programmatic.test.js │ │ │ ├── get-latest-info.full-mode.programmatic.test.js │ │ │ ├── get-latest-job-log-files.full-mode.programmatic.test.js │ │ │ ├── get-latest-warn.full-mode.programmatic.test.js │ │ │ ├── get-log-file-contents.full-mode.programmatic.test.js │ │ │ ├── get-sfcc-class-documentation.docs-only.programmatic.test.js │ │ │ ├── get-sfcc-class-info.docs-only.programmatic.test.js │ │ │ ├── get-sfra-categories.docs-only.programmatic.test.js │ │ │ ├── get-sfra-document.programmatic.test.js │ │ │ ├── get-sfra-documents-by-category.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definition.full-mode.programmatic.test.js │ │ │ ├── get-system-object-definitions.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definitions.full-mode.programmatic.test.js │ │ │ ├── list-log-files.full-mode.programmatic.test.js │ │ │ ├── list-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-best-practices.docs-only.programmatic.test.js │ │ │ ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-job-logs-by-name.full-mode.programmatic.test.js │ │ │ ├── search-job-logs.full-mode.programmatic.test.js │ │ │ ├── search-logs.full-mode.programmatic.test.js │ │ │ ├── search-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-sfcc-methods.docs-only.programmatic.test.js │ │ │ ├── search-sfra-documentation.docs-only.programmatic.test.js │ │ │ ├── search-site-preferences.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-groups.full-mode.programmatic.test.js │ │ │ ├── summarize-logs.full-mode.programmatic.test.js │ │ │ ├── tools.docs-only.programmatic.test.js │ │ │ └── tools.full-mode.programmatic.test.js │ │ ├── README.md │ │ ├── test-fixtures │ │ │ └── dw.json │ │ └── yaml │ │ ├── activate-code-version.docs-only.test.mcp.yml │ │ ├── activate-code-version.full-mode.test.mcp.yml │ │ ├── get_latest_error.test.mcp.yml │ │ ├── get-available-best-practice-guides.docs-only.test.mcp.yml │ │ ├── get-available-best-practice-guides.full-mode.test.mcp.yml │ │ ├── get-available-sfra-documents.docs-only.test.mcp.yml │ │ ├── get-available-sfra-documents.full-mode.test.mcp.yml │ │ ├── get-best-practice-guide.docs-only.test.mcp.yml │ │ ├── get-best-practice-guide.full-mode.test.mcp.yml │ │ ├── get-code-versions.docs-only.test.mcp.yml │ │ ├── get-code-versions.full-mode.test.mcp.yml │ │ ├── get-hook-reference.docs-only.test.mcp.yml │ │ ├── get-hook-reference.full-mode.test.mcp.yml │ │ ├── get-job-execution-summary.full-mode.test.mcp.yml │ │ ├── get-job-log-entries.full-mode.test.mcp.yml │ │ ├── get-latest-debug.full-mode.test.mcp.yml │ │ ├── get-latest-error.full-mode.test.mcp.yml │ │ ├── get-latest-info.full-mode.test.mcp.yml │ │ ├── get-latest-job-log-files.full-mode.test.mcp.yml │ │ ├── get-latest-warn.full-mode.test.mcp.yml │ │ ├── get-log-file-contents.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-documentation.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-documentation.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-info.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-info.full-mode.test.mcp.yml │ │ ├── get-sfra-categories.docs-only.test.mcp.yml │ │ ├── get-sfra-categories.full-mode.test.mcp.yml │ │ ├── get-sfra-document.docs-only.test.mcp.yml │ │ ├── get-sfra-document.full-mode.test.mcp.yml │ │ ├── get-sfra-documents-by-category.docs-only.test.mcp.yml │ │ ├── get-sfra-documents-by-category.full-mode.test.mcp.yml │ │ ├── get-system-object-definition.docs-only.test.mcp.yml │ │ ├── get-system-object-definition.full-mode.test.mcp.yml │ │ ├── get-system-object-definitions.docs-only.test.mcp.yml │ │ ├── get-system-object-definitions.full-mode.test.mcp.yml │ │ ├── list-log-files.full-mode.test.mcp.yml │ │ ├── list-sfcc-classes.docs-only.test.mcp.yml │ │ ├── list-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-best-practices.docs-only.test.mcp.yml │ │ ├── search-best-practices.full-mode.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.test.mcp.yml │ │ ├── search-job-logs-by-name.full-mode.test.mcp.yml │ │ ├── search-job-logs.full-mode.test.mcp.yml │ │ ├── search-logs.full-mode.test.mcp.yml │ │ ├── search-sfcc-classes.docs-only.test.mcp.yml │ │ ├── search-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-sfcc-methods.docs-only.test.mcp.yml │ │ ├── search-sfcc-methods.full-mode.test.mcp.yml │ │ ├── search-sfra-documentation.docs-only.test.mcp.yml │ │ ├── search-sfra-documentation.full-mode.test.mcp.yml │ │ ├── search-site-preferences.docs-only.test.mcp.yml │ │ ├── search-site-preferences.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-groups.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-groups.full-mode.test.mcp.yml │ │ ├── summarize-logs.full-mode.test.mcp.yml │ │ ├── tools.docs-only.test.mcp.yml │ │ └── tools.full-mode.test.mcp.yml │ ├── oauth-token.test.ts │ ├── ocapi-auth-client.test.ts │ ├── ocapi-client.test.ts │ ├── path-service.test.ts │ ├── query-builder.test.ts │ ├── referenced-types-extractor.test.ts │ ├── servers │ │ ├── sfcc-mock-server │ │ │ ├── mock-data │ │ │ │ └── ocapi │ │ │ │ ├── code-versions.json │ │ │ │ ├── custom-object-attributes-customapi.json │ │ │ │ ├── custom-object-attributes-globalsettings.json │ │ │ │ ├── custom-object-attributes-versionhistory.json │ │ │ │ ├── site-preferences-ccv.json │ │ │ │ ├── site-preferences-fastforward.json │ │ │ │ ├── site-preferences-sfra.json │ │ │ │ ├── site-preferences-storefront.json │ │ │ │ ├── site-preferences-system.json │ │ │ │ ├── system-object-attribute-groups-campaign.json │ │ │ │ ├── system-object-attribute-groups-category.json │ │ │ │ ├── system-object-attribute-groups-order.json │ │ │ │ ├── system-object-attribute-groups-product.json │ │ │ │ ├── system-object-attribute-groups-sitepreferences.json │ │ │ │ ├── system-object-attributes-customeraddress.json │ │ │ │ ├── system-object-attributes-product-expanded.json │ │ │ │ ├── system-object-attributes-product.json │ │ │ │ ├── system-object-definition-category.json │ │ │ │ ├── system-object-definition-customer.json │ │ │ │ ├── system-object-definition-customeraddress.json │ │ │ │ ├── system-object-definition-order.json │ │ │ │ ├── system-object-definition-product.json │ │ │ │ ├── system-object-definitions-old.json │ │ │ │ └── system-object-definitions.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── README.md │ │ │ ├── scripts │ │ │ │ └── setup-logs.js │ │ │ ├── server.js │ │ │ └── src │ │ │ ├── app.js │ │ │ ├── config │ │ │ │ └── server-config.js │ │ │ ├── middleware │ │ │ │ ├── auth.js │ │ │ │ ├── cors.js │ │ │ │ └── logging.js │ │ │ ├── routes │ │ │ │ ├── ocapi │ │ │ │ │ ├── code-versions-handler.js │ │ │ │ │ ├── oauth-handler.js │ │ │ │ │ ├── ocapi-error-utils.js │ │ │ │ │ ├── ocapi-utils.js │ │ │ │ │ ├── site-preferences-handler.js │ │ │ │ │ └── system-objects-handler.js │ │ │ │ ├── ocapi.js │ │ │ │ └── webdav.js │ │ │ └── utils │ │ │ ├── mock-data-loader.js │ │ │ └── webdav-xml.js │ │ └── sfcc-mock-server-manager.ts │ ├── sfcc-mock-server.test.ts │ ├── site-preferences-client.test.ts │ ├── system-objects-client.test.ts │ ├── utils.test.ts │ ├── validation-helpers.test.ts │ └── validator.test.ts ├── tsconfig.json └── tsconfig.test.json ``` # Files -------------------------------------------------------------------------------- /tests/mcp/node/get-sfcc-class-info.docs-only.programmatic.test.js: -------------------------------------------------------------------------------- ```javascript /** * Advanced programmatic tests for get_sfcc_class_info tool (docs-only mode) * * These tests go beyond what's possible with YAML testing to provide: * - Complex validation scenarios * - Concurrent request handling * - Advanced workflow simulation * - Stress testing * * @requires mcp-aegis */ import { test, describe, before, after, beforeEach } from 'node:test'; import { strict as assert } from 'node:assert'; import { connect } from 'mcp-aegis'; let client; describe('SFCC MCP Server - get_sfcc_class_info Tool (Documentation-Only Mode)', () => { // ================================================================================== // SETUP AND TEARDOWN // ================================================================================== before(async () => { client = await connect('./aegis.config.docs-only.json'); }); after(async () => { if (client?.connected) { await client.disconnect(); } }); beforeEach(() => { // CRITICAL: Clear all buffers to prevent leaking into next tests client.clearAllBuffers(); // Recommended - comprehensive protection }); // ================================================================================== // TOOL DISCOVERY AND METADATA TESTS // ================================================================================== test('should successfully connect and discover get_sfcc_class_info tool', async () => { const tools = await client.listTools(); const targetTool = tools.find(tool => tool.name === 'get_sfcc_class_info'); assert(targetTool, 'get_sfcc_class_info tool should be available'); assert.strictEqual(targetTool.name, 'get_sfcc_class_info'); assert(targetTool.description, 'Tool should have description'); }); test('should have properly structured tool metadata', async () => { const tools = await client.listTools(); const tool = tools.find(t => t.name === 'get_sfcc_class_info'); assert(tool.inputSchema, 'Tool should have input schema'); assert(tool.inputSchema.properties, 'Schema should have properties'); assert(tool.inputSchema.properties.className, 'Should have className parameter'); assert(tool.inputSchema.properties.expand, 'Should have expand parameter'); }); test('should validate className parameter schema', async () => { const tools = await client.listTools(); const tool = tools.find(t => t.name === 'get_sfcc_class_info'); const classNameParam = tool.inputSchema.properties.className; assert.strictEqual(classNameParam.type, 'string'); assert(classNameParam.description); }); test('should validate expand parameter schema', async () => { const tools = await client.listTools(); const tool = tools.find(t => t.name === 'get_sfcc_class_info'); const expandParam = tool.inputSchema.properties.expand; assert.strictEqual(expandParam.type, 'boolean'); assert(expandParam.description); }); // ================================================================================== // BASIC FUNCTIONALITY TESTS // ================================================================================== test('should execute with basic SFCC class successfully', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); assert(classInfo.description); assert(Array.isArray(classInfo.properties)); assert(Array.isArray(classInfo.methods)); }); test('should work with short class names', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'Product' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); assert(classInfo.description); }); test('should work with TopLevel classes', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'Customer' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert.strictEqual(classInfo.className, 'Customer'); assert(classInfo.packageName === 'dw.customer' || classInfo.packageName === 'TopLevel'); assert(classInfo.description); }); // ================================================================================== // EXPAND PARAMETER TESTS // ================================================================================== test('should handle expand parameter set to false', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', expand: false }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert.strictEqual(classInfo.className, 'Product'); assert(classInfo.properties); assert(classInfo.methods); }); test('should handle expand parameter set to true', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', expand: true }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert.strictEqual(classInfo.className, 'Product'); assert(classInfo.properties); assert(classInfo.methods); // When expand is true, we might get additional referenced type information }); // ================================================================================== // COMPLEX CLASS TESTS // ================================================================================== test('should handle complex classes with many methods', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert(classInfo.methods.length > 10, 'Product should have many methods'); assert(classInfo.properties.length > 5, 'Product should have many properties'); // Validate method structure const sampleMethod = classInfo.methods[0]; assert(sampleMethod.name, 'Method should have name'); assert(sampleMethod.signature, 'Method should have signature'); assert(sampleMethod.description, 'Method should have description'); }); test('should handle variation model classes', async () => { const testClasses = [ 'dw.catalog.ProductVariationModel', 'dw.catalog.ProductVariationAttribute', 'dw.catalog.ProductVariationAttributeValue' ]; for (const className of testClasses) { const result = await client.callTool('get_sfcc_class_info', { className }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert(classInfo.className, `${className} should have className`); assert(classInfo.description, `${className} should have description`); } }); test('should handle inventory-related classes', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.ProductInventoryList' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert.strictEqual(classInfo.className, 'ProductInventoryList'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); }); // ================================================================================== // ERROR HANDLING TESTS // ================================================================================== test('should handle invalid class names gracefully', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'NonExistentClass' }); // Should either return an error or empty results assert(result.content || result.error, 'Should have some response'); }); test('should handle empty class name', async () => { const result = await client.callTool('get_sfcc_class_info', { className: '' }); // Should handle empty input gracefully assert(result.content || result.error, 'Should handle empty className'); }); test('should handle missing className parameter', async () => { try { await client.callTool('get_sfcc_class_info', {}); assert.fail('Should have thrown an error for missing className'); } catch (error) { assert(error.message.includes('className') || error.message.includes('required'), 'Error should mention className requirement'); } }); test('should handle malformed class names', async () => { const malformedNames = [ 'dw.catalog..Product', '.Product', 'dw.', 'dw catalog Product', 'dw/catalog/Product' ]; for (const className of malformedNames) { const result = await client.callTool('get_sfcc_class_info', { className }); // Should handle malformed names without crashing assert(result, `Should handle malformed name: ${className}`); } }); test('should handle case sensitivity properly', async () => { const variations = [ 'dw.catalog.product', 'DW.CATALOG.PRODUCT', 'dw.Catalog.Product' ]; for (const className of variations) { const result = await client.callTool('get_sfcc_class_info', { className }); // Should either work or fail gracefully assert(result, `Should handle case variation: ${className}`); } }); // ================================================================================== // CONSISTENCY AND RELIABILITY TESTS // ================================================================================== test('should return consistent results for repeated calls', async () => { const className = 'dw.catalog.Catalog'; const result1 = await client.callTool('get_sfcc_class_info', { className }); const result2 = await client.callTool('get_sfcc_class_info', { className }); assertValidMCPResponse(result1); assertValidMCPResponse(result2); const class1 = JSON.parse(result1.content[0].text); const class2 = JSON.parse(result2.content[0].text); assert.deepStrictEqual(class1, class2, 'Results should be identical'); }); test('should handle concurrent requests properly', async () => { const classNames = [ 'dw.catalog.Product', 'dw.catalog.Category', 'dw.catalog.Catalog', 'dw.system.Site', 'dw.customer.Customer' ]; const promises = classNames.map(className => client.callTool('get_sfcc_class_info', { className }) ); const results = await Promise.all(promises); results.forEach((result, index) => { assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert(classInfo.className, `Result ${index} should have className`); }); }); // ================================================================================== // ADVANCED VALIDATION TESTS // ================================================================================== test('should validate complete JSON structure for various classes', async () => { const testClasses = [ 'dw.catalog.Product', 'dw.catalog.Category', 'dw.system.Site' ]; for (const className of testClasses) { const result = await client.callTool('get_sfcc_class_info', { className }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Validate required fields assert(classInfo.className, `${className} should have className`); assert(classInfo.packageName, `${className} should have packageName`); assert(classInfo.description, `${className} should have description`); assert(Array.isArray(classInfo.properties), `${className} should have properties array`); assert(Array.isArray(classInfo.methods), `${className} should have methods array`); assert(Array.isArray(classInfo.constants), `${className} should have constants array`); } }); test('should validate method signatures and descriptions', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Validate method structure classInfo.methods.slice(0, 5).forEach((method, index) => { assert(method.name, `Method ${index} should have name`); assert(method.signature, `Method ${index} should have signature`); assert(method.description, `Method ${index} should have description`); assert(method.signature.includes(method.name), `Method ${index} signature should contain method name`); }); }); test('should validate property structure', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Validate property structure classInfo.properties.slice(0, 5).forEach((property, index) => { assert(property.name, `Property ${index} should have name`); assert(property.type, `Property ${index} should have type`); assert(property.description, `Property ${index} should have description`); }); }); // ================================================================================== // INTEGRATION AND WORKFLOW TESTS // ================================================================================== test('should support class discovery workflow', async () => { // In docs-only mode, we test the main functionality directly const classResult = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Catalog' }); assertValidMCPResponse(classResult); const classInfo = JSON.parse(classResult.content[0].text); assert.strictEqual(classInfo.className, 'Catalog'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); assert(classInfo.description); }); test('should support development workflow simulation', async () => { // Simulate a developer exploring the Product class const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Check for commonly used methods const methodNames = classInfo.methods.map(m => m.name); const importantMethods = ['getID', 'getName', 'isOnline', 'isAvailable']; importantMethods.forEach(methodName => { assert(methodNames.includes(methodName), `Product class should have ${methodName} method`); }); }); // ================================================================================== // STRESS AND EDGE CASE TESTS // ================================================================================== test('should handle rapid successive calls without issues', async () => { const calls = 10; const className = 'dw.catalog.Category'; const promises = Array(calls).fill().map(() => client.callTool('get_sfcc_class_info', { className }) ); const results = await Promise.all(promises); results.forEach(result => { assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert.strictEqual(classInfo.className, 'Category'); }); }); test('should maintain accuracy across different parameter combinations', async () => { const testCases = [ { className: 'dw.catalog.Product', expand: false }, { className: 'dw.catalog.Product', expand: true }, { className: 'Product', expand: false }, { className: 'Product', expand: true } ]; const results = await Promise.all( testCases.map(params => client.callTool('get_sfcc_class_info', params)) ); results.forEach(result => { assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); }); }); // ================================================================================== // FILTERING PARAMETERS COMPREHENSIVE TESTS // ================================================================================== describe('Filtering Parameters', () => { test('should validate all filtering parameters exist in tool schema', async () => { const tools = await client.listTools(); const tool = tools.find(t => t.name === 'get_sfcc_class_info'); const props = tool.inputSchema.properties; // Validate all new filtering parameters are defined assert(props.includeDescription, 'Should have includeDescription parameter'); assert(props.includeConstants, 'Should have includeConstants parameter'); assert(props.includeProperties, 'Should have includeProperties parameter'); assert(props.includeMethods, 'Should have includeMethods parameter'); assert(props.includeInheritance, 'Should have includeInheritance parameter'); assert(props.search, 'Should have search parameter'); // Validate parameter types assert.strictEqual(props.includeDescription.type, 'boolean'); assert.strictEqual(props.includeConstants.type, 'boolean'); assert.strictEqual(props.includeProperties.type, 'boolean'); assert.strictEqual(props.includeMethods.type, 'boolean'); assert.strictEqual(props.includeInheritance.type, 'boolean'); assert.strictEqual(props.search.type, 'string'); }); test('should include all sections by default', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Verify all sections are present by default assert(classInfo.description, 'Should include description by default'); assert(Array.isArray(classInfo.constants), 'Should include constants array'); assert(Array.isArray(classInfo.properties), 'Should include properties array'); assert(Array.isArray(classInfo.methods), 'Should include methods array'); assert(classInfo.inheritance, 'Should include inheritance by default'); }); test('should exclude description when includeDescription is false', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeDescription: false }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Description should be excluded or empty assert(!classInfo.description || classInfo.description === '', 'Description should be excluded when includeDescription is false'); // Other sections should still be present assert(Array.isArray(classInfo.properties), 'Properties should still be included'); assert(Array.isArray(classInfo.methods), 'Methods should still be included'); }); test('should exclude constants when includeConstants is false', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeConstants: false }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Constants should be excluded or empty assert(!classInfo.constants || classInfo.constants.length === 0, 'Constants should be excluded when includeConstants is false'); // Other sections should still be present assert(classInfo.description, 'Description should still be included'); assert(Array.isArray(classInfo.properties), 'Properties should still be included'); }); test('should exclude properties when includeProperties is false', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeProperties: false }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Properties should be excluded or empty assert(!classInfo.properties || classInfo.properties.length === 0, 'Properties should be excluded when includeProperties is false'); // Other sections should still be present assert(classInfo.description, 'Description should still be included'); assert(Array.isArray(classInfo.methods), 'Methods should still be included'); }); test('should exclude methods when includeMethods is false', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeMethods: false }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Methods should be excluded or empty assert(!classInfo.methods || classInfo.methods.length === 0, 'Methods should be excluded when includeMethods is false'); // Other sections should still be present assert(classInfo.description, 'Description should still be included'); assert(Array.isArray(classInfo.properties), 'Properties should still be included'); }); test('should exclude inheritance when includeInheritance is false', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeInheritance: false }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Inheritance should be excluded or empty assert(!classInfo.inheritance || (Array.isArray(classInfo.inheritance) && classInfo.inheritance.length === 0) || classInfo.inheritance === '', 'Inheritance should be excluded when includeInheritance is false'); // Other sections should still be present assert(classInfo.description, 'Description should still be included'); assert(Array.isArray(classInfo.methods), 'Methods should still be included'); }); test('should create minimal response when all filters disabled', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeDescription: false, includeConstants: false, includeProperties: false, includeMethods: false, includeInheritance: false }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Only basic class info should remain assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); // All optional sections should be excluded or empty assert(!classInfo.description || classInfo.description === ''); assert(!classInfo.constants || classInfo.constants.length === 0); assert(!classInfo.properties || classInfo.properties.length === 0); assert(!classInfo.methods || classInfo.methods.length === 0); assert(!classInfo.inheritance || (Array.isArray(classInfo.inheritance) && classInfo.inheritance.length === 0) || classInfo.inheritance === ''); }); test('should combine filtering parameters effectively', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeDescription: true, includeConstants: false, includeProperties: true, includeMethods: false, includeInheritance: true }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Should include: description, properties, inheritance assert(classInfo.description, 'Description should be included'); assert(Array.isArray(classInfo.properties), 'Properties should be included'); assert(classInfo.inheritance, 'Inheritance should be included'); // Should exclude: constants, methods assert(!classInfo.constants || classInfo.constants.length === 0, 'Constants should be excluded'); assert(!classInfo.methods || classInfo.methods.length === 0, 'Methods should be excluded'); }); }); // ================================================================================== // SEARCH FUNCTIONALITY COMPREHENSIVE TESTS // ================================================================================== describe('Search Functionality', () => { test('should filter results with search parameter', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', search: 'name' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Search should filter properties and methods if (classInfo.properties && classInfo.properties.length > 0) { classInfo.properties.forEach(prop => { assert(prop.name.toLowerCase().includes('name') || (prop.description && prop.description.toLowerCase().includes('name')), `Property ${prop.name} should match search term 'name'`); }); } if (classInfo.methods && classInfo.methods.length > 0) { classInfo.methods.forEach(method => { assert(method.name.toLowerCase().includes('name') || (method.description && method.description.toLowerCase().includes('name')), `Method ${method.name} should match search term 'name'`); }); } }); test('should perform case-insensitive search', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', search: 'NAME' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Search should be case-insensitive if (classInfo.properties && classInfo.properties.length > 0) { classInfo.properties.forEach(prop => { assert(prop.name.toLowerCase().includes('name') || (prop.description && prop.description.toLowerCase().includes('name')), `Property ${prop.name} should match case-insensitive search 'NAME'`); }); } }); test('should combine search with filtering parameters', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeMethods: true, includeProperties: false, includeConstants: false, search: 'get' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Should only have methods (no properties/constants) assert(!classInfo.properties || classInfo.properties.length === 0, 'Properties should be excluded'); assert(!classInfo.constants || classInfo.constants.length === 0, 'Constants should be excluded'); // Methods should be filtered by search term if (classInfo.methods && classInfo.methods.length > 0) { classInfo.methods.forEach(method => { assert(method.name.toLowerCase().includes('get') || (method.description && method.description.toLowerCase().includes('get')), `Method ${method.name} should match search term 'get'`); }); } }); test('should handle search with no matches gracefully', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', search: 'zzznomatchesexpected' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Should still have basic class info assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); // Arrays should be empty (no matches) if (classInfo.properties) { assert.strictEqual(classInfo.properties.length, 0, 'Properties should be empty with no matches'); } if (classInfo.methods) { assert.strictEqual(classInfo.methods.length, 0, 'Methods should be empty with no matches'); } if (classInfo.constants) { assert.strictEqual(classInfo.constants.length, 0, 'Constants should be empty with no matches'); } }); test('should search across multiple field types', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', search: 'get' // Changed from 'id' to 'get' which is more common }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Should find matches in different sections let foundMatches = false; if (classInfo.properties && classInfo.properties.length > 0) { foundMatches = true; classInfo.properties.forEach(prop => { assert(prop.name.toLowerCase().includes('get') || (prop.description && prop.description.toLowerCase().includes('get')), `Property ${prop.name} should match search term 'get'`); }); } if (classInfo.methods && classInfo.methods.length > 0) { foundMatches = true; classInfo.methods.forEach(method => { assert(method.name.toLowerCase().includes('get') || (method.description && method.description.toLowerCase().includes('get')), `Method ${method.name} should match search term 'get'`); }); } // Should find at least some matches with 'get' in Product class assert(foundMatches, 'Should find matches for common term "get" in Product class'); }); test('should handle empty search string', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', search: '' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Empty search should return all results (no filtering) assert.strictEqual(classInfo.className, 'Product'); assert(classInfo.description, 'Should include description with empty search'); assert(Array.isArray(classInfo.properties), 'Should include all properties with empty search'); assert(Array.isArray(classInfo.methods), 'Should include all methods with empty search'); }); }); // ================================================================================== // RESPONSE STRUCTURE VALIDATION WITH dw.catalog.Product // ================================================================================== describe('Product Class Response Structure Validation', () => { test('should return comprehensive Product class information', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Validate basic structure assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); assert(typeof classInfo.description === 'string', 'Description should be string'); // Validate arrays are present and non-empty for a rich class like Product assert(Array.isArray(classInfo.properties), 'Properties should be array'); assert(classInfo.properties.length > 0, 'Product should have properties'); assert(Array.isArray(classInfo.methods), 'Methods should be array'); assert(classInfo.methods.length > 0, 'Product should have methods'); // Validate property structure classInfo.properties.forEach(prop => { assert(typeof prop.name === 'string', 'Property name should be string'); assert(typeof prop.type === 'string', 'Property type should be string'); // Description is optional but should be string if present if (prop.description) { assert(typeof prop.description === 'string', 'Property description should be string'); } }); // Validate method structure classInfo.methods.forEach(method => { assert(typeof method.name === 'string', 'Method name should be string'); assert(typeof method.signature === 'string', 'Method signature should be string'); // Return type and description are optional if (method.returnType) { assert(typeof method.returnType === 'string', 'Method returnType should be string'); } if (method.description) { assert(typeof method.description === 'string', 'Method description should be string'); } }); }); test('should validate filtered Product class responses maintain structure', async () => { const scenarios = [ { includeProperties: true, includeMethods: false }, { includeProperties: false, includeMethods: true }, { includeDescription: false, includeProperties: true }, { includeConstants: false, includeInheritance: false } ]; for (const scenario of scenarios) { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', ...scenario }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Basic info should always be present assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); // Validate conditional sections if (scenario.includeProperties === false) { assert(!classInfo.properties || classInfo.properties.length === 0, 'Properties should be excluded when includeProperties is false'); } else if (scenario.includeProperties !== false) { assert(Array.isArray(classInfo.properties), 'Properties should be array when included'); } if (scenario.includeMethods === false) { assert(!classInfo.methods || classInfo.methods.length === 0, 'Methods should be excluded when includeMethods is false'); } else if (scenario.includeMethods !== false) { assert(Array.isArray(classInfo.methods), 'Methods should be array when included'); } if (scenario.includeDescription === false) { assert(!classInfo.description || classInfo.description === '', 'Description should be excluded when includeDescription is false'); } } }); test('should validate Product class search results maintain proper structure', async () => { const searchTerms = ['get', 'set', 'name']; // Changed from including 'id' and 'price' to more reliable terms for (const searchTerm of searchTerms) { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', search: searchTerm }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Basic structure should be maintained assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); // Filtered arrays should still have proper structure if (classInfo.properties && classInfo.properties.length > 0) { classInfo.properties.forEach(prop => { assert(typeof prop.name === 'string', 'Filtered property name should be string'); assert(typeof prop.type === 'string', 'Filtered property type should be string'); // Verify search filtering worked const matchesSearch = prop.name.toLowerCase().includes(searchTerm.toLowerCase()) || (prop.description && prop.description.toLowerCase().includes(searchTerm.toLowerCase())); assert(matchesSearch, `Property ${prop.name} should match search term ${searchTerm}`); }); } if (classInfo.methods && classInfo.methods.length > 0) { classInfo.methods.forEach(method => { assert(typeof method.name === 'string', 'Filtered method name should be string'); assert(typeof method.signature === 'string', 'Filtered method signature should be string'); // Verify search filtering worked const matchesSearch = method.name.toLowerCase().includes(searchTerm.toLowerCase()) || (method.description && method.description.toLowerCase().includes(searchTerm.toLowerCase())); assert(matchesSearch, `Method ${method.name} should match search term ${searchTerm}`); }); } } }); test('should validate Product class with expand parameter maintains structure', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', expand: true }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Basic structure should be maintained assert.strictEqual(classInfo.className, 'Product'); assert.strictEqual(classInfo.packageName, 'dw.catalog'); assert(Array.isArray(classInfo.properties), 'Properties should be array'); assert(Array.isArray(classInfo.methods), 'Methods should be array'); // With expand=true, there might be additional type information // Validate that expanded information maintains proper structure classInfo.properties.forEach(prop => { assert(typeof prop.name === 'string', 'Expanded property name should be string'); assert(typeof prop.type === 'string', 'Expanded property type should be string'); }); classInfo.methods.forEach(method => { assert(typeof method.name === 'string', 'Expanded method name should be string'); assert(typeof method.signature === 'string', 'Expanded method signature should be string'); }); }); }); // ================================================================================== // PARAMETER COMBINATIONS AND EDGE CASES // ================================================================================== describe('Parameter Combinations and Edge Cases', () => { test('should handle all parameters together', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', expand: true, includeDescription: true, includeConstants: false, includeProperties: true, includeMethods: true, includeInheritance: false, search: 'get' }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Validate combination of parameters assert.strictEqual(classInfo.className, 'Product'); assert(classInfo.description, 'Description should be included'); assert(!classInfo.constants || classInfo.constants.length === 0, 'Constants should be excluded'); assert(!classInfo.inheritance || (Array.isArray(classInfo.inheritance) && classInfo.inheritance.length === 0) || classInfo.inheritance === '', 'Inheritance should be excluded'); // Properties and methods should be filtered by search if (classInfo.properties && classInfo.properties.length > 0) { classInfo.properties.forEach(prop => { assert(prop.name.toLowerCase().includes('get') || (prop.description && prop.description.toLowerCase().includes('get')), `Property ${prop.name} should match search term 'get'`); }); } if (classInfo.methods && classInfo.methods.length > 0) { classInfo.methods.forEach(method => { assert(method.name.toLowerCase().includes('get') || (method.description && method.description.toLowerCase().includes('get')), `Method ${method.name} should match search term 'get'`); }); } }); test('should handle contradictory filters gracefully', async () => { const result = await client.callTool('get_sfcc_class_info', { className: 'dw.catalog.Product', includeMethods: false, search: 'getName' // Searching for method while excluding methods }); assertValidMCPResponse(result); const classInfo = JSON.parse(result.content[0].text); // Should respect the filter over the search assert(!classInfo.methods || classInfo.methods.length === 0, 'Methods should be excluded even when searching for method names'); }); }); }); // ================================================================================== // HELPER FUNCTIONS // ================================================================================== /** * Validates that a response follows the MCP protocol structure */ function assertValidMCPResponse(result) { assert.ok(result, 'Result should exist'); assert.ok(result.content, 'Result should have content'); assert.ok(Array.isArray(result.content), 'Content should be an array'); assert.ok(result.content.length > 0, 'Content should not be empty'); assert.equal(result.content[0].type, 'text', 'Content type should be text'); assert.ok(typeof result.content[0].text === 'string', 'Content text should be string'); } ``` -------------------------------------------------------------------------------- /docs/dw_order/OrderMgr.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.order # Class OrderMgr ## Inheritance Hierarchy - Object - dw.order.OrderMgr ## Description Provides static helper methods for managing orders. Pipelet GetOrder and methods provided to access orders such as getOrder(String) and searchOrders(String, String, Object...) can be limited by the site preference 'Limit Storefront Order Access'. An insecure order access occurs in a storefront session when all of the following are true: The current storefront session isn’t the session in which the order was created. The session customer doesn’t match the order customer. The order status isn’t CREATED. When an order is accessed in an insecure manner: If the preference is ACTIVE, the action is disallowed and a SecurityException with a message beginning 'Unauthorized access to order' is thrown. If the preference is NOT ACTIVE, a SecurityException with a message beginning 'Unauthorized access to order' is logged as an error. In addition, the storefront should ensure the shopper is properly authenticated and authorized to read or modify the content of an order object. For more information, see Access Control. Don’t use dw.order.OrderMgr.searchOrder methods or processOrders(Function, String, Object...) immediately after creating or updating an order. The order search index updates asynchronously, typically within seconds but occasionally longer depending on search service load, so it might not include very recent changes. Instead, do one of the following: In the same request, pass the dw.order.Order object reference to the followup logic. For storefront use cases, especially when passing the order reference to a third party, use the order token for security by using getOrder(String, String). When implementing order history functionality, don't use the search or query methods in this class. Instead, use OrderHistory.getOrders(String, String, Object...). ## Constructor Summary ## Method Summary ### cancelOrder **Signature:** `static cancelOrder(order : Order) : Status` This method cancels an order. ### createOrder **Signature:** `static createOrder(basket : Basket) : Order` This method creates an order based on a basket. ### createOrder **Signature:** `static createOrder(basket : Basket, orderNo : String) : Order` This method functions the same as createOrder(Basket), but allows the optional specification of an orderNo. ### createOrderNo **Signature:** `static createOrderNo() : String` Creates an order number. ### createOrderSequenceNo **Signature:** `static createOrderSequenceNo() : String` Creates an order number. ### createShippingOrders **Signature:** `static createShippingOrders(order : Order) : Status` Triggers the shipping order creation for an order. ### describeOrder **Signature:** `static describeOrder() : ObjectTypeDefinition` Returns the meta data for Orders. ### failOrder **Signature:** `static failOrder(order : Order) : Status` This method fails an unplaced order and is usually called if payment could not be authorized. ### failOrder **Signature:** `static failOrder(order : Order, reopenBasketIfPossible : boolean) : Status` This method fails an unplaced order and is usually called if payment could not be authorized. ### getOrder **Signature:** `static getOrder(orderNumber : String) : Order` Returns the order with the specified order number. ### getOrder **Signature:** `static getOrder(orderNumber : String, orderToken : String) : Order` Resolves an order using the orderNumber and orderToken. ### placeOrder **Signature:** `static placeOrder(order : Order) : Status` This method places an order and is usually called after payment has been authorized. ### processOrders **Signature:** `static processOrders(processFunction : Function, queryString : String, args : Object...) : void` Executes a user-definable function on a set of orders. ### queryOrder **Signature:** `static queryOrder(queryString : String, args : Object...) : Order` Searches for a single order instance. ### queryOrders **Signature:** `static queryOrders(queryString : String, sortString : String, args : Object...) : SeekableIterator` Searches for order instances. ### queryOrders **Signature:** `static queryOrders(queryAttributes : Map, sortString : String) : SeekableIterator` Searches for order instances. ### searchOrder **Signature:** `static searchOrder(queryString : String, args : Object...) : Order` Searches for a single order instance. ### searchOrders **Signature:** `static searchOrders(queryString : String, sortString : String, args : Object...) : SeekableIterator` Searches for order instances. ### searchOrders **Signature:** `static searchOrders(queryAttributes : Map, sortString : String) : SeekableIterator` Searches for order instances. ### undoCancelOrder **Signature:** `static undoCancelOrder(order : Order) : Status` This method is used to turn a CANCELLED order into an OPEN order. ### undoFailOrder **Signature:** `static undoFailOrder(order : Order) : Status` This method is used to turn a FAILED order into a CREATED order. ## Method Detail ## Method Details ### cancelOrder **Signature:** `static cancelOrder(order : Order) : Status` **Description:** This method cancels an order. Only orders in status OPEN, NEW, or COMPLETED can be cancelled. Setting of cancel code and cancel description can be done by calling Order.setCancelCode(String) and Order.setCancelDescription(String). * If the order contains product or gift certificate line items associated with product list items, records of the purchase of the product list items will be removed. Inventory transactions and coupon redemptions associated with the order will be rolled back. It is important to consider that this method will cancel orders with gift certificate line items. If an order has any active post-processing objects (e.g. shipping orders, returns, appeasements), then it cannot be cancelled directly. Its status is set automatically, based on the statuses of its post-processing objects. To cancel such an order, you must cancel all related post-processing objects. If your B2C Commerce instance is integrated with Order Management, then you manage order statuses in Order Management. Use Order Management API endpoints. **Parameters:** - `order`: the order to be cancelled **Returns:** Status 'OK' or 'ERROR' --- ### createOrder **Signature:** `static createOrder(basket : Basket) : Order` **Description:** This method creates an order based on a basket. If successful, the new order will be in status Order.ORDER_STATUS_CREATED. The basket will be removed from the session and marked for removal. This method throws an APIException with type 'CreateOrderException' if any of the following conditions are encountered: any of the totals (net, gross, tax) of the basket is N/A any of the product items is not available (this takes previously reserved items into account) any campaign-based coupon in the basket is invalid (see CouponLineItem.isValid() the basket represents an order being edited, but the order has already been replaced by another order the basket represents an order being edited, but the customer associated with the original order is not the same as the current customer The method removes all empty shipments from the basket before creating the order. A shipment is said to be empty if all of the following are true: it contains no product or gift certificate line items all total prices (net, gross, tax) are 0.0 This method decrements inventory for all products contained in the order. A previous call to Basket.reserveInventory() is unnecessary and discouraged within the same request. The method takes any items with reserved inventory into account, allowing an early reservation of items, e.g. at the beginning of the checkout process. As described above, an APIException is thrown if any item is not available. If the basket contains product or gift certificate line items associated with product list items, the method updates the purchased quantity of the product list items; see ProductListItem.getPurchasedQuantity(). The system generates an order number via hook OrderHooks.createOrderNo(). If no hook is registered for the endpoint, the number is generated by calling createOrderSequenceNo(). The format of the number is based on the Order Number scheme defined in the Sequence Numbers preference configured for the site or organization. The number is guaranteed to be unique, but is not guaranteed to be sequential. It can be higher or lower than a previously created number. As a result, sorting orders by order number is not guaranteed to sort them in their order of creation. This method must not be used with the ReserveInventoryForOrder pipelet or Basket.reserveInventory() in the same request. When an order is created, search results don't include it until the next asynchronous update of the order search index. See OrderMgr. Please note that this method might result in an order with a different customer ID than the originating registered customer attached to the session. This happens if a registered customer logs in with the "RememberMe" flag set to true, but is later logged out (either explicitly, or automatically via session expiration) before calling this method. This is due to the internal order creation logic, which creates a new guest customer and attaches it to the order in such cases. To avoid this situation, have your custom code verify that the customer is authenticated before it calls this method. Usage: var basket : Basket; // known try { var order : Order = OrderMgr.createOrder(basket); } catch (e if e instanceof APIException && e.type === 'CreateOrderException') { // handle e } **Parameters:** - `basket`: basket to create an order for **Returns:** a new order **Throws:** - `CreateOrderException`: indicates the order could not be created --- ### createOrder **Signature:** `static createOrder(basket : Basket, orderNo : String) : Order` **Description:** This method functions the same as createOrder(Basket), but allows the optional specification of an orderNo. The orderNo must be unique within the context of a site. If the orderNo is not specified, the behavior is the same as that of createOrder(Basket). In that case, the system generates an order number via hook OrderHooks.createOrderNo(). If no hook is registered for the endpoint, the number is generated by calling createOrderSequenceNo(). The format of the number is based on the Order Number scheme defined in the Sequence Numbers preference configured for the site or organization. The number is guaranteed to be unique, but is not guaranteed to be sequential. It can be higher or lower than a previously created number. As a result, sorting orders by order number is not guaranteed to sort them in their order of creation. This method must not be used with the ReserveInventoryForOrder pipelet or Basket.reserveInventory() in the same request. When an order is created, search results don't include it until the next asynchronous update of the order search index. See OrderMgr. Please note that this method might result in an order with a different customer ID than the originating registered customer attached to the session. This happens if a registered customer logs in with the "RememberMe" flag set to true, but is later logged out (either explicitly, or automatically via session expiration) before calling this method. This is due to the internal order creation logic, which creates a new guest customer and attaches it to the order in such cases. To avoid this situation, have your custom code verify that the customer is authenticated before it calls this method. Usage: var basket : Basket; // known var orderNo : String; // known try { var order : Order = OrderMgr.createOrder(basket, orderNo); } catch (e if e instanceof APIException && e.type === 'CreateOrderException') { // handle e } **Parameters:** - `basket`: basket to create an order for - `orderNo`: optional order number; if null is specified, an order number is generated **Returns:** a new order **Throws:** - `CreateOrderException`: indicates the order could not be created --- ### createOrderNo **Signature:** `static createOrderNo() : String` **Description:** Creates an order number. The order number is created via hook OrderHooks.createOrderNo(). If no hook is registered for the endpoint, the number is generated by calling createOrderSequenceNo(). The format of the number is based on the Order Number scheme defined in the Sequence Numbers preference configured for the site or organization. The number is guaranteed to be unique, but is not guaranteed to be sequential. It can be higher or lower than a previously created number. As a result, sorting orders by order number is not guaranteed to sort them in their order of creation. **Returns:** an available order number **Throws:** CreateException - if order number creation failed --- ### createOrderSequenceNo **Signature:** `static createOrderSequenceNo() : String` **Description:** Creates an order number. The format of the number is based on the Order Number scheme defined in the Sequence Numbers preference configured for the site or organization. The number is guaranteed to be unique, but is not guaranteed to be sequential. It can be higher or lower than a previously created number. As a result, sorting orders by order number is not guaranteed to sort them in their order of creation. **Returns:** an available order number **Throws:** CreateException - if order number creation failed --- ### createShippingOrders **Signature:** `static createShippingOrders(order : Order) : Status` **Description:** Triggers the shipping order creation for an order. Must be run outside of a transaction. Will call hooks of the shipping order creation process, which are: ShippingOrderHooks.extensionPointPrepareCreateShippingOrders ShippingOrderHooks.extensionPointCreateShippingOrders ShippingOrderHooks.extensionPointAfterStatusChange ShippingOrderHooks.extensionPointNotifyStatusChange As a result, zero, one, or multiple ShippingOrders are created. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice. **Parameters:** - `order`: the order to run the shipping order creation for **Returns:** Status 'OK' or 'ERROR' with an error message --- ### describeOrder **Signature:** `static describeOrder() : ObjectTypeDefinition` **Description:** Returns the meta data for Orders. **Returns:** the meta data for Orders. --- ### failOrder **Signature:** `static failOrder(order : Order) : Status` **Description:** This method fails an unplaced order and is usually called if payment could not be authorized. The specified Order must be in status CREATED, and will be set to status FAILED. Inventory transactions and coupon redemptions associated with the Order will be rolled back. If the order is failed in the same session in which it was created, the basket will be reopened such that it can be used for a subsequent order. **Deprecated:** Please use failOrder(Order, Boolean) instead. **Parameters:** - `order`: the order to be placed **Returns:** Status 'OK' or 'ERROR' with an error message --- ### failOrder **Signature:** `static failOrder(order : Order, reopenBasketIfPossible : boolean) : Status` **Description:** This method fails an unplaced order and is usually called if payment could not be authorized. The specified Order must be in status CREATED, and will be set to status FAILED. Inventory transactions and coupon redemptions associated with the Order will be rolled back. A basket can only be reopened if no other basket for the customer exists at the moment of the call to failOrder, since a customer is limited to 1 storefront basket at a time. If, after order creation, a call was made to BasketMgr.getCurrentOrNewBasket() or pipelet GetBasket with parameter Create=true, then a new basket has been created, and failOrder cannot reopen the basket the order was created with. If a basket is reopened, it always masks sensitive information (e.g., credit card number), because during order creation, basket payment information is permanently masked. **Parameters:** - `order`: the order to be placed - `reopenBasketIfPossible`: reopen the basket if it still exists and limit for number of baskets is not reached **Returns:** Status 'OK' or 'ERROR' with an error message. Status detail basket contains the reopened basket, if it has been reopened successfully. --- ### getOrder **Signature:** `static getOrder(orderNumber : String) : Order` **Description:** Returns the order with the specified order number. Order access in the storefront can be limited; see the class description. Use getOrder(String, String) instead for secure access in a storefront session. If Limit Storefront Order Access site preference is enabled, this method throws an exception when an insecure access is attempted (refer to the conditions of insecure access in the description of OrderMgr class). Use getOrder(String, String) instead. **Parameters:** - `orderNumber`: the order number of the order to retrieve **Returns:** Order for the specified order number **See Also:** getOrder(String, String) **Throws:** SecurityException - thrown when the Limit Storefront Order Access preference is enabled and the order is insecurely accessed --- ### getOrder **Signature:** `static getOrder(orderNumber : String, orderToken : String) : Order` **Description:** Resolves an order using the orderNumber and orderToken. The order token is generated during order creation in a secure way that is designed to reduce the possibility of unauthorized access. You can retrieve the token via (Order.getOrderToken(). This version of the getOrder method doesn’t return an exception when the Limit Storefront Order Access site preference is enabled. Best security practice is to always enable this preference, and to use this method when appropriate. You should always use this method in the following cases. Integration use cases (such as asynchronous payment processing) When resolving orders from links (for example, order confirmation) Storefront use cases **Parameters:** - `orderNumber`: the order number of the order to retrieve - `orderToken`: the order token of the order to retrieve **Returns:** Order for the specified order number. null is returned if order is not found by number or token doesn’t correspond to the order found --- ### placeOrder **Signature:** `static placeOrder(order : Order) : Status` **Description:** This method places an order and is usually called after payment has been authorized. The specified order must be in status CREATED, and will be set to status NEW. If the order contains product or gift certificate line items associated with product list items, records of the purchase of the product list items will be made. For example, if the basket contains an item added from a gift registry, the purchase history of the respective gift registry item is updated. The order will count toward product and customer active data. Placing an order leads to the generation of shipment numbers for all shipments and the invoice number of the order. See Shipment.getShipmentNo() and Order.getInvoiceNo(). This is done using sequences. **Parameters:** - `order`: the order to be placed **Returns:** Status 'OK' or 'ERROR' with an error message --- ### processOrders **Signature:** `static processOrders(processFunction : Function, queryString : String, args : Object...) : void` **Description:** Executes a user-definable function on a set of orders. This method is intended to be used in batch processes and jobs, since it allows efficient processing of large result sets (which might take a while to process). First, a search with the given parameters is executed. Then the given function is executed once for each order of the search result. The order is handed over as the only parameter to this function. The search can be configured using a simple query language, which provides most common filter and operator functionality. The callback function will be supplied with a single argument of type 'Order'. When the function defines additional arguments, they will be undefined when called. When the method doesn't define any argument, it will be called anyway. Error during execution of the callback will be logged, and execution will continue with the next element from the result set. This method can be used as in this example (which counts the number of orders): var count=0; function callback(order: Order) { count++; dw.system.Logger.debug("order found: "+order.documentNo) } OrderMgr.processOrders(callback, "buyerno=1"); dw.system.Logger.debug("found "+count+" orders for buyerno 1"); **Parameters:** - `processFunction`: the function to execute for each order - `queryString`: the query string to use when searching for an order. - `args`: the query string arguments. --- ### queryOrder **Signature:** `static queryOrder(queryString : String, args : Object...) : Order` **Description:** Searches for a single order instance. Order access in the storefront can be limited; see the class description. The search can be configured using a simple query language, which provides most common filter and operator functionality. The identifier for an attribute to use in a query condition is always the ID of the attribute as defined in the type definition. For custom-defined attributes, the prefix 'custom' is required in the search term (e.g. custom.color = {1}), while for system attributes no prefix is used (e.g. name = {4}). Supported attribute value types with sample expression values: String 'String', 'Str*', 'Strin?' Integer 1, 3E4 Number 1.0, 3.99E5 Date yyyy-MM-dd e.g. 2007-05-31 (Default TimeZone = UTC) DateTime yyyy-MM-dd'T'hh:mm:ss+Z e.g. 2007-05-31T00:00+Z (Z TimeZone = UTC) or 2007-05-31T00:00:00 Boolean true, false Email '[email protected]', '*@demandware.com' Set of String 'String', 'Str*', 'Strin?' Set of Integer 1, 3E4 Set of Number 1.0, 3.99E5 Enum of String 'String', 'Str*', 'Strin?' Enum of Integer 1, 3E4 The following types of attributes are not queryable: Image HTML Text Quantity Password Note that some system attributes are not queryable by default, regardless of the actual value type code. The following operators are supported in a condition: = Equals - All types; supports NULL value (thumbnail = NULL) != Not equals - All types; supports NULL value (thumbnail != NULL) < Less than - Integer, Number, and Date types only > Greater than - Integer, Number, and Date types only <= Less or equals than - Integer, Number, and Date types only >= Greater or equals than - Integer, Number, and Date types only LIKE Like - String types and Email only; use if leading or trailing wildcards will be used to support substring search (e.g. custom.country LIKE 'US*') ILIKE Caseindependent Like - String types and Email only; use to support case-insensitive queries (e.g. custom.country ILIKE 'usa'); also supports wildcards for substring matching Conditions can be combined using logical expressions 'AND', 'OR', and 'NOT', and nested using parentheses, e.g. gender = {1} AND (age >= {2} OR (NOT profession LIKE {3})). The query language provides a placeholder syntax to pass objects as additional search parameters. Each passed object is related to a placeholder in the query string. The placeholder must be an Integer that is surrounded by braces. The first Integer value must be '0', the second '1', and so on, e.g. querySystemObjects("sample", "age = {0} or creationDate >= {1}", 18, date) If there is more than one object matching the specified query criteria, the result is not deterministic. In order to retrieve a single object from a sorted result set, it is recommended to use the following code: queryOrders("", "custom.myAttr asc", null).first(). The method first() returns only the next element and closes the iterator. This method is deprecated and will be removed in a future release. One of the following methods should be used instead: searchOrder(String, Object...), searchOrders(Map, String), and searchOrders(String, String, Object...) to search for orders and processOrders(Function, String, Object...) to search for and process orders in jobs. **Deprecated:** Please use searchOrder(String, Object...) instead. **Parameters:** - `queryString`: the query string that is used to locate the order. - `args`: one or more arguments to apply. **Returns:** the order that was found when executing the queryString. --- ### queryOrders **Signature:** `static queryOrders(queryString : String, sortString : String, args : Object...) : SeekableIterator` **Description:** Searches for order instances. Order access in the storefront can be limited; see the class description. The search can be configured using a simple query language, which provides most common filter and operator functionality. When implementing order history functionality, don't use the search or query methods in this class. Instead, use OrderHistory.getOrders(String, String, Object...). The identifier for an attribute to use in a query condition is always the ID of the attribute as defined in the type definition. For custom-defined attributes, the prefix 'custom' is required in the search term (e.g. custom.color = {1}), while for system attributes no prefix is used (e.g. name = {4}). Supported attribute value types with sample expression values: String 'String', 'Str*', 'Strin?' Integer 1, 3E4 Number 1.0, 3.99E5 Date yyyy-MM-dd e.g. 2007-05-31 (Default TimeZone = UTC) DateTime yyyy-MM-dd'T'hh:mm:ss+Z e.g. 2007-05-31T00:00+Z (Z TimeZone = UTC) or 2007-05-31T00:00:00 Boolean true, false Email '[email protected]', '*@demandware.com' Set of String 'String', 'Str*', 'Strin?' Set of Integer 1, 3E4 Set of Number 1.0, 3.99E5 Enum of String 'String', 'Str*', 'Strin?' Enum of Integer 1, 3E4 The following types of attributes are not queryable: Image HTML Text Quantity Password Note that some system attributes are not queryable by default, regardless of the actual value type code. The following operators are supported in a condition: = Equals - All types; supports NULL value (thumbnail = NULL) != Not equals - All types; supports NULL value (thumbnail != NULL) < Less than - Integer, Number, and Date types only > Greater than - Integer, Number, and Date types only <= Less or equals than - Integer, Number, and Date types only >= Greater or equals than - Integer, Number, and Date types only LIKE Like - String types and Email only; use if leading or trailing wildcards will be used to support substring search (e.g. custom.country LIKE 'US*') ILIKE Caseindependent Like - String types and Email only; use to support case-insensitive queries (e.g. custom.country ILIKE 'usa'); also supports wildcards for substring matching Conditions can be combined using logical expressions 'AND', 'OR', and 'NOT', and nested using parentheses, e.g. gender = {1} AND (age >= {2} OR (NOT profession LIKE {3})). The query language provides a placeholder syntax to pass objects as additional search parameters. Each passed object is related to a placeholder in the query string. The placeholder must be an Integer that is surrounded by braces. The first Integer value must be '0', the second '1', and so on, e.g. querySystemObjects("sample", "age = {0} or creationDate >= {1}", 18, date) The sorting parameter is optional and may contain a comma-separated list of attribute names to sort by. Each sort attribute name may be followed by an optional sort direction specifier ('asc' | 'desc'). The default sorting direction is ascending, if no direction was specified. Example: age desc, name Please note that specifying a localized custom attribute as the sorting attribute is currently not supported. Sometimes it is desired to get all instances of a specified type with a special sorting condition. This can be easily done by providing the 'type' of the custom object and the 'sortString' in combination with an empty 'queryString', e.g. queryOrders("sample", "", "custom.myAttr asc"). It is strongly recommended to call SeekableIterator.close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. This method is deprecated and will be removed in a future release. One of the following methods should be used instead: searchOrder(String, Object...), searchOrders(Map, String), and searchOrders(String, String, Object...) to search for orders, and processOrders(Function, String, Object...) to search for and process orders in jobs. **Deprecated:** Please use searchOrders(String, String, Object...) instead. **Parameters:** - `queryString`: the actual query. - `sortString`: an optional sorting, or null if no sorting is necessary. - `args`: one or more arguments for the query string. **Returns:** SeekableIterator containing the result set of the query. **See Also:** SeekableIterator.close() --- ### queryOrders **Signature:** `static queryOrders(queryAttributes : Map, sortString : String) : SeekableIterator` **Description:** Searches for order instances. Order access in the storefront can be limited; see the class description. The search can be configured with a map, which converts key-value pairs into a query expression. The key-value pairs are turned into a sequence of '=' or 'like' conditions, which are combined with AND statements. When implementing order history functionality, don't use the search or query methods in this class. Instead, use OrderHistory.getOrders(String, String, Object...). Example: A map with the key/value pairs: 'name'/'tom*', 'age'/66 will be converted as follows: "name like 'tom*' and age = 66" The identifier for an attribute to use in a query condition is always the ID of the attribute as defined in the type definition. For custom-defined attributes, the prefix 'custom' is required in the search term (e.g. custom.color = {1}), while for system attributes no prefix is used (e.g. name = {4}). Supported attribute value types with sample expression values: String 'String', 'Str*', 'Strin?' Integer 1, 3E4 Number 1.0, 3.99E5 Date yyyy-MM-dd e.g. 2007-05-31 (Default TimeZone = UTC) DateTime yyyy-MM-dd'T'hh:mm:ss+Z e.g. 2007-05-31T00:00+Z (Z TimeZone = UTC) or 2007-05-31T00:00:00 Boolean true, false Email '[email protected]', '*@demandware.com' Set of String 'String', 'Str*', 'Strin?' Set of Integer 1, 3E4 Set of Number 1.0, 3.99E5 Enum of String 'String', 'Str*', 'Strin?' Enum of Integer 1, 3E4 The following types of attributes are not queryable: Image HTML Text Quantity Password Note that some system attributes are not queryable by default, regardless of the actual value type code. The sorting parameter is optional and may contain a comma-separated list of attribute names to sort by. Each sort attribute name may be followed by an optional sort direction specifier ('asc' | 'desc'). The default sorting direction is ascending, if no direction was specified. Example: age desc, name Please note that specifying a localized custom attribute as the sorting attribute is currently not supported. It is strongly recommended to call the SeekableIterator.close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. This method is deprecated and will be removed in a future release. One of the following methods should be used instead: searchOrder(String, Object...), searchOrders(Map, String), and searchOrders(String, String, Object...) to search for orders and processOrders(Function, String, Object...) to search for and process orders in jobs. **Deprecated:** Please use searchOrders(Map, String) instead. **Parameters:** - `queryAttributes`: a set of key-value pairs that define the query. - `sortString`: an optional sorting, or null if no sorting is necessary. **Returns:** SeekableIterator containing the result set of the query. **See Also:** SeekableIterator.close() --- ### searchOrder **Signature:** `static searchOrder(queryString : String, args : Object...) : Order` **Description:** Searches for a single order instance. Order access in the storefront can be limited; see the class description. The search can be configured using a simple query language, which provides most common filter and operator functionality. The identifier for an attribute to use in a query condition is always the ID of the attribute as defined in the type definition. For custom-defined attributes the prefix 'custom' is required in the search term (e.g. custom.color = {1}), while for system attributes no prefix is used (e.g. name = {4}). Supported attribute value types with sample expression values: String 'String', 'Str*', 'Strin?' Integer 1, 3E4 Number 1.0, 3.99E5 Date yyyy-MM-dd e.g. 2007-05-31 (Default TimeZone = UTC) DateTime yyyy-MM-dd'T'hh:mm:ss+Z e.g. 2007-05-31T00:00+Z (Z TimeZone = UTC) or 2007-05-31T00:00:00 Boolean true, false Email '[email protected]', '*@demandware.com' Set of String 'String', 'Str*', 'Strin?' Set of Integer 1, 3E4 Set of Number 1.0, 3.99E5 Enum of String 'String', 'Str*', 'Strin?' Enum of Integer 1, 3E4 The following types of attributes are not queryable: Image HTML Text Quantity Password Note that some system attributes are not queryable by default, regardless of the actual value type code. The following operators are supported in a condition: = Equals - All types; supports NULL value (thumbnail = NULL) != Not equals - All types; supports NULL value (thumbnail != NULL) < Less than - Integer, Number, and Date types only > Greater than - Integer, Number, and Date types only <= Less or equals than - Integer, Number, and Date types only >= Greater or equals than - Integer, Number, and Date types only LIKE Like - String types and Email only; use if leading or trailing wildcards will be used to support substring search (e.g. custom.country LIKE 'US*') ILIKE Caseindependent Like - String types and Email only; use to support case-insensitive queries (e.g. custom.country ILIKE 'usa'), also supports wildcards for substring matching Conditions can be combined using logical expressions 'AND', 'OR', and 'NOT', and nested using parentheses, e.g. gender = {1} AND (age >= {2} OR (NOT profession LIKE {3})). The query language provides a placeholder syntax to pass objects as additional search parameters. Each passed object is related to a placeholder in the query string. The placeholder must be an Integer that is surrounded by braces. The first Integer value must be '0', the second '1', and so on, e.g. querySystemObjects("sample", "age = {0} or creationDate >= {1}", 18, date). If there is more than one object matching the specified query criteria, the result is not deterministic. In order to retrieve a single object from a sorted result set, it is recommended to use the following code: queryOrders("", "custom.myAttr asc", null).first(). The method first() returns only the next element and closes the iterator. If the order search API is configured to use the new Search Service, the following differences apply: wildcards will be filtered from the query (*, %, +) and replaced by spaces LIKE and ILIKE queries will be executed as fulltext queries (working on whole words), not as substring searches LIKE queries will always be case-insensitive using logical operators may change the execution of LIKE/ILIKE clauses to exact string comparison, depending on how they are combined using logical operators may result in degraded performance, depending on how they are combined Order search index updates are asynchronous, triggered only by committing changes to the underlying system. **Parameters:** - `queryString`: the query string that is used to locate the order. - `args`: one or more arguments to apply. **Returns:** the order that was found when executing the queryString. --- ### searchOrders **Signature:** `static searchOrders(queryString : String, sortString : String, args : Object...) : SeekableIterator` **Description:** Searches for order instances. Order access in the storefront can be limited; see the class description. The search can be configured using a simple query language, which provides most common filter and operator functionality. When implementing order history functionality, don't use the search or query methods in this class. Instead, use OrderHistory.getOrders(String, String, Object...). The identifier for an attribute to use in a query condition is always the ID of the attribute as defined in the type definition. For custom-defined attributes the prefix 'custom' is required in the search term (e.g. custom.color = {1}), while for system attributes no prefix is used (e.g. name = {4}). Supported attribute value types with sample expression values: String 'String', 'Str*', 'Strin?' Integer 1, 3E4 Number 1.0, 3.99E5 Date yyyy-MM-dd e.g. 2007-05-31 (Default TimeZone = UTC) DateTime yyyy-MM-dd'T'hh:mm:ss+Z e.g. 2007-05-31T00:00+Z (Z TimeZone = UTC) or 2007-05-31T00:00:00 Boolean true, false Email '[email protected]', '*@demandware.com' Set of String 'String', 'Str*', 'Strin?' Set of Integer 1, 3E4 Set of Number 1.0, 3.99E5 Enum of String 'String', 'Str*', 'Strin?' Enum of Integer 1, 3E4 The following types of attributes are not queryable: Image HTML Text Quantity Password Note that some system attributes are not queryable by default, regardless of the actual value type code. The following operators are supported in a condition: = Equals - All types; supports NULL value (thumbnail = NULL) != Not equals - All types; supports NULL value (thumbnail != NULL) < Less than - Integer, Number, and Date types only > Greater than - Integer, Number, and Date types only <= Less or equals than - Integer, Number, and Date types only >= Greater or equals than - Integer, Number, and Date types only LIKE Like - String types and Email only; use if leading or trailing wildcards will be used to support substring search (e.g. custom.country LIKE 'US*') ILIKE Caseindependent Like - String types and Email only; use to support case-insensitive queries (e.g. custom.country ILIKE 'usa'); also supports wildcards for substring matching Note that wildcards are not supported by Search Service. Conditions can be combined using logical expressions 'AND', 'OR', and 'NOT', and nested using parentheses, e.g. gender = {1} AND (age >= {2} OR (NOT profession LIKE {3})). The query language provides a placeholder syntax to pass objects as additional search parameters. Each passed object is related to a placeholder in the query string. The placeholder must be an Integer that is surrounded by braces. The first Integer value must be '0', the second '1', and so on, e.g. querySystemObjects("sample", "age = {0} or creationDate >= {1}", 18, date). The sorting parameter is optional and may contain a comma-separated list of attribute names to sort by. Each sort attribute name may be followed by an optional sort direction specifier ('asc' | 'desc'). The default sorting direction is ascending, if no direction was specified. Example: age desc, name Please note that specifying a localized custom attribute as the sorting attribute is currently not supported. Sometimes it is desired to get all instances of a specified type with a special sorting condition. This can be easily done by providing the 'type' of the custom object and the 'sortString' in combination with an empty 'queryString', e.g. queryOrders("sample", "", "custom.myAttr asc"). It is strongly recommended to call SeekableIterator.close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. If the order search API is configured to use the new Search Service, the following differences apply: wildcards will be filtered from the query (*, %, +) and replaced by spaces LIKE and ILIKE queries will be executed as fulltext queries (working on whole words), not as substring searches LIKE queries will always be case-insensitive using logical operators may change the execution of LIKE/ILIKE clauses to exact string comparison, depending on how they are combined using logical operators may result in degraded performance, depending on how they are combined the result is limited to a maximum of 1000 orders Order search index updates are asynchronous, triggered only by committing changes to the underlying system. **Parameters:** - `queryString`: the actual query. - `sortString`: an optional sorting or null if no sorting is necessary. - `args`: one or more arguments for the query string. **Returns:** SeekableIterator containing the result set of the query. **See Also:** SeekableIterator.close() --- ### searchOrders **Signature:** `static searchOrders(queryAttributes : Map, sortString : String) : SeekableIterator` **Description:** Searches for order instances. Order access in the storefront can be limited; see the class description. The search can be configured with a map, which converts key-value pairs into a query expression. The key-value pairs are turned into a sequence of '=' or 'like' conditions, which are combined with AND statements. Example: A map with the key/value pairs: 'name'/'tom*', 'age'/66 will be converted as follows: "name like 'tom*' and age = 66" Note that wildcards are not supported by Search Service. The identifier for an attribute to use in a query condition is always the ID of the attribute as defined in the type definition. For custom-defined attributes, the prefix 'custom' is required in the search term (e.g. custom.color = {1}), while for system attributes no prefix is used (e.g. name = {4}). Supported attribute value types with sample expression values: String 'String', 'Str*', 'Strin?' Integer 1, 3E4 Number 1.0, 3.99E5 Date yyyy-MM-dd e.g. 2007-05-31 (Default TimeZone = UTC) DateTime yyyy-MM-dd'T'hh:mm:ss+Z e.g. 2007-05-31T00:00+Z (Z TimeZone = UTC) or 2007-05-31T00:00:00 Boolean true, false Email '[email protected]', '*@demandware.com' Set of String 'String', 'Str*', 'Strin?' Set of Integer 1, 3E4 Set of Number 1.0, 3.99E5 Enum of String 'String', 'Str*', 'Strin?' Enum of Integer 1, 3E4 The following types of attributes are not queryable: Image HTML Text Quantity Password Note that some system attributes are not queryable by default, regardless of the actual value type code. The sorting parameter is optional and may contain a comma-separated list of attribute names to sort by. Each sort attribute name may be followed by an optional sort direction specifier ('asc' | 'desc'). The default sorting direction is ascending, if no direction was specified. Example: age desc, name Please note that specifying a localized custom attribute as the sorting attribute is currently not supported. It is strongly recommended to call SeekableIterator.close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. If the order search API is configured to use the new Search Service, the following differences apply: wildcards will be filtered from the query (*, %, +) and replaced by spaces LIKE and ILIKE queries will be executed as fulltext queries (working on whole words), not as substring searches LIKE queries will always be case-insensitive using logical operators may change the execution of LIKE/ILIKE clauses to exact string comparison, depending on how they are combined using logical operators may result in degraded performance, depending on how they are combined the result is limited to a maximum of 1000 orders Order search index updates are asynchronous, triggered only by committing changes to the underlying system. **Parameters:** - `queryAttributes`: a set of key-value pairs that define the query. - `sortString`: an optional sorting or null if no sorting is necessary. **Returns:** SeekableIterator containing the result set of the query. **See Also:** SeekableIterator.close() --- ### undoCancelOrder **Signature:** `static undoCancelOrder(order : Order) : Status` **Description:** This method is used to turn a CANCELLED order into an OPEN order. The specified order must be a cancelled order (Order.ORDER_STATUS_CANCELLED). The method will reserve inventory for all product line items, and create redemptions for all coupons. If successful, the status of the order will be changed to Order.ORDER_STATUS_OPEN. If the order contains product or gift certificate line items associated with product list items, records of the purchase of the product list items will be recreated. If the undoCancelOrder call fails, the transaction is marked as ‘rollback only’ – all changes in the associated transaction will no longer be committed. Possible error status codes are: OrderProcessStatusCodes.COUPON_INVALID - coupon is not active anymore or maximum amount of redemptions is reached OrderProcessStatusCodes.ORDER_NOT_CANCELLED - order is not in status Order.ORDER_STATUS_CANCELLED OrderProcessStatusCodes.INVENTORY_RESERVATION_FAILED - Inventory reservation for the order failed. In cases when availability is too low then undoCancel or undoFail results in a reservation failure. This can be avoided using the order site preferences to specifically allow overselling. See order site preferences under "Constraints for Undoing Failed/Cancelled Orders". **Parameters:** - `order`: the order on which to undo the cancel cancelOrder(Order) **Returns:** Status 'OK' or 'ERROR' with one of the error codes described above --- ### undoFailOrder **Signature:** `static undoFailOrder(order : Order) : Status` **Description:** This method is used to turn a FAILED order into a CREATED order. The specified order must be a failed order (Order.ORDER_STATUS_FAILED). The method will reserve inventory for all product line items, and create redemptions for all coupons. If successful, the status of the order will be changed to Order.ORDER_STATUS_CREATED. If the undoFailOrder call fails, the transaction is marked as ‘rollback only’ – all changes in the associated transaction will no longer be committed. Possible error status codes are: OrderProcessStatusCodes.COUPON_INVALID - coupon is not active anymore or maximum amount of redemptions is reached OrderProcessStatusCodes.ORDER_NOT_FAILED - order is not in status Order.ORDER_STATUS_FAILED OrderProcessStatusCodes.INVENTORY_RESERVATION_FAILED - Inventory reservation for the order failed. In cases when availability is too low then undoCancel or undoFail results in a reservation failure. This can be avoided using the order site preferences to specifically allow overselling. See order site preferences under "Constraints for Undoing Failed/Cancelled Orders". **Parameters:** - `order`: the order on which to undo the fail failOrder(Order) **Returns:** Status 'OK' or 'ERROR' with one of the error codes described above --- ``` -------------------------------------------------------------------------------- /docs/dw_crypto/Cipher.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.crypto # Class Cipher ## Inheritance Hierarchy - Object - dw.crypto.Cipher ## Description This class allows access to encryption services offered through the Java Cryptography Architecture (JCA). At this time the implementation of the encryption/decryption methods is based on the default JCE provider of the JDK. See the Java documentation for a reference guide to the underlying security provider and information about the Secure Sockets Extension. You can find a good overview of the essential purposes of cryptography and some common implementations in the Wikipedia article on cryptography. Also see the website of the National Institute of Standards and Technology. The format of various files used to hold keys, certificate signing requests, and the like, as well as some related algorithms, are defined in the PKCS series of documents published by RSALabs (the research arm of RSA Security). Many internet standards documenting security protocols and concepts are described in documents originally described as "Request For Comment" and thus widely known as RFCs. Many of them are available on the Internet FAQ Archives website. dw.crypto.Cipher is intentionally an Adapter to the full cryptography power supplied in the security provider implementation. Note: this class handles sensitive security-related data. Pay special attention to PCI DSS v3 requirements 2, 4, and 12. ## Constants ### CHAR_ENCODING **Type:** String = "UTF8" Strings containing keys, plain texts, cipher texts etc. are internally converted into byte arrays using this encoding (currently UTF8). ## Properties ## Constructor Summary Cipher() ## Method Summary ### decrypt **Signature:** `decrypt(base64Msg : String, key : String, transformation : String, saltOrIV : String, iterations : Number) : String` Decrypts the passed Base-64 encoded message using the passed key and applying the transformations described by the passed parameters. ### decrypt **Signature:** `decrypt(base64Msg : String, privateKey : KeyRef, transformation : String, saltOrIV : String, iterations : Number) : String` Alternative method to decrypt(String, String, String, String, Number), which allows to use a key in the keystore for the decryption. ### decrypt **Signature:** `decrypt(base64Msg : String, key : String, transformation : String, saltOrIV : String, iterations : Number) : String` Decrypts the passed Base-64 encoded message using the passed key and applying the transformations described by the passed parameters. ### decrypt **Signature:** `decrypt(base64Msg : String, privateKey : KeyRef, transformation : String, saltOrIV : String, iterations : Number) : String` Alternative method to decrypt_3(String, String, String, String, Number), which allows to use a key in the keystore for the decryption. ### decryptBytes **Signature:** `decryptBytes(encryptedBytes : Bytes, key : String, transformation : String, saltOrIV : String, iterations : Number) : Bytes` Lower-level decryption API. ### decryptBytes **Signature:** `decryptBytes(encryptedBytes : Bytes, privateKey : KeyRef, transformation : String, saltOrIV : String, iterations : Number) : Bytes` Alternative method to decryptBytes(Bytes, String, String, String, Number), which allows to use a key in the keystore for the decryption. ### decryptBytes **Signature:** `decryptBytes(encryptedBytes : Bytes, key : String, transformation : String, saltOrIV : String, iterations : Number) : Bytes` Lower-level decryption API. ### decryptBytes **Signature:** `decryptBytes(encryptedBytes : Bytes, privateKey : KeyRef, transformation : String, saltOrIV : String, iterations : Number) : Bytes` Alternative method to decryptBytes_3(Bytes, String, String, String, Number), which allows to use a key in the keystore for the decryption. ### encrypt **Signature:** `encrypt(message : String, key : String, transformation : String, saltOrIV : String, iterations : Number) : String` Encrypt the passed message by using the specified key and applying the transformations described by the specified parameters. ### encrypt **Signature:** `encrypt(message : String, publicKey : CertificateRef, transformation : String, saltOrIV : String, iterations : Number) : String` Alternative method to encrypt(String, String, String, String, Number), which allows you to use a key in the keystore for encryption. ### encrypt **Signature:** `encrypt(message : String, key : String, transformation : String, saltOrIV : String, iterations : Number) : String` Encrypt the passed message by using the specified key and applying the transformations described by the specified parameters. ### encrypt **Signature:** `encrypt(message : String, publicKey : CertificateRef, transformation : String, saltOrIV : String, iterations : Number) : String` Alternative method to encrypt_3(String, String, String, String, Number), which allows you to use a key in the keystore for encryption. ### encryptBytes **Signature:** `encryptBytes(messageBytes : Bytes, key : String, transformation : String, saltOrIV : String, iterations : Number) : Bytes` Lower-level encryption API. ### encryptBytes **Signature:** `encryptBytes(messageBytes : Bytes, publicKey : CertificateRef, transformation : String, saltOrIV : String, iterations : Number) : Bytes` Alternative method to encryptBytes(Bytes, String, String, String, Number), which allows to use a key in the keystore for the encryption. ### encryptBytes **Signature:** `encryptBytes(messageBytes : Bytes, key : String, transformation : String, saltOrIV : String, iterations : Number) : Bytes` Lower-level encryption API. ### encryptBytes **Signature:** `encryptBytes(messageBytes : Bytes, publicKey : CertificateRef, transformation : String, saltOrIV : String, iterations : Number) : Bytes` Alternative method to encryptBytes_3(Bytes, String, String, String, Number), which allows to use a key in the keystore for the encryption. ## Constructor Detail ## Method Detail ## Method Details ### decrypt **Signature:** `decrypt(base64Msg : String, key : String, transformation : String, saltOrIV : String, iterations : Number) : String` **Description:** Decrypts the passed Base-64 encoded message using the passed key and applying the transformations described by the passed parameters. Decryption is the process of getting back the original data from the cipher-text using a decryption key. **API Versioned:** From version 15.5. No longer available as of version 16.2. Requires Base64-encryption for the salt parameter. **Parameters:** - `base64Msg`: the base64 encoded cipher bytes - `key`: When using a symmetric cryptographic algorithm, use the same key to encrypt and decrypt. If the cryptographic algorithm is symmetric (for example, AES) or asymmetric (for example, RSA), the key needs to be passed as a base64-encoded string. The only exception is the symmetric cryptographic algorithms Password Based Encryption (PBE). With PBE the key needs to be passed as plain string (without any encoding). - `transformation`: The transformation has to be in "algorithm/mode/padding" format. See the corresponding encrypt method for supported transformations. - `saltOrIV`: Initialization value appropriate for the algorithm, this might be a Binary Salt or AlgorithmParameter or InitializationVector (see the corresponding encrypt method for details). Should be appropriate for the algorithm being used. If this value is null, a default initialization value will be used by the engine. The same value used to Encrypt needs to be supplied to the Decrypt function for many algorithms to successfully decrypt the data, so it is best practice to specify an appropriate value. - `iterations`: The number of passes to make when turning a passphrase into a key. This is only applicable for some types of algorithm. Password Based Encryption (PBE) algorithms use this parameter, and Block Encryption algorithms do not. If this value is relevant to the algorithm it would be best practice to supply it, as the same value would be needed to decrypt the data that was used to encrypt the data. **Returns:** the original plaintext message. --- ### decrypt **Signature:** `decrypt(base64Msg : String, privateKey : KeyRef, transformation : String, saltOrIV : String, iterations : Number) : String` **Description:** Alternative method to decrypt(String, String, String, String, Number), which allows to use a key in the keystore for the decryption. Note: Only asymmetric (public/private key pair) algorithms can be used with this method, since only those keys can be added to a keystore. **API Versioned:** From version 15.5. No longer available as of version 16.2. Requires Base64-encryption for the salt parameter. **Parameters:** - `base64Msg`: (see decrypt(String, String, String, String, Number)) - `privateKey`: A reference to a private key in the key store. - `transformation`: (see decrypt(String, String, String, String, Number)) - `saltOrIV`: (see decrypt(String, String, String, String, Number)) - `iterations`: (see decrypt(String, String, String, String, Number)) **Returns:** (see decrypt(String, String, String, String, Number)) --- ### decrypt **Signature:** `decrypt(base64Msg : String, key : String, transformation : String, saltOrIV : String, iterations : Number) : String` **Description:** Decrypts the passed Base-64 encoded message using the passed key and applying the transformations described by the passed parameters. Decryption is the process of getting back the original data from the cipher-text using a decryption key. **API Versioned:** From version 16.2. Does not use a default initialization vector. **Parameters:** - `base64Msg`: the base64 encoded cipher bytes - `key`: When using a symmetric cryptographic algorithm, use the same key to encrypt and decrypt. If the cryptographic algorithm is symmetric (for example, AES) or asymmetric (for example, RSA), the key needs to be passed as a base64-encoded string. The only exception is the symmetric cryptographic algorithms Password Based Encryption (PBE). With PBE the key needs to be passed as plain string (without any encoding). - `transformation`: The transformation has to be in "algorithm/mode/padding" format. See the corresponding encrypt method for supported transformations. - `saltOrIV`: Initialization value appropriate for the algorithm, this might be a Binary Salt or AlgorithmParameter or InitializationVector (see the corresponding encrypt method for details). Should be appropriate for the algorithm being used. The same value used to Encrypt needs to be supplied to the Decrypt function for many algorithms to successfully decrypt the data, so it is best practice to specify an appropriate value. - `iterations`: The number of passes to make when turning a passphrase into a key. This is only applicable for some types of algorithm. Password Based Encryption (PBE) algorithms use this parameter, and Block Encryption algorithms do not. If this value is relevant to the algorithm it would be best practice to supply it, as the same value would be needed to decrypt the data that was used to encrypt the data. **Returns:** the original plaintext message. --- ### decrypt **Signature:** `decrypt(base64Msg : String, privateKey : KeyRef, transformation : String, saltOrIV : String, iterations : Number) : String` **Description:** Alternative method to decrypt_3(String, String, String, String, Number), which allows to use a key in the keystore for the decryption. Note: Only asymmetric (public/private key pair) algorithms can be used with this method, since only those keys can be added to a keystore. **API Versioned:** From version 16.2. Does not use a default initialization vector. **Parameters:** - `base64Msg`: (see decrypt_3(String, String, String, String, Number)) - `privateKey`: A reference to a private key in the key store. - `transformation`: (see decrypt_3(String, String, String, String, Number)) - `saltOrIV`: (see decrypt_3(String, String, String, String, Number)) - `iterations`: (see decrypt_3(String, String, String, String, Number)) **Returns:** (see decrypt_3(String, String, String, String, Number)) --- ### decryptBytes **Signature:** `decryptBytes(encryptedBytes : Bytes, key : String, transformation : String, saltOrIV : String, iterations : Number) : Bytes` **Description:** Lower-level decryption API. Decrypts the passed bytes using the specified key and applying the transformations described by the specified parameters. Typical usage: var base64Msg : String = "some_encoded_encrypted_message"; var charset : String = "UTF8"; // or "windows-1252", etc. var encryptedBytes : Bytes = Encoding.fromBase64(base64Msg); var messageBytes : Bytes = Cipher.decryptBytes(encryptedBytes, key, transformation, salt, iterations); var message : String = messageBytes.toString(charset); **API Versioned:** From version 15.5. No longer available as of version 16.2. Requires Base64-encryption for the salt parameter. **Parameters:** - `encryptedBytes`: The bytes to decrypt. - `key`: The key to use for decryption. - `transformation`: The transformation used to originally encrypt. - `saltOrIV`: the salt or IV to use. - `iterations`: the iterations to use. **Returns:** The decrypted bytes. **See Also:** decrypt(String, String, String, String, Number) --- ### decryptBytes **Signature:** `decryptBytes(encryptedBytes : Bytes, privateKey : KeyRef, transformation : String, saltOrIV : String, iterations : Number) : Bytes` **Description:** Alternative method to decryptBytes(Bytes, String, String, String, Number), which allows to use a key in the keystore for the decryption. **API Versioned:** From version 15.5. No longer available as of version 16.2. Requires Base64-encryption for the salt parameter. **Parameters:** - `encryptedBytes`: (see decryptBytes(Bytes, String, String, String, Number)) - `privateKey`: A reference to a private key in the key store. - `transformation`: (see decryptBytes(Bytes, String, String, String, Number)) - `saltOrIV`: (see decryptBytes(Bytes, String, String, String, Number)) - `iterations`: (see decryptBytes(Bytes, String, String, String, Number)) **Returns:** (see decryptBytes(Bytes, String, String, String, Number)) --- ### decryptBytes **Signature:** `decryptBytes(encryptedBytes : Bytes, key : String, transformation : String, saltOrIV : String, iterations : Number) : Bytes` **Description:** Lower-level decryption API. Decrypts the passed bytes using the specified key and applying the transformations described by the specified parameters. Typical usage: var base64Msg : String = "some_encoded_encrypted_message"; var charset : String = "UTF8"; // or "windows-1252", etc. var encryptedBytes : Bytes = Encoding.fromBase64(base64Msg); var messageBytes : Bytes = Cipher.decryptBytes(encryptedBytes, key, transformation, salt, iterations); var message : String = messageBytes.toString(charset); **API Versioned:** From version 16.2. Does not use a default initialization vector. **Parameters:** - `encryptedBytes`: The bytes to decrypt. - `key`: The key to use for decryption. - `transformation`: The transformation used to originally encrypt. - `saltOrIV`: the salt or IV to use. - `iterations`: the iterations to use. **Returns:** The decrypted bytes. **See Also:** decrypt_3(String, String, String, String, Number) --- ### decryptBytes **Signature:** `decryptBytes(encryptedBytes : Bytes, privateKey : KeyRef, transformation : String, saltOrIV : String, iterations : Number) : Bytes` **Description:** Alternative method to decryptBytes_3(Bytes, String, String, String, Number), which allows to use a key in the keystore for the decryption. **API Versioned:** From version 16.2. Does not use a default initialization vector. **Parameters:** - `encryptedBytes`: (see decryptBytes_3(Bytes, String, String, String, Number)) - `privateKey`: A reference to a private key in the key store. - `transformation`: (see decryptBytes_3(Bytes, String, String, String, Number)) - `saltOrIV`: (see decryptBytes_3(Bytes, String, String, String, Number)) - `iterations`: (see decryptBytes_3(Bytes, String, String, String, Number)) **Returns:** (see decryptBytes_3(Bytes, String, String, String, Number)) --- ### encrypt **Signature:** `encrypt(message : String, key : String, transformation : String, saltOrIV : String, iterations : Number) : String` **Description:** Encrypt the passed message by using the specified key and applying the transformations described by the specified parameters. Encryption is the process of converting normal data or plain text to something incomprehensible or cipher-text by applying transformations, which are the operation (or set of operations) to be performed on given input to produce some output. A transformation always includes the name of a cryptographic algorithm (for example, RSA) and may be followed by a mode and padding scheme. The supported algorithms are listed in the parameter description below. The cryptographic algorithms can be partitioned into symmetric and asymmetric (or public key/private key). Symmetric or "secret key" algorithms use the same key to encrypt and to decrypt the data. Symmetric algorithms are what most people think of as codes: using a well-known algorithm and a secret key to encode information, which can be decoded using the same algorithm and the same key. The algorithm is not secret, the secrecy is inherent to guarding the key. A significant problem with symmetric ciphers is that it is difficult to transfer the keys themselves securely. Symmetric algorithms include password-based algorithms. AES with key length of 256 bits is the preferred choice for symmetric encryption going forward. Please consider switching to it if you are using any other scheme or if using AES with a shorter key length. The rest of the symmetric algorithms will be deprecated in the future. Asymmetric or "public key" cryptography uses a public/private key pair, and then publishes the public key. Only the holder of the private key will be able to decrypt. The public key and private key together are also called a "key pair". Data encrypted with one key can only be decrypted using the other key from the pair, and it is not possible to deduce one key from the other. This helps to solve the key distribution problem since it is possible to publicise one of the keys widely (the "public key") and keep the other a closely guarded secret (the "private key"). Many partners can then send data encrypted with the public key, but only the holder of the corresponding private key can decrypt it. Key pairs for asymmetric ciphers can be generated with an arbitrary tool. One of the most popular options is the open source tool OpenSSL. OpenSSL has a command-line syntax and is available on major platforms. The following steps are involved in creating an RSA key pair: Generate an RSA private key with keylength of 2048 bits. Store this key in a safe place. openssl genrsa -out rsaprivatekey.pem 2048 Generate a public key from the private key. You use the public key to encrypt messages with Cipher.encrypt. OpenSSL saves the key PEM-encoded; this means the key is saved with a base64 encoding. After you removed the header and footer lines you can pass the content directly to the API method. openssl rsa -in rsaprivatekey.pem -out publickey.pem -pubout Generate a private key in PKCS#8 format. You use that key to decrypt messages with Cipher.decrypt. OpenSSL saves the key PEM-encoded; this means the key is saved with a base64 encoding. After you removed the header and footer lines you can pass the content directly to the API method. openssl pkcs8 -topk8 -in rsaprivatekey.pem -out privatekey.pem -nocrypt Modes The following modes of operation are block cipher operations that are used with some algorithms. "NONE" no mode "CBC" Cipher Block Chaining (defined in FIPS PUB 81) "CTR" Counter mode or Segmented Integer Counter mode (defined in FIPS PUB 81) "CTS" CipherText Streaming mode "CFB" Cipher Feedback Mode, can be referred to with key length referenced as "CFB8","CFB16","CFB24".."CFB64" (defined in FIPS PUB 81) "ECB" Electronic Cook book as defined in: The National Institute of Standards and Technology (NIST) Federal Information Processing Standard (FIPS) PUB 81, "DES Modes of Operation," U.S. Department of Commerce, Dec 1980. "OFB" Output Feedback Mode, can be referred to with key length referenced as "OFB8","OFB16","OFB24".."OFB64" (defined in FIPS PUB 81) "PCBC" Propagating Cipher Block Chaining (defined in Kerberos V4) Paddings "NoPadding": No padding. OAEPWith<digest>And<mgf>Padding: Optimal Asymmetric Encryption Padding scheme defined in PKCS#1, where <digest> should be replaced by the message digest and <mgf> by the mask generation function. Examples: OAEPWITHSHA-256ANDMGF1PADDING, OAEPWITHSHA-384ANDMGF1PADDING, OAEPWITHSHA-512ANDMGF1PADDING ISO10126PADDING: the ISO10126-2:1991 DEA padding scheme PKCS1Padding: Public Key Cryptography Standard #1, a standard for padding from RSA Laboratories that can encrypt messages up to 11 bytes smaller than the modulus size in bytes. PKCS5Padding: Public Key Cryptography Standard #1, a standard for padding from RSA Laboratories, "PKCS#5: Password-Based Encryption Standard," version 1.5, November 1993. SSL3Padding: The padding scheme defined in the SSL Protocol Version 3.0, November 18, 1996, section 5.2.3.2 (CBC block cipher) **API Versioned:** From version 15.5. No longer available as of version 16.2. Requires Base64-encryption for the salt parameter. **Parameters:** - `message`: A string to encrypt (will be first converted with UTF-8 encoding into a byte stream) - `key`: A string ready for use with the algorithm. The key's format depends on the algorithm specified and the keys are assumed to be correctly formulated for the algorithm used, for example that the lengths are correct. Keys are not checked for validity. The cryptographic algorithms can be partitioned into symmetric and asymmetric (or public key/private key). Symmetric algorithms include password-based algorithms. Symmetric keys are usually a base64-encoded array of bytes. Asymmetric keys are "key pairs" with a public key and a private key. To encrypt using asymmetric algorithms, provide the public key. To decrypt using asymmetric algorithms, provide the private key from the same pair in PKCS#8 format, base64-encoded. See class documentation on how to generate a key pair. If the cryptographic algorithm is symmetric (for example, AES) or asymmetric (for example, RSA), the key needs to be passed as a base64-encoded string. The only exception is the symmetric cryptographic algorithms Password Based Encryption (PBE). With PBE the key needs to be passed as plain string (without any encoding). - `transformation`: The transformation has to be in "algorithm/mode/padding" format. Symmetric or "secret key" algorithms use the same key to encrypt and to decrypt the data. Asymmetric or "public key" cryptography uses a public/private key pair, and then publishes the public key. Only the holder of the private key will be able to decrypt. The public key and private key are also known as a "key pair". Supported Symmetric transformations include: "AES" or Rijndael, Advanced Encryption Standard as specified by NIST AES with key length of 256 is the preferred choice for symmetric encryption Keysizes: 128, 192, or 256 Modes: "ECB","CBC","PCBC","CTR","CTS","CFB","CFB8","CFB16","CFB24".."CFB64", "OFB","OFB8","OFB16","OFB24".."OFB64" Padding: "PKCS5Padding" Note that ARCFOUR, Blowfish, DES, RC2, DESede, DESedeWrap, PBEWithMD5AndDES, PBEWithMD5AndTripleDES1, PBEWithSHA1AndDESede and PBEWithSHA1AndRC2_40 transformations have been deprecated. Also, PKCS5Padding is the only supported Padding. NOPADDING and ISO10126PADDING have been deprecated. Supported Asymmetric transformations include: "RSA" Mode: "ECB" Padding: "OAEPWITHSHA-256ANDMGF1PADDING", "OAEPWITHSHA-384ANDMGF1PADDING", "OAEPWITHSHA-512ANDMGF1PADDING" Note that for RSA the key length should be at least 2048 bits. Also, the following Padding options have been deprecated: NOPADDING, PKCS1PADDING, OAEPWITHMD5ANDMGF1PADDING, OAEPWITHSHA1ANDMGF1PADDING and OAEPWITHSHA-1ANDMGF1PADDING. - `saltOrIV`: Initialization value appropriate for the algorithm, this might be a Binary Salt or AlgorithmParameter or InitializationVector. (As binary values cannot be passed, the equivalent Base64 String should be passed for any binary salt value). Should be appropriate for the algorithm being used. If this value is null, a default initialization value will be used by the engine. The same value used to Encrypt needs to be supplied to the Decrypt function for many algorithms to successfully decrypt the data, so it is best practice to specify an appropriate value. Requirements for the size and generation of DES initialization vectors (IV) are derived from FIPS 74 and FIPS 81 from the National Institute of Standards and Technology. CBC mode requires an IV with length 64 bits; CFB uses 48-64 bits; OFB uses 64 bits. If the IV is to be used with DES in the OFB mode, then it is not acceptable for the IV to remain fixed for multiple encryptions, if the same key is used for those encryptions. For Block Encryption algorithms this is the encoded Base64 String equivalent to the a random number to use as a "salt" to use with the algorithm. The algorithm must contain a Feedback Mode other than ECB. This must be a binary value that is exactly the same size as the algorithm block size. RC5 uses an optional 8-byte initialization vector (IV), but only in feedback mode (see CFB above). For Password Based Encryption algorithms, the salt is the encoded Base64 String equivalent to a random number value to transform the password into a key. PBE derives an encryption key from a password. In order to make the task of getting from password to key very time-consuming for an attacker, most PBE implementations will mix in a random number, known as a salt, to create the key. The salt value and the iteration count are then combined into a PBEParameterSpecification to initialize the cipher. The PKCS#5 spec from RSA Labs defines the parameters for password-based encryption (PBE). The RSA algorithm requires a salt with length as defined in PKCS#1. DSA has a specific initialization that uses three integers to build a DSAParameterSpec (a prime, a sub-prime and a base). To use this algorithm you should use the JCE or another provider to supply a DSAParameterSpec and then supply the Base64 equivalent string as the "salt". Please see the documentation from the provider for additional restrictions. - `iterations`: The number of passes to make when turning a passphrase into a key. This is only applicable for some types of algorithm. Password Based Encryption (PBE) algorithms use this parameter, and Block Encryption algorithms do not. If this value is relevant to the algorithm it would be best practice to supply it, as the same value would be needed to decrypt the data. **Returns:** the encrypted message encoded as a String using base 64 encoding. --- ### encrypt **Signature:** `encrypt(message : String, publicKey : CertificateRef, transformation : String, saltOrIV : String, iterations : Number) : String` **Description:** Alternative method to encrypt(String, String, String, String, Number), which allows you to use a key in the keystore for encryption. Note: Only asymmetric (public/private key pair) algorithms can be used with this method, since only those keys can be added to a keystore. For asymmetric algorithms a private/public key pair is required. Commerce Cloud Digital only allows you to add private keys in the format *.p12 and *.pfx. You can assign private keys an extra password in Business Manager. Public keys can only be imported as trusted certificates in the format *.crt, *.pem, *.der, and *.cer. Key pairs for asymmetric ciphers can be generated with an arbitrary tool. One of the most popular options is the open source tool OpenSSL. OpenSSL has a command-line syntax and is available on major platforms. The following steps are involved in creating an RSA key pair: Generate a public and a non-protected private key ( *.crt and *.key ). openssl req -x509 -newkey rsa:2048 -keyout nopass.key -out nopass.crt -days 365 -nodes Generate a keystore that contains the public and private keys ( *.p12 ). openssl pkcs12 -export -out nopass.p12 -inkey nopass.key -in nopass.crt To import a private or public key into the Digital keystore, navigate to Administration > Operations > Private Keys and Certificates Use a .p12 file to import a private key and a *.crt to import a public key. Typical usage: var plain : String = "some_plain_text"; var publicKeyRef = new CertificateRef("rsa-certificate-2048"); var cipher : Cipher = new Cipher(); var encrypted : String = cipher.encrypt(plain, publicKeyRef, "RSA", null, 0); **API Versioned:** From version 15.5. No longer available as of version 16.2. Requires Base64-encryption for the salt parameter. **Parameters:** - `message`: (see encrypt(String, String, String, String, Number)) - `publicKey`: A reference to a public key. - `transformation`: (see encrypt(String, String, String, String, Number)) - `saltOrIV`: (see encrypt(String, String, String, String, Number)) - `iterations`: (see encrypt(String, String, String, String, Number)) **Returns:** (see encrypt(String, String, String, String, Number)) --- ### encrypt **Signature:** `encrypt(message : String, key : String, transformation : String, saltOrIV : String, iterations : Number) : String` **Description:** Encrypt the passed message by using the specified key and applying the transformations described by the specified parameters. Encryption is the process of converting normal data or plain text to something incomprehensible or cipher-text by applying transformations, which are the operation (or set of operations) to be performed on given input to produce some output. A transformation always includes the name of a cryptographic algorithm (for example, RSA) and may be followed by a mode and padding scheme. The supported algorithms are listed in the parameter description below. The cryptographic algorithms can be partitioned into symmetric and asymmetric (or public key/private key). Symmetric or "secret key" algorithms use the same key to encrypt and to decrypt the data. Symmetric algorithms are what most people think of as codes: using a well-known algorithm and a secret key to encode information, which can be decoded using the same algorithm and the same key. The algorithm is not secret, the secrecy is inherent to guarding the key. A significant problem with symmetric ciphers is that it is difficult to transfer the keys themselves securely. Symmetric algorithms include password-based algorithms. AES with key length of 256 bits is the preferred choice for symmetric encryption going forward. Please consider switching to it if you are using any other scheme or if using AES with a shorter key length. The rest of the symmetric algorithms will be deprecated in the future. Asymmetric or "public key" cryptography uses a public/private key pair, and then publishes the public key. Only the holder of the private key will be able to decrypt. The public key and private key together are also called a "key pair". Data encrypted with one key can only be decrypted using the other key from the pair, and it is not possible to deduce one key from the other. This helps to solve the key distribution problem since it is possible to publicise one of the keys widely (the "public key") and keep the other a closely guarded secret (the "private key"). Many partners can then send data encrypted with the public key, but only the holder of the corresponding private key can decrypt it. Key pairs for asymmetric ciphers can be generated with an arbitrary tool. One of the most popular options is the open source tool OpenSSL. OpenSSL has a command-line syntax and is available on major platforms. The following steps are involved in creating an RSA key pair: Generate an RSA private key with keylength of 2048 bits. Store this key in a safe place. openssl genrsa -out rsaprivatekey.pem 2048 Generate a public key from the private key. You use the public key to encrypt messages with Cipher.encrypt. OpenSSL saves the key PEM-encoded; this means the key is saved with a base64 encoding. After you removed the header and footer lines you can pass the content directly to the API method. openssl rsa -in rsaprivatekey.pem -out publickey.pem -pubout Generate a private key in PKCS#8 format. You use that key to decrypt messages with Cipher.decrypt. OpenSSL saves the key PEM-encoded; this means the key is saved with a base64 encoding. After you removed the header and footer lines you can pass the content directly to the API method. openssl pkcs8 -topk8 -in rsaprivatekey.pem -out privatekey.pem -nocrypt Modes The following modes of operation are block cipher operations that are used with some algorithms. "NONE" no mode "CBC" Cipher Block Chaining (defined in FIPS PUB 81) "CTR" Counter mode or Segmented Integer Counter mode (defined in FIPS PUB 81) "CTS" CipherText Streaming mode "CFB" Cipher Feedback Mode, can be referred to with key length referenced as "CFB8","CFB16","CFB24".."CFB64" (defined in FIPS PUB 81) "ECB" Electronic Cook book as defined in: The National Institute of Standards and Technology (NIST) Federal Information Processing Standard (FIPS) PUB 81, "DES Modes of Operation," U.S. Department of Commerce, Dec 1980. "GCM" Galois/Counter Mode (defined in NIST SP 800-38D) "OFB" Output Feedback Mode, can be referred to with key length referenced as "OFB8","OFB16","OFB24".."OFB64" (defined in FIPS PUB 81) "PCBC" Propagating Cipher Block Chaining (defined in Kerberos V4) Paddings "NoPadding": No padding. OAEPWith<digest>And<mgf>Padding: Optimal Asymmetric Encryption Padding scheme defined in PKCS#1, where <digest> should be replaced by the message digest and <mgf> by the mask generation function. Examples: OAEPWITHSHA-256ANDMGF1PADDING, OAEPWITHSHA-384ANDMGF1PADDING, OAEPWITHSHA-512ANDMGF1PADDING ISO10126PADDING: the ISO10126-2:1991 DEA padding scheme PKCS1Padding: Public Key Cryptography Standard #1, a standard for padding from RSA Laboratories that can encrypt messages up to 11 bytes smaller than the modulus size in bytes. PKCS5Padding: Public Key Cryptography Standard #1, a standard for padding from RSA Laboratories, "PKCS#5: Password-Based Encryption Standard," version 1.5, November 1993. SSL3Padding: The padding scheme defined in the SSL Protocol Version 3.0, November 18, 1996, section 5.2.3.2 (CBC block cipher) **API Versioned:** From version 16.2. Does not use a default initialization vector. **Parameters:** - `message`: A string to encrypt (will be first converted with UTF-8 encoding into a byte stream) - `key`: A string ready for use with the algorithm. The key's format depends on the algorithm specified and the keys are assumed to be correctly formulated for the algorithm used, for example that the lengths are correct. Keys are not checked for validity. The cryptographic algorithms can be partitioned into symmetric and asymmetric (or public key/private key). Symmetric algorithms include password-based algorithms. Symmetric keys are usually a base64-encoded array of bytes. Asymmetric keys are "key pairs" with a public key and a private key. To encrypt using asymmetric algorithms, provide the public key. To decrypt using asymmetric algorithms, provide the private key from the same pair in PKCS#8 format, base64-encoded. See class documentation on how to generate a key pair. If the cryptographic algorithm is symmetric (for example, AES) or asymmetric (for example, RSA), the key needs to be passed as a base64-encoded string. The only exception is the symmetric cryptographic algorithms Password Based Encryption (PBE). With PBE the key needs to be passed as plain string (without any encoding). - `transformation`: The transformation has to be in "algorithm/mode/padding" format. Symmetric or "secret key" algorithms use the same key to encrypt and to decrypt the data. Asymmetric or "public key" cryptography uses a public/private key pair, and then publishes the public key. Only the holder of the private key will be able to decrypt. The public key and private key are also known as a "key pair". Supported Symmetric transformations include: "AES" or Rijndael, Advanced Encryption Standard as specified by NIST AES with key length of 256 is the preferred choice for symmetric encryption Keysizes: 128, 192, or 256 Modes: "GCM","ECB","CBC","PCBC","CTR","CTS","CFB","CFB8","CFB16","CFB24".."CFB64", "OFB","OFB8","OFB16","OFB24".."OFB64" Padding: "PKCS5Padding" or "NoPadding" (GCM only) Note that ARCFOUR, Blowfish, DES, RC2, DESede, DESedeWrap, PBEWithMD5AndDES, PBEWithMD5AndTripleDES1, PBEWithSHA1AndDESede and PBEWithSHA1AndRC2_40 transformations have been deprecated. PKCS5Padding is the only supported Padding for most modes. NOPADDING is only supported for GCM, and ISO10126PADDING has been deprecated. Supported Asymmetric transformations include: "RSA" Mode: "ECB" Padding: "OAEPWITHSHA-256ANDMGF1PADDING", "OAEPWITHSHA-384ANDMGF1PADDING", "OAEPWITHSHA-512ANDMGF1PADDING" Note that for RSA the key length should be at least 2048 bits. Also, the following Padding options have been deprecated: NOPADDING, PKCS1PADDING, OAEPWITHMD5ANDMGF1PADDING, OAEPWITHSHA1ANDMGF1PADDING and OAEPWITHSHA-1ANDMGF1PADDING. - `saltOrIV`: Initialization value appropriate for the algorithm, this might be a Binary Salt or AlgorithmParameter or InitializationVector. (As binary values cannot be passed, the equivalent Base64 String should be passed for any binary salt value). Should be appropriate for the algorithm being used. The same value used to Encrypt needs to be supplied to the Decrypt function for many algorithms to successfully decrypt the data, so it is best practice to specify an appropriate value. Requirements for the size and generation of DES initialization vectors (IV) are derived from FIPS 74 and FIPS 81 from the National Institute of Standards and Technology. CBC mode requires an IV with length 64 bits; CFB uses 48-64 bits; OFB uses 64 bits; GCM uses 96 bits. If the IV is to be used with DES in the OFB mode, then it is not acceptable for the IV to remain fixed for multiple encryptions, if the same key is used for those encryptions. For Block Encryption algorithms this is the encoded Base64 String equivalent to the a random number to use as a "salt" to use with the algorithm. The algorithm must contain a Feedback Mode other than ECB. This must be a binary value that is exactly the same size as the algorithm block size. RC5 uses an optional 8-byte initialization vector (IV), but only in feedback mode (see CFB above). For Password Based Encryption algorithms, the salt is the encoded Base64 String equivalent to a random number value to transform the password into a key. PBE derives an encryption key from a password. In order to make the task of getting from password to key very time-consuming for an attacker, most PBE implementations will mix in a random number, known as a salt, to create the key. The salt value and the iteration count are then combined into a PBEParameterSpecification to initialize the cipher. The PKCS#5 spec from RSA Labs defines the parameters for password-based encryption (PBE). The RSA algorithm requires a salt with length as defined in PKCS#1. DSA has a specific initialization that uses three integers to build a DSAParameterSpec (a prime, a sub-prime and a base). To use this algorithm you should use the JCE or another provider to supply a DSAParameterSpec and then supply the Base64 equivalent string as the "salt". Please see the documentation from the provider for additional restrictions. For GCM the base64-encoded initialization vector may be optionally suffixed with a vertical pipe followed by the number of bits in the tag length. If not present then the tag length will be 128 bits. This syntax is only supported for the GCM mode. - `iterations`: The number of passes to make when turning a passphrase into a key. This is only applicable for some types of algorithm. Password Based Encryption (PBE) algorithms use this parameter, and Block Encryption algorithms do not. If this value is relevant to the algorithm it would be best practice to supply it, as the same value would be needed to decrypt the data. **Returns:** the encrypted message encoded as a String using base 64 encoding. --- ### encrypt **Signature:** `encrypt(message : String, publicKey : CertificateRef, transformation : String, saltOrIV : String, iterations : Number) : String` **Description:** Alternative method to encrypt_3(String, String, String, String, Number), which allows you to use a key in the keystore for encryption. Note: Only asymmetric (public/private key pair) algorithms can be used with this method, since only those keys can be added to a keystore. For asymmetric algorithms a private/public key pair is required. Commerce Cloud Digital only allows you to add private keys in the format *.p12 and *.pfx. You can assign private keys an extra password in Business Manager. Public keys can only be imported as trusted certificates in the format *.crt, *.pem, *.der, and *.cer. Key pairs for asymmetric ciphers can be generated with an arbitrary tool. One of the most popular options is the open source tool OpenSSL. OpenSSL has a command-line syntax and is available on major platforms. The following steps are involved in creating an RSA key pair: Generate a public and a non-protected private key ( *.crt and *.key ). openssl req -x509 -newkey rsa:2048 -keyout nopass.key -out nopass.crt -days 365 -nodes Generate a keystore that contains the public and private keys ( *.p12 ). openssl pkcs12 -export -out nopass.p12 -inkey nopass.key -in nopass.crt To import a private or public key into the Digital keystore, navigate to Administration > Operations > Private Keys and Certificates Use a .p12 file to import a private key and a *.crt to import a public key. Typical usage: var plain : String = "some_plain_text"; var publicKeyRef = new CertificateRef("rsa-certificate-2048"); var cipher : Cipher = new Cipher(); var encrypted : String = cipher.encrypt(plain, publicKeyRef, "RSA", null, 0); **API Versioned:** From version 16.2. Does not use a default initialization vector. **Parameters:** - `message`: (see encrypt_3(String, String, String, String, Number)) - `publicKey`: A reference to a public key. - `transformation`: (see encrypt_3(String, String, String, String, Number)) - `saltOrIV`: (see encrypt_3(String, String, String, String, Number)) - `iterations`: (see encrypt_3(String, String, String, String, Number)) **Returns:** (see encrypt_3(String, String, String, String, Number)) --- ### encryptBytes **Signature:** `encryptBytes(messageBytes : Bytes, key : String, transformation : String, saltOrIV : String, iterations : Number) : Bytes` **Description:** Lower-level encryption API. Encrypts the passed bytes by using the specified key and applying the transformations described by the specified parameters. Typical usage: var message : String = "some_message"; var charset : String = "UTF8"; // or "windows-1252", etc. // encrypt the message var messageBytes : Bytes = new Bytes(message, charset); var encryptedBytes : Bytes = Cipher.encryptBytes(messageBytes, key, transformation, salt, iterations); var encrypted : String = Encoding.toBase64(encryptedBytes); **API Versioned:** From version 15.5. No longer available as of version 16.2. Requires Base64-encryption for the salt parameter. **Parameters:** - `messageBytes`: The bytes to encrypt. - `key`: The key to use for encryption. - `transformation`: The transformation to apply. - `saltOrIV`: Initialization value appropriate for the algorithm. - `iterations`: The number of passes to make when turning a passphrase into a key. **Returns:** the encrypted bytes. **See Also:** encrypt(String, String, String, String, Number) --- ### encryptBytes **Signature:** `encryptBytes(messageBytes : Bytes, publicKey : CertificateRef, transformation : String, saltOrIV : String, iterations : Number) : Bytes` **Description:** Alternative method to encryptBytes(Bytes, String, String, String, Number), which allows to use a key in the keystore for the encryption. Note: Only asymmetric (public/private key pair) algorithms can be used with this method, since only those keys can be added to a keystore. **API Versioned:** From version 15.5. No longer available as of version 16.2. Requires Base64-encryption for the salt parameter. **Parameters:** - `messageBytes`: (see encryptBytes(Bytes, String, String, String, Number)) - `publicKey`: A reference to a public key. - `transformation`: (see encryptBytes(Bytes, String, String, String, Number)) - `saltOrIV`: (see encryptBytes(Bytes, String, String, String, Number)) - `iterations`: (see encryptBytes(Bytes, String, String, String, Number)) **Returns:** (see encryptBytes(Bytes, String, String, String, Number)) --- ### encryptBytes **Signature:** `encryptBytes(messageBytes : Bytes, key : String, transformation : String, saltOrIV : String, iterations : Number) : Bytes` **Description:** Lower-level encryption API. Encrypts the passed bytes by using the specified key and applying the transformations described by the specified parameters. Typical usage: var message : String = "some_message"; var charset : String = "UTF8"; // or "windows-1252", etc. // encrypt the message var messageBytes : Bytes = new Bytes(message, charset); var encryptedBytes : Bytes = Cipher.encryptBytes(messageBytes, key, transformation, salt, iterations); var encrypted : String = Encoding.toBase64(encryptedBytes); **API Versioned:** From version 16.2. Does not use a default initialization vector. **Parameters:** - `messageBytes`: The bytes to encrypt. - `key`: The key to use for encryption. - `transformation`: The transformation to apply. - `saltOrIV`: Initialization value appropriate for the algorithm. - `iterations`: The number of passes to make when turning a passphrase into a key. **Returns:** the encrypted bytes. **See Also:** encrypt_3(String, String, String, String, Number) --- ### encryptBytes **Signature:** `encryptBytes(messageBytes : Bytes, publicKey : CertificateRef, transformation : String, saltOrIV : String, iterations : Number) : Bytes` **Description:** Alternative method to encryptBytes_3(Bytes, String, String, String, Number), which allows to use a key in the keystore for the encryption. Note: Only asymmetric (public/private key pair) algorithms can be used with this method, since only those keys can be added to a keystore. **API Versioned:** From version 16.2. Does not use a default initialization vector. **Parameters:** - `messageBytes`: (see encryptBytes_3(Bytes, String, String, String, Number)) - `publicKey`: A reference to a public key. - `transformation`: (see encryptBytes_3(Bytes, String, String, String, Number)) - `saltOrIV`: (see encryptBytes_3(Bytes, String, String, String, Number)) - `iterations`: (see encryptBytes_3(Bytes, String, String, String, Number)) **Returns:** (see encryptBytes_3(Bytes, String, String, String, Number)) --- ``` -------------------------------------------------------------------------------- /src/core/tool-definitions.ts: -------------------------------------------------------------------------------- ```typescript /** * MCP Tool Definitions for SFCC Development * * This module contains all the tool definitions organized by category * to keep the main server file clean and maintainable. */ export const SFCC_DOCUMENTATION_TOOLS = [ { name: 'get_sfcc_class_info', description: 'Get detailed information about an SFCC class including properties, methods, and description. Use this when you need to understand what a specific SFCC class does, what methods/properties are available, or when implementing features that use SFCC APIs. Essential for cartridge development (controllers, scripts, templates, rest-apis) using the dw.* namespace in the SFCC Rhino environment.', inputSchema: { type: 'object', properties: { className: { type: 'string', description: "The SFCC class name (e.g., 'Catalog', 'dw.catalog.Catalog')", }, expand: { type: 'boolean', description: 'Whether to include detailed information about referenced types used by this class (default: false)', default: false, }, includeDescription: { type: 'boolean', description: 'Whether to include the class description in the response (default: true)', default: true, }, includeConstants: { type: 'boolean', description: 'Whether to include constants in the response (default: true)', default: true, }, includeProperties: { type: 'boolean', description: 'Whether to include properties in the response (default: true)', default: true, }, includeMethods: { type: 'boolean', description: 'Whether to include methods in the response (default: true)', default: true, }, includeInheritance: { type: 'boolean', description: 'Whether to include inheritance hierarchy in the response (default: true)', default: true, }, search: { type: 'string', description: 'Optional search term to filter constants, properties, methods, and inheritance entries. Case-insensitive search across names and descriptions. Only one word at a time (e.g., "get", "create", "order"). Combining multiple words or looking for multiple items at the same time is not supported.', }, }, required: ['className'], }, }, { name: 'search_sfcc_classes', description: "Search for SFCC classes by name or functionality. Use this when you know part of a class name or need to find classes related to specific functionality (e.g., search 'catalog' to find catalog-related classes). Perfect starting point when you're unsure of the exact class name or exploring available APIs for a feature area.", inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query for class names. Only use one word at a time (e.g., "catalog", "order", "customer"). Combining multiple words or attempting to look for multiple classes at the same time is not supported.', }, }, required: ['query'], }, }, { name: 'search_sfcc_methods', description: 'Search for methods across all SFCC classes by method name. Use this when you know the method name but not which class it belongs to, or when looking for similar methods across different classes. Helpful for discovering all available methods that perform similar operations.', inputSchema: { type: 'object', properties: { methodName: { type: 'string', description: 'Method name to search for. Only use one word at a time (e.g., "get", "create", "update"). Combining multiple words or looking for multiple methods at the same time is not supported.', }, }, required: ['methodName'], }, }, { name: 'list_sfcc_classes', description: "Get a complete list of all available SFCC classes. Use this for exploration and discovery when you need to understand the full scope of SFCC APIs, or when you're new to SFCC development and want to see what's available. Good starting point for understanding the SFCC class hierarchy.", inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_sfcc_class_documentation', description: "Get the complete raw documentation for an SFCC class. Use this when you need comprehensive details about a class including examples, detailed descriptions, and full context. Best for in-depth understanding when the basic class info isn't sufficient.", inputSchema: { type: 'object', properties: { className: { type: 'string', description: 'The SFCC class name', }, }, required: ['className'], }, }, ]; export const BEST_PRACTICES_TOOLS = [ { name: 'get_available_best_practice_guides', description: 'Get a list of all available SFCC best practice and how-to guides. Use this first to discover what guidance is available before implementing any SFCC features. Essential for understanding what best practice resources exist for cartridge creation, hooks, controllers, and custom endpoints', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_best_practice_guide', description: 'Get a complete best practice and how-to guide with all sections and content. Use this when implementing specific SFCC features like cartridges, ISML templates, OCAPI/SCAPI hooks, SFRA controllers, or custom endpoints. Always consult the relevant guide before writing code to ensure you follow SFCC best practices, security guidelines, and proper architecture patterns.', inputSchema: { type: 'object', properties: { guideName: { type: 'string', enum: ['cartridge_creation', 'isml_templates', 'job_framework', 'localserviceregistry', 'ocapi_hooks', 'scapi_hooks', 'sfra_controllers', 'sfra_models', 'sfra_client_side_js', 'sfra_scss', 'scapi_custom_endpoint', 'performance', 'security'], description: 'The guide name (e.g., \'cartridge_creation\', \'isml_templates\', \'job_framework\', \'localserviceregistry\', \'ocapi_hooks\', \'scapi_hooks\', \'sfra_controllers\', \'sfra_models\', \'sfra_client_side_js\', \'sfra_scss\', \'scapi_custom_endpoint\', \'performance\', \'security\')', }, }, required: ['guideName'], }, }, { name: 'search_best_practices', description: 'Search across all best practice guides for specific terms, patterns, or concepts. Use this when you need guidance on specific topics like validation, security, performance optimization, error handling, or any development pattern. Perfect for finding relevant best practices without reading entire guides.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: "Search term or concept (e.g., 'validation', 'security', 'performance'). Use single words for best results as the API does not support complex queries.", }, }, required: ['query'], }, }, { name: 'get_hook_reference', description: "Get comprehensive hook reference tables showing all available OCAPI or SCAPI hook endpoints and extension points. Use this when implementing hooks to see all available extension points, understand hook signatures, and ensure you're using the correct hook for your use case. Essential reference when extending SFCC APIs.", inputSchema: { type: 'object', properties: { guideName: { type: 'string', description: 'The hook guide name', enum: ['ocapi_hooks', 'scapi_hooks'], }, }, required: ['guideName'], }, }, ]; export const SFRA_DOCUMENTATION_TOOLS = [ { name: 'get_available_sfra_documents', description: 'Get a list of all available SFRA (Storefront Reference Architecture) documentation. Use this to discover what SFRA classes, modules, and models are documented, including Server, Request, Response, QueryString, render module, and comprehensive model documentation for account, cart, products, pricing, billing, shipping, and more. Essential for understanding SFRA architecture and available functionality.', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_sfra_document', description: 'Get complete SFRA class, module, or model documentation with detailed information about properties, methods, and usage examples. Use this when working with SFRA controllers, middleware, models, or when you need to understand how SFRA components work together. Perfect for implementing SFRA-based features. Now supports all 26+ SFRA documents including core classes, product models, order/cart models, customer models, pricing models, and more.', inputSchema: { type: 'object', properties: { documentName: { type: 'string', description: 'The SFRA document name (e.g., \'server\', \'request\', \'response\', \'querystring\', \'render\', \'cart\', \'product-full\', \'account\', \'billing\', \'shipping\', etc.). Use get_available_sfra_documents to see all available options.', }, }, required: ['documentName'], }, }, { name: 'search_sfra_documentation', description: 'Search across all SFRA documentation for specific terms, concepts, or functionality. Use this when you need to find specific SFRA features, understand how to implement controller patterns, locate model information, or find information about routing, middleware, request handling, response management, cart functionality, product models, or customer management. Enhanced with relevance scoring and categorization.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: "Search term or concept (e.g., 'middleware', 'routing', 'render', 'querystring', 'cache', 'cart', 'product', 'billing', 'shipping', 'account', 'pricing')", }, }, required: ['query'], }, }, { name: 'get_sfra_documents_by_category', description: 'Get SFRA documents filtered by category. Use this to explore documents in specific functional areas like core SFRA classes, product models, order/cart functionality, customer management, pricing, or store models. Perfect for discovering related documentation and understanding functional groupings.', inputSchema: { type: 'object', properties: { category: { type: 'string', enum: ['core', 'product', 'order', 'customer', 'pricing', 'store', 'other'], description: 'Category to filter by: core (Server, Request, Response, etc.), product (product models), order (cart, billing, shipping), customer (account, address), pricing (price models), store (store models), other (utilities)', }, }, required: ['category'], }, }, { name: 'get_sfra_categories', description: 'Get all available SFRA document categories with counts and descriptions. Use this to understand the organization of SFRA documentation and discover what types of functionality are available. Helpful for exploring the full scope of SFRA capabilities.', inputSchema: { type: 'object', properties: {}, }, }, ]; export const LOG_TOOLS = [ { name: 'get_latest_error', description: 'Get the latest error messages from SFCC logs. Use this when debugging failed operations, investigating crashes, exceptions, or when code is not working as expected. Essential for troubleshooting critical issues, API failures, database connection problems, or when users report bugs. Errors indicate something went wrong and needs immediate attention.', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'Date in YYYYMMDD format (default: today)', }, limit: { type: 'number', description: 'Number of error entries to return (default: 20)', default: 20, }, }, }, }, { name: 'get_latest_warn', description: 'Get the latest warning messages from SFCC logs. Use this to identify potential issues, deprecated features being used, performance concerns, or configurations that might cause problems later. Warnings help prevent future errors and optimize code quality. Check warnings when code works but performance is slow or when preparing for production deployment.', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'Date in YYYYMMDD format (default: today)', }, limit: { type: 'number', description: 'Number of warning entries to return (default: 20)', default: 20, }, }, }, }, { name: 'get_latest_info', description: 'Get the latest info messages from SFCC logs. Use this to understand application flow, verify that operations completed successfully, track business logic execution, or monitor normal system behavior. Info logs help confirm that features are working correctly and provide context about what the system is doing during normal operation.', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'Date in YYYYMMDD format (default: today)', }, limit: { type: 'number', description: 'Number of info entries to return (default: 20)', default: 20, }, }, }, }, { name: 'get_latest_debug', description: "Get the latest debug messages from SFCC logs. Use this for detailed troubleshooting when you need to trace code execution step-by-step, inspect variable values, understand complex business logic flow, or investigate subtle bugs. Debug logs provide the most detailed information and are essential when standard error logs don't provide enough context to solve the problem.", inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'Date in YYYYMMDD format (default: today)', }, limit: { type: 'number', description: 'Number of debug entries to return (default: 20)', default: 20, }, }, }, }, { name: 'summarize_logs', description: 'Get a comprehensive overview of all log activity with counts and key issues for a specific date. Use this as the first step when investigating problems to quickly understand the overall health of the system, identify the most frequent errors, and get a high-level view before diving into specific log types. Perfect for daily health checks, incident response, or when you need to quickly assess if there are any major issues.', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'Date in YYYYMMDD format (default: today)', }, }, }, }, { name: 'search_logs', description: "Search for specific patterns, keywords, or error messages across SFCC logs. Use this when you know what you're looking for - specific error messages, function names, API calls, user IDs, order numbers, or any custom identifiers. Essential for tracking down specific issues, following a transaction through the system, or finding all instances of a particular problem pattern.", inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'Search pattern or keyword to find in logs', }, logLevel: { type: 'string', enum: ['error', 'warn', 'info', 'debug'], description: 'Restrict search to specific log level for more focused results', }, date: { type: 'string', description: 'Date in YYYYMMDD format (default: today)', }, limit: { type: 'number', description: 'Number of matching entries to return (default: 20)', default: 20, }, }, required: ['pattern'], }, }, { name: 'list_log_files', description: 'List all available log files with metadata including sizes and modification dates. Use this to understand what log data is available, check if logs are being generated properly, or when you need to investigate issues from specific time periods. Helpful for determining log retention and identifying the best date range for investigation.', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_log_file_contents', description: 'Get the complete contents of a specific log file. Use this when you need to read the full content of a specific log file identified by the list_log_files tool or job log files from get_latest_job_log_files. Essential for detailed analysis of specific log files, reading complete error traces, or when you need the full context of a log file rather than just recent entries.', inputSchema: { type: 'object', properties: { filename: { type: 'string', description: 'The complete filename or path of the log file to read. For standard logs, use just the filename (e.g., "error-blade-20240820-000000.log"). For job logs, use the full path as returned by get_latest_job_log_files (e.g., "jobs/JobName/Job-JobName-12345.log")', }, maxBytes: { type: 'number', description: 'Maximum number of bytes to read from the file (default: 1MB). Use this to limit the amount of data returned for very large files.', default: 1048576, }, tailOnly: { type: 'boolean', description: 'Whether to read only the tail (end) of the file instead of the full contents (default: false). Set to true for large files to get recent entries.', default: false, }, }, required: ['filename'], }, }, ]; export const JOB_LOG_TOOLS = [ { name: 'get_latest_job_log_files', description: 'Get the latest job log files from the SFCC jobs folder. Use this to discover recent job executions, identify available job logs, and understand which jobs have run recently. Job logs are stored in a deeper folder structure (/Logs/jobs/[job name ID]/Job-*.log) and contain all log levels (error, warn, info, debug) in single files. Essential for debugging custom job steps and monitoring job execution.', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Number of job log files to return (default: 10)', default: 10, }, }, }, }, { name: 'search_job_logs_by_name', description: 'Search for job log files by job name. Use this when you want to find logs for a specific job to debug custom job steps or understand job execution patterns. Job names are typically the system ID or custom-configured names for jobs.', inputSchema: { type: 'object', properties: { jobName: { type: 'string', description: 'The job name to search for (partial matches supported)', }, limit: { type: 'number', description: 'Number of job log files to return (default: 10)', default: 10, }, }, required: ['jobName'], }, }, { name: 'get_job_log_entries', description: 'Get job log entries for a specific log level or all levels from recent job executions. Unlike standard logs, job logs contain all log levels in one file, making this tool perfect for debugging custom job code. Use this to see what happened during job execution, track errors in job steps, or monitor job performance.', inputSchema: { type: 'object', properties: { level: { type: 'string', enum: ['error', 'warn', 'info', 'debug', 'all'], description: 'Log level to retrieve (default: all). Use "all" to see all log levels from job executions.', default: 'all', }, limit: { type: 'number', description: 'Number of job log entries to return (default: 10)', default: 10, }, jobName: { type: 'string', description: 'Optional job name to filter results to a specific job', }, }, }, }, { name: 'search_job_logs', description: 'Search for specific patterns, error messages, or keywords within job logs. Use this when debugging specific job issues, looking for custom logging messages in your job steps, tracking job variables, or finding specific execution patterns. Essential for troubleshooting custom job code.', inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'Search pattern or keyword to find in job logs', }, level: { type: 'string', enum: ['error', 'warn', 'info', 'debug', 'all'], description: 'Restrict search to specific log level (default: all)', default: 'all', }, limit: { type: 'number', description: 'Number of matching entries to return (default: 20)', default: 20, }, jobName: { type: 'string', description: 'Optional job name to restrict search to a specific job', }, }, required: ['pattern'], }, }, { name: 'get_job_execution_summary', description: 'Get a comprehensive execution summary for a specific job including timing, status, error counts, and step information. Use this to understand job performance, identify bottlenecks, check execution status, or get an overview of what happened during job execution. Perfect for monitoring job health and debugging job step issues.', inputSchema: { type: 'object', properties: { jobName: { type: 'string', description: 'The job name to get execution summary for', }, }, required: ['jobName'], }, }, ]; export const SYSTEM_OBJECT_TOOLS = [ { name: 'get_system_object_definitions', description: 'Get all system object definitions from SFCC with their main metadata, not including attributes. Use this to discover what system objects are available in the SFCC instance, understand the basic data model, or when you need to see all objects at once. Essential for understanding the complete SFCC data structure and identifying objects. You can also discover which objects are "Custom Objects" by looking at the _type field in the response.', inputSchema: { type: 'object', properties: { start: { type: 'number', description: 'Optional start index for retrieving items from a given index (default: 0)', default: 0, }, count: { type: 'number', description: 'Optional count for retrieving only a subset of items (default: 200)', default: 200, }, select: { type: 'string', description: "Property selector using OCAPI select syntax. Controls which fields are returned in the response. Examples: '(**)' for all properties, '(start, total)' for pagination info only, '(data.(object_type))' for only object_type in data array, '(data.(object_type, display_name))' for specific fields in data array, '(start, data.(**))' for pagination info plus all data properties. Use parentheses to group field selections and dot notation to traverse object hierarchies.", default: '(**)', }, }, }, }, { name: 'get_system_object_definition', description: 'Get basic metadata about a specific SFCC system object definition including counts and configuration flags. Returns information like attribute count, group count, display name, creation date, and object type flags (content_object, queryable, read_only). Use this when you need to understand the basic structure and configuration of system objects like Product, Customer, Order, Category, or Site. For detailed attribute information, use get_system_object_attribute_definitions instead. You can not fetch "Custom Objects" with this API.', inputSchema: { type: 'object', properties: { objectType: { type: 'string', description: "The system object type (e.g., 'Product', 'Customer', 'Order', 'Category', 'Site')", }, }, required: ['objectType'], }, }, { name: 'search_system_object_attribute_definitions', description: 'Search for specific attribute definitions within a system object type using complex queries. Use this when you need to find attributes by name, type, or other properties rather than retrieving all attributes. Supports text search on id/display_name/description, filtering by properties like mandatory/searchable/system, and sorting. Essential for finding custom attributes or attributes with specific characteristics. To get all attributes, use a match_all_query.', inputSchema: { type: 'object', properties: { objectType: { type: 'string', description: "The system object type to search within (e.g., 'Product', 'Customer', 'Order', 'Category', 'Site')", }, searchRequest: { type: 'object', description: 'The search request with query, sorting, and pagination options', properties: { query: { type: 'object', description: 'Query to filter attribute definitions', properties: { text_query: { type: 'object', description: 'Search for text in specific fields', properties: { fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in (e.g., ["id", "display_name", "description"])', }, search_phrase: { type: 'string', description: 'Text to search for', }, }, required: ['fields', 'search_phrase'], }, term_query: { type: 'object', description: 'Search for exact term matches', properties: { fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in', }, operator: { type: 'string', description: 'Query operator (e.g., "is", "one_of")', }, values: { type: 'array', items: { type: 'string' }, description: 'Values to match', }, }, required: ['fields', 'operator', 'values'], }, bool_query: { type: 'object', description: 'Combine multiple queries with boolean logic', properties: { must: { type: 'array', items: { type: 'object' }, description: 'Queries that must match (AND)', }, must_not: { type: 'array', items: { type: 'object' }, description: 'Queries that must not match', }, should: { type: 'array', items: { type: 'object' }, description: 'Queries that should match (OR)', }, }, }, match_all_query: { type: 'object', description: 'Match all documents query - matches all documents in the namespace and document type. Useful when you just want to filter results or have no constraints.', properties: {}, }, }, }, sorts: { type: 'array', description: 'Sort criteria', items: { type: 'object', properties: { field: { type: 'string', description: 'Field to sort by', }, sort_order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order (default: asc)', }, }, required: ['field'], }, }, start: { type: 'number', description: 'Start index for pagination (default: 0)', default: 0, }, count: { type: 'number', description: 'Number of results to return (default: 200)', default: 200, }, select: { type: 'string', description: "Property selector (e.g., '(**)' for all properties)", }, }, }, }, required: ['objectType', 'searchRequest'], }, }, { name: 'search_site_preferences', description: 'Search site preferences across sites in the specified preference group and instance. Use this to find specific site preferences by name, description, or value type. Essential for discovering custom site preferences, understanding preference configurations, or when working with site-specific settings. Use this tool when generating or debugging code that accesses site preferences (e.g., dw.system.Site.current.preferences.custom.yourPreference, Site.getCurrent().getCustomPreferenceValue()) to validate preference names, understand their data types, discover available preferences in a group, or troubleshoot preference-related issues. Supports complex queries with text search, filtering, and sorting.', inputSchema: { type: 'object', properties: { groupId: { type: 'string', description: 'The ID of the preference group to search within', }, instanceType: { type: 'string', enum: ['staging', 'development', 'sandbox', 'production'], description: 'The instance type to search preferences for. Since this MCP server is aimed at development and testing, the default is "sandbox".', default: 'sandbox', }, searchRequest: { type: 'object', description: 'The search request with query, sorting, and pagination options', properties: { query: { type: 'object', description: 'Query to filter site preferences', properties: { text_query: { type: 'object', description: 'Search for text in specific fields', properties: { fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in (e.g., ["id", "display_name", "description"])', }, search_phrase: { type: 'string', description: 'Text to search for', }, }, required: ['fields', 'search_phrase'], }, term_query: { type: 'object', description: 'Search for exact term matches', properties: { fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in', }, operator: { type: 'string', description: 'Query operator (e.g., "is", "one_of")', }, values: { type: 'array', items: { type: 'string' }, description: 'Values to match', }, }, required: ['fields', 'operator', 'values'], }, bool_query: { type: 'object', description: 'Combine multiple queries with boolean logic', properties: { must: { type: 'array', items: { type: 'object' }, description: 'Queries that must match (AND)', }, must_not: { type: 'array', items: { type: 'object' }, description: 'Queries that must not match', }, should: { type: 'array', items: { type: 'object' }, description: 'Queries that should match (OR)', }, }, }, match_all_query: { type: 'object', description: 'Match all documents query - matches all documents in the namespace and document type. Useful when you just want to filter results or have no constraints.', properties: {}, }, }, }, sorts: { type: 'array', description: 'Sort criteria', items: { type: 'object', properties: { field: { type: 'string', description: 'Field to sort by (id, display_name, description, value_type)', }, sort_order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order (default: asc)', }, }, required: ['field'], }, }, start: { type: 'number', description: 'Start index for pagination (default: 0)', default: 0, }, count: { type: 'number', description: 'Number of results to return (default: 200)', default: 200, }, select: { type: 'string', description: "Property selector (e.g., '(**)' for all properties)", }, }, }, options: { type: 'object', description: 'Additional options for the search', properties: { maskPasswords: { type: 'boolean', description: 'Whether to mask password type preference values (default: true)', default: true, }, expand: { type: 'string', description: 'Expand options (use "value" to retrieve value definitions)', }, }, }, }, required: ['groupId', 'instanceType', 'searchRequest'], }, }, { name: 'search_system_object_attribute_groups', description: 'Search attribute groups for a specific system object type. Use this to discover available attribute groups, which is essential for finding site preference groups (use "SitePreferences" as objectType) needed for the site preferences search API. Supports complex queries with text search, filtering, and sorting on group properties.', inputSchema: { type: 'object', properties: { objectType: { type: 'string', description: 'The system object type to search attribute groups for (e.g., "Product", "Customer", "SitePreferences")', }, searchRequest: { type: 'object', description: 'The search request with query, sorting, and pagination options', properties: { query: { type: 'object', description: 'Query to filter attribute groups', properties: { text_query: { type: 'object', description: 'Search for text in specific fields', properties: { fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in (e.g., ["id", "display_name", "description"])', }, search_phrase: { type: 'string', description: 'Text to search for', }, }, required: ['fields', 'search_phrase'], }, term_query: { type: 'object', description: 'Search for exact term matches', properties: { fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in', }, operator: { type: 'string', description: 'Query operator (e.g., "is", "one_of")', }, values: { type: 'array', items: { type: 'string' }, description: 'Values to match', }, }, required: ['fields', 'operator', 'values'], }, bool_query: { type: 'object', description: 'Combine multiple queries with boolean logic', properties: { must: { type: 'array', items: { type: 'object' }, description: 'Queries that must match (AND)', }, must_not: { type: 'array', items: { type: 'object' }, description: 'Queries that must not match', }, should: { type: 'array', items: { type: 'object' }, description: 'Queries that should match (OR)', }, }, }, match_all_query: { type: 'object', description: 'Match all documents query - matches all documents in the namespace and document type. Useful when you just want to filter results or have no constraints.', properties: {}, }, }, }, sorts: { type: 'array', description: 'Sort criteria', items: { type: 'object', properties: { field: { type: 'string', description: 'Field to sort by (id, display_name, description, position, internal)', }, sort_order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order (default: asc)', }, }, required: ['field'], }, }, start: { type: 'number', description: 'Start index for pagination (default: 0)', default: 0, }, count: { type: 'number', description: 'Number of results to return (default: 200)', default: 200, }, select: { type: 'string', description: "Property selector (e.g., '(**)' for all properties)", }, }, }, }, required: ['objectType', 'searchRequest'], }, }, { name: 'search_custom_object_attribute_definitions', description: 'Search for specific attribute definitions within a custom object type using complex queries. Use this when you need to find attributes by name, type, or other properties for custom objects rather than system objects. Supports text search on id/display_name/description, filtering by properties like mandatory/searchable/system, and sorting. Essential for finding custom attributes or attributes with specific characteristics in custom object definitions. Custom objects are user-defined data structures that extend SFCC functionality.', inputSchema: { type: 'object', properties: { objectType: { type: 'string', description: "The custom object type to search within (e.g., 'Global_String', 'MyCustomObject')", }, searchRequest: { type: 'object', description: 'The search request with query, sorting, and pagination options', properties: { query: { type: 'object', description: 'Query to filter attribute definitions', properties: { text_query: { type: 'object', description: 'Search for text in specific fields', properties: { fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in (e.g., ["id", "display_name", "description"])', }, search_phrase: { type: 'string', description: 'Text to search for', }, }, required: ['fields', 'search_phrase'], }, term_query: { type: 'object', description: 'Search for exact term matches', properties: { fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in', }, operator: { type: 'string', description: 'Query operator (e.g., "is", "one_of")', }, values: { type: 'array', items: { type: 'string' }, description: 'Values to match', }, }, required: ['fields', 'operator', 'values'], }, bool_query: { type: 'object', description: 'Combine multiple queries with boolean logic', properties: { must: { type: 'array', items: { type: 'object' }, description: 'Queries that must match (AND)', }, must_not: { type: 'array', items: { type: 'object' }, description: 'Queries that must not match', }, should: { type: 'array', items: { type: 'object' }, description: 'Queries that should match (OR)', }, }, }, match_all_query: { type: 'object', description: 'Match all documents query - matches all documents in the namespace and document type. Useful when you just want to filter results or have no constraints.', properties: {}, }, }, }, sorts: { type: 'array', description: 'Sort criteria', items: { type: 'object', properties: { field: { type: 'string', description: 'Field to sort by', }, sort_order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order (default: asc)', }, }, required: ['field'], }, }, start: { type: 'number', description: 'Start index for pagination (default: 0)', default: 0, }, count: { type: 'number', description: 'Number of results to return (default: 200)', default: 200, }, select: { type: 'string', description: "Property selector (e.g., '(**)' for all properties)", }, }, }, }, required: ['objectType', 'searchRequest'], }, }, ]; export const CARTRIDGE_GENERATION_TOOLS = [ { name: 'generate_cartridge_structure', description: 'Generate a complete cartridge directory structure with all necessary files and configurations. Use this when creating new cartridges to ensure proper organization and include all required components. This tool creates all necessary files directly in the specified target directory, ensuring the cartridge is created exactly where needed in your project structure.', inputSchema: { type: 'object', properties: { cartridgeName: { type: 'string', description: 'Name of the cartridge (e.g., "plugin_example")', }, targetPath: { type: 'string', description: 'Target directory path where the cartridge files should be placed. If not specified, files will be placed in the current working directory. Use absolute paths for best results (e.g., "/Users/username/projects/my-sfcc-project/").', }, fullProjectSetup: { type: 'boolean', description: 'Whether to create a complete project setup (package.json, webpack, etc.) or just add a cartridge to existing project structure. Use true for new projects, false to add cartridge to existing projects. Always send the root of the project directory as the targetPath.', default: true, }, }, required: ['cartridgeName'], }, }, ]; export const CODE_VERSION_TOOLS = [ { name: 'get_code_versions', description: 'Get all code versions from an SFCC instance. Use this to view available code versions for deployment management and code-switch fixes. Essential for troubleshooting SCAPI endpoint registration issues, job deployment problems, and when you need to switch between different code versions to resolve deployment conflicts.', inputSchema: { type: 'object', properties: {}, }, }, { name: 'activate_code_version', description: 'Activate a specific code version on the SFCC instance. Use this to perform code-switch fixes for SCAPI endpoint registration issues, job deployment problems, and deployment conflicts. Only inactive code versions can be activated, and activating a version will automatically deactivate the currently active version.', inputSchema: { type: 'object', properties: { codeVersionId: { type: 'string', description: 'The ID of the code version to activate. Must be an existing inactive code version.', }, }, required: ['codeVersionId'], }, }, ]; ```