The IProductOrderGET Apex interface provides extensibility for TMF622 GET operations through Apex pre- and post-hooks and GraphQL query customization. Implementations can validate incoming requests, modify the primary read query, and refine or overwrite the response to support business-specific retrieval rules while maintaining TMF-compliant behavior.
This interface supports the following hooks.
transformRequest
applyCustomValidations
customiseGraphQLQuery
handlePostOperation
Note
The configureDefaultValidations hook is not used for Product Order GET. getDefaultValidationSpec() returns {}. If implemented, return the map unchanged.
Retrieval Lifecycle Use Cases and Hook Mapping
This table lists common use cases and the hooks required for the GET operation.
Use Case
Hook(s)
Description
Example Scenarios
Benefit
Resolve a legacy or alias order id
transformRequest
Replace context["id"] with the canonical Order identifier before GraphQL construction.
• Map LEGACY-* / PoNumber to Order.Id • Accept a partner order id and resolve OrderNumber
Fetch the canonical Order without changing the client URL contract
Stash an entitlement flag
transformRequest
Put extra keys on the shared context map for later hooks.
• isPremiumTenant for validation and post-processing
Cross-hook coordination without extra round-trips
Register a custom sub-resource
transformRequest, customiseGraphQLQuery
Put customSubResourceMappings on context and inject the matching GraphQL node. Both steps are required.
• Serve GET /productOrder/{id}/orderAdjustmentGroup
Extend GET with org-specific child resources
Reject the request with a business rule
applyCustomValidations
Only GET hook that surfaces an HTTP error. Return top-level validationStatus = "fail".
• Cap filter criteria • Forbid jeopardy-alert fields for non-premium tenants
• Add NegotiatedPrice__c { value } at the root Order
Shape the primary read without forking the handler
Filter or sort nested order items
customiseGraphQLQuery
addFilters / orderBy on productOrder.productOrderItem. URL sort still wins over Apex orderBy.
• Quantity greater than 1 • EffectiveDate DESC
One API supports channel-specific query shape
Overwrite the GET body
handlePostOperation
GET applies the return when payloadOverwritten is true. Single GET: Map. List GET: recordList or a raw List of maps.
• Add customStatusLabel • Drop Cancelled rows from list GET
Refine or filter the client-visible TMF payload
transformRequest
This hook transforms the request context before processing any operation. It is invoked early in the request lifecycle, allowing implementers to adjust the context for subsequent extensibility hooks.
Mutating context["id"] is written back via updateIdFromContext. If the Apex result throws, the exception is caught and logged as WARNING — the original id is kept and GET continues.
1global static Map<String, Object> transformRequest(Map<String, Object> context) {2 if (context == null) {3 return context;4 }56 context.put('isPremiumTenant', checkPremiumEntitlement());78 String requestedId = (String) context.get('id');9 if (requestedId != null && requestedId.startsWith('LEGACY-')) {10 Order o = [SELECT Id FROM Order WHERE PoNumber = :requestedId LIMIT 1];11 context.put('id', o.Id);12 }13 return context;14}
Caveat. Appending to context["fields"] does not change the primary GraphQL query. This handler hardcodes the query in buildPrimaryGraphQLQuery. Use customiseGraphQLQuery.
applyCustomValidations
This hook validates custom business logic before retrieving a Product Order. If validation fails, it rejects the request and returns an error response.
The handler processes return values as follows.
Success
Return a map with validationStatus set to any value other than "fail" (case-insensitive), or an empty map.
The API request continues normally.
Other fields in the map are logged but not used.
Failure
Return a map with validationStatus set to "fail" (case-insensitive).
The API request is terminated immediately.
Raise a ValidationException containing:
message: value from validationMessage (or “Validation failed” by default)
details: value from validationDetails (optional statusCode / errorCode)
This hook modifies the primary GraphQL read query before execution. GET is the only Product Order verb that builds an initial read query.
Return {"nodes": [ QueryTransformationNode, ... ]}. The graphQLAsMap argument is {"query": "<base query>"} — informational only. There is no supported way to hand back a rewritten query string.
Node shape
1{2 "path": "productOrder.productOrderItem",3 "addFields": ["CustomField__c { value }"],4 "addFilters": { "clearExistingFilters": false, "filterGraphQlSnippet": "Status: {eq: \"Active\"}" },5 "orderBy": { "clearExistingOrderBy": false, "orderGraphQlSnippet": "CreatedDate: {order: DESC}" },6 "insertGraphQlSnippet": "customField: CustomObject__r { Id Name { value } }"7}
path addresses aliases, not Salesforce relationship names. Addressable paths:
An unmatched path silently produces no transformation for that node (WARNING log). Auxiliary queries (jeopardy alerts, order-item attributes, relationships, tax lines) run insideexecuteGraphQL after this hook and cannot be transformed here.
1global static Map<String, Object> customiseGraphQLQuery(2 Map<String, Object> graphQLAsMap, Map<String, Object> context) {3 return new Map<String, Object>{4 'nodes' => new List<Object>{5 new Map<String, Object>{6 'path' => 'productOrder',7 'addFields' => new List<Object>{ 'NegotiatedPrice__c { value }' }8 },9 new Map<String, Object>{10 'path' => 'productOrder.productOrderItem',11 'addFilters' => new Map<String, Object>{12 'clearExistingFilters' => false,13 'filterGraphQlSnippet' => 'Quantity: {gt: 1}'14 }15 }16 }17 };18}
handlePostOperation
This hook post-processes the API response after GraphQL execution and TMF conversion. Unlike POST, PATCH, and DELETE, GET applies this return value when payloadOverwritten is true.
Parameters
graphQLQueryResultAsMap — raw GraphQL {data, errors, extensions}
constructedTMFResponse — already-TMF622-shaped output. Single GET: flat map. List GET: {"recordList": [...]}.
1global class ProductOrderGETExtension implements comms_apex_ext.IProductOrderGET {23 global static Map<String, Object> transformRequest(Map<String, Object> context) {4 context.put('isPremiumTenant', checkPremiumEntitlement());5 String requestedId = (String) context.get('id');6 if (requestedId != null && requestedId.startsWith('LEGACY-')) {7 Order o = [SELECT Id FROM Order WHERE PoNumber = :requestedId LIMIT 1];8 context.put('id', o.Id);9 }10 return context;11 }1213 global static Map<String, Boolean> configureDefaultValidations(14 Map<String, Boolean> defaultValidationConfiguration,15 Map<String, Object> context) {16 return defaultValidationConfiguration;17 }1819 global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {20 Boolean isPremium = (Boolean) context.get('isPremiumTenant');21 List<Object> fields = (List<Object>) context.get('fields');22 if (isPremium != Boolean.TRUE && fields != null23 && fields.contains('productorderjeopardyalert')) {24 return new Map<String, Object>{25 'validationStatus' => 'fail',26 'validationMessage' => 'Jeopardy alerts require a premium entitlement.',27 'validationDetails' => new Map<String, Object>{ 'statusCode' => 'FORBIDDEN' }28 };29 }30 return new Map<String, Object>();31 }3233 global static Map<String, Object> customiseGraphQLQuery(34 Map<String, Object> graphQLAsMap, Map<String, Object> context) {35 return new Map<String, Object>{36 'nodes' => new List<Object>{37 new Map<String, Object>{38 'path' => 'productOrder',39 'addFields' => new List<Object>{ 'NegotiatedPrice__c { value }' }40 }41 }42 };43 }4445 global static Map<String, Object> handlePostOperation(46 Map<String, Object> graphQLQueryResultAsMap,47 Map<String, Object> constructedTMFResponse,48 Map<String, Object> context) {49 System.debug('ProductOrder GET by ' + UserInfo.getName());50 return new Map<String, Object>{ 'payloadOverwritten' => false };51 }52}
GraphQL Query Transformations
Query transformations enhance GraphQL queries by adding fields, applying filters, or modifying sort order, while preserving the original query definition.
Note
For Product Order GET, use handler aliases such as productOrder and productOrder.productOrderItem. Do not use Salesforce relationship names or uiapi.query.Order.
Query Enhancements: Add Fields
Original Query (GraphQL): primary Order selection from buildPrimaryGraphQLQuery.