Monday, October 25, 2021

Adobe Experience Manager(AEM) Content Translation - Deep Dive(Part1)

The website translation is the process of taking your website content in its original language(e.g en) and adapting it into other languages e.g es, to make it is accessible and useable across global customers. Content Translation allows you to create an initial version of a page based on an existing version from a different language. Automate the translation of page content, assets, etc to create and maintain multilingual websites through the translation process.

The Adobe Experience Manager Translation Framework enables website content managers to easily submit content for professional translation and automatically receive translations back in AEM. All that tedious manual effort is replaced by a streamlined process that delivers personalized customer experiences.

Most of these contents are already part Adobe documents, thought of sharing my learning for reference and also if it helps someone with the same need.

There are two types of content translations

Human Translation:


Content is sent to your translation provider and translated by professional translators. When complete, the translated content is returned and imported into AEM. When your translation provider is integrated with AEM(translation provider enables the connector to integrate AEM and TMS(Translation Management System) system to automate the translation process), content is automatically sent between AEM and the translation provider.

Machine Translation:


The machine translation service immediately translates your content. The content is shared with Translation Management Systems through connectors (APIs) and the translated content is immediately sent back to AEM through the same connector API. There is no Human step involved the end-to-end translation process is automated through the connector. The translation time is very less and the end-to-end process completes quickly.

Machine translation is generally used where the quality-level requirement is not as stringent as where the human translation is required. Use machine translation where the quantity of content, speed of content creation, and budget make it impossible to use human translation.

Translation Integration Framework:


AEM provides Translation Integration Framework to orchestrate the translation process between AEM and the TMS system, the Translation Integration Framework integrates with third-party translation services to orchestrate the translation of AEM content. The Translation Integration Framework can be configured based on our translation needs, also TIF helps AEM to integrate with the TMS system to translate the content/assets.

By default, AEM TIF supports 

Human Translation
  • Export the content for translation 
  • Translate the content
  • Import the translated content back to AEM
The TIF framework supports different content formats for importing/exporting the content -  XML, XLIF 1.2, XLIF 2.0, and JSON. The connector specific to vendors can provide the support for Human translations - import/export the content based on the custom TMS needs

Machine Translation

AEM includes the capability to connect to Microsoft Translator(Trial) by default. Multiple other vendors provide support(Connectors) to translate the AEM content.

Refer to the below diagram for overall all orchestrated translation process flow

AEM Content Translation

TIF Configurations:


The default TIF configuration specifies how to translate your content. The configuration includes the following information:
  • Which translation service provider to use.
  • Whether human or machine translation is to be performed.
  • Whether to translate other content that is associated with a page or asset, such as tags.
Tools -- Cloud Services -- Translation Cloud Services

AEM Content Translation

libs -- Translation Integration -- Default Configuration

AEM Content Translation

Modify the site/asset specific configurations(the editable configuration is editable only for the first time after same should be managed through /con/global)

AEM Content Translation


AEM Content Translation


AEM Content Translation

AEM Content Translation

Unselect "Translate Assets" to stop translating asset binaries.

On save, the new framework configuration is enabled under /con/global - applicable for all contexts

AEM Content Translation

 Context-specific configurations can be enabled under the required configuration folder - override for a specific context

AEM Content Translation


Refer to https://experienceleague.adobe.com/docs/experience-manager-cloud-service/sites/administering/reusing-content/translation/integration-framework.html?lang=en to understand more on the specific configurations. (the translation configurations are applied based on Context-aware configuration)

Export formats:


As we discussed by default the Translation Integration Framework export the content in XML format but the framework supports XLIF 1.2, XLIF 2.0, and JSON formats. The export format can be configured through http://localhost:4502/system/console/configMgr/com.adobe.cq.wcm.translation.impl.TranslationPlatformConfigurationImpl configuration.

AEM Content Translation

Change the configuration based on the need - the OSGI configuration should be enabled through code, the translation content will be exported in a format based on this configuration.

The XLIF export will not be supported by default, you will be receiving the below exception while enabling XLIF transport.

Caused by: com.day.cq.workflow.WorkflowException: Error while translating language copy for /content/projects/test/jcr:content/dashboard/gadgets/translationjob.
at com.adobe.cq.wcm.translation.impl.process.SyncTranslationProcess.execute(SyncTranslationProcess.java:136) [com.day.cq.wcm.cq-wcm-translation:1.6.102]
at com.day.cq.workflow.compatibility.CQWorkflowProcessRunner.execute(CQWorkflowProcessRunner.java:93) [com.day.cq.workflow.cq-workflow-impl:6.3.16]
... 9 common frames omitted
Caused by: com.adobe.granite.translation.api.TranslationException: Failed to perform :START_EXPORT
at com.adobe.cq.wcm.translation.impl.TranslationPodImpl.syncTranslation(TranslationPodImpl.java:2566) [com.day.cq.wcm.cq-wcm-translation:1.6.102]
at com.adobe.cq.wcm.translation.impl.process.SyncTranslationProcess.syncTranslationJob(SyncTranslationProcess.java:171) [com.day.cq.wcm.cq-wcm-translation:1.6.102]
at com.adobe.cq.wcm.translation.impl.process.SyncTranslationProcess.execute(SyncTranslationProcess.java:131) [com.day.cq.wcm.cq-wcm-translation:1.6.102]
... 10 common frames omitted
Caused by: com.adobe.granite.translation.api.xliff.TranslationXLIFFServiceException: XLIFF service is not available
at com.adobe.cq.wcm.translation.impl.TranslationObjectImpl.exportToXLIFFString(TranslationObjectImpl.java:883) [com.day.cq.wcm.cq-wcm-translation:1.6.102]
at com.adobe.cq.wcm.translation.impl.TranslationObjectImpl.exportFileForTranslation(TranslationObjectImpl.java:944) [com.day.cq.wcm.cq-wcm-translation:1.6.102]
at com.adobe.cq.wcm.translation.impl.TranslationPodImpl.startExportNow(TranslationPodImpl.java:948) [com.day.cq.wcm.cq-wcm-translation:1.6.102]
at com.adobe.cq.wcm.translation.impl.TranslationPodImpl.syncTranslation(TranslationPodImpl.java:2512) [com.day.cq.wcm.cq-wcm-translation:1.6.102]
... 12 common frames omitted


Translation Integration Configuration:


AEM provides Translation Configuration UI to manage the content translation rules to control the properties and references that will get translated. Translation rules identify content in AEM to be extracted for translation.  Out-of-the-box translation rules cover common use cases such as Text components and alt text for Image components. Depending on a project's translation requirements additional rules may be needed. In general translation rules allow users to specify:
  • Properties that should be translated based on path and/or resource type
  • Filters for properties that should NOT be translated
  • Referenced content that should be translated (i.e Images or Content Fragments)
The default rules cover the out-of-the-box components and properties but custom rule configuration is required to handle the translation for the custom components.

The UI can be accessed through, Tools -- General -- Translation Configuration

AEM Content Translation

 
AEM Content Translation


The configuration rules are stored as an XML file, the default file is available under /libs/settings/translation/rules/translation_rules.xml, the file will be stored under /conf/global/settings/translation/rules/translation_rules.xml on the first modification(the file under /libs/ should not be directly modified)

AEM Content Translation

For the versions prior to 6.3, the file is stored under /etc/workflow/models/translation/translation_rules.xml - the UI is not available for the AEM versions prior to AEM 6.3

Let us now see how the custom components properties can be enabled for translation - the custom components properties(nonstandard) are not considered by Translation Framework for sending to translation.

I have a custom component with some standard properties and some custom, the standard properties e.g text are by default considered for translation but the custom properties e.g testProp not considered for translation.
AEM Content Translation


AEM Content Translation


AEM Content Translation

Under "General"

AEM Content Translation


Now enable the configuration for custom component properties, the filters and references can be added if required

Click on "Add Component", enter component resource path "mysite/components/helloworld"

AEM Content Translation


AEM Content Translation

Now add the new property under the custom component section, add the new property and save the rule

AEM Content Translation

Now the new properties will be exported for translation. The other contexts(/content/dam and /content/forms) can be based on the need, also a new context can be added if required.

AEM Content Translation


Additional Languages for Translation:


The original list of supported languages for translation in AEM under  /libs/wcm/core/resources/languages 

AEM Content Translation

Overlay /libs/wcm/core/resources/languages to /apps(/apps/wcm/core/resources/languages) (manage the changes through code)

AEM Content Translation

Copy the language node from /libs/wcm/core/resources/languages and paste to /apps/wcm/core/resources/languages(remove the old node and rename the new node to language)
 
Enable the additional languages(copy-paste the individual node and modify the values)

AEM Content Translation

Update the OSGi configurations -Day CQ WCM Language Manager with the new location

AEM Content Translation

Restart the AEM instance, Now the new language will be available while creating a translation project to translate the content(let us see more details on Translation Projects in upcoming blogs)

AEM Content Translation



Let us see in the next blog how to use the Translation Framework to translate the content. - Adobe Experience Manager(AEM) Content Translation - Deep Dive(Part2)


References:


Thursday, October 21, 2021

Microsoft Translator Service - Issue while fetching the Access Token | AEM(Adobe Experience Manager)

Microsoft Translator - Issue while fetching the Access Token | AEM(Adobe Experience Manager) Translation

I was getting a "Failed To connect" message while trying to enable the configuration for MS Translator from AEM

Creating a Translation Integration Configuration - Tools -> Cloud Services -> Translation Cloud Services.

AEM Content Translation

AEM Content Translation

Receiving "Failed to Connect" error while the connector trying to establish the connection with the Azure Translator service.

The below error was displayed in the error.log but the error was not providing the real root cause

21.10.2021 11:06:02.390 *ERROR* [[0:0:0:0:0:0:0:1] [1634832361866] POST /services/accesstoken/verify HTTP/1.1] org.apache.sling.engine.impl.SlingRequestProcessorImpl service: Uncaught Throwable
java.lang.NullPointerException: null
21.10.2021 11:06:02.391 *WARN* [[0:0:0:0:0:0:0:1] [1634832361866] POST /services/accesstoken/verify HTTP/1.1] com.day.cq.wcm.core.impl.components.ComponentCacheImpl Requested Path /services/accesstoken/verify.servlet is not in available search paths

Enabled a logger to capture the MS Translation Connector logs with Trace level - the error messages are printed in Trace level.

AEM Logger

The below error was displayed  in the log file

21.10.2021 11:35:04.769 *TRACE* [[0:0:0:0:0:0:0:1] [1634834104174] POST /services/accesstoken/verify HTTP/1.1] com.adobe.granite.translation.connector.msft.core.impl.util.AccessTokenImpl Error while accessing token -->{"error":{"code":"401","message": "Access denied due to invalid subscription key or wrong API endpoint. Make sure to provide a valid key for an active subscription and use a correct regional API endpoint for your resource."}}   - Tarce Log

The correct Token was configured also the API endpoint is enabled from the MS Translation Connector.

The request to get the access token to the following API was failing with a 401 error —  https://api.cognitive.microsoft.com/sts/v1.0/issueToken

The actual root cause was the region configured while creating the translation service in the Azure portal, I have selected the region as East US

AEM Content Translation

But the region  for the Microsoft Translator should be enabled as global instead of selecting the local regions - the MS Translator API only works with global regions

The issue is resolved after enabling Translator service with the global region

MS Translator


AEM Content Translation


Wednesday, October 20, 2021

How to identify/handle duplicate assets in AEM(Adobe Experience Manager) Assets?

How to identify/handle duplicate assets in AEM(Adobe Experience Manager) Assets?


In this blog post, let us explore how to identify/handle the duplicate assets in Adobe Experience Manager Assets.

The default behavior is while uploading the asset if the asset already exists with the same name under the same folder, the popup will be displayed - you should be able to keep both files or replace the existing one or create a new version for the existing files, this will not check the binary to identify the duplicate files.

AEM assets

Duplicate Detection:


The duplicate detection feature(binary level) can be enabled if required, Duplicate detection is disabled by default. If you attempt to upload an asset that exists in Adobe Experience Manager Assets, the duplicate detection feature reports the asset as duplicate.

The duplicate detection feature can be enabled through the "Day CQ DAM Create Asset" OSGI config - http://localhost:4502/system/console/configMgr/com.day.cq.dam.core.impl.servlet.CreateAssetServlet(ensure the OSGI configuration changes are enabled through code)

AEM assets

 When a user attempts to upload an asset that exists in AEM, the system checks for conflict and indicates it. The assets are identified using SHA-1 hash stored at jcr:content/metadata/dam:sha1, which means duplicate assets are detected irrespective of the filenames.

The SHA-1 hash is generated during the asset creation and stored under jcr:content/metadata/dam:sha1 - the SHA-1 hash is generated based on the asset binary

AEM Assets

Now while you upload the same image, the duplicate detection feature reports the asset as a duplicate asset - the duplicate detection feature verifies the assets across the DAM repository irrespective of the name.

AEM assets

The duplicate asset can be kept or skipped from upload(Delete)

The duplicate detection feature helps us to avoid duplicate uploads of the same files if the same file already exists in the system.

MSM for Assets:


The MSM for Assets can be used to reuse the same assets within different websites by customizing the required metadata.

Using MSM for Assets, you can:
  • Create assets once and then make copies of these assets to reuse in other areas of the site.
  • Keep multiple copies in synchronization and update the original primary copy once to push the changes to the child copies.
  • Make local changes by temporarily or permanently suspending the linking between parent and child assets.
MSM maintains a live relationship between the source asset and its live copies so that:
  • Changes to the source assets are applied (rolled out) to live copies as well, that is the live copies are synchronized with the source.
  • You can update the live copies by suspending the live relationship or removing the inheritance for a few limited fields. The modifications to the source are no longer applied to the live copy.
To create the live copy, select the asset, Create, Live Copy

AEM Assets


Select the destination folder, where the live copy asset should be stored

AEM Assets

Enter title, change the Name if required, select the rollout config

AEM Assets


AEM assets

If required, the inheritance can be broken on the copied asset the local changes can be enabled, the source changes will be reflected on the copied assets on rollout/synchronization

AEM Assets

You can completely remove the relationship between a source and a live copy using Detach action. The live copy becomes a stand-alone asset or folder after it is detached. You can undo all the local modifications and revert the asset to the state of its source

AEM Assets


Please note, the live copy asset is also considered duplicate and reported.

Identify the Existing Duplicate:


The below steps can be followed to identify the existing duplicate assets.

Get all the asset files under a specific folder with the SHA1 value through Query Builder.

http://localhost:4502/bin/querybuilder.json?p.hits=selective&path=/content/dam&p.properties=jcr:path%20jcr:content/metadata/dam:sha1&p.limit=-1&property=jcr:content/metadata/dam:sha1&property.operation=exists

Use the below query to identify if the asset is a live copy

http://localhost:4502/bin/querybuilder.json?p.hits=selective&path=/content/dam&p.properties=jcr:path%20/jcr:content/cq:LiveSyncConfig/cq:master%20jcr:content/metadata/dam:sha1&p.limit=-1&property=jcr:content/metadata/dam:sha1&property.operation=exists

This will provide the output in JSON

AEM Assets

Convert JSON to CSV - use any JSON to CSV converter, I am using https://konklone.io/json/

AEM Assets

Download the CSV and open it in Excel

Now let’s highlight the cells with duplicate values. Select column "jcr:content/metadata/dam:sha1",Click Home/Conditional-formatting/Highlighted Cells Rules/Duplicate-Values. Then click ok on the next populated window

AEM Assets

AEM Assets


Apply the filter by selected cells color, select the one of the duplicated SHA1 value - Right-Click - Filter - Filter By Selected cell color

AEM Assets

Now you should be able to see all the files which are duplicates
AEM Assets



Now you should be able to perform required operations on the duplicated assets

e.g Delete the duplicated asset or delete and create live copy asset etc

 Delete specific asset - curl -u admin:admin -k -X DELETE http://localhost:4502/content/dam/email/hibernate4-2.png


Create Live Copy -  curl 'http://localhost:4502/bin/wcmcommand' --data-raw '_charset_=utf-8&cmd=createLiveCopy&destPath=%2Fcontent%2Fdam%2Femail&title=test5&label=hibernate4-sc1.png&srcPath=%2Fcontent%2Fdam%2Ftest%2Fhibernate4.png'

You can create a script file with multiple curl commands to delete the duplicate assets and/or to create the live copies if required, even you write a groovy script to delete the assets and or to create live copies for the assets.

References:





Friday, October 15, 2021

How to deliver headless content through GraphQL API and Content Fragments? | AEM(Adobe Experience Manager)

How to deliver headless content through GraphQL API and Content Fragments? | AEM(Adobe Experience Manager)

Adobe Experience Manager (AEM) Content Fragments allow you to design, create, curate and publish page-independent content. They allow you to prepare content ready for use in multiple locations/over multiple channels.

GraphQL is a query language and server-side runtime for application programming interfaces (APIs) that prioritizes giving clients exactly the data they request and no more. GraphQL is designed to make APIs fast, flexible, and developer-friendly.

AEM as a Cloud Service and AEM 6.5.10.0+ version supports GraphQL API to expose the Content Fragment to enable the headless content experience. AEM as a Cloud Service and AEM 6.5.10.0 versions enable the GraphQL runtime platform to expose the Content Fragments through GraphQL API.

Using the GraphQL API in AEM enables the efficient delivery of Content Fragments to JavaScript clients in headless CMS implementations:
  • Avoiding iterative API requests as with REST,
  • Ensuring that delivery is limited to the specific requirements,
  • Allowing for bulk delivery of exactly what is needed for rendering as the response to a single API query.

AEM GraphQL API


GraphiQL Interface:


AEM GraphQL implementation provides a standard GraphQL interface to directly input, and test queries. The interface should be installed separately, the interface can be downloaded from Adobe software-distribution - https://experience.adobe.com/#/downloads/content/software-distribution/en/aemcloud.html?package=/content/software-distribution/en/details.html/content/dam/aemcloud/public/aem-graphql/graphiql-0.0.6.zip, install the latest version of the interface

AEM GraphQL API

GraphiQL interface is bound to the global endpoint (and does not work with other endpoints for specific Sites configurations) - the global endpoint includes all the site-specific configurations(the models created under site-specific conf folders).


AEM GraphQL API


You may receive the below error while executing the API's in the AEM Author

{
  "message": "Failed to execute 'text' on 'Response': body stream already read",
  "stack": "TypeError: Failed to execute 'text' on 'Response': body stream already read\n    at http://localhost:4502/content/graphiql.html:81:33\n    at async Object.onRun (https://unpkg.com/graphiql/graphiql.min.js:1:533801)"
}

AEM GraphQL API

The actual root cause was the CSRF filter blocking the requests in AEM Author, the path white listing looks to be not enabled while upgrading from 6.5.8.0 to 6.5.10.0(but it worked for me while upgrading from 6.5.0.0 to 6.5.10.0), the whitelisting is already enabled in AEM as a Cloud Service, ensure the blow paths are whitelisted in the CSRF filter.

AEM graphql API

Configuration Browsers - Enable Content Fragment Model/GraphQL Persistent Queries:


Enable the Content Fragment Model/GraphQL Persistent Queries through configuration browser for the global sites configurations and the site-specific configurations.

Tools -- General -- Configuration Browser -- Select Global folder -- Properties, ensure Content Fragment Models and GraphQL Persistent Queries are selected

AEM GraphQL API


Enable Content Fragment Model(Model your content):

Model your content structure using AEM Content Fragment Models and Content Fragments.

Let us now enable the content fragment model that should be used to generate Content Fragments that will be exposed to the external system through GraphQL API for consuming the content.

Tools -- Assets -- Content Fragment Models, Select the global or Site-Specific folder(I am creating under the site-specific folder) and create a new Model. Ensure the model is enabled, GraphQL schema will be generated only for the models that are enabled

AEM GraphQL API

GraphQL tab displays the API details

AEM GraphQL API

Open the model and enable the required fields - additional data types e.g Content Reference, Fragment Reference, JSON Object and etc can be used if required while structuring the Content Fragment Model. The referenced Model(Fragment) data can also be fetched through the parent GraphQL API e.g. if Employee Model references Person Model, the associated Person data can be fetched through Employee GraphQL API.

AEM GraphQL API

The default Cache configurations can be modified if required through Content Fragment Model properties

AEM GraphQL API

GraphQL Endpoints:


AEM endpoint helps to access the GraphQL schema, also send GraphQL queries and receive the response.

Define the API endpoint by going to Tools -- Assets -- GraphQL

The global endpoint will be defined by default(enable if missing), define the site-specific endpoints if required 
(global endpoint expose all the models if required site-specific endpoints can be enabled but the whitelisting and other configurations should be enabled for site-specific endpoints)

AEM GraphQL API

AEM GraphQL API


The endpoint should be published to make it available to the publisher. Ensure the appropriate security permissions are set up while enabling the GraphQL API endpoints in publishers. 

GraphQL Schema:


GraphQL is a strongly typed API, which means that data must be clearly structured and organized by type.

The GraphQL specification provides a series of guidelines on how to create a robust API for interrogating data on a certain instance. To do this, a client needs to fetch the Schema, which contains all the types necessary for a query.

In AEM, the schema is generated based on the Content Fragment Model, the schema is flexible. This means that it is auto-generated each and every time a Content Fragment Model is created, updated, or deleted. The data schema caches are also refreshed when you update a Content Fragment Model.

The schema can be accessed through endpoint API - http://localhost:4502/content/cq:graphql/global/endpoint.GQLschema

Also, schema and schema docs can be fetched through the GraphQL interface(additional fields can be added if required)

{
  __schema {
    types {
      name
      description
    }
  }
}

AEM GraphQL API


Create Content Fragments:


Now create the required Content Fragments under the appropriate asset folder(e.g mysite) with the required data, select the Content Fragment Model created in the earlier step.

Ensure the Content Fragment Models are enabled for the asset folders(e.g mysite)

AEM GraphQL API


AEM GraphQL API

Enter the required data and save it, the model should be published to enable the access with publish server.

AEM GraphQL API


Content Fragments now enables preview for GrapQL data, the preview will work only if the site-specific endpoint is registered.

AEM GraphQL API

Register the site-specific endpoint to preview the content

AEM GraphQL API

Now you should be able to preview the data

AEM GraphQL API

You should be able to copy the direct GraphQL data URL - http://localhost:4502/content/dam/mysite/person1.cfm.gql.json ( this will expose the Content Fragment Data as JSON)

Fetching the Data:


Now the data can be queried based on the GraphQL API's defined in the schema - http://localhost:4502/content/cq:graphql/global/endpoint.GQLschema

"""
Get a single `PersonModel`, specified by its path and optional variation
"""
personByPath(_path: String!, variation: String): PersonModelResult! @fetcher(name : "sites/default", source : "personByPath")
"""

"""
Get multiple `PersonModel` objects
"""
personList(filter: PersonModelFilter, variation: String, _locale: String): PersonModelResults! @fetcher(name : "sites/default", source : "personList")
"""
Fetch the person details by path:

The personByPath API can be used to fetch content fragment specific data(the Content Fragment created based on Person Model) by specifying the content fragment path, modify the attributes(I am fetching firstName, lastName, and email) based on your needs

query{
  personByPath(_path:"/content/dam/mysite/person1"){
   item {      
      firstName
      lastName
      email
    }
  }
}

AEM GraphQL API


Fetch Person Lists:

The personList API can be used to fetch a list of Content Fragments created using the Person Model.

query{
  personList{
    items {
      _path
      firstName
      lastName
      email      
      
    }
  }
}

AEM GraphQL API


Filter based on expression:

The filters can be specified to fetch specific content fragments from the list of content fragments by specifying filter expressions.

query {
  personList(filter: {
    firstName: {
     _expressions: [
        {
          value: "Test1"
          _operator: EQUALS_NOT
        }
    ]
    }
  }) {
    items {
      firstName
      lastName
      email
    }
  }
}

AEM GraphQL API

For the Multiline Text fields, the below properties can be used to fectch the different format data

html
plaintext
markdown
json

query{
  personByPath(_path:"/content/dam/mysite/person1"){
   item {      
      firstName
      lastName
      email
      additionalDetails
     {
      html
      markdown
      plaintext
      json
     }
    }
  }
}

AEM GraphQL API


The metadata and variation details can also be fetched, also other expressions can be used to filter the data - refer to https://github.com/AdobeDocs/experience-manager-cloud-service.en/blob/master/help/assets/content-fragments/content-fragments-graphql-samples.md#content-fragment-structure-graphql for more query operations and fields.

The GraphQL variables and Directives can be used to filter the data, refer https://experienceleague.adobe.com/docs/experience-manager-cloud-service/assets/admin/graphql-api-content-fragments.html?lang=en for more details.

Persisted Queries (Caching):


Prepare query with a POST request, it can be executed with a GET request and the responses can be cached by HTTP caches or a CDN. The Persisted queries help to improve the overall performance and also reduce the load on AEM servers.

Create Persisted Query(PUT) - may be the easy option is to copy the curl command from the GraphQL interface and modify 

AEM GraphQL API

The persisted query can be created by sending the PUT request to http://localhost:4502/graphql/persist.json/<Site Name>/<Query Name>

I am using the basic authentication for the demo but the token-based authentication should be used for AEM as a Cloud Service.

curl -u admin:admin -X PUT 'http://localhost:4502/graphql/persist.json/mysite/person' \
  -H 'Content-Type: application/json' \
  --data-raw '{"query":"query{\n  personByPath(_path:\"/content/dam/mysite/person1\"){\n   item {      \n      firstName\n      lastName\n      email\n    }\n  }\n}","variables":null}'

The output will provide a short path that can be used to execute the query e.g /mysite/person

Execute Persisted Query - the persisted query can be executed by sending get request to 
http://localhost:4502/graphql/execute.json/<Query Shortname>

curl -u admin:admin -X GET http://localhost:4502/graphql/execute.json/mysite/person

The existing persisted queries can be modified by sending the POST request to http://localhost:4502/graphql/persist.json/<Site Name>/<Query Name>

curl -u admin:admin -X POST 'http://localhost:4502/graphql/persist.json/mysite/persion' \
  -H 'Content-Type: application/json' \
  --data-raw '{"query":"query{\n  personByPath(_path:\"/content/dam/mysite/person1\"){\n   item {      \n      firstName\n      lastName\n      email\n    }\n  }\n}","variables":null}'

The max cache header value can be specified while creating the persisted queries - this will help us to cache the query result for an extended time

curl -u admin:admin -X PUT 'http://localhost:4502/graphql/persist.json/mysite/persion' \
  -H 'Content-Type: application/json' \
  --data-raw '{"query":"query{\n  personByPath(_path:\"/content/dam/mysite/person1\"){\n   item {      \n      firstName\n      lastName\n      email\n    }\n  }\n}","variables":null,"cache-control":{"max-age":300}}'

The persisted  queries are stored under /conf/<Site Name>/settings/graphql/persistedQueries

AEM GraphQL API

The list of Persisted queries can be fetched through /graphql/list.json endpoint - curl -u admin:admin -X GET   http://localhost:4502/graphql/list.json

To delete the existing query, send the DELETE request to the /graphql/persist.json/<Short Path> -  curl -u admin:admin -X DELETE   http://localhost:4502/graphql/persist.json/mysite/test



Consume GraphQL API Data from External System:


The external system can retrieve the Content Fragment data by sending the POST request to /content/_cq_graphql/global/endpoint.json GraphQL API endpoint(copy the curl request from GraphQL interface UI)

I am using the basic authentication for the demo but the token-based authentication should be used for AEM as a Cloud Service(Let me explore and share this later). Every Content Fragment model can be accessed through a global endpoint but individual endpoints can be enabled if required.

  curl -u admin:admin 'http://localhost:4502/content/_cq_graphql/global/endpoint.json' \
  -H 'Content-Type: application/json' \
   --data-raw '{"query":"query{\n  personByPath(_path:\"/content/dam/mysite/person1\"){\n   item {      \n      firstName\n      lastName\n      email\n    }\n  }\n}","variables":null}'


The CORS and  Referrer filter should be configured to enable the access for external systems(for the demo I am showing the OSGI console but enable through code - AEM as a Cloud service won't provide access to OSGi console, ready only view is provided through Developer console - refer https://www.albinsblog.com/2021/10/developer-console-and-adobe-io-cli-for-cloud-manager-to-debug-aem-as-a-cloud-service.html for more details)

Enable the external domains as Allowed Origins and the below paths as Allowed Paths  through Adobe Granite Cross-Origin Resource Sharing Policy
/content/_cq_graphql/global/endpoint.json // everything should be accessible through global, individual endpoints can be enabled if required
 /graphql/execute.json/.* // endpoint to execute the persisted queries

AEM GraphQL API


The CORS Filter should be enabled while testing thorugh localhost SPA application, also ensure the Authorization token is whitelisted.

AEM Headless GraphQL CORS

Referrer filter must be configured to allow access from external hosts.

AEM GraphQL API

Now the external system will be able to fetch the Content Fragment data from AEM through GraphQL API queries.

The GraphQL API of AEM provides a powerful query language to expose data of Content Fragments to downstream applications to support headless content sharing with external systems. Content Fragment models define the data schema that is used by Content Fragments. Whenever a Content Fragment Model is created or updated, the schema is translated and added to the “graph” that makes up the GraphQL API.

The GraphiQL IDE allows you to quickly test and refine the queries and data returned. GraphiQL also provides easy access to the documentation, making it easy to learn and understand what methods are available.

The AEM GraphQL API currently not supporting some of the GraphQL API features such as Mutations, Subscriptions, and Paging/Sorting, the API is read-only and the Asset API should be used to perform the data management operations.

References: