This is page 36 of 61. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .DS_Store ├── .github │ ├── dependabot.yml │ ├── instructions │ │ ├── mcp-node-tests.instructions.md │ │ └── mcp-yml-tests.instructions.md │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── documentation.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── PULL_REQUEST_TEMPLATE │ │ ├── bug_fix.md │ │ ├── documentation.md │ │ └── new_tool.md │ ├── pull_request_template.md │ └── workflows │ ├── ci.yml │ ├── deploy-pages.yml │ ├── publish.yml │ └── update-docs.yml ├── .gitignore ├── .husky │ └── pre-commit ├── aegis.config.docs-only.json ├── aegis.config.json ├── aegis.config.with-dw.json ├── AGENTS.md ├── ai-instructions │ ├── claude-desktop │ │ └── claude_custom_instructions.md │ ├── cursor │ │ └── .cursor │ │ └── rules │ │ ├── debugging-workflows.mdc │ │ ├── hooks-development.mdc │ │ ├── isml-templates.mdc │ │ ├── job-framework.mdc │ │ ├── performance-optimization.mdc │ │ ├── scapi-endpoints.mdc │ │ ├── security-patterns.mdc │ │ ├── sfcc-development.mdc │ │ ├── sfra-controllers.mdc │ │ ├── sfra-models.mdc │ │ ├── system-objects.mdc │ │ └── testing-patterns.mdc │ └── github-copilot │ └── copilot-instructions.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── docs │ ├── best-practices │ │ ├── cartridge_creation.md │ │ ├── isml_templates.md │ │ ├── job_framework.md │ │ ├── localserviceregistry.md │ │ ├── ocapi_hooks.md │ │ ├── performance.md │ │ ├── scapi_custom_endpoint.md │ │ ├── scapi_hooks.md │ │ ├── security.md │ │ ├── sfra_client_side_js.md │ │ ├── sfra_controllers.md │ │ ├── sfra_models.md │ │ └── sfra_scss.md │ ├── dw_campaign │ │ ├── ABTest.md │ │ ├── ABTestMgr.md │ │ ├── ABTestSegment.md │ │ ├── AmountDiscount.md │ │ ├── ApproachingDiscount.md │ │ ├── BonusChoiceDiscount.md │ │ ├── BonusDiscount.md │ │ ├── Campaign.md │ │ ├── CampaignMgr.md │ │ ├── CampaignStatusCodes.md │ │ ├── Coupon.md │ │ ├── CouponMgr.md │ │ ├── CouponRedemption.md │ │ ├── CouponStatusCodes.md │ │ ├── Discount.md │ │ ├── DiscountPlan.md │ │ ├── FixedPriceDiscount.md │ │ ├── FixedPriceShippingDiscount.md │ │ ├── FreeDiscount.md │ │ ├── FreeShippingDiscount.md │ │ ├── PercentageDiscount.md │ │ ├── PercentageOptionDiscount.md │ │ ├── PriceBookPriceDiscount.md │ │ ├── Promotion.md │ │ ├── PromotionMgr.md │ │ ├── PromotionPlan.md │ │ ├── SlotContent.md │ │ ├── SourceCodeGroup.md │ │ ├── SourceCodeInfo.md │ │ ├── SourceCodeStatusCodes.md │ │ └── TotalFixedPriceDiscount.md │ ├── dw_catalog │ │ ├── Catalog.md │ │ ├── CatalogMgr.md │ │ ├── Category.md │ │ ├── CategoryAssignment.md │ │ ├── CategoryLink.md │ │ ├── PriceBook.md │ │ ├── PriceBookMgr.md │ │ ├── Product.md │ │ ├── ProductActiveData.md │ │ ├── ProductAttributeModel.md │ │ ├── ProductAvailabilityLevels.md │ │ ├── ProductAvailabilityModel.md │ │ ├── ProductInventoryList.md │ │ ├── ProductInventoryMgr.md │ │ ├── ProductInventoryRecord.md │ │ ├── ProductLink.md │ │ ├── ProductMgr.md │ │ ├── ProductOption.md │ │ ├── ProductOptionModel.md │ │ ├── ProductOptionValue.md │ │ ├── ProductPriceInfo.md │ │ ├── ProductPriceModel.md │ │ ├── ProductPriceTable.md │ │ ├── ProductSearchHit.md │ │ ├── ProductSearchModel.md │ │ ├── ProductSearchRefinementDefinition.md │ │ ├── ProductSearchRefinements.md │ │ ├── ProductSearchRefinementValue.md │ │ ├── ProductVariationAttribute.md │ │ ├── ProductVariationAttributeValue.md │ │ ├── ProductVariationModel.md │ │ ├── Recommendation.md │ │ ├── SearchModel.md │ │ ├── SearchRefinementDefinition.md │ │ ├── SearchRefinements.md │ │ ├── SearchRefinementValue.md │ │ ├── SortingOption.md │ │ ├── SortingRule.md │ │ ├── Store.md │ │ ├── StoreGroup.md │ │ ├── StoreInventoryFilter.md │ │ ├── StoreInventoryFilterValue.md │ │ ├── StoreMgr.md │ │ ├── Variant.md │ │ └── VariationGroup.md │ ├── dw_content │ │ ├── Content.md │ │ ├── ContentMgr.md │ │ ├── ContentSearchModel.md │ │ ├── ContentSearchRefinementDefinition.md │ │ ├── ContentSearchRefinements.md │ │ ├── ContentSearchRefinementValue.md │ │ ├── Folder.md │ │ ├── Library.md │ │ ├── MarkupText.md │ │ └── MediaFile.md │ ├── dw_crypto │ │ ├── CertificateRef.md │ │ ├── CertificateUtils.md │ │ ├── Cipher.md │ │ ├── Encoding.md │ │ ├── JWE.md │ │ ├── JWEHeader.md │ │ ├── JWS.md │ │ ├── JWSHeader.md │ │ ├── KeyRef.md │ │ ├── Mac.md │ │ ├── MessageDigest.md │ │ ├── SecureRandom.md │ │ ├── Signature.md │ │ ├── WeakCipher.md │ │ ├── WeakMac.md │ │ ├── WeakMessageDigest.md │ │ ├── WeakSignature.md │ │ └── X509Certificate.md │ ├── dw_customer │ │ ├── AddressBook.md │ │ ├── AgentUserMgr.md │ │ ├── AgentUserStatusCodes.md │ │ ├── AuthenticationStatus.md │ │ ├── Credentials.md │ │ ├── Customer.md │ │ ├── CustomerActiveData.md │ │ ├── CustomerAddress.md │ │ ├── CustomerCDPData.md │ │ ├── CustomerContextMgr.md │ │ ├── CustomerGroup.md │ │ ├── CustomerList.md │ │ ├── CustomerMgr.md │ │ ├── CustomerPasswordConstraints.md │ │ ├── CustomerPaymentInstrument.md │ │ ├── CustomerStatusCodes.md │ │ ├── EncryptedObject.md │ │ ├── ExternalProfile.md │ │ ├── OrderHistory.md │ │ ├── ProductList.md │ │ ├── ProductListItem.md │ │ ├── ProductListItemPurchase.md │ │ ├── ProductListMgr.md │ │ ├── ProductListRegistrant.md │ │ ├── Profile.md │ │ └── Wallet.md │ ├── dw_extensions.applepay │ │ ├── ApplePayHookResult.md │ │ └── ApplePayHooks.md │ ├── dw_extensions.facebook │ │ ├── FacebookFeedHooks.md │ │ └── FacebookProduct.md │ ├── dw_extensions.paymentrequest │ │ ├── PaymentRequestHookResult.md │ │ └── PaymentRequestHooks.md │ ├── dw_extensions.payments │ │ ├── SalesforceBancontactPaymentDetails.md │ │ ├── SalesforceCardPaymentDetails.md │ │ ├── SalesforceEpsPaymentDetails.md │ │ ├── SalesforceIdealPaymentDetails.md │ │ ├── SalesforceKlarnaPaymentDetails.md │ │ ├── SalesforcePaymentDetails.md │ │ ├── SalesforcePaymentIntent.md │ │ ├── SalesforcePaymentMethod.md │ │ ├── SalesforcePaymentRequest.md │ │ ├── SalesforcePaymentsHooks.md │ │ ├── SalesforcePaymentsMgr.md │ │ ├── SalesforcePaymentsSiteConfiguration.md │ │ ├── SalesforcePayPalOrder.md │ │ ├── SalesforcePayPalOrderAddress.md │ │ ├── SalesforcePayPalOrderPayer.md │ │ ├── SalesforcePayPalPaymentDetails.md │ │ ├── SalesforceSepaDebitPaymentDetails.md │ │ └── SalesforceVenmoPaymentDetails.md │ ├── dw_extensions.pinterest │ │ ├── PinterestAvailability.md │ │ ├── PinterestFeedHooks.md │ │ ├── PinterestOrder.md │ │ ├── PinterestOrderHooks.md │ │ └── PinterestProduct.md │ ├── dw_io │ │ ├── CSVStreamReader.md │ │ ├── CSVStreamWriter.md │ │ ├── File.md │ │ ├── FileReader.md │ │ ├── FileWriter.md │ │ ├── InputStream.md │ │ ├── OutputStream.md │ │ ├── PrintWriter.md │ │ ├── RandomAccessFileReader.md │ │ ├── Reader.md │ │ ├── StringWriter.md │ │ ├── Writer.md │ │ ├── XMLIndentingStreamWriter.md │ │ ├── XMLStreamConstants.md │ │ ├── XMLStreamReader.md │ │ └── XMLStreamWriter.md │ ├── dw_job │ │ ├── JobExecution.md │ │ └── JobStepExecution.md │ ├── dw_net │ │ ├── FTPClient.md │ │ ├── FTPFileInfo.md │ │ ├── HTTPClient.md │ │ ├── HTTPRequestPart.md │ │ ├── Mail.md │ │ ├── SFTPClient.md │ │ ├── SFTPFileInfo.md │ │ ├── WebDAVClient.md │ │ └── WebDAVFileInfo.md │ ├── dw_object │ │ ├── ActiveData.md │ │ ├── CustomAttributes.md │ │ ├── CustomObject.md │ │ ├── CustomObjectMgr.md │ │ ├── Extensible.md │ │ ├── ExtensibleObject.md │ │ ├── Note.md │ │ ├── ObjectAttributeDefinition.md │ │ ├── ObjectAttributeGroup.md │ │ ├── ObjectAttributeValueDefinition.md │ │ ├── ObjectTypeDefinition.md │ │ ├── PersistentObject.md │ │ ├── SimpleExtensible.md │ │ └── SystemObjectMgr.md │ ├── dw_order │ │ ├── AbstractItem.md │ │ ├── AbstractItemCtnr.md │ │ ├── Appeasement.md │ │ ├── AppeasementItem.md │ │ ├── Basket.md │ │ ├── BasketMgr.md │ │ ├── BonusDiscountLineItem.md │ │ ├── CouponLineItem.md │ │ ├── CreateAgentBasketLimitExceededException.md │ │ ├── CreateBasketFromOrderException.md │ │ ├── CreateCouponLineItemException.md │ │ ├── CreateOrderException.md │ │ ├── CreateTemporaryBasketLimitExceededException.md │ │ ├── GiftCertificate.md │ │ ├── GiftCertificateLineItem.md │ │ ├── GiftCertificateMgr.md │ │ ├── GiftCertificateStatusCodes.md │ │ ├── Invoice.md │ │ ├── InvoiceItem.md │ │ ├── LineItem.md │ │ ├── LineItemCtnr.md │ │ ├── Order.md │ │ ├── OrderAddress.md │ │ ├── OrderItem.md │ │ ├── OrderMgr.md │ │ ├── OrderPaymentInstrument.md │ │ ├── OrderProcessStatusCodes.md │ │ ├── PaymentCard.md │ │ ├── PaymentInstrument.md │ │ ├── PaymentMethod.md │ │ ├── PaymentMgr.md │ │ ├── PaymentProcessor.md │ │ ├── PaymentStatusCodes.md │ │ ├── PaymentTransaction.md │ │ ├── PriceAdjustment.md │ │ ├── PriceAdjustmentLimitTypes.md │ │ ├── ProductLineItem.md │ │ ├── ProductShippingCost.md │ │ ├── ProductShippingLineItem.md │ │ ├── ProductShippingModel.md │ │ ├── Return.md │ │ ├── ReturnCase.md │ │ ├── ReturnCaseItem.md │ │ ├── ReturnItem.md │ │ ├── Shipment.md │ │ ├── ShipmentShippingCost.md │ │ ├── ShipmentShippingModel.md │ │ ├── ShippingLineItem.md │ │ ├── ShippingLocation.md │ │ ├── ShippingMethod.md │ │ ├── ShippingMgr.md │ │ ├── ShippingOrder.md │ │ ├── ShippingOrderItem.md │ │ ├── SumItem.md │ │ ├── TaxGroup.md │ │ ├── TaxItem.md │ │ ├── TaxMgr.md │ │ ├── TrackingInfo.md │ │ └── TrackingRef.md │ ├── dw_order.hooks │ │ ├── CalculateHooks.md │ │ ├── OrderHooks.md │ │ ├── PaymentHooks.md │ │ ├── ReturnHooks.md │ │ └── ShippingOrderHooks.md │ ├── dw_rpc │ │ ├── SOAPUtil.md │ │ ├── Stub.md │ │ └── WebReference.md │ ├── dw_suggest │ │ ├── BrandSuggestions.md │ │ ├── CategorySuggestions.md │ │ ├── ContentSuggestions.md │ │ ├── CustomSuggestions.md │ │ ├── ProductSuggestions.md │ │ ├── SearchPhraseSuggestions.md │ │ ├── SuggestedCategory.md │ │ ├── SuggestedContent.md │ │ ├── SuggestedPhrase.md │ │ ├── SuggestedProduct.md │ │ ├── SuggestedTerm.md │ │ ├── SuggestedTerms.md │ │ ├── Suggestions.md │ │ └── SuggestModel.md │ ├── dw_svc │ │ ├── FTPService.md │ │ ├── FTPServiceDefinition.md │ │ ├── HTTPFormService.md │ │ ├── HTTPFormServiceDefinition.md │ │ ├── HTTPService.md │ │ ├── HTTPServiceDefinition.md │ │ ├── LocalServiceRegistry.md │ │ ├── Result.md │ │ ├── Service.md │ │ ├── ServiceCallback.md │ │ ├── ServiceConfig.md │ │ ├── ServiceCredential.md │ │ ├── ServiceDefinition.md │ │ ├── ServiceProfile.md │ │ ├── ServiceRegistry.md │ │ ├── SOAPService.md │ │ └── SOAPServiceDefinition.md │ ├── dw_system │ │ ├── AgentUserStatusCodes.md │ │ ├── Cache.md │ │ ├── CacheMgr.md │ │ ├── HookMgr.md │ │ ├── InternalObject.md │ │ ├── JobProcessMonitor.md │ │ ├── Log.md │ │ ├── Logger.md │ │ ├── LogNDC.md │ │ ├── OrganizationPreferences.md │ │ ├── Pipeline.md │ │ ├── PipelineDictionary.md │ │ ├── RemoteInclude.md │ │ ├── Request.md │ │ ├── RequestHooks.md │ │ ├── Response.md │ │ ├── RESTErrorResponse.md │ │ ├── RESTResponseMgr.md │ │ ├── RESTSuccessResponse.md │ │ ├── SearchStatus.md │ │ ├── Session.md │ │ ├── Site.md │ │ ├── SitePreferences.md │ │ ├── Status.md │ │ ├── StatusItem.md │ │ ├── System.md │ │ └── Transaction.md │ ├── dw_util │ │ ├── ArrayList.md │ │ ├── Assert.md │ │ ├── BigInteger.md │ │ ├── Bytes.md │ │ ├── Calendar.md │ │ ├── Collection.md │ │ ├── Currency.md │ │ ├── DateUtils.md │ │ ├── Decimal.md │ │ ├── FilteringCollection.md │ │ ├── Geolocation.md │ │ ├── HashMap.md │ │ ├── HashSet.md │ │ ├── Iterator.md │ │ ├── LinkedHashMap.md │ │ ├── LinkedHashSet.md │ │ ├── List.md │ │ ├── Locale.md │ │ ├── Map.md │ │ ├── MapEntry.md │ │ ├── MappingKey.md │ │ ├── MappingMgr.md │ │ ├── PropertyComparator.md │ │ ├── SecureEncoder.md │ │ ├── SecureFilter.md │ │ ├── SeekableIterator.md │ │ ├── Set.md │ │ ├── SortedMap.md │ │ ├── SortedSet.md │ │ ├── StringUtils.md │ │ ├── Template.md │ │ └── UUIDUtils.md │ ├── dw_value │ │ ├── EnumValue.md │ │ ├── MimeEncodedText.md │ │ ├── Money.md │ │ └── Quantity.md │ ├── dw_web │ │ ├── ClickStream.md │ │ ├── ClickStreamEntry.md │ │ ├── Cookie.md │ │ ├── Cookies.md │ │ ├── CSRFProtection.md │ │ ├── Form.md │ │ ├── FormAction.md │ │ ├── FormElement.md │ │ ├── FormElementValidationResult.md │ │ ├── FormField.md │ │ ├── FormFieldOption.md │ │ ├── FormFieldOptions.md │ │ ├── FormGroup.md │ │ ├── FormList.md │ │ ├── FormListItem.md │ │ ├── Forms.md │ │ ├── HttpParameter.md │ │ ├── HttpParameterMap.md │ │ ├── LoopIterator.md │ │ ├── PageMetaData.md │ │ ├── PageMetaTag.md │ │ ├── PagingModel.md │ │ ├── Resource.md │ │ ├── URL.md │ │ ├── URLAction.md │ │ ├── URLParameter.md │ │ ├── URLRedirect.md │ │ ├── URLRedirectMgr.md │ │ └── URLUtils.md │ ├── sfra │ │ ├── account.md │ │ ├── address.md │ │ ├── billing.md │ │ ├── cart.md │ │ ├── categories.md │ │ ├── content.md │ │ ├── locale.md │ │ ├── order.md │ │ ├── payment.md │ │ ├── price-default.md │ │ ├── price-range.md │ │ ├── price-tiered.md │ │ ├── product-bundle.md │ │ ├── product-full.md │ │ ├── product-line-items.md │ │ ├── product-search.md │ │ ├── product-tile.md │ │ ├── querystring.md │ │ ├── render.md │ │ ├── request.md │ │ ├── response.md │ │ ├── server.md │ │ ├── shipping.md │ │ ├── store.md │ │ ├── stores.md │ │ └── totals.md │ └── TopLevel │ ├── APIException.md │ ├── arguments.md │ ├── Array.md │ ├── ArrayBuffer.md │ ├── BigInt.md │ ├── Boolean.md │ ├── ConversionError.md │ ├── DataView.md │ ├── Date.md │ ├── Error.md │ ├── ES6Iterator.md │ ├── EvalError.md │ ├── Fault.md │ ├── Float32Array.md │ ├── Float64Array.md │ ├── Function.md │ ├── Generator.md │ ├── global.md │ ├── Int16Array.md │ ├── Int32Array.md │ ├── Int8Array.md │ ├── InternalError.md │ ├── IOError.md │ ├── Iterable.md │ ├── Iterator.md │ ├── JSON.md │ ├── Map.md │ ├── Math.md │ ├── Module.md │ ├── Namespace.md │ ├── Number.md │ ├── Object.md │ ├── QName.md │ ├── RangeError.md │ ├── ReferenceError.md │ ├── RegExp.md │ ├── Set.md │ ├── StopIteration.md │ ├── String.md │ ├── Symbol.md │ ├── SyntaxError.md │ ├── SystemError.md │ ├── TypeError.md │ ├── Uint16Array.md │ ├── Uint32Array.md │ ├── Uint8Array.md │ ├── Uint8ClampedArray.md │ ├── URIError.md │ ├── WeakMap.md │ ├── WeakSet.md │ ├── XML.md │ ├── XMLList.md │ └── XMLStreamError.md ├── docs-site │ ├── .gitignore │ ├── App.tsx │ ├── components │ │ ├── Badge.tsx │ │ ├── BreadcrumbSchema.tsx │ │ ├── CodeBlock.tsx │ │ ├── Collapsible.tsx │ │ ├── ConfigBuilder.tsx │ │ ├── ConfigHero.tsx │ │ ├── ConfigModeTabs.tsx │ │ ├── icons.tsx │ │ ├── Layout.tsx │ │ ├── LightCodeContainer.tsx │ │ ├── NewcomerCTA.tsx │ │ ├── NextStepsStrip.tsx │ │ ├── OnThisPage.tsx │ │ ├── Search.tsx │ │ ├── SEO.tsx │ │ ├── Sidebar.tsx │ │ ├── StructuredData.tsx │ │ ├── ToolCard.tsx │ │ ├── ToolFilters.tsx │ │ ├── Typography.tsx │ │ └── VersionBadge.tsx │ ├── constants.tsx │ ├── index.html │ ├── main.tsx │ ├── metadata.json │ ├── package-lock.json │ ├── package.json │ ├── pages │ │ ├── AIInterfacesPage.tsx │ │ ├── ConfigurationPage.tsx │ │ ├── DevelopmentPage.tsx │ │ ├── ExamplesPage.tsx │ │ ├── FeaturesPage.tsx │ │ ├── HomePage.tsx │ │ ├── SecurityPage.tsx │ │ ├── ToolsPage.tsx │ │ └── TroubleshootingPage.tsx │ ├── postcss.config.js │ ├── public │ │ ├── .well-known │ │ │ └── security.txt │ │ ├── 404.html │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── explain-product-pricing-methods-no-mcp.png │ │ ├── explain-product-pricing-methods.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── llms.txt │ │ ├── robots.txt │ │ ├── site.webmanifest │ │ └── sitemap.xml │ ├── README.md │ ├── scripts │ │ ├── generate-search-index.js │ │ ├── generate-sitemap.js │ │ └── search-dev.js │ ├── src │ │ └── styles │ │ ├── input.css │ │ └── prism-theme.css │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── types.ts │ ├── utils │ │ ├── search.ts │ │ └── toolsData.ts │ └── vite.config.ts ├── eslint.config.js ├── jest.config.js ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── scripts │ └── convert-docs.js ├── SECURITY.md ├── server.json ├── src │ ├── clients │ │ ├── base │ │ │ ├── http-client.ts │ │ │ ├── oauth-token.ts │ │ │ └── ocapi-auth-client.ts │ │ ├── best-practices-client.ts │ │ ├── cartridge-generation-client.ts │ │ ├── docs │ │ │ ├── class-content-parser.ts │ │ │ ├── class-name-resolver.ts │ │ │ ├── documentation-scanner.ts │ │ │ ├── index.ts │ │ │ └── referenced-types-extractor.ts │ │ ├── docs-client.ts │ │ ├── log-client.ts │ │ ├── logs │ │ │ ├── index.ts │ │ │ ├── log-analyzer.ts │ │ │ ├── log-client.ts │ │ │ ├── log-constants.ts │ │ │ ├── log-file-discovery.ts │ │ │ ├── log-file-reader.ts │ │ │ ├── log-formatter.ts │ │ │ ├── log-processor.ts │ │ │ ├── log-types.ts │ │ │ └── webdav-client-manager.ts │ │ ├── ocapi │ │ │ ├── code-versions-client.ts │ │ │ ├── site-preferences-client.ts │ │ │ └── system-objects-client.ts │ │ ├── ocapi-client.ts │ │ └── sfra-client.ts │ ├── config │ │ ├── configuration-factory.ts │ │ └── dw-json-loader.ts │ ├── core │ │ ├── handlers │ │ │ ├── abstract-log-tool-handler.ts │ │ │ ├── base-handler.ts │ │ │ ├── best-practices-handler.ts │ │ │ ├── cartridge-handler.ts │ │ │ ├── client-factory.ts │ │ │ ├── code-version-handler.ts │ │ │ ├── docs-handler.ts │ │ │ ├── job-log-handler.ts │ │ │ ├── job-log-tool-config.ts │ │ │ ├── log-handler.ts │ │ │ ├── log-tool-config.ts │ │ │ ├── sfra-handler.ts │ │ │ ├── system-object-handler.ts │ │ │ └── validation-helpers.ts │ │ ├── server.ts │ │ └── tool-definitions.ts │ ├── index.ts │ ├── main.ts │ ├── services │ │ ├── file-system-service.ts │ │ ├── index.ts │ │ └── path-service.ts │ ├── tool-configs │ │ ├── best-practices-tool-config.ts │ │ ├── cartridge-tool-config.ts │ │ ├── code-version-tool-config.ts │ │ ├── docs-tool-config.ts │ │ ├── job-log-tool-config.ts │ │ ├── log-tool-config.ts │ │ ├── sfra-tool-config.ts │ │ └── system-object-tool-config.ts │ ├── types │ │ └── types.ts │ └── utils │ ├── cache.ts │ ├── job-log-tool-config.ts │ ├── job-log-utils.ts │ ├── log-cache.ts │ ├── log-tool-config.ts │ ├── log-tool-constants.ts │ ├── log-tool-utils.ts │ ├── logger.ts │ ├── ocapi-url-builder.ts │ ├── path-resolver.ts │ ├── query-builder.ts │ ├── utils.ts │ └── validator.ts ├── tests │ ├── __mocks__ │ │ ├── docs-client.ts │ │ ├── src │ │ │ └── clients │ │ │ └── base │ │ │ └── http-client.js │ │ └── webdav.js │ ├── base-handler.test.ts │ ├── base-http-client.test.ts │ ├── best-practices-handler.test.ts │ ├── cache.test.ts │ ├── cartridge-handler.test.ts │ ├── class-content-parser.test.ts │ ├── class-name-resolver.test.ts │ ├── client-factory.test.ts │ ├── code-version-handler.test.ts │ ├── code-versions-client.test.ts │ ├── config.test.ts │ ├── configuration-factory.test.ts │ ├── docs-handler.test.ts │ ├── documentation-scanner.test.ts │ ├── file-system-service.test.ts │ ├── job-log-handler.test.ts │ ├── job-log-utils.test.ts │ ├── log-client.test.ts │ ├── log-handler.test.ts │ ├── log-processor.test.ts │ ├── logger.test.ts │ ├── mcp │ │ ├── AGENTS.md │ │ ├── node │ │ │ ├── activate-code-version-advanced.full-mode.programmatic.test.js │ │ │ ├── code-versions.full-mode.programmatic.test.js │ │ │ ├── generate-cartridge-structure.docs-only.programmatic.test.js │ │ │ ├── get-available-best-practice-guides.docs-only.programmatic.test.js │ │ │ ├── get-available-sfra-documents.programmatic.test.js │ │ │ ├── get-best-practice-guide.docs-only.programmatic.test.js │ │ │ ├── get-hook-reference.docs-only.programmatic.test.js │ │ │ ├── get-job-execution-summary.full-mode.programmatic.test.js │ │ │ ├── get-job-log-entries.full-mode.programmatic.test.js │ │ │ ├── get-latest-debug.full-mode.programmatic.test.js │ │ │ ├── get-latest-error.full-mode.programmatic.test.js │ │ │ ├── get-latest-info.full-mode.programmatic.test.js │ │ │ ├── get-latest-job-log-files.full-mode.programmatic.test.js │ │ │ ├── get-latest-warn.full-mode.programmatic.test.js │ │ │ ├── get-log-file-contents.full-mode.programmatic.test.js │ │ │ ├── get-sfcc-class-documentation.docs-only.programmatic.test.js │ │ │ ├── get-sfcc-class-info.docs-only.programmatic.test.js │ │ │ ├── get-sfra-categories.docs-only.programmatic.test.js │ │ │ ├── get-sfra-document.programmatic.test.js │ │ │ ├── get-sfra-documents-by-category.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definition.full-mode.programmatic.test.js │ │ │ ├── get-system-object-definitions.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definitions.full-mode.programmatic.test.js │ │ │ ├── list-log-files.full-mode.programmatic.test.js │ │ │ ├── list-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-best-practices.docs-only.programmatic.test.js │ │ │ ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-job-logs-by-name.full-mode.programmatic.test.js │ │ │ ├── search-job-logs.full-mode.programmatic.test.js │ │ │ ├── search-logs.full-mode.programmatic.test.js │ │ │ ├── search-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-sfcc-methods.docs-only.programmatic.test.js │ │ │ ├── search-sfra-documentation.docs-only.programmatic.test.js │ │ │ ├── search-site-preferences.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-groups.full-mode.programmatic.test.js │ │ │ ├── summarize-logs.full-mode.programmatic.test.js │ │ │ ├── tools.docs-only.programmatic.test.js │ │ │ └── tools.full-mode.programmatic.test.js │ │ ├── README.md │ │ ├── test-fixtures │ │ │ └── dw.json │ │ └── yaml │ │ ├── activate-code-version.docs-only.test.mcp.yml │ │ ├── activate-code-version.full-mode.test.mcp.yml │ │ ├── get_latest_error.test.mcp.yml │ │ ├── get-available-best-practice-guides.docs-only.test.mcp.yml │ │ ├── get-available-best-practice-guides.full-mode.test.mcp.yml │ │ ├── get-available-sfra-documents.docs-only.test.mcp.yml │ │ ├── get-available-sfra-documents.full-mode.test.mcp.yml │ │ ├── get-best-practice-guide.docs-only.test.mcp.yml │ │ ├── get-best-practice-guide.full-mode.test.mcp.yml │ │ ├── get-code-versions.docs-only.test.mcp.yml │ │ ├── get-code-versions.full-mode.test.mcp.yml │ │ ├── get-hook-reference.docs-only.test.mcp.yml │ │ ├── get-hook-reference.full-mode.test.mcp.yml │ │ ├── get-job-execution-summary.full-mode.test.mcp.yml │ │ ├── get-job-log-entries.full-mode.test.mcp.yml │ │ ├── get-latest-debug.full-mode.test.mcp.yml │ │ ├── get-latest-error.full-mode.test.mcp.yml │ │ ├── get-latest-info.full-mode.test.mcp.yml │ │ ├── get-latest-job-log-files.full-mode.test.mcp.yml │ │ ├── get-latest-warn.full-mode.test.mcp.yml │ │ ├── get-log-file-contents.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-documentation.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-documentation.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-info.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-info.full-mode.test.mcp.yml │ │ ├── get-sfra-categories.docs-only.test.mcp.yml │ │ ├── get-sfra-categories.full-mode.test.mcp.yml │ │ ├── get-sfra-document.docs-only.test.mcp.yml │ │ ├── get-sfra-document.full-mode.test.mcp.yml │ │ ├── get-sfra-documents-by-category.docs-only.test.mcp.yml │ │ ├── get-sfra-documents-by-category.full-mode.test.mcp.yml │ │ ├── get-system-object-definition.docs-only.test.mcp.yml │ │ ├── get-system-object-definition.full-mode.test.mcp.yml │ │ ├── get-system-object-definitions.docs-only.test.mcp.yml │ │ ├── get-system-object-definitions.full-mode.test.mcp.yml │ │ ├── list-log-files.full-mode.test.mcp.yml │ │ ├── list-sfcc-classes.docs-only.test.mcp.yml │ │ ├── list-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-best-practices.docs-only.test.mcp.yml │ │ ├── search-best-practices.full-mode.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.test.mcp.yml │ │ ├── search-job-logs-by-name.full-mode.test.mcp.yml │ │ ├── search-job-logs.full-mode.test.mcp.yml │ │ ├── search-logs.full-mode.test.mcp.yml │ │ ├── search-sfcc-classes.docs-only.test.mcp.yml │ │ ├── search-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-sfcc-methods.docs-only.test.mcp.yml │ │ ├── search-sfcc-methods.full-mode.test.mcp.yml │ │ ├── search-sfra-documentation.docs-only.test.mcp.yml │ │ ├── search-sfra-documentation.full-mode.test.mcp.yml │ │ ├── search-site-preferences.docs-only.test.mcp.yml │ │ ├── search-site-preferences.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-groups.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-groups.full-mode.test.mcp.yml │ │ ├── summarize-logs.full-mode.test.mcp.yml │ │ ├── tools.docs-only.test.mcp.yml │ │ └── tools.full-mode.test.mcp.yml │ ├── oauth-token.test.ts │ ├── ocapi-auth-client.test.ts │ ├── ocapi-client.test.ts │ ├── path-service.test.ts │ ├── query-builder.test.ts │ ├── referenced-types-extractor.test.ts │ ├── servers │ │ ├── sfcc-mock-server │ │ │ ├── mock-data │ │ │ │ └── ocapi │ │ │ │ ├── code-versions.json │ │ │ │ ├── custom-object-attributes-customapi.json │ │ │ │ ├── custom-object-attributes-globalsettings.json │ │ │ │ ├── custom-object-attributes-versionhistory.json │ │ │ │ ├── site-preferences-ccv.json │ │ │ │ ├── site-preferences-fastforward.json │ │ │ │ ├── site-preferences-sfra.json │ │ │ │ ├── site-preferences-storefront.json │ │ │ │ ├── site-preferences-system.json │ │ │ │ ├── system-object-attribute-groups-campaign.json │ │ │ │ ├── system-object-attribute-groups-category.json │ │ │ │ ├── system-object-attribute-groups-order.json │ │ │ │ ├── system-object-attribute-groups-product.json │ │ │ │ ├── system-object-attribute-groups-sitepreferences.json │ │ │ │ ├── system-object-attributes-customeraddress.json │ │ │ │ ├── system-object-attributes-product-expanded.json │ │ │ │ ├── system-object-attributes-product.json │ │ │ │ ├── system-object-definition-category.json │ │ │ │ ├── system-object-definition-customer.json │ │ │ │ ├── system-object-definition-customeraddress.json │ │ │ │ ├── system-object-definition-order.json │ │ │ │ ├── system-object-definition-product.json │ │ │ │ ├── system-object-definitions-old.json │ │ │ │ └── system-object-definitions.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── README.md │ │ │ ├── scripts │ │ │ │ └── setup-logs.js │ │ │ ├── server.js │ │ │ └── src │ │ │ ├── app.js │ │ │ ├── config │ │ │ │ └── server-config.js │ │ │ ├── middleware │ │ │ │ ├── auth.js │ │ │ │ ├── cors.js │ │ │ │ └── logging.js │ │ │ ├── routes │ │ │ │ ├── ocapi │ │ │ │ │ ├── code-versions-handler.js │ │ │ │ │ ├── oauth-handler.js │ │ │ │ │ ├── ocapi-error-utils.js │ │ │ │ │ ├── ocapi-utils.js │ │ │ │ │ ├── site-preferences-handler.js │ │ │ │ │ └── system-objects-handler.js │ │ │ │ ├── ocapi.js │ │ │ │ └── webdav.js │ │ │ └── utils │ │ │ ├── mock-data-loader.js │ │ │ └── webdav-xml.js │ │ └── sfcc-mock-server-manager.ts │ ├── sfcc-mock-server.test.ts │ ├── site-preferences-client.test.ts │ ├── system-objects-client.test.ts │ ├── utils.test.ts │ ├── validation-helpers.test.ts │ └── validator.test.ts ├── tsconfig.json └── tsconfig.test.json ``` # Files -------------------------------------------------------------------------------- /docs/dw_catalog/VariationGroup.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.catalog 2 | 3 | # Class VariationGroup 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.catalog.Product 11 | - dw.catalog.VariationGroup 12 | 13 | ## Description 14 | 15 | Class representing a group of variants within a master product who share a common value for one or more variation attribute values. Variation groups are used to simplify merchandising of products. From a more technical perspective, variation groups are defined by two things: A relation to a master product. A set of variation attributes which have fixed values. A variant of the related master product is considered in the group if and only if it matches on the fixed variation attribute values. Similar to a Variant, a VariationGroup does a fallback to the master product for all attributes (name, description, etc) and relations (recommendations, etc). 16 | 17 | ## Properties 18 | 19 | ### allProductLinks 20 | 21 | **Type:** Collection (Read Only) 22 | 23 | All product links of the product variation group. 24 | 25 | If the variation group does not define any product links, but the master product 26 | does, the product links of the master are returned. 27 | 28 | ### brand 29 | 30 | **Type:** String (Read Only) 31 | 32 | The brand of the product variation group. 33 | 34 | If the variation group does not define an own value for 'brand', the value of 35 | the master product is returned. 36 | 37 | ### classificationCategory 38 | 39 | **Type:** Category (Read Only) 40 | 41 | The classification category of the product variation group. 42 | 43 | Please note that the classification category is always inherited 44 | from the master and cannot be overridden by the variation group. 45 | 46 | ### custom 47 | 48 | **Type:** CustomAttributes (Read Only) 49 | 50 | The custom attributes of the variation group. 51 | 52 | Custom attributes are inherited from the master product and can 53 | be overridden by the variation group. 54 | 55 | ### EAN 56 | 57 | **Type:** String (Read Only) 58 | 59 | The EAN of the product variation group. 60 | 61 | If the variation group does not define an own value for 'EAN', the value of 62 | the master product is returned. 63 | 64 | ### image 65 | 66 | **Type:** MediaFile (Read Only) 67 | 68 | The image of the product variation group. 69 | 70 | If the variation group does not define an own value for 'image', the value of 71 | the master product is returned. 72 | 73 | ### longDescription 74 | 75 | **Type:** MarkupText (Read Only) 76 | 77 | The long description of the product variation group. 78 | 79 | If the variation group does not define an own value for 'longDescription', the value of 80 | the master product is returned. 81 | 82 | ### manufacturerName 83 | 84 | **Type:** String (Read Only) 85 | 86 | The manufacturer name of the product variation group. 87 | 88 | If the variation group does not define an own value for 'manufacturerName', the value of 89 | the master product is returned. 90 | 91 | ### manufacturerSKU 92 | 93 | **Type:** String (Read Only) 94 | 95 | The manufacturer sku of the product variation group. 96 | 97 | If the variation group does not define an own value for 'manufacturerSKU', the value of 98 | the master product is returned. 99 | 100 | ### masterProduct 101 | 102 | **Type:** Product (Read Only) 103 | 104 | The ProductMaster for this mastered product. 105 | 106 | ### name 107 | 108 | **Type:** String (Read Only) 109 | 110 | The name of the product variation group. 111 | 112 | If the variation group does not define an own value for 'name', the value of 113 | the master product is returned. 114 | 115 | ### onlineFrom 116 | 117 | **Type:** Date (Read Only) 118 | 119 | The onlineFrom date of the product variation group. 120 | 121 | If the variation group does not define an own value for 'onlineFrom', the value of 122 | the master product is returned. 123 | 124 | ### onlineTo 125 | 126 | **Type:** Date (Read Only) 127 | 128 | The onlineTo date of the product variation group. 129 | 130 | If the variation group does not define an own value for 'onlineTo', the value of 131 | the master product is returned. 132 | 133 | ### optionProduct 134 | 135 | **Type:** boolean (Read Only) 136 | 137 | Returns 'true' if the variation group has any options, otherwise 'false'. 138 | Method also returns 'true' if the variation group has not any options, 139 | but the related master product has options. 140 | 141 | ### pageDescription 142 | 143 | **Type:** String (Read Only) 144 | 145 | The pageDescription of the product variation group. 146 | 147 | If the variation group does not define an own value for 'pageDescription', the value of 148 | the master product is returned. 149 | 150 | ### pageKeywords 151 | 152 | **Type:** String (Read Only) 153 | 154 | The pageKeywords of the product variation group. 155 | 156 | If the variation group does not define an own value for 'pageKeywords', the value of 157 | the master product is returned. 158 | 159 | ### pageTitle 160 | 161 | **Type:** String (Read Only) 162 | 163 | The pageTitle of the product variation group. 164 | 165 | If the variation group does not define an own value for 'pageTitle', the value of 166 | the master product is returned. 167 | 168 | ### pageURL 169 | 170 | **Type:** String (Read Only) 171 | 172 | The pageURL of the product variation group. 173 | 174 | If the variation group does not define an own value for 'pageURL', the value of 175 | the master product is returned. 176 | 177 | ### productLinks 178 | 179 | **Type:** Collection (Read Only) 180 | 181 | All product links of the product variation group for which the target 182 | product is assigned to the current site catalog. 183 | 184 | If the variation group does not define any product links, but the master product 185 | does, the product links of the master are returned. 186 | 187 | ### shortDescription 188 | 189 | **Type:** MarkupText (Read Only) 190 | 191 | The short description of the product variation group. 192 | 193 | If the variation group does not define an own value for 'shortDescription', the value of 194 | the master product is returned. 195 | 196 | ### taxClassID 197 | 198 | **Type:** String (Read Only) 199 | 200 | The tax class id of the product variation group. 201 | 202 | If the variation group does not define an own value for 'taxClassID', the value of 203 | the master product is returned. 204 | 205 | ### template 206 | 207 | **Type:** String (Read Only) 208 | 209 | The rendering template name of the product variation group. 210 | 211 | If the variation group does not define an own value for 'template', the value of 212 | the master product is returned. 213 | 214 | ### thumbnail 215 | 216 | **Type:** MediaFile (Read Only) 217 | 218 | The thumbnail image of the product variation group. 219 | 220 | If the variation group does not define an own value for 'thumbnailImage', the value of 221 | the master product is returned. 222 | 223 | ### unit 224 | 225 | **Type:** String (Read Only) 226 | 227 | The sales unit of the product variation group as defined by the 228 | master product. 229 | 230 | If the variation group does not define an own value for 'unit', the value of 231 | the master product is returned. 232 | 233 | ### unitQuantity 234 | 235 | **Type:** Quantity (Read Only) 236 | 237 | The unitQuantity of the product variation group as defined by the 238 | master product. 239 | 240 | If the variation group does not define an own value for 'unitQuantity', the value of 241 | the master product is returned. 242 | 243 | ### UPC 244 | 245 | **Type:** String (Read Only) 246 | 247 | The UPC of the product variation group. 248 | 249 | If the variation group does not define an own value for 'UPC', the value of 250 | the master product is returned. 251 | 252 | ## Constructor Summary 253 | 254 | ## Method Summary 255 | 256 | ### getAllProductLinks 257 | 258 | **Signature:** `getAllProductLinks() : Collection` 259 | 260 | Returns all product links of the product variation group. 261 | 262 | ### getAllProductLinks 263 | 264 | **Signature:** `getAllProductLinks(type : Number) : Collection` 265 | 266 | Returns all product links of the specified type of the product variation group. 267 | 268 | ### getBrand 269 | 270 | **Signature:** `getBrand() : String` 271 | 272 | Returns the brand of the product variation group. 273 | 274 | ### getClassificationCategory 275 | 276 | **Signature:** `getClassificationCategory() : Category` 277 | 278 | Returns the classification category of the product variation group. 279 | 280 | ### getCustom 281 | 282 | **Signature:** `getCustom() : CustomAttributes` 283 | 284 | Returns the custom attributes of the variation group. 285 | 286 | ### getEAN 287 | 288 | **Signature:** `getEAN() : String` 289 | 290 | Returns the EAN of the product variation group. 291 | 292 | ### getImage 293 | 294 | **Signature:** `getImage() : MediaFile` 295 | 296 | Returns the image of the product variation group. 297 | 298 | ### getLongDescription 299 | 300 | **Signature:** `getLongDescription() : MarkupText` 301 | 302 | Returns the long description of the product variation group. 303 | 304 | ### getManufacturerName 305 | 306 | **Signature:** `getManufacturerName() : String` 307 | 308 | Returns the manufacturer name of the product variation group. 309 | 310 | ### getManufacturerSKU 311 | 312 | **Signature:** `getManufacturerSKU() : String` 313 | 314 | Returns the manufacturer sku of the product variation group. 315 | 316 | ### getMasterProduct 317 | 318 | **Signature:** `getMasterProduct() : Product` 319 | 320 | Returns the ProductMaster for this mastered product. 321 | 322 | ### getName 323 | 324 | **Signature:** `getName() : String` 325 | 326 | Returns the name of the product variation group. 327 | 328 | ### getOnlineFrom 329 | 330 | **Signature:** `getOnlineFrom() : Date` 331 | 332 | Returns the onlineFrom date of the product variation group. 333 | 334 | ### getOnlineTo 335 | 336 | **Signature:** `getOnlineTo() : Date` 337 | 338 | Returns the onlineTo date of the product variation group. 339 | 340 | ### getPageDescription 341 | 342 | **Signature:** `getPageDescription() : String` 343 | 344 | Returns the pageDescription of the product variation group. 345 | 346 | ### getPageKeywords 347 | 348 | **Signature:** `getPageKeywords() : String` 349 | 350 | Returns the pageKeywords of the product variation group. 351 | 352 | ### getPageTitle 353 | 354 | **Signature:** `getPageTitle() : String` 355 | 356 | Returns the pageTitle of the product variation group. 357 | 358 | ### getPageURL 359 | 360 | **Signature:** `getPageURL() : String` 361 | 362 | Returns the pageURL of the product variation group. 363 | 364 | ### getProductLinks 365 | 366 | **Signature:** `getProductLinks() : Collection` 367 | 368 | Returns all product links of the product variation group for which the target product is assigned to the current site catalog. 369 | 370 | ### getProductLinks 371 | 372 | **Signature:** `getProductLinks(type : Number) : Collection` 373 | 374 | Returns all product links of the specified type of the product variation group for which the target product is assigned to the current site catalog. 375 | 376 | ### getRecommendations 377 | 378 | **Signature:** `getRecommendations(type : Number) : Collection` 379 | 380 | Retrieve the sorted collection of recommendations of the specified type for this product variation group. 381 | 382 | ### getShortDescription 383 | 384 | **Signature:** `getShortDescription() : MarkupText` 385 | 386 | Returns the short description of the product variation group. 387 | 388 | ### getTaxClassID 389 | 390 | **Signature:** `getTaxClassID() : String` 391 | 392 | Returns the tax class id of the product variation group. 393 | 394 | ### getTemplate 395 | 396 | **Signature:** `getTemplate() : String` 397 | 398 | Returns the rendering template name of the product variation group. 399 | 400 | ### getThumbnail 401 | 402 | **Signature:** `getThumbnail() : MediaFile` 403 | 404 | Returns the thumbnail image of the product variation group. 405 | 406 | ### getUnit 407 | 408 | **Signature:** `getUnit() : String` 409 | 410 | Returns the sales unit of the product variation group as defined by the master product. 411 | 412 | ### getUnitQuantity 413 | 414 | **Signature:** `getUnitQuantity() : Quantity` 415 | 416 | Returns the unitQuantity of the product variation group as defined by the master product. 417 | 418 | ### getUPC 419 | 420 | **Signature:** `getUPC() : String` 421 | 422 | Returns the UPC of the product variation group. 423 | 424 | ### isOptionProduct 425 | 426 | **Signature:** `isOptionProduct() : boolean` 427 | 428 | Returns 'true' if the variation group has any options, otherwise 'false'. 429 | 430 | ## Method Detail 431 | 432 | ## Method Details 433 | 434 | ### getAllProductLinks 435 | 436 | **Signature:** `getAllProductLinks() : Collection` 437 | 438 | **Description:** Returns all product links of the product variation group. If the variation group does not define any product links, but the master product does, the product links of the master are returned. 439 | 440 | **Returns:** 441 | 442 | All product links of the variation group or master 443 | 444 | --- 445 | 446 | ### getAllProductLinks 447 | 448 | **Signature:** `getAllProductLinks(type : Number) : Collection` 449 | 450 | **Description:** Returns all product links of the specified type of the product variation group. If the variation group does not define any product links, but the master product does, the product links of the master are returned. 451 | 452 | **Parameters:** 453 | 454 | - `type`: Type of the product link 455 | 456 | **Returns:** 457 | 458 | Product links of specified type of the variation group or master 459 | 460 | --- 461 | 462 | ### getBrand 463 | 464 | **Signature:** `getBrand() : String` 465 | 466 | **Description:** Returns the brand of the product variation group. If the variation group does not define an own value for 'brand', the value of the master product is returned. 467 | 468 | **Returns:** 469 | 470 | The brand of the variation group or master 471 | 472 | --- 473 | 474 | ### getClassificationCategory 475 | 476 | **Signature:** `getClassificationCategory() : Category` 477 | 478 | **Description:** Returns the classification category of the product variation group. Please note that the classification category is always inherited from the master and cannot be overridden by the variation group. 479 | 480 | **Returns:** 481 | 482 | The classification category as defined for the master product of the variation group 483 | 484 | --- 485 | 486 | ### getCustom 487 | 488 | **Signature:** `getCustom() : CustomAttributes` 489 | 490 | **Description:** Returns the custom attributes of the variation group. Custom attributes are inherited from the master product and can be overridden by the variation group. 491 | 492 | **Returns:** 493 | 494 | the custom attributes of the variation group. 495 | 496 | --- 497 | 498 | ### getEAN 499 | 500 | **Signature:** `getEAN() : String` 501 | 502 | **Description:** Returns the EAN of the product variation group. If the variation group does not define an own value for 'EAN', the value of the master product is returned. 503 | 504 | **Returns:** 505 | 506 | The EAN of the variation group or master 507 | 508 | --- 509 | 510 | ### getImage 511 | 512 | **Signature:** `getImage() : MediaFile` 513 | 514 | **Description:** Returns the image of the product variation group. If the variation group does not define an own value for 'image', the value of the master product is returned. 515 | 516 | **Returns:** 517 | 518 | The image of the variation group or master 519 | 520 | --- 521 | 522 | ### getLongDescription 523 | 524 | **Signature:** `getLongDescription() : MarkupText` 525 | 526 | **Description:** Returns the long description of the product variation group. If the variation group does not define an own value for 'longDescription', the value of the master product is returned. 527 | 528 | **Returns:** 529 | 530 | The long description name of the variation group or master 531 | 532 | --- 533 | 534 | ### getManufacturerName 535 | 536 | **Signature:** `getManufacturerName() : String` 537 | 538 | **Description:** Returns the manufacturer name of the product variation group. If the variation group does not define an own value for 'manufacturerName', the value of the master product is returned. 539 | 540 | **Returns:** 541 | 542 | The manufacturer name of the variation group or master 543 | 544 | --- 545 | 546 | ### getManufacturerSKU 547 | 548 | **Signature:** `getManufacturerSKU() : String` 549 | 550 | **Description:** Returns the manufacturer sku of the product variation group. If the variation group does not define an own value for 'manufacturerSKU', the value of the master product is returned. 551 | 552 | **Returns:** 553 | 554 | The manufacturer sku of the variation group or master 555 | 556 | --- 557 | 558 | ### getMasterProduct 559 | 560 | **Signature:** `getMasterProduct() : Product` 561 | 562 | **Description:** Returns the ProductMaster for this mastered product. 563 | 564 | **Returns:** 565 | 566 | the ProductMaster of this mastered product 567 | 568 | --- 569 | 570 | ### getName 571 | 572 | **Signature:** `getName() : String` 573 | 574 | **Description:** Returns the name of the product variation group. If the variation group does not define an own value for 'name', the value of the master product is returned. 575 | 576 | **Returns:** 577 | 578 | The name of the variation group or master 579 | 580 | --- 581 | 582 | ### getOnlineFrom 583 | 584 | **Signature:** `getOnlineFrom() : Date` 585 | 586 | **Description:** Returns the onlineFrom date of the product variation group. If the variation group does not define an own value for 'onlineFrom', the value of the master product is returned. 587 | 588 | **Returns:** 589 | 590 | The onlineFrom date of the variation group or master 591 | 592 | --- 593 | 594 | ### getOnlineTo 595 | 596 | **Signature:** `getOnlineTo() : Date` 597 | 598 | **Description:** Returns the onlineTo date of the product variation group. If the variation group does not define an own value for 'onlineTo', the value of the master product is returned. 599 | 600 | **Returns:** 601 | 602 | The onlineTo date of the variation group or master 603 | 604 | --- 605 | 606 | ### getPageDescription 607 | 608 | **Signature:** `getPageDescription() : String` 609 | 610 | **Description:** Returns the pageDescription of the product variation group. If the variation group does not define an own value for 'pageDescription', the value of the master product is returned. 611 | 612 | **Returns:** 613 | 614 | The pageDescription of the variation group or master 615 | 616 | --- 617 | 618 | ### getPageKeywords 619 | 620 | **Signature:** `getPageKeywords() : String` 621 | 622 | **Description:** Returns the pageKeywords of the product variation group. If the variation group does not define an own value for 'pageKeywords', the value of the master product is returned. 623 | 624 | **Returns:** 625 | 626 | The pageKeywords of the variation group or master 627 | 628 | --- 629 | 630 | ### getPageTitle 631 | 632 | **Signature:** `getPageTitle() : String` 633 | 634 | **Description:** Returns the pageTitle of the product variation group. If the variation group does not define an own value for 'pageTitle', the value of the master product is returned. 635 | 636 | **Returns:** 637 | 638 | The pageTitle of the variation group or master 639 | 640 | --- 641 | 642 | ### getPageURL 643 | 644 | **Signature:** `getPageURL() : String` 645 | 646 | **Description:** Returns the pageURL of the product variation group. If the variation group does not define an own value for 'pageURL', the value of the master product is returned. 647 | 648 | **Returns:** 649 | 650 | The pageURL of the variation group or master 651 | 652 | --- 653 | 654 | ### getProductLinks 655 | 656 | **Signature:** `getProductLinks() : Collection` 657 | 658 | **Description:** Returns all product links of the product variation group for which the target product is assigned to the current site catalog. If the variation group does not define any product links, but the master product does, the product links of the master are returned. 659 | 660 | **Returns:** 661 | 662 | Product links of the variation group or master 663 | 664 | --- 665 | 666 | ### getProductLinks 667 | 668 | **Signature:** `getProductLinks(type : Number) : Collection` 669 | 670 | **Description:** Returns all product links of the specified type of the product variation group for which the target product is assigned to the current site catalog. If the variation group does not define any product links of the specified type, but the master product does, the product links of the master are returned. 671 | 672 | **Parameters:** 673 | 674 | - `type`: Type of the product link 675 | 676 | **Returns:** 677 | 678 | Product links of specified type of the variation group or master 679 | 680 | --- 681 | 682 | ### getRecommendations 683 | 684 | **Signature:** `getRecommendations(type : Number) : Collection` 685 | 686 | **Description:** Retrieve the sorted collection of recommendations of the specified type for this product variation group. The types (cross-sell, up-sell, etc) are enumerated in the dw.catalog.Recommendation class. Only recommendations which are stored in the current site catalog are returned. Furthermore, a recommendation is only returned if the target of the recommendation is assigned to the current site catalog. If the variation group does not define any recommendations, but the master product does, the recommendations of the master are returned. 687 | 688 | **Parameters:** 689 | 690 | - `type`: the recommendation type 691 | 692 | **Returns:** 693 | 694 | the sorted collection, never null but possibly empty. 695 | 696 | --- 697 | 698 | ### getShortDescription 699 | 700 | **Signature:** `getShortDescription() : MarkupText` 701 | 702 | **Description:** Returns the short description of the product variation group. If the variation group does not define an own value for 'shortDescription', the value of the master product is returned. 703 | 704 | **Returns:** 705 | 706 | The short description name of the variation group or master 707 | 708 | --- 709 | 710 | ### getTaxClassID 711 | 712 | **Signature:** `getTaxClassID() : String` 713 | 714 | **Description:** Returns the tax class id of the product variation group. If the variation group does not define an own value for 'taxClassID', the value of the master product is returned. 715 | 716 | **Returns:** 717 | 718 | The tax class id of the variation group or master 719 | 720 | --- 721 | 722 | ### getTemplate 723 | 724 | **Signature:** `getTemplate() : String` 725 | 726 | **Description:** Returns the rendering template name of the product variation group. If the variation group does not define an own value for 'template', the value of the master product is returned. 727 | 728 | **Returns:** 729 | 730 | The rendering template name of the variation group or master 731 | 732 | --- 733 | 734 | ### getThumbnail 735 | 736 | **Signature:** `getThumbnail() : MediaFile` 737 | 738 | **Description:** Returns the thumbnail image of the product variation group. If the variation group does not define an own value for 'thumbnailImage', the value of the master product is returned. 739 | 740 | **Returns:** 741 | 742 | The thumbnail image of the variation group or master 743 | 744 | --- 745 | 746 | ### getUnit 747 | 748 | **Signature:** `getUnit() : String` 749 | 750 | **Description:** Returns the sales unit of the product variation group as defined by the master product. If the variation group does not define an own value for 'unit', the value of the master product is returned. 751 | 752 | **Returns:** 753 | 754 | The sales unit of the variation group or master 755 | 756 | --- 757 | 758 | ### getUnitQuantity 759 | 760 | **Signature:** `getUnitQuantity() : Quantity` 761 | 762 | **Description:** Returns the unitQuantity of the product variation group as defined by the master product. If the variation group does not define an own value for 'unitQuantity', the value of the master product is returned. 763 | 764 | **Returns:** 765 | 766 | The unitQuantity of the variation group or master 767 | 768 | --- 769 | 770 | ### getUPC 771 | 772 | **Signature:** `getUPC() : String` 773 | 774 | **Description:** Returns the UPC of the product variation group. If the variation group does not define an own value for 'UPC', the value of the master product is returned. 775 | 776 | **Returns:** 777 | 778 | The UPC of the variation group or master 779 | 780 | --- 781 | 782 | ### isOptionProduct 783 | 784 | **Signature:** `isOptionProduct() : boolean` 785 | 786 | **Description:** Returns 'true' if the variation group has any options, otherwise 'false'. Method also returns 'true' if the variation group has not any options, but the related master product has options. 787 | 788 | **Returns:** 789 | 790 | true if the variation group has any options, false otherwise. 791 | 792 | --- ``` -------------------------------------------------------------------------------- /tests/mcp/yaml/search-sfcc-methods.full-mode.test.mcp.yml: -------------------------------------------------------------------------------- ```yaml 1 | # ================================================================================== 2 | # SFCC MCP Server - search_sfcc_methods Tool YAML Tests 3 | # Comprehensive testing for SFCC method search functionality 4 | # Tests both successful responses and error handling scenarios 5 | # 6 | # Quick Test Commands: 7 | # aegis "tests/mcp/yaml/search-sfcc-methods.full-mode.test.mcp.yml" --config "aegis.config.with-dw.json" --verbose 8 | # aegis "tests/mcp/yaml/search-sfcc-methods.full-mode.test.mcp.yml" --config "aegis.config.with-dw.json" --debug --timing 9 | # aegis query search_sfcc_methods '{"methodName": "get"}' --config "aegis.config.with-dw.json" 10 | # aegis query search_sfcc_methods '{"methodName": "create"}' --config "aegis.config.with-dw.json" 11 | # ================================================================================== 12 | description: "SFCC MCP Server search_sfcc_methods tool - comprehensive validation" 13 | 14 | # ================================================================================== 15 | # BASIC TOOL STRUCTURE VALIDATION 16 | # ================================================================================== 17 | tests: 18 | - it: "should list search_sfcc_methods tool in available tools" 19 | request: 20 | jsonrpc: "2.0" 21 | id: "tool-available" 22 | method: "tools/list" 23 | params: {} 24 | expect: 25 | response: 26 | jsonrpc: "2.0" 27 | id: "tool-available" 28 | result: 29 | match:extractField: "tools.*.name" 30 | value: "match:arrayContains:search_sfcc_methods" 31 | stderr: "toBeEmpty" 32 | 33 | - it: "should have search_sfcc_methods in tools list with proper structure" 34 | request: 35 | jsonrpc: "2.0" 36 | id: "tool-metadata" 37 | method: "tools/list" 38 | params: {} 39 | expect: 40 | response: 41 | jsonrpc: "2.0" 42 | id: "tool-metadata" 43 | result: 44 | tools: "match:arrayContains:name:search_sfcc_methods" 45 | stderr: "toBeEmpty" 46 | 47 | - it: "should have tool with meaningful description" 48 | request: 49 | jsonrpc: "2.0" 50 | id: "tool-description-quality" 51 | method: "tools/list" 52 | params: {} 53 | expect: 54 | response: 55 | jsonrpc: "2.0" 56 | id: "tool-description-quality" 57 | result: 58 | tools: 59 | match:arrayContains:name:search_sfcc_methods 60 | stderr: "toBeEmpty" 61 | 62 | - it: "should have proper input schema for methodName parameter" 63 | request: 64 | jsonrpc: "2.0" 65 | id: "tool-schema-validation" 66 | method: "tools/list" 67 | params: {} 68 | expect: 69 | response: 70 | jsonrpc: "2.0" 71 | id: "tool-schema-validation" 72 | result: 73 | match:extractField: "tools.*.name" 74 | value: "match:arrayContains:search_sfcc_methods" 75 | stderr: "toBeEmpty" 76 | 77 | # ================================================================================== 78 | # SUCCESSFUL METHOD SEARCH OPERATIONS 79 | # ================================================================================== 80 | 81 | - it: "should successfully search for 'get' methods returning valid JSON array" 82 | request: 83 | jsonrpc: "2.0" 84 | id: "search-get-methods" 85 | method: "tools/call" 86 | params: 87 | name: "search_sfcc_methods" 88 | arguments: 89 | methodName: "get" 90 | expect: 91 | response: 92 | jsonrpc: "2.0" 93 | id: "search-get-methods" 94 | result: 95 | content: 96 | - type: "text" 97 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid JSON array structure 98 | isError: false 99 | stderr: "toBeEmpty" 100 | 101 | - it: "should find multiple get methods with proper structure" 102 | request: 103 | jsonrpc: "2.0" 104 | id: "search-get-structure" 105 | method: "tools/call" 106 | params: 107 | name: "search_sfcc_methods" 108 | arguments: 109 | methodName: "get" 110 | expect: 111 | response: 112 | jsonrpc: "2.0" 113 | id: "search-get-structure" 114 | result: 115 | content: 116 | - type: "text" 117 | text: "match:contains:className" # Should contain className field 118 | isError: false 119 | stderr: "toBeEmpty" 120 | 121 | - it: "should find get methods with method objects containing name and signature" 122 | request: 123 | jsonrpc: "2.0" 124 | id: "search-get-method-details" 125 | method: "tools/call" 126 | params: 127 | name: "search_sfcc_methods" 128 | arguments: 129 | methodName: "get" 130 | expect: 131 | response: 132 | jsonrpc: "2.0" 133 | id: "search-get-method-details" 134 | result: 135 | content: 136 | - type: "text" 137 | text: "match:contains:signature" # Should contain signature information 138 | isError: false 139 | stderr: "toBeEmpty" 140 | 141 | - it: "should successfully search for 'create' methods" 142 | request: 143 | jsonrpc: "2.0" 144 | id: "search-create-methods" 145 | method: "tools/call" 146 | params: 147 | name: "search_sfcc_methods" 148 | arguments: 149 | methodName: "create" 150 | expect: 151 | response: 152 | jsonrpc: "2.0" 153 | id: "search-create-methods" 154 | result: 155 | content: 156 | - type: "text" 157 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid JSON array 158 | isError: false 159 | stderr: "toBeEmpty" 160 | 161 | - it: "should find create methods containing relevant class information" 162 | request: 163 | jsonrpc: "2.0" 164 | id: "search-create-content" 165 | method: "tools/call" 166 | params: 167 | name: "search_sfcc_methods" 168 | arguments: 169 | methodName: "create" 170 | expect: 171 | response: 172 | jsonrpc: "2.0" 173 | id: "search-create-content" 174 | result: 175 | content: 176 | - type: "text" 177 | text: "match:contains:dw_" # Should contain SFCC class references 178 | isError: false 179 | stderr: "toBeEmpty" 180 | 181 | - it: "should successfully search for 'toString' method" 182 | request: 183 | jsonrpc: "2.0" 184 | id: "search-tostring-method" 185 | method: "tools/call" 186 | params: 187 | name: "search_sfcc_methods" 188 | arguments: 189 | methodName: "toString" 190 | expect: 191 | response: 192 | jsonrpc: "2.0" 193 | id: "search-tostring-method" 194 | result: 195 | content: 196 | - type: "text" 197 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid JSON array 198 | isError: false 199 | stderr: "toBeEmpty" 200 | 201 | - it: "should successfully search for 'getValue' method" 202 | request: 203 | jsonrpc: "2.0" 204 | id: "search-getvalue-method" 205 | method: "tools/call" 206 | params: 207 | name: "search_sfcc_methods" 208 | arguments: 209 | methodName: "getValue" 210 | expect: 211 | response: 212 | jsonrpc: "2.0" 213 | id: "search-getvalue-method" 214 | result: 215 | content: 216 | - type: "text" 217 | text: "match:contains:getValue" # Should contain the searched method name 218 | isError: false 219 | stderr: "toBeEmpty" 220 | 221 | - it: "should handle case-sensitive method search" 222 | request: 223 | jsonrpc: "2.0" 224 | id: "search-case-sensitive" 225 | method: "tools/call" 226 | params: 227 | name: "search_sfcc_methods" 228 | arguments: 229 | methodName: "GET" # Uppercase should be different from 'get' 230 | expect: 231 | response: 232 | jsonrpc: "2.0" 233 | id: "search-case-sensitive" 234 | result: 235 | content: 236 | - type: "text" 237 | text: "match:regex:\\[[\\s\\S]*\\]" # Should still return valid array (might be empty) 238 | isError: false 239 | stderr: "toBeEmpty" 240 | 241 | # ================================================================================== 242 | # EMPTY RESULT VALIDATION 243 | # ================================================================================== 244 | 245 | - it: "should return empty array for non-existent method names" 246 | request: 247 | jsonrpc: "2.0" 248 | id: "search-nonexistent-method" 249 | method: "tools/call" 250 | params: 251 | name: "search_sfcc_methods" 252 | arguments: 253 | methodName: "zzznothingfound" 254 | expect: 255 | response: 256 | jsonrpc: "2.0" 257 | id: "search-nonexistent-method" 258 | result: 259 | content: 260 | - type: "text" 261 | text: "match:regex:^\\[\\s*\\]$" # Empty JSON array 262 | isError: false 263 | stderr: "toBeEmpty" 264 | 265 | - it: "should return empty array for very specific method name" 266 | request: 267 | jsonrpc: "2.0" 268 | id: "search-specific-nonexistent" 269 | method: "tools/call" 270 | params: 271 | name: "search_sfcc_methods" 272 | arguments: 273 | methodName: "verySpecificMethodThatDoesNotExist123" 274 | expect: 275 | response: 276 | jsonrpc: "2.0" 277 | id: "search-specific-nonexistent" 278 | result: 279 | content: 280 | - type: "text" 281 | text: "[]" # Exact empty array 282 | isError: false 283 | stderr: "toBeEmpty" 284 | 285 | - it: "should return empty array for special characters in method name" 286 | request: 287 | jsonrpc: "2.0" 288 | id: "search-special-chars" 289 | method: "tools/call" 290 | params: 291 | name: "search_sfcc_methods" 292 | arguments: 293 | methodName: "method@#$%" 294 | expect: 295 | response: 296 | jsonrpc: "2.0" 297 | id: "search-special-chars" 298 | result: 299 | content: 300 | - type: "text" 301 | text: "match:regex:^\\[\\s*\\]$" # Empty array for invalid method names 302 | isError: false 303 | stderr: "toBeEmpty" 304 | 305 | # ================================================================================== 306 | # ERROR HANDLING VALIDATION 307 | # ================================================================================== 308 | 309 | - it: "should return error for empty method name" 310 | request: 311 | jsonrpc: "2.0" 312 | id: "error-empty-method" 313 | method: "tools/call" 314 | params: 315 | name: "search_sfcc_methods" 316 | arguments: 317 | methodName: "" 318 | expect: 319 | response: 320 | jsonrpc: "2.0" 321 | id: "error-empty-method" 322 | result: 323 | content: 324 | - type: "text" 325 | text: "match:contains:Error" 326 | isError: true 327 | stderr: "toBeEmpty" 328 | 329 | - it: "should return specific error message for empty method name" 330 | request: 331 | jsonrpc: "2.0" 332 | id: "error-empty-message" 333 | method: "tools/call" 334 | params: 335 | name: "search_sfcc_methods" 336 | arguments: 337 | methodName: "" 338 | expect: 339 | response: 340 | jsonrpc: "2.0" 341 | id: "error-empty-message" 342 | result: 343 | content: 344 | - type: "text" 345 | text: "match:contains:non-empty string" 346 | isError: true 347 | stderr: "toBeEmpty" 348 | 349 | - it: "should return error for missing methodName parameter" 350 | request: 351 | jsonrpc: "2.0" 352 | id: "error-missing-param" 353 | method: "tools/call" 354 | params: 355 | name: "search_sfcc_methods" 356 | arguments: {} 357 | expect: 358 | response: 359 | jsonrpc: "2.0" 360 | id: "error-missing-param" 361 | result: 362 | content: 363 | - type: "text" 364 | text: "match:contains:Error" 365 | isError: true 366 | stderr: "toBeEmpty" 367 | 368 | - it: "should return error for whitespace-only method name" 369 | request: 370 | jsonrpc: "2.0" 371 | id: "error-whitespace-method" 372 | method: "tools/call" 373 | params: 374 | name: "search_sfcc_methods" 375 | arguments: 376 | methodName: " " 377 | expect: 378 | response: 379 | jsonrpc: "2.0" 380 | id: "error-whitespace-method" 381 | result: 382 | content: 383 | - type: "text" 384 | text: "match:contains:Error" 385 | isError: true 386 | stderr: "toBeEmpty" 387 | 388 | - it: "should return error for null method name" 389 | request: 390 | jsonrpc: "2.0" 391 | id: "error-null-method" 392 | method: "tools/call" 393 | params: 394 | name: "search_sfcc_methods" 395 | arguments: 396 | methodName: null 397 | expect: 398 | response: 399 | jsonrpc: "2.0" 400 | id: "error-null-method" 401 | result: 402 | content: 403 | - type: "text" 404 | text: "match:contains:Error" 405 | isError: true 406 | stderr: "toBeEmpty" 407 | 408 | - it: "should return error for non-string method name (number)" 409 | request: 410 | jsonrpc: "2.0" 411 | id: "error-number-method" 412 | method: "tools/call" 413 | params: 414 | name: "search_sfcc_methods" 415 | arguments: 416 | methodName: 123 417 | expect: 418 | response: 419 | jsonrpc: "2.0" 420 | id: "error-number-method" 421 | result: 422 | content: 423 | - type: "text" 424 | text: "match:contains:Error" 425 | isError: true 426 | stderr: "toBeEmpty" 427 | 428 | - it: "should return error for non-string method name (boolean)" 429 | request: 430 | jsonrpc: "2.0" 431 | id: "error-boolean-method" 432 | method: "tools/call" 433 | params: 434 | name: "search_sfcc_methods" 435 | arguments: 436 | methodName: true 437 | expect: 438 | response: 439 | jsonrpc: "2.0" 440 | id: "error-boolean-method" 441 | result: 442 | content: 443 | - type: "text" 444 | text: "match:contains:Error" 445 | isError: true 446 | stderr: "toBeEmpty" 447 | 448 | # ================================================================================== 449 | # PERFORMANCE AND RESPONSE TIME VALIDATION 450 | # ================================================================================== 451 | 452 | - it: "should complete method search within reasonable time (large result set)" 453 | request: 454 | jsonrpc: "2.0" 455 | id: "perf-large-search" 456 | method: "tools/call" 457 | params: 458 | name: "search_sfcc_methods" 459 | arguments: 460 | methodName: "get" # Common method name with many results 461 | expect: 462 | response: 463 | jsonrpc: "2.0" 464 | id: "perf-large-search" 465 | result: 466 | content: 467 | - type: "text" 468 | text: "match:regex:\\[[\\s\\S]*\\]" 469 | isError: false 470 | performance: 471 | maxResponseTime: "3000ms" # Allow more time for comprehensive search 472 | stderr: "toBeEmpty" 473 | 474 | - it: "should complete method search quickly for empty results" 475 | request: 476 | jsonrpc: "2.0" 477 | id: "perf-empty-search" 478 | method: "tools/call" 479 | params: 480 | name: "search_sfcc_methods" 481 | arguments: 482 | methodName: "zzznothingfound" 483 | expect: 484 | response: 485 | jsonrpc: "2.0" 486 | id: "perf-empty-search" 487 | result: 488 | content: 489 | - type: "text" 490 | text: "[]" # Exact empty array 491 | isError: false 492 | performance: 493 | maxResponseTime: "1000ms" # Empty searches should be faster 494 | stderr: "toBeEmpty" 495 | 496 | - it: "should handle error cases quickly" 497 | request: 498 | jsonrpc: "2.0" 499 | id: "perf-error-handling" 500 | method: "tools/call" 501 | params: 502 | name: "search_sfcc_methods" 503 | arguments: 504 | methodName: "" 505 | expect: 506 | response: 507 | jsonrpc: "2.0" 508 | id: "perf-error-handling" 509 | result: 510 | content: 511 | - type: "text" 512 | text: "match:contains:Error" 513 | isError: true 514 | performance: 515 | maxResponseTime: "500ms" # Error validation should be very fast 516 | stderr: "toBeEmpty" 517 | 518 | # ================================================================================== 519 | # EDGE CASE VALIDATION 520 | # ================================================================================== 521 | 522 | - it: "should handle single character method search" 523 | request: 524 | jsonrpc: "2.0" 525 | id: "edge-single-char" 526 | method: "tools/call" 527 | params: 528 | name: "search_sfcc_methods" 529 | arguments: 530 | methodName: "a" 531 | expect: 532 | response: 533 | jsonrpc: "2.0" 534 | id: "edge-single-char" 535 | result: 536 | content: 537 | - type: "text" 538 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid JSON array 539 | isError: false 540 | stderr: "toBeEmpty" 541 | 542 | - it: "should handle very long method name search" 543 | request: 544 | jsonrpc: "2.0" 545 | id: "edge-long-method" 546 | method: "tools/call" 547 | params: 548 | name: "search_sfcc_methods" 549 | arguments: 550 | methodName: "thisIsAVeryLongMethodNameThatProbablyDoesNotExistInTheSystem" 551 | expect: 552 | response: 553 | jsonrpc: "2.0" 554 | id: "edge-long-method" 555 | result: 556 | content: 557 | - type: "text" 558 | text: "match:regex:^\\[\\s*\\]$" # Likely empty array for very long names 559 | isError: false 560 | stderr: "toBeEmpty" 561 | 562 | - it: "should handle method names with numbers" 563 | request: 564 | jsonrpc: "2.0" 565 | id: "edge-method-with-numbers" 566 | method: "tools/call" 567 | params: 568 | name: "search_sfcc_methods" 569 | arguments: 570 | methodName: "method123" 571 | expect: 572 | response: 573 | jsonrpc: "2.0" 574 | id: "edge-method-with-numbers" 575 | result: 576 | content: 577 | - type: "text" 578 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid response 579 | isError: false 580 | stderr: "toBeEmpty" 581 | 582 | - it: "should handle method names with underscores" 583 | request: 584 | jsonrpc: "2.0" 585 | id: "edge-method-underscore" 586 | method: "tools/call" 587 | params: 588 | name: "search_sfcc_methods" 589 | arguments: 590 | methodName: "method_name" 591 | expect: 592 | response: 593 | jsonrpc: "2.0" 594 | id: "edge-method-underscore" 595 | result: 596 | content: 597 | - type: "text" 598 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid response 599 | isError: false 600 | stderr: "toBeEmpty" 601 | 602 | # ================================================================================== 603 | # CONTENT STRUCTURE VALIDATION 604 | # ================================================================================== 605 | 606 | - it: "should return methods with complete information structure (if results exist)" 607 | request: 608 | jsonrpc: "2.0" 609 | id: "content-structure-complete" 610 | method: "tools/call" 611 | params: 612 | name: "search_sfcc_methods" 613 | arguments: 614 | methodName: "get" # Method likely to have results 615 | expect: 616 | response: 617 | jsonrpc: "2.0" 618 | id: "content-structure-complete" 619 | result: 620 | content: 621 | - type: "text" 622 | text: "match:contains:description" # Should include method descriptions 623 | isError: false 624 | stderr: "toBeEmpty" 625 | 626 | - it: "should include both className and method information" 627 | request: 628 | jsonrpc: "2.0" 629 | id: "content-both-class-method" 630 | method: "tools/call" 631 | params: 632 | name: "search_sfcc_methods" 633 | arguments: 634 | methodName: "create" 635 | expect: 636 | response: 637 | jsonrpc: "2.0" 638 | id: "content-both-class-method" 639 | result: 640 | content: 641 | - type: "text" 642 | text: "match:regex:[\\s\\S]*className[\\s\\S]*method[\\s\\S]*" # Contains both className and method 643 | isError: false 644 | stderr: "toBeEmpty" 645 | 646 | - it: "should maintain consistent response format across different searches" 647 | request: 648 | jsonrpc: "2.0" 649 | id: "content-consistent-format" 650 | method: "tools/call" 651 | params: 652 | name: "search_sfcc_methods" 653 | arguments: 654 | methodName: "getValue" 655 | expect: 656 | response: 657 | jsonrpc: "2.0" 658 | id: "content-consistent-format" 659 | result: 660 | content: 661 | - type: "text" 662 | text: "match:type:string" # Always returns string content 663 | isError: false 664 | stderr: "toBeEmpty" 665 | 666 | # ================================================================================== 667 | # INTEGRATION VALIDATION 668 | # ================================================================================== 669 | 670 | - it: "should be available in full-mode mode" 671 | request: 672 | jsonrpc: "2.0" 673 | id: "integration-full-mode" 674 | method: "tools/list" 675 | params: {} 676 | expect: 677 | response: 678 | jsonrpc: "2.0" 679 | id: "integration-full-mode" 680 | result: 681 | match:extractField: "tools.*.name" 682 | value: "match:arrayContains:search_sfcc_methods" 683 | stderr: "toBeEmpty" 684 | 685 | - it: "should work with SFCC namespace prefixes in results" 686 | request: 687 | jsonrpc: "2.0" 688 | id: "integration-namespace-prefix" 689 | method: "tools/call" 690 | params: 691 | name: "search_sfcc_methods" 692 | arguments: 693 | methodName: "get" 694 | expect: 695 | response: 696 | jsonrpc: "2.0" 697 | id: "integration-namespace-prefix" 698 | result: 699 | content: 700 | - type: "text" 701 | text: "match:regex:[\\s\\S]*dw_[a-z]+\\.[A-Z][\\s\\S]*" # Contains SFCC class patterns like dw_catalog.Product 702 | isError: false 703 | stderr: "toBeEmpty" 704 | 705 | - it: "should return methods from multiple SFCC namespaces" 706 | request: 707 | jsonrpc: "2.0" 708 | id: "integration-multiple-namespaces" 709 | method: "tools/call" 710 | params: 711 | name: "search_sfcc_methods" 712 | arguments: 713 | methodName: "toString" # Common method across many classes 714 | expect: 715 | response: 716 | jsonrpc: "2.0" 717 | id: "integration-multiple-namespaces" 718 | result: 719 | content: 720 | - type: "text" 721 | text: "match:regex:[\\s\\S]*dw_[a-z]+[\\s\\S]*" # Should find methods in various dw_ namespaces 722 | isError: false 723 | stderr: "toBeEmpty" 724 | ``` -------------------------------------------------------------------------------- /tests/mcp/yaml/search-sfcc-methods.docs-only.test.mcp.yml: -------------------------------------------------------------------------------- ```yaml 1 | # ================================================================================== 2 | # SFCC MCP Server - search_sfcc_methods Tool YAML Tests 3 | # Comprehensive testing for SFCC method search functionality 4 | # Tests both successful responses and error handling scenarios 5 | # 6 | # Quick Test Commands: 7 | # aegis "tests/mcp/yaml/search-sfcc-methods.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --verbose 8 | # aegis "tests/mcp/yaml/search-sfcc-methods.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --debug --timing 9 | # aegis query search_sfcc_methods '{"methodName": "get"}' --config "aegis.config.docs-only.json" 10 | # aegis query search_sfcc_methods '{"methodName": "create"}' --config "aegis.config.docs-only.json" 11 | # ================================================================================== 12 | description: "SFCC MCP Server search_sfcc_methods tool - comprehensive validation" 13 | 14 | # ================================================================================== 15 | # BASIC TOOL STRUCTURE VALIDATION 16 | # ================================================================================== 17 | tests: 18 | - it: "should list search_sfcc_methods tool in available tools" 19 | request: 20 | jsonrpc: "2.0" 21 | id: "tool-available" 22 | method: "tools/list" 23 | params: {} 24 | expect: 25 | response: 26 | jsonrpc: "2.0" 27 | id: "tool-available" 28 | result: 29 | match:extractField: "tools.*.name" 30 | value: "match:arrayContains:search_sfcc_methods" 31 | stderr: "toBeEmpty" 32 | 33 | - it: "should have search_sfcc_methods in tools list with proper structure" 34 | request: 35 | jsonrpc: "2.0" 36 | id: "tool-metadata" 37 | method: "tools/list" 38 | params: {} 39 | expect: 40 | response: 41 | jsonrpc: "2.0" 42 | id: "tool-metadata" 43 | result: 44 | tools: "match:arrayContains:name:search_sfcc_methods" 45 | stderr: "toBeEmpty" 46 | 47 | - it: "should have tool with meaningful description" 48 | request: 49 | jsonrpc: "2.0" 50 | id: "tool-description-quality" 51 | method: "tools/list" 52 | params: {} 53 | expect: 54 | response: 55 | jsonrpc: "2.0" 56 | id: "tool-description-quality" 57 | result: 58 | tools: 59 | match:arrayContains:name:search_sfcc_methods 60 | stderr: "toBeEmpty" 61 | 62 | - it: "should have proper input schema for methodName parameter" 63 | request: 64 | jsonrpc: "2.0" 65 | id: "tool-schema-validation" 66 | method: "tools/list" 67 | params: {} 68 | expect: 69 | response: 70 | jsonrpc: "2.0" 71 | id: "tool-schema-validation" 72 | result: 73 | match:extractField: "tools.*.name" 74 | value: "match:arrayContains:search_sfcc_methods" 75 | stderr: "toBeEmpty" 76 | 77 | # ================================================================================== 78 | # SUCCESSFUL METHOD SEARCH OPERATIONS 79 | # ================================================================================== 80 | 81 | - it: "should successfully search for 'get' methods returning valid JSON array" 82 | request: 83 | jsonrpc: "2.0" 84 | id: "search-get-methods" 85 | method: "tools/call" 86 | params: 87 | name: "search_sfcc_methods" 88 | arguments: 89 | methodName: "get" 90 | expect: 91 | response: 92 | jsonrpc: "2.0" 93 | id: "search-get-methods" 94 | result: 95 | content: 96 | - type: "text" 97 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid JSON array structure 98 | isError: false 99 | stderr: "toBeEmpty" 100 | 101 | - it: "should find multiple get methods with proper structure" 102 | request: 103 | jsonrpc: "2.0" 104 | id: "search-get-structure" 105 | method: "tools/call" 106 | params: 107 | name: "search_sfcc_methods" 108 | arguments: 109 | methodName: "get" 110 | expect: 111 | response: 112 | jsonrpc: "2.0" 113 | id: "search-get-structure" 114 | result: 115 | content: 116 | - type: "text" 117 | text: "match:contains:className" # Should contain className field 118 | isError: false 119 | stderr: "toBeEmpty" 120 | 121 | - it: "should find get methods with method objects containing name and signature" 122 | request: 123 | jsonrpc: "2.0" 124 | id: "search-get-method-details" 125 | method: "tools/call" 126 | params: 127 | name: "search_sfcc_methods" 128 | arguments: 129 | methodName: "get" 130 | expect: 131 | response: 132 | jsonrpc: "2.0" 133 | id: "search-get-method-details" 134 | result: 135 | content: 136 | - type: "text" 137 | text: "match:contains:signature" # Should contain signature information 138 | isError: false 139 | stderr: "toBeEmpty" 140 | 141 | - it: "should successfully search for 'create' methods" 142 | request: 143 | jsonrpc: "2.0" 144 | id: "search-create-methods" 145 | method: "tools/call" 146 | params: 147 | name: "search_sfcc_methods" 148 | arguments: 149 | methodName: "create" 150 | expect: 151 | response: 152 | jsonrpc: "2.0" 153 | id: "search-create-methods" 154 | result: 155 | content: 156 | - type: "text" 157 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid JSON array 158 | isError: false 159 | stderr: "toBeEmpty" 160 | 161 | - it: "should find create methods containing relevant class information" 162 | request: 163 | jsonrpc: "2.0" 164 | id: "search-create-content" 165 | method: "tools/call" 166 | params: 167 | name: "search_sfcc_methods" 168 | arguments: 169 | methodName: "create" 170 | expect: 171 | response: 172 | jsonrpc: "2.0" 173 | id: "search-create-content" 174 | result: 175 | content: 176 | - type: "text" 177 | text: "match:contains:dw_" # Should contain SFCC class references 178 | isError: false 179 | stderr: "toBeEmpty" 180 | 181 | - it: "should successfully search for 'toString' method" 182 | request: 183 | jsonrpc: "2.0" 184 | id: "search-tostring-method" 185 | method: "tools/call" 186 | params: 187 | name: "search_sfcc_methods" 188 | arguments: 189 | methodName: "toString" 190 | expect: 191 | response: 192 | jsonrpc: "2.0" 193 | id: "search-tostring-method" 194 | result: 195 | content: 196 | - type: "text" 197 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid JSON array 198 | isError: false 199 | stderr: "toBeEmpty" 200 | 201 | - it: "should successfully search for 'getValue' method" 202 | request: 203 | jsonrpc: "2.0" 204 | id: "search-getvalue-method" 205 | method: "tools/call" 206 | params: 207 | name: "search_sfcc_methods" 208 | arguments: 209 | methodName: "getValue" 210 | expect: 211 | response: 212 | jsonrpc: "2.0" 213 | id: "search-getvalue-method" 214 | result: 215 | content: 216 | - type: "text" 217 | text: "match:contains:getValue" # Should contain the searched method name 218 | isError: false 219 | stderr: "toBeEmpty" 220 | 221 | - it: "should handle case-sensitive method search" 222 | request: 223 | jsonrpc: "2.0" 224 | id: "search-case-sensitive" 225 | method: "tools/call" 226 | params: 227 | name: "search_sfcc_methods" 228 | arguments: 229 | methodName: "GET" # Uppercase should be different from 'get' 230 | expect: 231 | response: 232 | jsonrpc: "2.0" 233 | id: "search-case-sensitive" 234 | result: 235 | content: 236 | - type: "text" 237 | text: "match:regex:\\[[\\s\\S]*\\]" # Should still return valid array (might be empty) 238 | isError: false 239 | stderr: "toBeEmpty" 240 | 241 | # ================================================================================== 242 | # EMPTY RESULT VALIDATION 243 | # ================================================================================== 244 | 245 | - it: "should return empty array for non-existent method names" 246 | request: 247 | jsonrpc: "2.0" 248 | id: "search-nonexistent-method" 249 | method: "tools/call" 250 | params: 251 | name: "search_sfcc_methods" 252 | arguments: 253 | methodName: "zzznothingfound" 254 | expect: 255 | response: 256 | jsonrpc: "2.0" 257 | id: "search-nonexistent-method" 258 | result: 259 | content: 260 | - type: "text" 261 | text: "match:regex:^\\[\\s*\\]$" # Empty JSON array 262 | isError: false 263 | stderr: "toBeEmpty" 264 | 265 | - it: "should return empty array for very specific method name" 266 | request: 267 | jsonrpc: "2.0" 268 | id: "search-specific-nonexistent" 269 | method: "tools/call" 270 | params: 271 | name: "search_sfcc_methods" 272 | arguments: 273 | methodName: "verySpecificMethodThatDoesNotExist123" 274 | expect: 275 | response: 276 | jsonrpc: "2.0" 277 | id: "search-specific-nonexistent" 278 | result: 279 | content: 280 | - type: "text" 281 | text: "[]" # Exact empty array 282 | isError: false 283 | stderr: "toBeEmpty" 284 | 285 | - it: "should return empty array for special characters in method name" 286 | request: 287 | jsonrpc: "2.0" 288 | id: "search-special-chars" 289 | method: "tools/call" 290 | params: 291 | name: "search_sfcc_methods" 292 | arguments: 293 | methodName: "method@#$%" 294 | expect: 295 | response: 296 | jsonrpc: "2.0" 297 | id: "search-special-chars" 298 | result: 299 | content: 300 | - type: "text" 301 | text: "match:regex:^\\[\\s*\\]$" # Empty array for invalid method names 302 | isError: false 303 | stderr: "toBeEmpty" 304 | 305 | # ================================================================================== 306 | # ERROR HANDLING VALIDATION 307 | # ================================================================================== 308 | 309 | - it: "should return error for empty method name" 310 | request: 311 | jsonrpc: "2.0" 312 | id: "error-empty-method" 313 | method: "tools/call" 314 | params: 315 | name: "search_sfcc_methods" 316 | arguments: 317 | methodName: "" 318 | expect: 319 | response: 320 | jsonrpc: "2.0" 321 | id: "error-empty-method" 322 | result: 323 | content: 324 | - type: "text" 325 | text: "match:contains:Error" 326 | isError: true 327 | stderr: "toBeEmpty" 328 | 329 | - it: "should return specific error message for empty method name" 330 | request: 331 | jsonrpc: "2.0" 332 | id: "error-empty-message" 333 | method: "tools/call" 334 | params: 335 | name: "search_sfcc_methods" 336 | arguments: 337 | methodName: "" 338 | expect: 339 | response: 340 | jsonrpc: "2.0" 341 | id: "error-empty-message" 342 | result: 343 | content: 344 | - type: "text" 345 | text: "match:contains:non-empty string" 346 | isError: true 347 | stderr: "toBeEmpty" 348 | 349 | - it: "should return error for missing methodName parameter" 350 | request: 351 | jsonrpc: "2.0" 352 | id: "error-missing-param" 353 | method: "tools/call" 354 | params: 355 | name: "search_sfcc_methods" 356 | arguments: {} 357 | expect: 358 | response: 359 | jsonrpc: "2.0" 360 | id: "error-missing-param" 361 | result: 362 | content: 363 | - type: "text" 364 | text: "match:contains:Error" 365 | isError: true 366 | stderr: "toBeEmpty" 367 | 368 | - it: "should return error for whitespace-only method name" 369 | request: 370 | jsonrpc: "2.0" 371 | id: "error-whitespace-method" 372 | method: "tools/call" 373 | params: 374 | name: "search_sfcc_methods" 375 | arguments: 376 | methodName: " " 377 | expect: 378 | response: 379 | jsonrpc: "2.0" 380 | id: "error-whitespace-method" 381 | result: 382 | content: 383 | - type: "text" 384 | text: "match:contains:Error" 385 | isError: true 386 | stderr: "toBeEmpty" 387 | 388 | - it: "should return error for null method name" 389 | request: 390 | jsonrpc: "2.0" 391 | id: "error-null-method" 392 | method: "tools/call" 393 | params: 394 | name: "search_sfcc_methods" 395 | arguments: 396 | methodName: null 397 | expect: 398 | response: 399 | jsonrpc: "2.0" 400 | id: "error-null-method" 401 | result: 402 | content: 403 | - type: "text" 404 | text: "match:contains:Error" 405 | isError: true 406 | stderr: "toBeEmpty" 407 | 408 | - it: "should return error for non-string method name (number)" 409 | request: 410 | jsonrpc: "2.0" 411 | id: "error-number-method" 412 | method: "tools/call" 413 | params: 414 | name: "search_sfcc_methods" 415 | arguments: 416 | methodName: 123 417 | expect: 418 | response: 419 | jsonrpc: "2.0" 420 | id: "error-number-method" 421 | result: 422 | content: 423 | - type: "text" 424 | text: "match:contains:Error" 425 | isError: true 426 | stderr: "toBeEmpty" 427 | 428 | - it: "should return error for non-string method name (boolean)" 429 | request: 430 | jsonrpc: "2.0" 431 | id: "error-boolean-method" 432 | method: "tools/call" 433 | params: 434 | name: "search_sfcc_methods" 435 | arguments: 436 | methodName: true 437 | expect: 438 | response: 439 | jsonrpc: "2.0" 440 | id: "error-boolean-method" 441 | result: 442 | content: 443 | - type: "text" 444 | text: "match:contains:Error" 445 | isError: true 446 | stderr: "toBeEmpty" 447 | 448 | # ================================================================================== 449 | # PERFORMANCE AND RESPONSE TIME VALIDATION 450 | # ================================================================================== 451 | 452 | - it: "should complete method search within reasonable time (large result set)" 453 | request: 454 | jsonrpc: "2.0" 455 | id: "perf-large-search" 456 | method: "tools/call" 457 | params: 458 | name: "search_sfcc_methods" 459 | arguments: 460 | methodName: "get" # Common method name with many results 461 | expect: 462 | response: 463 | jsonrpc: "2.0" 464 | id: "perf-large-search" 465 | result: 466 | content: 467 | - type: "text" 468 | text: "match:regex:\\[[\\s\\S]*\\]" 469 | isError: false 470 | performance: 471 | maxResponseTime: "3000ms" # Allow more time for comprehensive search 472 | stderr: "toBeEmpty" 473 | 474 | - it: "should complete method search quickly for empty results" 475 | request: 476 | jsonrpc: "2.0" 477 | id: "perf-empty-search" 478 | method: "tools/call" 479 | params: 480 | name: "search_sfcc_methods" 481 | arguments: 482 | methodName: "zzznothingfound" 483 | expect: 484 | response: 485 | jsonrpc: "2.0" 486 | id: "perf-empty-search" 487 | result: 488 | content: 489 | - type: "text" 490 | text: "[]" # Exact empty array 491 | isError: false 492 | performance: 493 | maxResponseTime: "1000ms" # Empty searches should be faster 494 | stderr: "toBeEmpty" 495 | 496 | - it: "should handle error cases quickly" 497 | request: 498 | jsonrpc: "2.0" 499 | id: "perf-error-handling" 500 | method: "tools/call" 501 | params: 502 | name: "search_sfcc_methods" 503 | arguments: 504 | methodName: "" 505 | expect: 506 | response: 507 | jsonrpc: "2.0" 508 | id: "perf-error-handling" 509 | result: 510 | content: 511 | - type: "text" 512 | text: "match:contains:Error" 513 | isError: true 514 | performance: 515 | maxResponseTime: "500ms" # Error validation should be very fast 516 | stderr: "toBeEmpty" 517 | 518 | # ================================================================================== 519 | # EDGE CASE VALIDATION 520 | # ================================================================================== 521 | 522 | - it: "should handle single character method search" 523 | request: 524 | jsonrpc: "2.0" 525 | id: "edge-single-char" 526 | method: "tools/call" 527 | params: 528 | name: "search_sfcc_methods" 529 | arguments: 530 | methodName: "a" 531 | expect: 532 | response: 533 | jsonrpc: "2.0" 534 | id: "edge-single-char" 535 | result: 536 | content: 537 | - type: "text" 538 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid JSON array 539 | isError: false 540 | stderr: "toBeEmpty" 541 | 542 | - it: "should handle very long method name search" 543 | request: 544 | jsonrpc: "2.0" 545 | id: "edge-long-method" 546 | method: "tools/call" 547 | params: 548 | name: "search_sfcc_methods" 549 | arguments: 550 | methodName: "thisIsAVeryLongMethodNameThatProbablyDoesNotExistInTheSystem" 551 | expect: 552 | response: 553 | jsonrpc: "2.0" 554 | id: "edge-long-method" 555 | result: 556 | content: 557 | - type: "text" 558 | text: "match:regex:^\\[\\s*\\]$" # Likely empty array for very long names 559 | isError: false 560 | stderr: "toBeEmpty" 561 | 562 | - it: "should handle method names with numbers" 563 | request: 564 | jsonrpc: "2.0" 565 | id: "edge-method-with-numbers" 566 | method: "tools/call" 567 | params: 568 | name: "search_sfcc_methods" 569 | arguments: 570 | methodName: "method123" 571 | expect: 572 | response: 573 | jsonrpc: "2.0" 574 | id: "edge-method-with-numbers" 575 | result: 576 | content: 577 | - type: "text" 578 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid response 579 | isError: false 580 | stderr: "toBeEmpty" 581 | 582 | - it: "should handle method names with underscores" 583 | request: 584 | jsonrpc: "2.0" 585 | id: "edge-method-underscore" 586 | method: "tools/call" 587 | params: 588 | name: "search_sfcc_methods" 589 | arguments: 590 | methodName: "method_name" 591 | expect: 592 | response: 593 | jsonrpc: "2.0" 594 | id: "edge-method-underscore" 595 | result: 596 | content: 597 | - type: "text" 598 | text: "match:regex:\\[[\\s\\S]*\\]" # Valid response 599 | isError: false 600 | stderr: "toBeEmpty" 601 | 602 | # ================================================================================== 603 | # CONTENT STRUCTURE VALIDATION 604 | # ================================================================================== 605 | 606 | - it: "should return methods with complete information structure (if results exist)" 607 | request: 608 | jsonrpc: "2.0" 609 | id: "content-structure-complete" 610 | method: "tools/call" 611 | params: 612 | name: "search_sfcc_methods" 613 | arguments: 614 | methodName: "get" # Method likely to have results 615 | expect: 616 | response: 617 | jsonrpc: "2.0" 618 | id: "content-structure-complete" 619 | result: 620 | content: 621 | - type: "text" 622 | text: "match:contains:description" # Should include method descriptions 623 | isError: false 624 | stderr: "toBeEmpty" 625 | 626 | - it: "should include both className and method information" 627 | request: 628 | jsonrpc: "2.0" 629 | id: "content-both-class-method" 630 | method: "tools/call" 631 | params: 632 | name: "search_sfcc_methods" 633 | arguments: 634 | methodName: "create" 635 | expect: 636 | response: 637 | jsonrpc: "2.0" 638 | id: "content-both-class-method" 639 | result: 640 | content: 641 | - type: "text" 642 | text: "match:regex:[\\s\\S]*className[\\s\\S]*method[\\s\\S]*" # Contains both className and method 643 | isError: false 644 | stderr: "toBeEmpty" 645 | 646 | - it: "should maintain consistent response format across different searches" 647 | request: 648 | jsonrpc: "2.0" 649 | id: "content-consistent-format" 650 | method: "tools/call" 651 | params: 652 | name: "search_sfcc_methods" 653 | arguments: 654 | methodName: "getValue" 655 | expect: 656 | response: 657 | jsonrpc: "2.0" 658 | id: "content-consistent-format" 659 | result: 660 | content: 661 | - type: "text" 662 | text: "match:type:string" # Always returns string content 663 | isError: false 664 | stderr: "toBeEmpty" 665 | 666 | # ================================================================================== 667 | # INTEGRATION VALIDATION 668 | # ================================================================================== 669 | 670 | - it: "should be available in docs-only mode" 671 | request: 672 | jsonrpc: "2.0" 673 | id: "integration-docs-only" 674 | method: "tools/list" 675 | params: {} 676 | expect: 677 | response: 678 | jsonrpc: "2.0" 679 | id: "integration-docs-only" 680 | result: 681 | match:extractField: "tools.*.name" 682 | value: "match:arrayContains:search_sfcc_methods" 683 | stderr: "toBeEmpty" 684 | 685 | - it: "should work with SFCC namespace prefixes in results" 686 | request: 687 | jsonrpc: "2.0" 688 | id: "integration-namespace-prefix" 689 | method: "tools/call" 690 | params: 691 | name: "search_sfcc_methods" 692 | arguments: 693 | methodName: "get" 694 | expect: 695 | response: 696 | jsonrpc: "2.0" 697 | id: "integration-namespace-prefix" 698 | result: 699 | content: 700 | - type: "text" 701 | text: "match:regex:[\\s\\S]*dw_[a-z]+\\.[A-Z][\\s\\S]*" # Contains SFCC class patterns like dw_catalog.Product 702 | isError: false 703 | stderr: "toBeEmpty" 704 | 705 | - it: "should return methods from multiple SFCC namespaces" 706 | request: 707 | jsonrpc: "2.0" 708 | id: "integration-multiple-namespaces" 709 | method: "tools/call" 710 | params: 711 | name: "search_sfcc_methods" 712 | arguments: 713 | methodName: "toString" # Common method across many classes 714 | expect: 715 | response: 716 | jsonrpc: "2.0" 717 | id: "integration-multiple-namespaces" 718 | result: 719 | content: 720 | - type: "text" 721 | text: "match:regex:[\\s\\S]*dw_[a-z]+[\\s\\S]*" # Should find methods in various dw_ namespaces 722 | isError: false 723 | stderr: "toBeEmpty" 724 | ``` -------------------------------------------------------------------------------- /tests/mcp/node/get-sfra-document.programmatic.test.js: -------------------------------------------------------------------------------- ```javascript 1 | import { test, describe, before, after, beforeEach } from 'node:test'; 2 | import { strict as assert } from 'node:assert'; 3 | import { connect } from 'mcp-aegis'; 4 | 5 | describe('get_sfra_document Tool - Programmatic Tests', () => { 6 | let client; 7 | 8 | before(async () => { 9 | client = await connect('./aegis.config.docs-only.json'); 10 | }); 11 | 12 | after(async () => { 13 | if (client?.connected) { 14 | await client.disconnect(); 15 | } 16 | }); 17 | 18 | beforeEach(() => { 19 | client.clearAllBuffers(); 20 | }); 21 | 22 | describe('Protocol Compliance & Tool Discovery', () => { 23 | test('should be registered and accessible', async () => { 24 | const tools = await client.listTools(); 25 | 26 | const sfraDocTool = tools.find(tool => tool.name === 'get_sfra_document'); 27 | assert.ok(sfraDocTool, 'get_sfra_document tool should be available'); 28 | 29 | // Validate tool schema structure 30 | assertToolSchema(sfraDocTool); 31 | 32 | // Validate specific schema requirements 33 | const { inputSchema } = sfraDocTool; 34 | assert.ok(inputSchema.properties.documentName, 'Should have documentName parameter'); 35 | assert.equal(inputSchema.properties.documentName.type, 'string'); 36 | assert.ok(inputSchema.required.includes('documentName'), 'documentName should be required'); 37 | }); 38 | 39 | test('should have comprehensive tool description', async () => { 40 | const tools = await client.listTools(); 41 | const sfraDocTool = tools.find(tool => tool.name === 'get_sfra_document'); 42 | 43 | const description = sfraDocTool.description.toLowerCase(); 44 | assert.ok(description.includes('sfra'), 'Description should mention SFRA'); 45 | assert.ok(description.includes('documentation'), 'Description should mention documentation'); 46 | assert.ok(description.includes('sfra') || description.includes('class') || description.includes('model'), 47 | 'Description should mention SFRA classes or models'); 48 | assert.ok(description.length > 100, 'Description should be comprehensive'); 49 | }); 50 | }); 51 | 52 | describe('Core SFRA Documents - Dynamic Discovery', () => { 53 | const coreDocuments = [ 54 | { name: 'server', expectedContent: ['Class Server', 'middleware', 'routing'] }, 55 | { name: 'request', expectedContent: ['Class Request', 'HTTP', 'session'] }, 56 | { name: 'response', expectedContent: ['Class Response', 'render', 'redirect'] }, 57 | { name: 'querystring', expectedContent: ['QueryString', 'parameters', 'URL'] }, 58 | { name: 'render', expectedContent: ['render', 'template', 'ISML'] } 59 | ]; 60 | 61 | coreDocuments.forEach(({ name, expectedContent }) => { 62 | test(`should retrieve ${name} document with rich content`, async () => { 63 | const result = await client.callTool('get_sfra_document', { documentName: name }); 64 | 65 | // Basic response validation 66 | assertValidMCPResponse(result); 67 | assert.equal(result.isError, false, `${name} document should load successfully`); 68 | 69 | // Parse and validate JSON content 70 | const documentData = parseDocumentJSON(result.content[0].text); 71 | 72 | // Validate document structure 73 | assertDocumentStructure(documentData, name); 74 | 75 | // Validate expected content is present 76 | expectedContent.forEach(content => { 77 | assert.ok( 78 | documentData.content.toLowerCase().includes(content.toLowerCase()), 79 | `${name} document should contain "${content}"` 80 | ); 81 | }); 82 | }); 83 | }); 84 | 85 | test('should maintain consistent document structure across all core docs', async () => { 86 | const results = []; 87 | 88 | // Process documents sequentially to avoid message stream interference 89 | for (let i = 0; i < coreDocuments.length; i++) { 90 | const { name } = coreDocuments[i]; 91 | const result = await client.callTool('get_sfra_document', { documentName: name }); 92 | const documentData = parseDocumentJSON(result.content[0].text); 93 | results.push({ name, documentData }); 94 | } 95 | 96 | // Validate all documents have consistent structure 97 | results.forEach(({ name, documentData }) => { 98 | assert.ok(documentData.title, `${name} should have title`); 99 | assert.ok(Array.isArray(documentData.sections), `${name} should have sections array`); 100 | assert.ok(documentData.content, `${name} should have content`); 101 | assert.ok(documentData.type, `${name} should have type`); 102 | assert.ok(documentData.category, `${name} should have category`); 103 | assert.ok(documentData.filename, `${name} should have filename`); 104 | assert.ok(documentData.lastModified, `${name} should have lastModified`); 105 | 106 | // Validate sections are comprehensive 107 | assert.ok(documentData.sections.length >= 5, `${name} should have at least 5 sections`); 108 | 109 | // Validate content is substantial 110 | assert.ok(documentData.content.length > 1000, `${name} should have substantial content`); 111 | }); 112 | assert.ok(results.length > 0, 'Should have processed core documents'); 113 | }); 114 | }); 115 | 116 | describe('Model Documents - Advanced Validation', () => { 117 | const modelDocuments = [ 118 | { name: 'product-full', category: 'product', keywords: ['product', 'pricing', 'variation'] }, 119 | { name: 'cart', category: 'order', keywords: ['cart', 'basket', 'items'] }, 120 | { name: 'account', category: 'customer', keywords: ['account', 'customer', 'profile'] }, 121 | { name: 'billing', category: 'order', keywords: ['billing', 'payment', 'address'] }, 122 | { name: 'shipping', category: 'order', keywords: ['shipping', 'delivery', 'address'] } 123 | ]; 124 | 125 | test('should retrieve all model documents with correct categorization', async () => { 126 | const results = []; 127 | 128 | // Process documents sequentially to avoid message stream interference 129 | for (let i = 0; i < modelDocuments.length; i++) { 130 | const { name, category, keywords } = modelDocuments[i]; 131 | const result = await client.callTool('get_sfra_document', { documentName: name }); 132 | assert.equal(result.isError, false, `${name} should load successfully`); 133 | 134 | const documentData = parseDocumentJSON(result.content[0].text); 135 | 136 | // Validate keywords are present in content 137 | const content = documentData.content.toLowerCase(); 138 | const keywordMatches = keywords.filter(keyword => 139 | content.includes(keyword.toLowerCase()) 140 | ); 141 | 142 | assert.ok(keywordMatches.length >= 1, 143 | `${name} should contain at least one keyword: ${keywords.join(', ')}`); 144 | 145 | results.push({ name, documentData, category, keywordMatches }); 146 | } 147 | 148 | // Group by category and validate 149 | const categoryGroups = results.reduce((groups, { name, category }) => { 150 | if (!groups[category]) groups[category] = []; 151 | groups[category].push(name); 152 | return groups; 153 | }, {}); 154 | 155 | // Validate category distribution 156 | Object.entries(categoryGroups).forEach(([category, docs]) => { 157 | assert.ok(docs.length > 0, `Category ${category} should have documents`); 158 | }); 159 | }); 160 | 161 | test('should handle hyphenated document names correctly', async () => { 162 | const hyphenatedDocs = modelDocuments 163 | .filter(({ name }) => name.includes('-')) 164 | .map(({ name }) => name); 165 | 166 | assert.ok(hyphenatedDocs.length > 0, 'Should have hyphenated document names to test'); 167 | 168 | for (const docName of hyphenatedDocs) { 169 | const result = await client.callTool('get_sfra_document', { documentName: docName }); 170 | assert.equal(result.isError, false, `Hyphenated document ${docName} should work`); 171 | 172 | const documentData = parseDocumentJSON(result.content[0].text); 173 | // More flexible filename checking - just ensure it's related to the document 174 | const filenameRelated = documentData.filename.toLowerCase().includes(docName.toLowerCase()) || 175 | documentData.filename.toLowerCase().replace(/-/g, '').includes(docName.toLowerCase().replace(/-/g, '')) || 176 | documentData.title.toLowerCase().includes(docName.toLowerCase().split('-')[0]); 177 | assert.ok(filenameRelated, 178 | `Filename should be related to ${docName}: ${documentData.filename}`); 179 | 180 | } 181 | }); 182 | }); 183 | 184 | describe('Case Sensitivity & Input Validation', () => { 185 | const caseTestCases = [ 186 | { input: 'server', expected: true, description: 'lowercase' }, 187 | { input: 'SERVER', expected: true, description: 'uppercase' }, 188 | { input: 'Server', expected: true, description: 'mixed case' }, 189 | { input: 'SeRvEr', expected: true, description: 'random case' }, 190 | { input: 'request', expected: true, description: 'lowercase request' }, 191 | { input: 'REQUEST', expected: true, description: 'uppercase request' } 192 | ]; 193 | 194 | test('should handle case variations consistently', async () => { 195 | const results = []; 196 | 197 | // Process case variations sequentially to avoid message stream interference 198 | for (let i = 0; i < caseTestCases.length; i++) { 199 | const { input, expected, description } = caseTestCases[i]; 200 | const result = await client.callTool('get_sfra_document', { documentName: input }); 201 | const success = !result.isError; 202 | 203 | assert.equal(success, expected, 204 | `${description} "${input}" should ${expected ? 'succeed' : 'fail'}`); 205 | 206 | if (success) { 207 | const documentData = parseDocumentJSON(result.content[0].text); 208 | // Case variations should return the same document 209 | assert.ok(documentData.title.includes('Server') || documentData.title.includes('Request'), 210 | 'Should return valid SFRA document'); 211 | } 212 | 213 | results.push({ input, success, description }); 214 | 215 | } 216 | 217 | // Verify case insensitivity 218 | const serverVariants = results 219 | .filter(({ input }) => input.toLowerCase() === 'server') 220 | .filter(({ success }) => success); 221 | 222 | assert.ok(serverVariants.length > 1, 'Multiple case variations should work for server'); 223 | assert.ok(serverVariants.length > 0, 'Should handle case insensitive variations'); 224 | }); 225 | 226 | test('should validate input parameters thoroughly', async () => { 227 | const invalidInputs = [ 228 | { args: {}, expectedError: 'documentName', description: 'missing parameter' }, 229 | { args: { documentName: '' }, expectedError: 'non-empty', description: 'empty string' }, 230 | { args: { documentName: null }, expectedError: 'string', description: 'null value' }, 231 | { args: { documentName: 123 }, expectedError: 'string', description: 'numeric value' }, 232 | { args: { documentName: 'invalid/path/name' }, expectedError: 'not found', description: 'invalid path' }, 233 | { args: { documentName: 'definitely-does-not-exist' }, expectedError: 'not found', description: 'nonexistent doc' } 234 | ]; 235 | 236 | for (const { args, expectedError, description } of invalidInputs) { 237 | const result = await client.callTool('get_sfra_document', args); 238 | 239 | assert.equal(result.isError, true, `${description} should result in error`); 240 | assert.ok(result.content[0].text.toLowerCase().includes(expectedError.toLowerCase()), 241 | `${description} should mention "${expectedError}"`); 242 | 243 | } 244 | }); 245 | }); 246 | 247 | 248 | describe('Content Quality & Completeness Analysis', () => { 249 | test('should provide comprehensive documentation content', async () => { 250 | const qualityChecks = [ 251 | { doc: 'server', minSections: 10, minContentLength: 5000, requiredSections: ['Method Summary', 'Method Detail', 'Usage Examples'] }, 252 | { doc: 'request', minSections: 8, minContentLength: 4000, requiredSections: ['Properties', 'Constructor Summary', 'Property Details'] }, 253 | { doc: 'response', minSections: 8, minContentLength: 4000, requiredSections: ['Method Summary', 'Properties'] } 254 | ]; 255 | 256 | for (const { doc, minSections, minContentLength, requiredSections } of qualityChecks) { 257 | const result = await client.callTool('get_sfra_document', { documentName: doc }); 258 | const documentData = parseDocumentJSON(result.content[0].text); 259 | 260 | // Section count validation 261 | assert.ok(documentData.sections.length >= minSections, 262 | `${doc} should have at least ${minSections} sections (has ${documentData.sections.length})`); 263 | 264 | // Content length validation 265 | assert.ok(documentData.content.length >= minContentLength, 266 | `${doc} should have substantial content (${documentData.content.length} chars, min ${minContentLength})`); 267 | 268 | // Required sections validation 269 | requiredSections.forEach(requiredSection => { 270 | const hasSection = documentData.sections.some(section => 271 | section.toLowerCase().includes(requiredSection.toLowerCase()) 272 | ); 273 | assert.ok(hasSection, `${doc} should have "${requiredSection}" section`); 274 | }); 275 | 276 | } 277 | }); 278 | 279 | test('should include code examples and usage patterns', async () => { 280 | const docsWithExamples = ['server', 'request', 'response']; 281 | 282 | for (const doc of docsWithExamples) { 283 | const result = await client.callTool('get_sfra_document', { documentName: doc }); 284 | const documentData = parseDocumentJSON(result.content[0].text); 285 | 286 | const content = documentData.content.toLowerCase(); 287 | 288 | // Check for code examples 289 | const hasCodeBlocks = content.includes('```') || content.includes('```javascript'); 290 | const hasUsageExamples = content.includes('example') && ( 291 | content.includes('function') || 292 | content.includes('var ') || 293 | content.includes('new ') || 294 | content.includes('server.') 295 | ); 296 | 297 | // Check for method signatures and constructor patterns 298 | const hasSignatures = content.includes('signature:') || 299 | (content.includes('(') && content.includes(')')) || 300 | content.includes('constructor') || 301 | content.includes('method'); 302 | 303 | assert.ok(hasCodeBlocks || hasUsageExamples || hasSignatures, 304 | `${doc} should contain code examples, usage patterns, or method signatures`); 305 | 306 | // Additional signature verification 307 | assert.ok(hasSignatures, `${doc} should contain method signatures or constructors`); 308 | 309 | // Note: Validated code examples and signatures for ${doc} 310 | 311 | } 312 | }); 313 | 314 | test('should maintain cross-reference consistency', async () => { 315 | const docs = ['server', 'request', 'response']; 316 | const documentContents = {}; 317 | 318 | // Load all documents 319 | for (const doc of docs) { 320 | const result = await client.callTool('get_sfra_document', { documentName: doc }); 321 | const documentData = parseDocumentJSON(result.content[0].text); 322 | documentContents[doc] = documentData.content.toLowerCase(); 323 | 324 | } 325 | 326 | // Check cross-references 327 | const crossRefs = [ 328 | { from: 'server', to: 'request', mention: 'request' }, 329 | { from: 'server', to: 'response', mention: 'response' }, 330 | { from: 'request', to: 'session', mention: 'session' } 331 | ]; 332 | 333 | crossRefs.forEach(({ from, to, mention }) => { 334 | if (documentContents[from] && documentContents[to]) { 335 | assert.ok(documentContents[from].includes(mention), 336 | `${from} document should reference ${mention}`); 337 | } 338 | }); 339 | }); 340 | }); 341 | 342 | describe('Error Recovery & Resilience', () => { 343 | test('should recover gracefully from various error conditions', async () => { 344 | const errorScenarios = [ 345 | { name: 'nonexistent-doc', expectError: true }, 346 | { name: 'server', expectError: false }, // Should work after error 347 | { name: '', expectError: true }, 348 | { name: 'request', expectError: false }, // Should work after another error 349 | { name: 'invalid/path', expectError: true }, 350 | { name: 'response', expectError: false } // Final success 351 | ]; 352 | 353 | for (const { name, expectError } of errorScenarios) { 354 | const result = await client.callTool('get_sfra_document', { documentName: name }); 355 | 356 | if (expectError) { 357 | assert.equal(result.isError, true, `${name || 'empty'} should result in error`); 358 | } else { 359 | assert.equal(result.isError, false, `${name} should succeed after error`); 360 | const documentData = parseDocumentJSON(result.content[0].text); 361 | assert.ok(documentData.title, `${name} should return valid document`); 362 | } 363 | 364 | } 365 | }); 366 | 367 | test('should maintain consistent behavior under stress', async () => { 368 | const stressTests = [ 369 | // Mix of valid and invalid requests 370 | ...Array(5).fill('server'), 371 | ...Array(3).fill('nonexistent'), 372 | ...Array(5).fill('request'), 373 | ...Array(2).fill(''), 374 | ...Array(5).fill('response') 375 | ]; 376 | 377 | let successCount = 0; 378 | let errorCount = 0; 379 | 380 | for (const docName of stressTests) { 381 | const result = await client.callTool('get_sfra_document', { documentName: docName }); 382 | 383 | if (result.isError) { 384 | errorCount++; 385 | } else { 386 | successCount++; 387 | // Validate successful responses are still valid 388 | const documentData = parseDocumentJSON(result.content[0].text); 389 | assert.ok(documentData.title, 'Successful responses should remain valid'); 390 | } 391 | } 392 | 393 | assert.ok(successCount > 0, 'Should have some successful requests'); 394 | assert.ok(errorCount > 0, 'Should have some failed requests'); 395 | assert.ok(successCount > 0, 'Should have successful requests'); 396 | assert.ok(errorCount >= 0, 'Should handle errors gracefully'); 397 | }); 398 | }); 399 | 400 | describe('Memory & Resource Management', () => { 401 | test('should handle large document content efficiently', async () => { 402 | // Test with documents that likely have substantial content 403 | const largeDocs = ['server', 'product-full', 'cart']; 404 | const memoryBefore = process.memoryUsage(); 405 | 406 | for (const doc of largeDocs) { 407 | const result = await client.callTool('get_sfra_document', { documentName: doc }); 408 | assert.equal(result.isError, false, `${doc} should load successfully`); 409 | 410 | const documentData = parseDocumentJSON(result.content[0].text); 411 | assert.ok(documentData.content.length > 0, `${doc} should have content`); 412 | 413 | } 414 | 415 | const memoryAfter = process.memoryUsage(); 416 | const memoryIncrease = (memoryAfter.heapUsed - memoryBefore.heapUsed) / 1024 / 1024; 417 | 418 | // Memory increase should be reasonable (allowing for some overhead) 419 | assert.ok(memoryIncrease < 50, 420 | `Memory increase should be reasonable: ${memoryIncrease.toFixed(1)}MB`); 421 | }); 422 | }); 423 | }); 424 | 425 | // Helper Classes and Functions 426 | 427 | 428 | function assertValidMCPResponse(result) { 429 | assert.ok(result.content, 'Should have content'); 430 | assert.ok(Array.isArray(result.content), 'Content should be array'); 431 | assert.equal(typeof result.isError, 'boolean', 'isError should be boolean'); 432 | assert.equal(result.content.length, 1, 'Should have exactly one content item'); 433 | assert.equal(result.content[0].type, 'text', 'Content should be text type'); 434 | } 435 | 436 | function assertToolSchema(tool) { 437 | assert.ok(tool.name, 'Tool should have name'); 438 | assert.ok(tool.description, 'Tool should have description'); 439 | assert.ok(tool.inputSchema, 'Tool should have schema'); 440 | assert.equal(tool.inputSchema.type, 'object', 'Schema should be object type'); 441 | assert.ok(tool.inputSchema.properties, 'Schema should have properties'); 442 | assert.ok(tool.inputSchema.required, 'Schema should have required fields'); 443 | } 444 | 445 | function parseDocumentJSON(textContent) { 446 | try { 447 | return JSON.parse(textContent); 448 | } catch (error) { 449 | assert.fail(`Document content should be valid JSON: ${error.message}`); 450 | } 451 | } 452 | 453 | function assertDocumentStructure(documentData, documentName) { 454 | assert.ok(documentData.title, `${documentName} should have title`); 455 | assert.ok(Array.isArray(documentData.sections), `${documentName} should have sections array`); 456 | assert.ok(documentData.content, `${documentName} should have content`); 457 | assert.ok(documentData.type, `${documentName} should have type`); 458 | assert.ok(documentData.category, `${documentName} should have category`); 459 | assert.ok(documentData.filename, `${documentName} should have filename`); 460 | assert.ok(documentData.lastModified, `${documentName} should have lastModified`); 461 | 462 | // Validate lastModified is a valid date string 463 | assert.ok(!isNaN(Date.parse(documentData.lastModified)), 464 | `${documentName} lastModified should be valid date`); 465 | 466 | // Validate filename corresponds to document 467 | const filenameBase = documentData.filename.toLowerCase().replace(/\.md$/, ''); 468 | const docNameBase = documentName.toLowerCase(); 469 | assert.ok(filenameBase.includes(docNameBase) || docNameBase.includes(filenameBase), 470 | `${documentName} filename should correspond to document name`); 471 | } 472 | ``` -------------------------------------------------------------------------------- /docs/dw_net/HTTPClient.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.net 2 | 3 | # Class HTTPClient 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.net.HTTPClient 9 | 10 | ## Description 11 | 12 | The HTTPClient class supports the HTTP methods GET, POST, HEAD, PUT, PATCH, OPTIONS, and DELETE. If a secure connection via HTTPS is established the used server certificate or the signing CAs certificate needs to be imported into the customer key store via Business Manager. Note: when this class is used with sensitive data, be careful in persisting sensitive information. Key selection for mutual TLS: Check if there is an explicit identity requested setIdentity(KeyRef) Else, Check if there is a mapping for hostname in the keystore Deprecated: Select an arbitrary private key from the keystore var httpClient : HTTPClient = new HTTPClient(); var message : String; httpClient.open('GET', 'http://www.myinstance.com/feed.xml'); httpClient.setTimeout(3000); httpClient.send(); if (httpClient.statusCode == 200) { message = httpClient.text; } else { // error handling message="An error occurred with status code "+httpClient.statusCode; } 13 | 14 | ## Constants 15 | 16 | ### DEFAULT_GET_FILE_SIZE 17 | 18 | **Type:** Number = 5242880 19 | 20 | The default size for sendAndReceiveToFile() returning a File is 5MB deprecated in favor of MAX_GET_FILE_SIZE 21 | 22 | ### MAX_GET_FILE_SIZE 23 | 24 | **Type:** Number = 209715200 25 | 26 | The maximum permitted size (in bytes) of an HTTP response when calling operations which write the response to file. (200MB) 27 | 28 | ### MAX_GET_MEM_SIZE 29 | 30 | **Type:** Number = 10485760 31 | 32 | The maximum permitted size (in bytes) of an HTTP response when calling operations which store the response in memory. (10MB) 33 | 34 | ## Properties 35 | 36 | ### allowRedirect 37 | 38 | **Type:** boolean 39 | 40 | Determines whether redirect handling is enabled. 41 | 42 | ### allResponseHeaders 43 | 44 | **Type:** HashMap (Read Only) 45 | 46 | All response headers as a map containing the name and value of the response header. 47 | 48 | ### bytes 49 | 50 | **Type:** Bytes (Read Only) 51 | 52 | The bytes in the message body for HTTP status codes between 200 and 299. 53 | 54 | ### errorBytes 55 | 56 | **Type:** Bytes (Read Only) 57 | 58 | The returned message body as bytes for HTTP status code greater or equal to 400. Error messages are not 59 | written to the response file. 60 | 61 | ### errorText 62 | 63 | **Type:** String (Read Only) 64 | 65 | The returned message body as text for HTTP status code greater or equal to 400. Error messages are not 66 | written to the response file. 67 | 68 | ### hostNameVerification 69 | 70 | **Type:** boolean 71 | 72 | Determines whether host name verification is enabled. 73 | 74 | ### identity 75 | 76 | **Type:** KeyRef 77 | 78 | Gets the identity used for mutual TLS (mTLS). 79 | 80 | ### responseHeaders 81 | 82 | **Type:** Map (Read Only) 83 | 84 | All response headers as a map in which each entry represents an individual header. The key of the entry 85 | holds the header name and the entry value holds a list of all header values. 86 | 87 | ### statusCode 88 | 89 | **Type:** Number (Read Only) 90 | 91 | The status code of the last HTTP operation. 92 | 93 | ### statusMessage 94 | 95 | **Type:** String (Read Only) 96 | 97 | The message text of the last HTTP operation. 98 | 99 | ### text 100 | 101 | **Type:** String (Read Only) 102 | 103 | The returned message body as text for HTTP status codes between 200 and 299. 104 | 105 | ### timeout 106 | 107 | **Type:** Number 108 | 109 | The timeout for this client, in milliseconds. 110 | 111 | ## Constructor Summary 112 | 113 | HTTPClient() 114 | 115 | ## Method Summary 116 | 117 | ### enableCaching 118 | 119 | **Signature:** `enableCaching(ttl : Number) : void` 120 | 121 | Calling this method enables caching for GET requests. 122 | 123 | ### getAllowRedirect 124 | 125 | **Signature:** `getAllowRedirect() : boolean` 126 | 127 | Determines whether redirect handling is enabled. 128 | 129 | ### getAllResponseHeaders 130 | 131 | **Signature:** `getAllResponseHeaders() : HashMap` 132 | 133 | Returns all response headers as a map containing the name and value of the response header. 134 | 135 | ### getBytes 136 | 137 | **Signature:** `getBytes() : Bytes` 138 | 139 | Returns the bytes in the message body for HTTP status codes between 200 and 299. 140 | 141 | ### getErrorBytes 142 | 143 | **Signature:** `getErrorBytes() : Bytes` 144 | 145 | Returns the returned message body as bytes for HTTP status code greater or equal to 400. 146 | 147 | ### getErrorText 148 | 149 | **Signature:** `getErrorText() : String` 150 | 151 | Returns the returned message body as text for HTTP status code greater or equal to 400. 152 | 153 | ### getHostNameVerification 154 | 155 | **Signature:** `getHostNameVerification() : boolean` 156 | 157 | Determines whether host name verification is enabled. 158 | 159 | ### getIdentity 160 | 161 | **Signature:** `getIdentity() : KeyRef` 162 | 163 | Gets the identity used for mutual TLS (mTLS). 164 | 165 | ### getResponseHeader 166 | 167 | **Signature:** `getResponseHeader(header : String) : String` 168 | 169 | Returns a specific response header from the last HTTP operation. 170 | 171 | ### getResponseHeaders 172 | 173 | **Signature:** `getResponseHeaders(name : String) : List` 174 | 175 | Returns all the values of a response header from the last HTTP operation as a list of strings. 176 | 177 | ### getResponseHeaders 178 | 179 | **Signature:** `getResponseHeaders() : Map` 180 | 181 | Returns all response headers as a map in which each entry represents an individual header. 182 | 183 | ### getStatusCode 184 | 185 | **Signature:** `getStatusCode() : Number` 186 | 187 | Returns the status code of the last HTTP operation. 188 | 189 | ### getStatusMessage 190 | 191 | **Signature:** `getStatusMessage() : String` 192 | 193 | Returns the message text of the last HTTP operation. 194 | 195 | ### getText 196 | 197 | **Signature:** `getText() : String` 198 | 199 | Returns the returned message body as text for HTTP status codes between 200 and 299. 200 | 201 | ### getText 202 | 203 | **Signature:** `getText(encoding : String) : String` 204 | 205 | Returns the returned message body as text for HTTP status codes between 200 and 299. 206 | 207 | ### getTimeout 208 | 209 | **Signature:** `getTimeout() : Number` 210 | 211 | Returns the timeout for this client, in milliseconds. 212 | 213 | ### open 214 | 215 | **Signature:** `open(method : String, url : String) : void` 216 | 217 | Opens the specified URL using the specified method. 218 | 219 | ### open 220 | 221 | **Signature:** `open(method : String, url : String, async : boolean, user : String, password : String) : void` 222 | 223 | Deprecated method. 224 | 225 | ### open 226 | 227 | **Signature:** `open(method : String, url : String, user : String, password : String) : void` 228 | 229 | Opens the specified URL with the in parameter method specified Http method with given credentials [user, password] using HTTP basic authentication. 230 | 231 | ### send 232 | 233 | **Signature:** `send() : void` 234 | 235 | Sends an HTTP request. 236 | 237 | ### send 238 | 239 | **Signature:** `send(text : String) : void` 240 | 241 | This method performs the actual HTTP communication. 242 | 243 | ### send 244 | 245 | **Signature:** `send(text : String, encoding : String) : void` 246 | 247 | This method performs the actual HTTP communication. 248 | 249 | ### send 250 | 251 | **Signature:** `send(file : File) : void` 252 | 253 | This method performs the actual HTTP communication. 254 | 255 | ### sendAndReceiveToFile 256 | 257 | **Signature:** `sendAndReceiveToFile(file : File) : boolean` 258 | 259 | This method performs the actual HTTP communication. 260 | 261 | ### sendAndReceiveToFile 262 | 263 | **Signature:** `sendAndReceiveToFile(text : String, outFile : File) : boolean` 264 | 265 | This method performs the actual HTTP communication. 266 | 267 | ### sendAndReceiveToFile 268 | 269 | **Signature:** `sendAndReceiveToFile(text : String, encoding : String, outFile : File) : boolean` 270 | 271 | This method performs the actual HTTP communication. 272 | 273 | ### sendBytes 274 | 275 | **Signature:** `sendBytes(body : Bytes) : void` 276 | 277 | This method performs the actual HTTP communication. 278 | 279 | ### sendBytesAndReceiveToFile 280 | 281 | **Signature:** `sendBytesAndReceiveToFile(body : Bytes, outFile : File) : boolean` 282 | 283 | This method performs the actual HTTP communication. 284 | 285 | ### sendMultiPart 286 | 287 | **Signature:** `sendMultiPart(parts : HTTPRequestPart...) : boolean` 288 | 289 | Sends a multipart HTTP request. 290 | 291 | ### setAllowRedirect 292 | 293 | **Signature:** `setAllowRedirect(allowRedirect : boolean) : void` 294 | 295 | Sets whether automatic HTTP redirect handling is enabled. 296 | 297 | ### setHostNameVerification 298 | 299 | **Signature:** `setHostNameVerification(enable : boolean) : void` 300 | 301 | Sets whether certificate host name verification is enabled. 302 | 303 | ### setIdentity 304 | 305 | **Signature:** `setIdentity(keyRef : KeyRef) : void` 306 | 307 | Sets the identity (private key) to use when mutual TLS (mTLS) is configured. 308 | 309 | ### setRequestHeader 310 | 311 | **Signature:** `setRequestHeader(key : String, value : String) : void` 312 | 313 | Sets a request header for the next HTTP operation. 314 | 315 | ### setTimeout 316 | 317 | **Signature:** `setTimeout(timeoutMillis : Number) : void` 318 | 319 | Sets the timeout for connections made with this client to the given number of milliseconds. 320 | 321 | ## Constructor Detail 322 | 323 | ## Method Detail 324 | 325 | ## Method Details 326 | 327 | ### enableCaching 328 | 329 | **Signature:** `enableCaching(ttl : Number) : void` 330 | 331 | **Description:** Calling this method enables caching for GET requests. It basically means that a response is cached, and before making a request the HTTP client looks into the cache to determine whether the response is already available. Only responses with a status code of 2xx, with a content length, with a size less than 50k, and which are not intended to be immediately written to a file are cached. The provided parameter defines the TTL (time to live) for the cached content. A value of 0 disables caching. The URL and the username are used as cache keys. The total size of the cacheable content and the number of cached items is limited and automatically managed by the system. Cache control information send by the remote server is ignored. Caching HTTP responses should be done very carefully. It is important to ensure that the response really depends only on the URL and doesn't contain any remote state information or time information which is independent of the URL. It is also important to verify that the application sends exactly the same URL multiple times. 332 | 333 | **Parameters:** 334 | 335 | - `ttl`: the TTL for the cached content in secs 336 | 337 | --- 338 | 339 | ### getAllowRedirect 340 | 341 | **Signature:** `getAllowRedirect() : boolean` 342 | 343 | **Description:** Determines whether redirect handling is enabled. 344 | 345 | **Returns:** 346 | 347 | true if redirect handling is enabled, false otherwise. 348 | 349 | --- 350 | 351 | ### getAllResponseHeaders 352 | 353 | **Signature:** `getAllResponseHeaders() : HashMap` 354 | 355 | **Description:** Returns all response headers as a map containing the name and value of the response header. 356 | 357 | **Deprecated:** 358 | 359 | Use getResponseHeaders() instead. 360 | 361 | **Returns:** 362 | 363 | a map containing the names and corresponding values of the response headers. 364 | 365 | --- 366 | 367 | ### getBytes 368 | 369 | **Signature:** `getBytes() : Bytes` 370 | 371 | **Description:** Returns the bytes in the message body for HTTP status codes between 200 and 299. 372 | 373 | **Returns:** 374 | 375 | the returned message body as bytes. 376 | 377 | --- 378 | 379 | ### getErrorBytes 380 | 381 | **Signature:** `getErrorBytes() : Bytes` 382 | 383 | **Description:** Returns the returned message body as bytes for HTTP status code greater or equal to 400. Error messages are not written to the response file. 384 | 385 | **Returns:** 386 | 387 | the returned message body as bytes. 388 | 389 | --- 390 | 391 | ### getErrorText 392 | 393 | **Signature:** `getErrorText() : String` 394 | 395 | **Description:** Returns the returned message body as text for HTTP status code greater or equal to 400. Error messages are not written to the response file. 396 | 397 | **Returns:** 398 | 399 | the returned message body as text. 400 | 401 | --- 402 | 403 | ### getHostNameVerification 404 | 405 | **Signature:** `getHostNameVerification() : boolean` 406 | 407 | **Description:** Determines whether host name verification is enabled. 408 | 409 | **Returns:** 410 | 411 | true if verification is enabled, false otherwise 412 | 413 | --- 414 | 415 | ### getIdentity 416 | 417 | **Signature:** `getIdentity() : KeyRef` 418 | 419 | **Description:** Gets the identity used for mutual TLS (mTLS). 420 | 421 | **Returns:** 422 | 423 | Reference to the private key, or null if not configured 424 | 425 | --- 426 | 427 | ### getResponseHeader 428 | 429 | **Signature:** `getResponseHeader(header : String) : String` 430 | 431 | **Description:** Returns a specific response header from the last HTTP operation. The method returns null if the specific header was not returned. 432 | 433 | **Parameters:** 434 | 435 | - `header`: the name of the response header to locate. 436 | 437 | **Returns:** 438 | 439 | the value of the specified header or null if the header cannot be found. 440 | 441 | --- 442 | 443 | ### getResponseHeaders 444 | 445 | **Signature:** `getResponseHeaders(name : String) : List` 446 | 447 | **Description:** Returns all the values of a response header from the last HTTP operation as a list of strings. This reflects the fact that a specific header, e.g. "Set-Cookie", may be set multiple times. In case there is no such header, the method returns an empty list. 448 | 449 | **Parameters:** 450 | 451 | - `name`: The name of the response header to locate. 452 | 453 | **Returns:** 454 | 455 | The values of the specified header as a list of strings or an empty list if the header cannot be found. 456 | 457 | --- 458 | 459 | ### getResponseHeaders 460 | 461 | **Signature:** `getResponseHeaders() : Map` 462 | 463 | **Description:** Returns all response headers as a map in which each entry represents an individual header. The key of the entry holds the header name and the entry value holds a list of all header values. 464 | 465 | **Returns:** 466 | 467 | A map containing the names and corresponding values of the response headers. 468 | 469 | --- 470 | 471 | ### getStatusCode 472 | 473 | **Signature:** `getStatusCode() : Number` 474 | 475 | **Description:** Returns the status code of the last HTTP operation. 476 | 477 | **Returns:** 478 | 479 | the status code of the last HTTP operation. 480 | 481 | --- 482 | 483 | ### getStatusMessage 484 | 485 | **Signature:** `getStatusMessage() : String` 486 | 487 | **Description:** Returns the message text of the last HTTP operation. 488 | 489 | **Returns:** 490 | 491 | the message text of the last HTTP operation. 492 | 493 | --- 494 | 495 | ### getText 496 | 497 | **Signature:** `getText() : String` 498 | 499 | **Description:** Returns the returned message body as text for HTTP status codes between 200 and 299. 500 | 501 | **Returns:** 502 | 503 | the returned message body as text. 504 | 505 | --- 506 | 507 | ### getText 508 | 509 | **Signature:** `getText(encoding : String) : String` 510 | 511 | **Description:** Returns the returned message body as text for HTTP status codes between 200 and 299. 512 | 513 | **Parameters:** 514 | 515 | - `encoding`: the character encoding to use. 516 | 517 | **Returns:** 518 | 519 | String the encoded String. 520 | 521 | --- 522 | 523 | ### getTimeout 524 | 525 | **Signature:** `getTimeout() : Number` 526 | 527 | **Description:** Returns the timeout for this client, in milliseconds. 528 | 529 | **Returns:** 530 | 531 | the timeout in milliseconds 532 | 533 | --- 534 | 535 | ### open 536 | 537 | **Signature:** `open(method : String, url : String) : void` 538 | 539 | **Description:** Opens the specified URL using the specified method. The following methods are supported: GET, POST, HEAD, PUT, PATCH, OPTIONS, and DELETE 540 | 541 | **Parameters:** 542 | 543 | - `method`: the method to use for opening the URL. 544 | - `url`: the url to open. 545 | 546 | --- 547 | 548 | ### open 549 | 550 | **Signature:** `open(method : String, url : String, async : boolean, user : String, password : String) : void` 551 | 552 | **Description:** Deprecated method. 553 | 554 | **Deprecated:** 555 | 556 | Use open(String, String, String, String) instead. 557 | 558 | **Parameters:** 559 | 560 | - `method`: the method to use for opening the URL. 561 | - `url`: the url to open. 562 | - `async`: true if asynchronous. 563 | - `user`: name of the user. 564 | - `password`: password. 565 | 566 | --- 567 | 568 | ### open 569 | 570 | **Signature:** `open(method : String, url : String, user : String, password : String) : void` 571 | 572 | **Description:** Opens the specified URL with the in parameter method specified Http method with given credentials [user, password] using HTTP basic authentication. The following methods are supported: GET, POST, HEAD, PUT, PATCH, OPTIONS, and DELETE 573 | 574 | **Parameters:** 575 | 576 | - `method`: HTTP method 577 | - `url`: the url 578 | - `user`: name of the user 579 | - `password`: password 580 | 581 | --- 582 | 583 | ### send 584 | 585 | **Signature:** `send() : void` 586 | 587 | **Description:** Sends an HTTP request. 588 | 589 | --- 590 | 591 | ### send 592 | 593 | **Signature:** `send(text : String) : void` 594 | 595 | **Description:** This method performs the actual HTTP communication. The text is sent as a request body. If the text is null no data will be sent to the HTTP server. 596 | 597 | **Parameters:** 598 | 599 | - `text`: text String to be sent as request body. 600 | 601 | --- 602 | 603 | ### send 604 | 605 | **Signature:** `send(text : String, encoding : String) : void` 606 | 607 | **Description:** This method performs the actual HTTP communication. The text is sent as a request body. If the text is null no data will be sent to the HTTP server. 608 | 609 | **Parameters:** 610 | 611 | - `text`: text String to be sent as request body. 612 | - `encoding`: character encoding name. 613 | 614 | --- 615 | 616 | ### send 617 | 618 | **Signature:** `send(file : File) : void` 619 | 620 | **Description:** This method performs the actual HTTP communication. Sends the file to the HTTP server. The file content is sent as a request body and is sent "as-is" (text or binary). 621 | 622 | **Parameters:** 623 | 624 | - `file`: File to be sent. 625 | 626 | --- 627 | 628 | ### sendAndReceiveToFile 629 | 630 | **Signature:** `sendAndReceiveToFile(file : File) : boolean` 631 | 632 | **Description:** This method performs the actual HTTP communication. If the file is null no data will be sent to the HTTP server. If this method is used with a GET then the file parameter will contain the contents retrieved. When using this method with a PUT/POST then the contents of the file parameter will be sent to the server. 633 | 634 | **Parameters:** 635 | 636 | - `file`: local file used to read from or write to, depending on the method used. 637 | 638 | **Returns:** 639 | 640 | true if the returned code was a positive status code 641 | 642 | --- 643 | 644 | ### sendAndReceiveToFile 645 | 646 | **Signature:** `sendAndReceiveToFile(text : String, outFile : File) : boolean` 647 | 648 | **Description:** This method performs the actual HTTP communication. If the text is null no data will be sent to the HTTP server. 649 | 650 | **Parameters:** 651 | 652 | - `text`: text String to be sent. 653 | - `outFile`: local file to write to. 654 | 655 | **Returns:** 656 | 657 | true if the returned code was a positive status code 658 | 659 | --- 660 | 661 | ### sendAndReceiveToFile 662 | 663 | **Signature:** `sendAndReceiveToFile(text : String, encoding : String, outFile : File) : boolean` 664 | 665 | **Description:** This method performs the actual HTTP communication. If the text is null no data will be sent to the HTTP server. 666 | 667 | **Parameters:** 668 | 669 | - `text`: text String to be sent. 670 | - `encoding`: character encoding name. 671 | - `outFile`: local file to write to. 672 | 673 | **Returns:** 674 | 675 | true if the returned code was a positive status code 676 | 677 | --- 678 | 679 | ### sendBytes 680 | 681 | **Signature:** `sendBytes(body : Bytes) : void` 682 | 683 | **Description:** This method performs the actual HTTP communication. The bytes are sent as a request body. If the bytes are null no data will be sent to the HTTP server. 684 | 685 | **Parameters:** 686 | 687 | - `body`: Bytes to be sent as request body. 688 | 689 | --- 690 | 691 | ### sendBytesAndReceiveToFile 692 | 693 | **Signature:** `sendBytesAndReceiveToFile(body : Bytes, outFile : File) : boolean` 694 | 695 | **Description:** This method performs the actual HTTP communication. If the body is null no data will be sent to the HTTP server. 696 | 697 | **Parameters:** 698 | 699 | - `body`: Bytes to be sent. 700 | - `outFile`: local file to write to. 701 | 702 | **Returns:** 703 | 704 | true if the returned code was a positive status code 705 | 706 | **Throws:** 707 | 708 | - IOException 709 | 710 | --- 711 | 712 | ### sendMultiPart 713 | 714 | **Signature:** `sendMultiPart(parts : HTTPRequestPart...) : boolean` 715 | 716 | **Description:** Sends a multipart HTTP request. This method should only be called if the connection to the remote URL was opened with a POST or PATCH method. All other methods will result in an exception being thrown. The request is constructed from the passed array of parts. 717 | 718 | **Parameters:** 719 | 720 | - `parts`: List of part objects representing either string or file parts. 721 | 722 | **Returns:** 723 | 724 | true if the returned code was a positive status code. 725 | 726 | --- 727 | 728 | ### setAllowRedirect 729 | 730 | **Signature:** `setAllowRedirect(allowRedirect : boolean) : void` 731 | 732 | **Description:** Sets whether automatic HTTP redirect handling is enabled. The default value is true. Set it to false to disable all redirects. 733 | 734 | **Parameters:** 735 | 736 | - `allowRedirect`: true or false for enabling or disabling automatic HTTP redirect 737 | 738 | --- 739 | 740 | ### setHostNameVerification 741 | 742 | **Signature:** `setHostNameVerification(enable : boolean) : void` 743 | 744 | **Description:** Sets whether certificate host name verification is enabled. The default value is true. Set it to false to disable host name verification. 745 | 746 | **Parameters:** 747 | 748 | - `enable`: true to enable host name verification or false to disable it. 749 | 750 | --- 751 | 752 | ### setIdentity 753 | 754 | **Signature:** `setIdentity(keyRef : KeyRef) : void` 755 | 756 | **Description:** Sets the identity (private key) to use when mutual TLS (mTLS) is configured. If this is not set and mTLS is used then the private key will be chosen from the key store based on the host name. If this is set to a reference named "__NONE__" then no private key will be used even if one is requested by the remote server. 757 | 758 | **Parameters:** 759 | 760 | - `keyRef`: Reference to the private key 761 | 762 | --- 763 | 764 | ### setRequestHeader 765 | 766 | **Signature:** `setRequestHeader(key : String, value : String) : void` 767 | 768 | **Description:** Sets a request header for the next HTTP operation. 769 | 770 | **Parameters:** 771 | 772 | - `key`: the request header. 773 | - `value`: the request headers' value. 774 | 775 | --- 776 | 777 | ### setTimeout 778 | 779 | **Signature:** `setTimeout(timeoutMillis : Number) : void` 780 | 781 | **Description:** Sets the timeout for connections made with this client to the given number of milliseconds. If the given timeout is less than or equal to zero, the timeout is set to a maximum value of 2 or 15 minutes, depending on the context. This timeout value controls both the "connection timeout" (how long it takes to connect to the remote host) and the "socket timeout" (how long, after connecting, it will wait without any data being read). Therefore, in the worst case scenario, the total time of inactivity could be twice as long as the specified value. The maximum timeout is 15 minutes when the client is used in a job, and 2 minutes otherwise. The default timeout for a new client is the maximum timeout value. This method can be called at any time, and will affect the next connection made with this client. It is not possible to set the timeout for an open connection. You should always set a reasonable timeout (e.g., a few seconds). Allowing connections to run long can result in thread exhaustion. 782 | 783 | **Parameters:** 784 | 785 | - `timeoutMillis`: timeout, in milliseconds, up to a maximum of 2 or 15 minutes, depending on the context. 786 | 787 | --- ``` -------------------------------------------------------------------------------- /docs-site/pages/ConfigurationPage.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | import SEO from '../components/SEO'; 4 | import BreadcrumbSchema from '../components/BreadcrumbSchema'; 5 | import StructuredData from '../components/StructuredData'; 6 | import { PageSubtitle, H2, H3 } from '../components/Typography'; 7 | import CodeBlock, { InlineCode } from '../components/CodeBlock'; 8 | import ConfigHero from '../components/ConfigHero'; 9 | import ConfigModeTabs from '../components/ConfigModeTabs'; 10 | import ConfigBuilder from '../components/ConfigBuilder'; 11 | import { SITE_DATES } from '../constants'; 12 | import Collapsible from '../components/Collapsible'; 13 | 14 | const ConfigurationPage: React.FC = () => { 15 | const configurationStructuredData = { 16 | "@context": "https://schema.org", 17 | "@type": "TechArticle", 18 | "headline": "Configuration Guide - SFCC Development MCP Server", 19 | "description": "Complete configuration guide for SFCC Development MCP Server. Learn dw.json setup, environment variables, operating modes, authentication, security best practices, and how to enable logs, job logs, system & custom objects, site preferences, and code versions.", 20 | "author": { 21 | "@type": "Person", 22 | "name": "Thomas Theunen" 23 | }, 24 | "publisher": { 25 | "@type": "Person", 26 | "name": "Thomas Theunen" 27 | }, 28 | "datePublished": SITE_DATES.PUBLISHED, 29 | "dateModified": SITE_DATES.MODIFIED, 30 | "url": "https://sfcc-mcp-dev.rhino-inquisitor.com/configuration/", 31 | "about": [ 32 | { 33 | "@type": "SoftwareApplication", 34 | "name": "SFCC Development MCP Server", 35 | "applicationCategory": "DeveloperApplication", 36 | "operatingSystem": "Node.js", 37 | "offers": { 38 | "@type": "Offer", 39 | "price": "0", 40 | "priceCurrency": "USD", 41 | "availability": "https://schema.org/InStock" 42 | } 43 | } 44 | ] 45 | }; 46 | 47 | return ( 48 | <div className="max-w-6xl mx-auto px-6 py-8"> 49 | <SEO 50 | title="Configuration Guide" 51 | description="Complete configuration guide for SFCC Development MCP Server. Learn dw.json setup, environment variables, operating modes, authentication, security best practices, and how to enable logs, job logs, system & custom objects, site preferences, and code versions." 52 | keywords="SFCC MCP configuration, dw.json setup, SFCC authentication, OCAPI credentials, WebDAV configuration, Commerce Cloud API setup, SFCC development environment" 53 | canonical="/configuration/" 54 | ogType="article" 55 | /> 56 | <BreadcrumbSchema items={[ 57 | { name: "Home", url: "/" }, 58 | { name: "Configuration", url: "/configuration/" } 59 | ]} /> 60 | <StructuredData structuredData={configurationStructuredData} /> 61 | 62 | <ConfigHero /> 63 | <div className="mb-16 bg-gradient-to-r from-blue-50 via-indigo-50 to-purple-50 rounded-2xl p-8 shadow-xl border border-blue-100"> 64 | <div className="text-center mb-10"> 65 | <H2 id="quick-start" className="text-3xl font-bold mb-3">🚀 Quick Start</H2> 66 | <p className="text-gray-700 max-w-3xl mx-auto text-lg">Pick a mode, generate <InlineCode>dw.json</InlineCode>, run the server. Minimal friction, fast feedback.</p> 67 | </div> 68 | <ConfigModeTabs /> 69 | </div> 70 | <div className="mb-20 bg-gradient-to-r from-emerald-50 via-teal-50 to-cyan-50 rounded-2xl p-8 shadow-xl border border-emerald-100"> 71 | <div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6 mb-6"> 72 | <div> 73 | <H2 id="interactive-config" className="text-2xl font-bold mb-2">🧪 Interactive dw.json Builder</H2> 74 | <p className="text-sm text-gray-600 max-w-xl">Start minimal (only <InlineCode>hostname</InlineCode> + <InlineCode>username/password</InlineCode> for runtime & job logs). Add Data API credentials when you need system & custom objects, site preferences or code version tooling.</p> 75 | </div> 76 | <div className="rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-xs text-emerald-800 max-w-sm"> 77 | <p className="font-semibold mb-1">Tips</p> 78 | <ul className="list-disc pl-4 space-y-1"> 79 | <li>Keep separate <InlineCode>dw.dev.json</InlineCode> / <InlineCode>dw.qa.json</InlineCode></li> 80 | <li>Use env vars to override secrets in CI</li> 81 | <li>Remove unused OAuth fields to stay minimal</li> 82 | </ul> 83 | </div> 84 | </div> 85 | <ConfigBuilder /> 86 | </div> 87 | <div className="space-y-20 mb-20"> 88 | <div className="space-y-16"> 89 | <section id="data-api" className="scroll-mt-24"> 90 | <div className="mb-4 space-y-2"> 91 | <span className="inline-block bg-yellow-100 text-yellow-700 text-[11px] font-semibold px-3 py-1 rounded-full tracking-wide uppercase">Optional – Enables Data API Tools</span> 92 | <h2 id="data-api-configuration" className="text-2xl font-bold text-gray-900">🔧 Data API Configuration</h2> 93 | </div> 94 | <p className="text-gray-600 mb-6 text-sm">Required for system object definitions, custom object attribute searches, site preferences & code version tools. Not required for runtime logs or job log analysis (basic auth only). Skip entirely if you only need docs + log visibility.</p> 95 | <div className="space-y-6"> 96 | <Collapsible id="api-client" title="Step 1: Create API Client (Account Manager)" intent="info" defaultOpen> 97 | <ol className="list-decimal list-inside space-y-1"> 98 | <li>Login to <strong>Account Manager</strong> (not Business Manager)</li> 99 | <li>Navigate to <strong>API Client</strong> → Add API Client</li> 100 | <li>Set name (e.g. <InlineCode>SFCC Dev MCP</InlineCode>) & generate password</li> 101 | <li>Select <strong>SFCC</strong> scope and grant required roles</li> 102 | </ol> 103 | </Collapsible> 104 | <Collapsible id="bm-settings" title="Step 2: Business Manager Data API Settings" intent="warn"> 105 | <ol className="list-decimal list-inside space-y-1 mb-4"> 106 | <li>Business Manager → Administration → Site Development → Open Commerce API Settings</li> 107 | <li>Open the <strong>Data API</strong> tab</li> 108 | <li>Add client with required resources (below)</li> 109 | </ol> 110 | <CodeBlock language="json" code={`{\n \"_v\": \"23.2\",\n \"clients\": [{\n \"client_id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n \"resources\": [\n { \"resource_id\": \"/system_object_definitions\", \"methods\": [\"get\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" },\n { \"resource_id\": \"/system_object_definitions/*\", \"methods\": [\"get\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" },\n { \"resource_id\": \"/system_object_definition_search\", \"methods\": [\"post\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" },\n { \"resource_id\": \"/system_object_definitions/*/attribute_definition_search\", \"methods\": [\"post\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" },\n { \"resource_id\": \"/system_object_definitions/*/attribute_group_search\", \"methods\": [\"post\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" },\n { \"resource_id\": \"/custom_object_definitions/*/attribute_definition_search\", \"methods\": [\"post\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" },\n { \"resource_id\": \"/site_preferences/preference_groups/*/*/preference_search\", \"methods\": [\"post\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" },\n { \"resource_id\": \"/code_versions\", \"methods\": [\"get\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" },\n { \"resource_id\": \"/code_versions/*\", \"methods\": [\"get\", \"patch\"], \"read_attributes\": \"(**)\", \"write_attributes\": \"(**)\" }\n ]\n }]\n}\n`} /> 111 | <ul className="text-xs text-green-700 bg-green-50 border border-green-200 rounded-lg p-4 mt-4 space-y-1"> 112 | <li><strong>Client ID</strong> must match your credentials</li> 113 | <li>Allow <InlineCode>get</InlineCode>/<InlineCode>post</InlineCode> for search + retrieval (and <InlineCode>patch</InlineCode> for code version activation)</li> 114 | <li><InlineCode>(**)</InlineCode> attributes needed for broad introspection</li> 115 | </ul> 116 | </Collapsible> 117 | <Collapsible id="update-config" title="Step 3: Update dw.json" intent="plain"> 118 | <CodeBlock language="json" code={`{\n \"hostname\": \"your-instance.sandbox.us01.dx.commercecloud.salesforce.com\",\n \"username\": \"your-username\",\n \"password\": \"your-password\",\n \"client-id\": \"your-ocapi-client-id\",\n \"client-secret\": \"your-ocapi-client-secret\"\n}`} /> 119 | <p className="text-[11px] text-gray-500 -mt-3">Add <InlineCode>client-id</InlineCode>/<InlineCode>client-secret</InlineCode> only when you need Data API tooling (system & custom objects, site preferences, code versions). Omit for docs + log visibility only.</p> 120 | </Collapsible> 121 | <Collapsible id="data-api-troubleshooting" title="Troubleshooting Data API Access" intent="danger"> 122 | <ul className="list-disc pl-5 text-sm space-y-2"> 123 | <li><strong>403</strong>: Missing scope/role – verify Account Manager roles + BM resource mapping</li> 124 | <li><strong>401</strong>: Credential mismatch – regenerate client secret</li> 125 | <li><strong>Missing resource</strong>: Pattern mismatch – copy exact resource IDs above</li> 126 | </ul> 127 | <CodeBlock language="bash" code={`npx sfcc-dev-mcp --dw-json /Users/username/sfcc-project/dw.json --debug true`} /> 128 | </Collapsible> 129 | </div> 130 | <p className="text-[11px] text-gray-500 mt-2">Total surface: 36+ specialized tools spanning documentation, best practices, SFRA, cartridge generation, runtime logs, job logs, system & custom objects, site preferences, and code versions.</p> 131 | </section> 132 | <section id="security" className="scroll-mt-24 space-y-6"> 133 | <div className="mb-4 space-y-2"> 134 | <span className="inline-block bg-blue-100 text-blue-700 text-[11px] font-semibold px-3 py-1 rounded-full tracking-wide uppercase">Protect Credentials</span> 135 | <h2 id="security-basics" className="text-2xl font-bold text-gray-900">🔒 Security Basics</h2> 136 | </div> 137 | <div className="grid md:grid-cols-2 gap-6"> 138 | <div className="rounded-xl border border-gray-200 bg-white p-5"> 139 | <h4 className="font-semibold text-sm mb-3">.gitignore</h4> 140 | <CodeBlock language="bash" code={`echo 'dw.json' >> .gitignore\necho '*.dw.json' >> .gitignore`} /> 141 | </div> 142 | <div className="rounded-xl border border-gray-200 bg-white p-5"> 143 | <h4 className="font-semibold text-sm mb-3">Environment Overrides</h4> 144 | <CodeBlock language="bash" code={`export SFCC_CLIENT_SECRET=\"your-secret\"\nexport SFCC_PASSWORD=\"your-password\"\n\nnpx sfcc-dev-mcp --dw-json /Users/username/sfcc-project/dw.json`} /> 145 | </div> 146 | <div className="rounded-xl border border-gray-200 bg-white p-5"> 147 | <h4 className="font-semibold text-sm mb-3">Permissions</h4> 148 | <CodeBlock language="bash" code={`chmod 600 dw.json\nls -la dw.json`} /> 149 | </div> 150 | <div className="rounded-xl border border-gray-200 bg-white p-5"> 151 | <h4 className="font-semibold text-sm mb-3">Debug Mode</h4> 152 | <CodeBlock language="bash" code={`npx sfcc-dev-mcp --dw-json /Users/username/sfcc-project/dw.json --debug true`} /> 153 | </div> 154 | </div> 155 | </section> 156 | <section id="reference" className="scroll-mt-24 space-y-8"> 157 | <div className="mb-4 space-y-2"> 158 | <span className="inline-block bg-purple-100 text-purple-700 text-[11px] font-semibold px-3 py-1 rounded-full tracking-wide uppercase">Reference</span> 159 | <h2 id="key-reference-tables" className="text-2xl font-bold text-gray-900">📋 Key Reference Tables</h2> 160 | </div> 161 | <div> 162 | <H3 id="field-reference" className="text-lg font-semibold mb-3">Supported dw.json Fields</H3> 163 | <div className="overflow-x-auto text-sm"> 164 | <table className="min-w-full bg-white border border-gray-200 rounded-lg"> 165 | <thead className="bg-gray-50"> 166 | <tr> 167 | <th className="px-3 py-2 text-left font-semibold text-gray-900 border-b">Field</th> 168 | <th className="px-3 py-2 text-left font-semibold text-gray-900 border-b">Required For</th> 169 | <th className="px-3 py-2 text-left font-semibold text-gray-900 border-b">Notes</th> 170 | </tr> 171 | </thead> 172 | <tbody className="divide-y divide-gray-200"> 173 | <tr> 174 | <td className="px-3 py-2 font-mono text-xs">hostname</td> 175 | <td className="px-3 py-2 text-xs">All authenticated tools</td> 176 | <td className="px-3 py-2 text-xs">Sandbox domain</td> 177 | </tr> 178 | <tr> 179 | <td className="px-3 py-2 font-mono text-xs">username/password</td> 180 | <td className="px-3 py-2 text-xs">Logs & Job Logs (WebDAV)</td> 181 | <td className="px-3 py-2 text-xs">WebDAV auth (runtime + job log files)</td> 182 | </tr> 183 | <tr> 184 | <td className="px-3 py-2 font-mono text-xs">client-id/secret</td> 185 | <td className="px-3 py-2 text-xs">System & custom objects, site prefs, code versions</td> 186 | <td className="px-3 py-2 text-xs">OCAPI Data API + code version management</td> 187 | </tr> 188 | <tr> 189 | <td className="px-3 py-2 font-mono text-xs">code-version</td> 190 | <td className="px-3 py-2 text-xs">Code version operations</td> 191 | <td className="px-3 py-2 text-xs">Optional</td> 192 | </tr> 193 | <tr> 194 | <td className="px-3 py-2 font-mono text-xs">site-id</td> 195 | <td className="px-3 py-2 text-xs">Site-specific actions</td> 196 | <td className="px-3 py-2 text-xs">Optional</td> 197 | </tr> 198 | </tbody> 199 | </table> 200 | </div> 201 | </div> 202 | <div> 203 | <H3 id="tool-availability" className="text-lg font-semibold mb-3">Tool Availability by Mode</H3> 204 | <div className="overflow-x-auto text-sm"> 205 | <table className="min-w-full bg-white border border-gray-200 rounded-lg"> 206 | <thead className="bg-gray-50"> 207 | <tr> 208 | <th className="px-3 py-2 text-left font-semibold text-gray-900 border-b">Category</th> 209 | <th className="px-3 py-2 text-left font-semibold text-gray-900 border-b">Docs Only</th> 210 | <th className="px-3 py-2 text-left font-semibold text-gray-900 border-b">Full Mode</th> 211 | </tr> 212 | </thead> 213 | <tbody className="divide-y divide-gray-200"> 214 | <tr> 215 | <td className="px-3 py-2 text-xs font-medium">Documentation</td> 216 | <td className="px-3 py-2 text-xs">✔</td> 217 | <td className="px-3 py-2 text-xs">✔</td> 218 | </tr> 219 | <tr> 220 | <td className="px-3 py-2 text-xs font-medium">Best Practices</td> 221 | <td className="px-3 py-2 text-xs">✔</td> 222 | <td className="px-3 py-2 text-xs">✔</td> 223 | </tr> 224 | <tr> 225 | <td className="px-3 py-2 text-xs font-medium">SFRA Docs</td> 226 | <td className="px-3 py-2 text-xs">✔</td> 227 | <td className="px-3 py-2 text-xs">✔</td> 228 | </tr> 229 | <tr> 230 | <td className="px-3 py-2 text-xs font-medium">Cartridge Generation</td> 231 | <td className="px-3 py-2 text-xs">✔</td> 232 | <td className="px-3 py-2 text-xs">✔</td> 233 | </tr> 234 | <tr> 235 | <td className="px-3 py-2 text-xs font-medium">Log Analysis (runtime)</td> 236 | <td className="px-3 py-2 text-xs">—</td> 237 | <td className="px-3 py-2 text-xs">✔</td> 238 | </tr> 239 | <tr> 240 | <td className="px-3 py-2 text-xs font-medium">Job Logs</td> 241 | <td className="px-3 py-2 text-xs">—</td> 242 | <td className="px-3 py-2 text-xs">✔</td> 243 | </tr> 244 | <tr> 245 | <td className="px-3 py-2 text-xs font-medium">System & Custom Objects / Site Prefs</td> 246 | <td className="px-3 py-2 text-xs">—</td> 247 | <td className="px-3 py-2 text-xs">✔</td> 248 | </tr> 249 | <tr> 250 | <td className="px-3 py-2 text-xs font-medium">Code Versions</td> 251 | <td className="px-3 py-2 text-xs">—</td> 252 | <td className="px-3 py-2 text-xs">✔</td> 253 | </tr> 254 | </tbody> 255 | </table> 256 | </div> 257 | </div> 258 | </section> 259 | <section id="troubleshooting" className="scroll-mt-24 space-y-6"> 260 | <div className="mb-4 space-y-2"> 261 | <span className="inline-block bg-red-100 text-red-700 text-[11px] font-semibold px-3 py-1 rounded-full tracking-wide uppercase">Common Issues</span> 262 | <h2 id="troubleshooting" className="text-2xl font-bold text-gray-900">🐛 Troubleshooting</h2> 263 | </div> 264 | <div className="grid md:grid-cols-3 gap-5"> 265 | <div className="bg-white border border-gray-200 rounded-xl p-4"> 266 | <h4 className="font-semibold text-sm mb-2">Auth Failed</h4> 267 | <ul className="text-xs text-gray-600 space-y-1 list-disc pl-4"> 268 | <li>Verify hostname format</li> 269 | <li>Check username/password</li> 270 | <li>Match client-id/secret</li> 271 | <li>Run with <InlineCode>--debug</InlineCode></li> 272 | </ul> 273 | </div> 274 | <div className="bg-white border border-gray-200 rounded-xl p-4"> 275 | <h4 className="font-semibold text-sm mb-2">dw.json Missing</h4> 276 | <ul className="text-xs text-gray-600 space-y-1 list-disc pl-4"> 277 | <li>Add <InlineCode>--dw-json /Users/username/sfcc-project/dw.json</InlineCode></li> 278 | <li>Create file in current dir</li> 279 | <li>Use env vars instead</li> 280 | <li>Check permissions (600)</li> 281 | </ul> 282 | </div> 283 | <div className="bg-white border border-gray-200 rounded-xl p-4"> 284 | <h4 className="font-semibold text-sm mb-2">System Object Denied</h4> 285 | <ul className="text-xs text-gray-600 space-y-1 list-disc pl-4"> 286 | <li>Complete Data API config</li> 287 | <li>Verify resource IDs</li> 288 | <li>Ensure (**) attributes</li> 289 | <li>Check client roles</li> 290 | </ul> 291 | </div> 292 | </div> 293 | </section> 294 | </div> 295 | </div> 296 | <div className="mb-24"> 297 | <div className="text-center mb-8"> 298 | <h2 id="next-steps" className="text-2xl font-bold text-gray-900 mb-2">🔗 Next Steps</h2> 299 | <PageSubtitle className="text-base text-gray-600">Pick your next path—feature surface or concrete tool list.</PageSubtitle> 300 | </div> 301 | <div className="flex flex-col sm:flex-row gap-4 justify-center"> 302 | <NavLink to="/features/" className="group bg-gradient-to-r from-blue-600 to-purple-600 text-white px-8 py-4 rounded-xl font-semibold text-lg shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 no-underline hover:no-underline focus:no-underline"> 303 | Features Overview 304 | <span className="ml-2 group-hover:translate-x-1 inline-block transition-transform">→</span> 305 | </NavLink> 306 | <NavLink to="/tools/" className="border-2 border-gray-300 text-gray-700 px-8 py-4 rounded-xl font-semibold text-lg hover:border-blue-500 hover:text-blue-600 transition-all duration-300 no-underline hover:no-underline focus:no-underline"> 307 | Available Tools 308 | </NavLink> 309 | </div> 310 | </div> 311 | </div> 312 | ); 313 | }; 314 | 315 | export default ConfigurationPage; 316 | ```