IOrganizationPartyManagementGET Apex Interface

The IOrganizationPartyManagementGET Apex interface provides extensibility for GET operations through Apex pre- and post-hooks and GraphQL query customization. Implementations can validate incoming requests, modify query structures, and refine the response to support business-specific retrieval rules while maintaining TMF-compliant behavior.

This interface supports the following hooks.

Retrieve Operation Use Cases 

Use CaseHook(s)DescriptionExample ScenariosBenefit
External-ID ResolutiontransformRequestTransforms a legacy or external identifier into the canonical resource ID expected by the retrieve operation and adds it to the shared request context as { id }.- Resolve a legacy customer key to a Contact ID
- Map a partner-system identifier to { id }
- Normalize identifiers from migrated systems
- Translate an account-specific reference into the canonical ID
Allows legacy clients and external integrations to retrieve resources using familiar identifiers without changing the shared API contract.
Role-Based or Regional Access ControlapplyCustomValidationsApplies authorization and data-governance rules before the query executes. Access can be evaluated using the caller’s role, region, business unit, channel, or other request context.- Restrict access to customers outside the user’s assigned region
- Permit only supervisors to retrieve high-value customer records
- Block partner channels from accessing internal-only resources
- Enforce business-unit data boundaries
Enforces data-governance policies and prevents unauthorized access to protected records.
Toggle or Add Validation FlagsconfigureDefaultValidationsEnables, disables, or extends built-in validation flags for retrieve requests according to organization-specific policies.- Require record-existence validation
- Enable stricter identifier validation
- Disable checks that do not apply to trusted internal channels
- Apply different validation policies by tenant or environment
Allows each organization to relax or tighten retrieval checks without modifying the shared implementation.
Add Custom Fields or Server-Side FilterscustomiseGraphQLQueryModifies the GraphQL query before execution by adding custom fields, relationships, filters, or server-side constraints.- Add organization-specific Contact fields
- Include entitlement or customer-hierarchy data
- Apply regional, tenant, or lifecycle-status filters
- Request a reduced field set for mobile clients
Returns richer, client-specific data without changing the base schema or introducing additional API versions.
Response EnrichmenthandlePostOperationProcesses the query result before it is returned to the client. It can add calculated values, derived fields, audit metadata, or information from related systems.- Add a calculated customer health score
- Include retrieval audit metadata
- Flag preferred or high-risk customers
- Add aggregated financial, service, or usage insights
Delivers derived fields and audit metadata while preserving the underlying API schema.

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.

Hook Method 

Map<String, Object> transformRequest(Map<String, Object> context)

Sample Apex Implementation 

Resolve an external ID to a Salesforce Contact ID.

1global class OrganizationGetTransformRequestExt implements comms_apex_ext.IOrganizationPartyManagementGET {
2    global static Map<String, Object> transformRequest(Map<String, Object> context) {
3        if (context == null) {
4            return context;
5        }
6
7
8        Object idObj = context.get('id');
9        if (!(idObj instanceof String)) {
10            return context;
11        }
12        String idValue = (String) idObj;
13
14
15        // A 15- or 18-character value already looks like a Salesforce Id; leave it as-is.
16        if (idValue.length() == 15 || idValue.length() == 18) {
17            return context;
18        }
19
20
21        // Otherwise treat it as an external key and resolve it to an Account Id.
22        List<Account> accounts = [
23            SELECT Id FROM Account WHERE External_Id__c = :idValue LIMIT 1
24        ];
25        if (!accounts.isEmpty()) {
26            context.put('id', accounts[0].Id);
27        }
28        return context;
29    }
30    // ... other hooks return null / empty map ...
31}

configureDefaultValidations 

This hook enables, disables, or modifies the built-in validation rules before they execute.

Hook Method 

Map<String, Boolean> configureDefaultValidations(Map<String, Boolean> defaultValidationConfiguration, Map<String, Object> context)

Sample Apex Implementation 

Add a custom flag consumed by applyCustomValidations.

1global class OrganizationGetConfigValidationsExt implements comms_apex_ext.IOrganizationPartyManagementGET {
2    global static Map<String, Boolean> configureDefaultValidations(
3        Map<String, Boolean> defaultValidationConfiguration,
4        Map<String, Object> context
5    ) {
6        Map<String, Boolean> overrides = defaultValidationConfiguration != null
7            ? defaultValidationConfiguration.clone()
8            : new Map<String, Boolean>();
9        overrides.put('segmentAccessCheck', true);   // your own flag, read in applyCustomValidations
10        return overrides;
11    }
12    // ... other hooks return null / empty map ...
13}

applyCustomValidations 

This hook validates custom business logic before retrieving customer record. 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 “pass” (case-insensitive).
  • 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 key (or “Validation failed” by default)
    • details: value from validationDetails key (optional)
  • The client receives an HTTP 400 error response.

Hook Method 

Map<String, Object> applyCustomValidations(Map<String, Object> context)

Sample Apex Implementation 

Role-based/regional access control.

1global class OrganizationGetRbacExt implements comms_apex_ext.IOrganizationPartyManagementGET {
2    global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {
3        String organizationId = (String) context.get('id');
4        Map<String, Object> result = new Map<String, Object>();
5        try {
6            User currentUser = [SELECT Id, Profile.Name FROM User WHERE Id = :UserInfo.getUserId() LIMIT 1];
7            if ('Customer Service Rep'.equals(currentUser.Profile.Name) && organizationId != null) {
8                Account a = [SELECT Id, BillingCountry FROM Account WHERE Id = :organizationId LIMIT 1];
9                if (!'US'.equals(a.BillingCountry)) {
10                    result.put('validationStatus', 'fail');
11                    result.put('validationMessage', 'User does not have access to organizations in this region');
12                    result.put('validationDetails', new Map<String, Object>{
13                        'statusCode' => 'BAD_REQUEST',
14                        'errorCode'  => 'REGION_RESTRICTION'
15                    });
16                }
17            }
18        } catch (Exception e) {
19            result.put('validationStatus', 'fail');
20            result.put('validationMessage', 'Validation error: ' + e.getMessage());
21        }
22        return result;   // empty => proceed; validationStatus="fail" => HTTP 400
23    }
24    // ... other hooks return null / empty map ...
25}

customiseGraphQLQuery 

This hook modifies the GraphQL query before execution to add fields, filters, or transformations.

Hook Method 

Map<String, Object> customiseGraphQLQuery(Map<String, Object> graphQLAsMap, Map<String, Object> context)

Sample Apex Implementation 

Add custom fields and a filter: For the Organization GET, target the organizationData, which represents the Account node.

1global class OrganizationGetCustomGraphQLExt implements comms_apex_ext.IOrganizationPartyManagementGET {
2    global static Map<String, Object> customiseGraphQLQuery(
3        Map<String, Object> graphQLAsMap,
4        Map<String, Object> context
5    ) {
6        List<Map<String, Object>> nodes = new List<Map<String, Object>>();
7        // Add custom Account fields.
8        nodes.add(new Map<String, Object>{
9            'path'      => 'organizationData',
10            'addFields' => new List<String>{ 'Industry', 'AnnualRevenue' }
11        });
12
13        // Only return active accounts (append to any existing WHERE clause).
14        nodes.add(new Map<String, Object>{
15            'path' => 'organizationData',
16            'addFilters' => new Map<String, Object>{
17                'clearExistingFilters' => false,
18                'filterGraphQlSnippet' => '{ Active__c: { eq: true } }'
19            }
20        });
21
22        return new Map<String, Object>{ 'nodes' => nodes };
23    }
24    // ... other hooks return null / empty map ...
25}

handlePostOperation 

This hook post-processes and transforms the API response after data retrieval.

Hook Method 

Map<String, Object> handlePostOperation(Map<String, Object> graphQLQueryResultAsMap, Map<String, Object> constructedTMFResponse, Map<String, Object> context)

Sample Apex Implementation 

Enrich the response in place.

1global class OrganizationGetEnrichExt implements comms_apex_ext.IOrganizationPartyManagementGET {
2    global static Map<String, Object> handlePostOperation(
3        Map<String, Object> graphQLQueryResultAsMap,
4        Map<String, Object> constructedTMFResponse,
5        Map<String, Object> context
6    ) {
7        if (constructedTMFResponse != null) {
8            constructedTMFResponse.put('retrievedBy', UserInfo.getName());
9            constructedTMFResponse.put('lastRetrievedAt',
10                DateTime.now().format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\''));
11        }
12        // Returning null keeps the mutated constructedTMFResponse as the final payload.
13        return null;
14    }
15    // ... other hooks return null / empty map ...
16}

Full Implementation Example 

Here’s a complete sample Apex implementation using all supported hooks.

1/**
2 * Complete TMF632 Organization (Party Management) GET API extension,
3 * demonstrating all five lifecycle hooks with business logic.
4 */
5global class OrganizationPartyManagementGETExtension implements comms_apex_ext.IOrganizationPartyManagementGET {
6
7
8    // Hook 1: normalize / resolve the request context.
9    global static Map<String, Object> transformRequest(Map<String, Object> context) {
10        if (context != null) {
11            context.put('transformedBy', 'apex');
12        }
13        return context;
14    }
15
16
17    // Hook 2: keep default validations unchanged (GET has none).
18    global static Map<String, Boolean> configureDefaultValidations(
19        Map<String, Boolean> defaultValidationConfiguration,
20        Map<String, Object> context
21    ) {
22        return new Map<String, Boolean>();
23    }
24
25
26    // Hook 3: block requests for disallowed fields.
27    global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {
28        if (context != null && context.get('fields') instanceof List<Object>) {
29            List<Object> fields = (List<Object>) context.get('fields');
30            if (fields != null && fields.contains('invalidField')) {
31                return new Map<String, Object>{
32                    'validationStatus'  => 'fail',
33                    'validationMessage' => 'Invalid field requested'
34                };
35            }
36        }
37        return new Map<String, Object>();
38    }
39
40
41    // Hook 4: add custom fields + an active-only filter.
42    global static Map<String, Object> customiseGraphQLQuery(
43        Map<String, Object> graphQLAsMap,
44        Map<String, Object> context
45    ) {
46        List<Map<String, Object>> nodes = new List<Map<String, Object>>();
47        nodes.add(new Map<String, Object>{
48            'path'      => 'organizationData',
49            'addFields' => new List<String>{ 'Industry', 'AnnualRevenue' }
50        });
51        nodes.add(new Map<String, Object>{
52            'path' => 'organizationData',
53            'addFilters' => new Map<String, Object>{
54                'clearExistingFilters' => false,
55                'filterGraphQlSnippet' => '{ Active__c: { eq: true } }'
56            }
57        });
58        return new Map<String, Object>{ 'nodes' => nodes };
59    }
60
61
62    // Hook 5: enrich the response in place.
63    global static Map<String, Object> handlePostOperation(
64        Map<String, Object> graphQLQueryResultAsMap,
65        Map<String, Object> constructedTMFResponse,
66        Map<String, Object> context
67    ) {
68        if (constructedTMFResponse != null) {
69            constructedTMFResponse.put('retrievedBy', UserInfo.getName());
70        }
71        return null;
72    }
73}

Response Structure 

Single Organization Response (GraphQL) 

1{
2  "id": "001xx000003DHP",
3  "href": "/connect/comms/partymanagement/v5/organization/001xx000003DHP",
4  "@type": "Organization",
5  "@baseType": "Party",
6  "name": "Acme Corporation",
7  "organizationType": "Customer",
8  "contactMedium": [
9    {
10      "@type": "GeographicAddressContactMedium",
11      "street1": "1 Market St",
12      "city": "San Francisco",
13      "stateOrProvince": "CA",
14      "postCode": "94105",
15      "country": "US"
16    },
17    { "@type": "PhoneContactMedium", "phoneNumber": "+1-555-0100" },
18    { "@type": "FaxContactMedium", "faxNumber": "+1-555-0101" }
19  ],
20  "relatedParty": [
21    {
22      "@type": "RelatedPartyRefOrPartyRoleRef",
23      "role": "Decision Maker",
24      "partyOrPartyRole": {
25        "id": "003xx000004MZZ",
26        "href": "/connect/comms/partymanagement/v5/individual/003xx000004MZZ",
27        "name": "John Smith",
28        "@type": "PartyRef",
29        "@referredType": "Individual"
30      }
31    },
32    {
33      "@type": "RelatedPartyRefOrPartyRoleRef",
34      "role": "Supplier",
35      "partyOrPartyRole": {
36        "id": "001xx000003DaB",
37        "href": "/connect/comms/partymanagement/v5/organization/001xx000003DaB",
38        "name": "Globex Supplies",
39        "@type": "PartyRef",
40        "@referredType": "Organization"
41      }
42    }
43  ],
44  "organizationParentRelationship": {
45    "@type": "OrganizationParentRelationship",
46    "organization": {
47      "id": "001xx000003DEF",
48      "href": "/connect/comms/partymanagement/v5/organization/001xx000003DEF",
49      "name": "Acme Holdings",
50      "@type": "OrganizationRef"
51    }
52  },
53  "organizationChildRelationship": [
54    {
55      "@type": "OrganizationChildRelationship",
56      "organization": {
57        "id": "001xx000003DXY",
58        "href": "/connect/comms/partymanagement/v5/organization/001xx000003DXY",
59        "name": "Acme West",
60        "@type": "OrganizationRef"
61      }
62    }
63  ]
64}

List Response (GraphQL) 

Default (without pageInfo) — a bare JSON array of Organization objects:

1[
2  {
3    "id": "001xx000003DHP",
4    "href": "/connect/comms/partymanagement/v5/organization/001xx000003DHP",
5    "@type": "Organization",
6    "@baseType": "Party",
7    "name": "Acme Corporation",
8    "organizationType": "Customer"
9  },
10  {
11    "id": "001xx000003DaB",
12    "href": "/connect/comms/partymanagement/v5/organization/001xx000003DaB",
13    "@type": "Organization",
14    "@baseType": "Party",
15    "name": "Globex Supplies",
16    "organizationType": "Partner"
17  }
18]

GraphQL Query Transformations 

Query transformations provide the ability to enhance GraphQL queries by adding fields, applying filters, or modifying the sort order, while preserving the original query definition.

Query Enhancements: Fields and Filters 

Original Query (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5}
6}

Transformation Instructions (JSON):

1{
2"nodes": [
3{
4"path": "myAccounts",
5"addFields": ["email", "phone"],
6"addFilters": {
7"clearExistingFilters": true,
8"filterGraphQlSnippet": "{ status: { eq: \"ACTIVE\" } }"
9}
10}
11]
12}

Result (GraphQL):

1query {
2myAccounts: accounts(where: { status: { eq: "ACTIVE" } }) {
3id
4name
5email
6phone
7}
8}

Query Enhancements: Fields, Filters, and Ordering 

Original Query (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5}
6}

Transformation Instructions (JSON):

1{
2"nodes": [
3{
4"path": "myAccounts",
5"addFields": ["revenue", "industry"],
6"addFilters": {
7"clearExistingFilters": true,
8"filterGraphQlSnippet": "{
9and: [
10{ status: { eq: \"ACTIVE\" } },
11{ revenue: { gte: 1000000 } }
12]
13}"
14},
15"orderBy": {
16"clearExistingOrderBy": true,
17"orderGraphQlSnippet": "{ revenue: { order: DESC } }"
18}
19}
20]
21}

Result (GraphQL):

1query {
2myAccounts: accounts(
3where: {
4and: [
5{ status: { eq: "ACTIVE" } },
6{ revenue: { gte: 1000000 } }
7]
8},
9orderBy: { revenue: { order: DESC } }
10) {
11id
12name
13revenue
14industry
15}
16}

Query Enhancements: Multiple Query Paths with Different Transformations 

Original Query (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5myContacts: contacts {
6id
7name
8}
9}
10}

Transformation Instructions (JSON):

1{
2"nodes": [
3{
4"path": "myAccounts",
5"addFields": ["email"],
6"addFilters": {
7"clearExistingFilters": true,
8"filterGraphQlSnippet": "{ status: { eq: \"ACTIVE\" } }"
9}
10},
11{
12"path": "myAccounts.myContacts",
13"addFields": ["email", "phone"],
14"orderBy": {
15"clearExistingOrderBy": true,
16"orderGraphQlSnippet": "{ name: { order: ASC } }"
17}
18}
19]
20}

Result (GraphQL):

1query {
2myAccounts: accounts(where: { status: { eq: "ACTIVE" } }) {
3id
4name
5email
6myContacts: contacts(orderBy: { name: { order: ASC } }) {
7id
8name
9email
10phone
11}
12}
13}

Query Enhancements: Fields and Snippets 

Original Query (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5}
6}

Transformation Instructions (JSON):

1{
2"nodes": [
3{
4"path": "myAccounts",
5"addFields": ["email"]
6},
7{
8"path": "metrics",
9"insertGraphQlSnippet": "{
10metrics: systemMetrics {
11totalCount
12activeCount
13lastSyncTime
14}
15}"
16}
17]
18}

Result (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5email
6}
7metrics: systemMetrics {
8totalCount
9activeCount
10lastSyncTime
11}
12}