Akeneo:connect

Akeneo:connect is a CELUM extension which allows you to synchronize previews, IDs, URLs and metadata from CELUM to Akeneo.

In the other direction you can synchronize the product structure and attributes from Akeneo to CELUM. It is also possible to write data from Akeneo to the CELUM structure to efficiently use the search of CELUM.

Akeneo:connect diagram

1. Requirements

  • Akeneo 2.3
  • CELUM 5.13.3

2. Installation

Akeneo

  • Create an API key Akeneo configure API Key
  • Create an API role: Akeneo:connect requires readwrite access to attributes and assets and readonly access to everything else Akeneo configure role
  • Create an API user with the role you have just created

CELUM

  • Place the provided Akeneo:connect jar file in home/appserver/lib/
  • Create a root node for the Akeneo product hierarchy. It is recommended to create a new node type for this.
  • Create a new text information field 'hierarchyIdentifier' and assign it to the node type of the product hierarchy root node.
  • Set at least the required properties (see section below) in home/appserver/conf/custom.properties
  • Create home/appserver/spring/akeneo-hierarchy-config.xml (see section below)
  • Restart CELUM appserver

3. Configuration

Properties

To be configured in {home}/appserver/conf/custom.properties

API Credentials

akeneoConnector.api.baseUrl

type: String, required: yes, default: -

The base URL of your Akeneo instance

e.g. akeneoConnector.api.baseUrl=https://akeneo-demo.brix.ch

akeneoConnector.api.clientId

type: String, required: yes, default: -

The client ID of the Akeneo API key

e.g. akeneoConnector.api.clientId=1_3f8ostwi792c88sasdws123ww0skkgog0kg4c13k04s84k448g

akeneoConnector.api.clientSecret

type: String, required: yes, default: -

The client secret of the Akeneo API key

e.g. akeneoConnector.api.clientSecret=1fyrte3werps088w8sw1111kk0c4s04s0sc4wgso8c8css8kgso

akeneoConnector.api.username

type: String, required: yes, default: -

The name of the Akeneo API user

e.g. akeneoConnector.api.username=user_name

akeneoConnector.api.password

type: String, required: yes, default: -

The password of the Akeneo API user

e.g. akeneoConnector.api.password=password

Other mandatory properties

akeneoConnector.license

type: String, required: yes, default: -

The license for Akeneo:connect provided by brix cross media

e.g. akeneoConnector.license=B3Kj3MbcupVIF/zHV2fn0uwmjRpaQ4YcynIjENCxA0l4ncLRYyUZwubIhXKpgW/+baPziK

akeneoConnector.identifierAttributeCode

type: String, required: yes, default: sku

The code of the identifier attribute in akeneo

e.g. akeneoConnector.identifierAttributeCode=art_no

akeneoConnector.api.labelChannelCode (deprecated)
akeneoConnector.api.channelPrecedence (since v1.10.8)

type: List of Strings (comma-separated), required: yes, default: -

Priority list of channel codes to consider when synchronizing attributes with different values per channel

e.g. akeneoConnector.api.channelPrecedence=ecommerce,print

akeneoConnector.informationFieldId.hierarchyIdentifier

type: Number, required: yes, default: -

The ID of the newly created node information field (see above)

This is a technical information field for storing identifier data needed by the sync algorithm. It is recommended to hide it for all users (except super users) via role configuration.

e.g. akeneoConnector.informationFieldId.hierarchyIdentifier=205

akeneoConnector.hierarchySync.rootNodeId

type: Number, required: yes, default: -

The ID of the root node of the product hierarchy synchronized from Akeneo

e.g. akeneoConnector.hierarchySync.rootNodeId=17362

akeneoConnector.hierarchyNodeTypeId

type: Number, required: yes (until v1.9.6-2.3 / v1.10.5-3.0, removed in newer versions), default: -

The ID of the node type of the akeneo product hierarchy nodes

e.g. akeneoConnector.hierarchyNodeTypeId=104

Optional API properties

The provided default values should be fine for most cases.

akeneoConnector.api.timeZoneId

type: String, required: no, default: UTC

The configured time zone on your Akeneo server instance.

Server instances hosted by Akeneo (*.cloud.akeneo.com) always have UTC configured.

e.g. akeneoConnector.api.timeZoneId=Europe/Vienna

akeneoConnector.defaultCountryCode.{languageCode}

type: String, required: no, defaults: en_US, de_DE, fr_FR, es_ES, it_IT

Defines the default country code (variant) for a language.

Because CELUM does not support language variants, the connector needs to decide which language variant to use if Akeneo returns data for multiple variants of the same language e.g. en_US and en_GB.

Example:

akeneoConnector.defaultCountryCode.en=GB
akeneoConnector.defaultCountryCode.de=CH
akeneoConnector.defaultCountryCode.fr=BE
akeneoConnector.api.requiredLocales

type: List of Strings (comma-separated), required: no, default: -

List of locales to include in Akeneo API responses

Setting this property may slightly improve performance by stripping unnecessary locale data from all API responses.

e.g. akeneoConnector.api.requiredLocales=en_US,de_DE,fr_FR

akeneoConnector.api.pageLimit

type: Number, required: no, default: 100

The maximum number of records returned per API request

e.g. akeneoConnector.api.pageLimit=50

Consider to decrease this value if you experience request timeouts. The maximum value supported by the Akeneo API is 100.

akeneoConnector.api.connectTimeout

type: Number, required: no, default: 10

Timeout in seconds for establishing a connection to the Akeneo API

e.g. akeneoConnector.api.connectTimeout=10

akeneoConnector.api.readTimeout

type: Number, required: no, default: 300 (5 minutes)

Timeout in seconds when reading data from the Akeneo API

e.g. akeneoConnector.api.readTimeout=300

akeneoConnector.api.writeTimeout

type: Number, required: no, default: 600 (10 minutes)

Timeout in seconds when writing data to the Akeneo API

e.g. akeneoConnector.api.writeTimeout=600

Task Properties

akeneoConnector.hierarchySync.taskGroup

type: String, required: no, default: Akeneo Connect

The name of the CELUM task group which synchronizes the structure in to CELUM.

e.g. akeneoConnector.hierarchySync.taskGroup=Akeneo:connect

akeneoConnector.hierarchySync.taskName.fullSync

type: String, required: no, default: Hierarchy Full Sync

The name of the CELUM task which synchronizes the structure in to CELUM.

e.g. akeneoConnector.hierarchySync.taskName.fullSync=Full Synchronisation

akeneoConnector.hierarchySync.taskCron.fullSync

type: String, required: no, default: -

A Quartz cron expression for scheduling the full sync task. If this property is left empty, the task will only run when started manually via CELUM System Tasks.

e.g. akeneoConnector.hierarchySync.taskCron.fullSync=0 0 6/12 * * ?

akeneoConnector.hierarchySync.taskName.deltaSync

type: String, required: no, default: Hierarchy Delta Sync

The name of the CELUM task which synchronizes the structure in to CELUM.

e.g. akeneoConnector.hierarchySync.taskName.deltaSync=Delta Synchronisation

akeneoConnector.hierarchySync.taskCron.deltaSync

type: String, required: no, default: -

A Quartz cron expression for scheduling the delta sync task. If this property is left empty, the task will only run when started manually via CELUM System Tasks.

e.g. akeneoConnector.hierarchySync.taskCron.deltaSync=0 0 0-5,9-17,21-23 * * ?

akeneoConnector.hierarchySync.errorMailRecipients

type: List of Strings (comma-separated), required: no, default: -

List of e-mail addresses which will receive an error report in case of failure of a synchronisation task.

e.g. akeneoConnector.hierarchySync.errorMailRecipients=marketing@example.com,it@example.com

akeneoConnector.hierarchySync.familyFilter

type: List of Strings (comma-separated), required: no, default: -

List of Akeneo family codes

If set, only products from the listed families will be synchronized, all products otherwise.

e.g. akeneoConnector.hierarchySync.familyFilter=clothing

Export Properties

akeneoConnector.export.triggerOnDirectAssignment

type: Boolean, required: no, default: true

Whether to trigger exports to Akeneo when an asset is directly assigned to an Akeneo hierarchy node

e.g. akeneoConnector.export.triggerOnDirectAssignment=false

akeneoConnector.export.triggerFieldId

type: Number, required: no, default: -

The ID of a node-referencing information field which refers to the synchronized Akeneo product hierarchy

If this property is set, exports to Akeneo are triggered when the configured information field is updated on an asset.

e.g. akeneoConnector.export.triggerFieldId=235

akeneoConnector.export.clearTriggerFieldOnMoveToTrashbin

type: Boolean, required: no, default: true (since v1.9.8-2.3 / v1.10.7-3.0)

Whether to clear the configured trigger information field when an asset is moved to the trashbin

By default no metadata is removed when an asset is moved to the trashbin. With this property set to true, the configured trigger field is cleared when an asset is moved to the trashbin, which will consequently trigger removal of the exported asset data from Akeneo.

e.g. akeneoConnector.export.clearTriggerFieldOnMoveToTrashbin=true

akeneoConnector.export.considerAssetAvailability

type: Boolean, required: no, default: false

Whether to consider asset availability status for exports

If this property is set to true, exports to akeneo will only be triggered if the availability status of the asset is 'active'. Furthermore, an already exported asset will be removed from Akeneo when its availability status changes to 'inactive'.

e.g. akeneoConnector.export.considerAssetAvailability=true

Example Configuration

#### Akeneo Connector ####
akeneoConnector.license={provided by brix}

# API credentials
akeneoConnector.api.baseUrl=https://akeneo-demo.brix.ch
akeneoConnector.api.clientId=iue5vmu489ughstusm5tzkljxy59zuhrtshnj4w5
akeneoConnector.api.clientSecret=lkuizes5v89z4tvohjsbpgsdtg5oö8ughtgjbdg
akeneoConnector.api.username=celum_api
akeneoConnector.api.password=kljt89PpPwtgtdjigsg!

# ID of the node information field for storing the hierarchy identifier
akeneoConnector.informationFieldId.hierarchyIdentifier=301

# ID of the root node of the product hierarchy synchronized from Akeneo
akeneoConnector.hierarchySync.rootNodeId=1736

# code of the identifier attribute in akeneo
akeneoConnector.identifierAttributeCode=art_no

# prefer attribute data from ecommerce channel, fallback to print
akeneoConnector.api.channelPrecedence=ecommerce,print

# run full sync every saturday at 1 am
akeneoConnector.hierarchySync.taskCron.fullSync=0 0 1 * * SAT

# run delta sync every 4 hours
akeneoConnector.hierarchySync.taskCron.deltaSync=0 0 0/4 * * ?

# send error report on sync task failure
akeneoConnector.hierarchySync.errorMailRecipients=marketing@example.com,it@example.com

# don't trigger exports when an asset is directly assigned to an product node
akeneoConnector.export.triggerOnDirectAssignment=false

# trigger exports when a product node is assigned via node referencing field with ID 195
akeneoConnector.export.triggerFieldId=195

# clear this information field when an asset is moved to the trash bin
akeneoConnector.export.clearTriggerFieldOnMoveToTrashbin=true

# only export 'active' assets to akeneo and remove 'inactive' ones
akeneoConnector.export.considerAssetAvailability=true

Hierarchy Configuration

akeneo-hierarchy-config.xml Example

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

  <util:map id="akeneoConnectorHierarchyConfigsByFamily" key-type="java.lang.String" value-type="ch.brix.akeneoConnector.config.HierarchyConfiguration">
    <entry key="default">
      <bean class="ch.brix.akeneoConnector.config.HierarchyConfiguration">

        <property name="filterExpressions">
          <util:list>
            <!-- see https://api.akeneo.com/documentation/filter.html -->
            <bean class="ch.brix.akeneoConnector.config.FilterExpression">
              <!-- value for query parameter 'search' -->
              <constructor-arg name="expression">
                <value>{"web_blocked":[{"operator":"=","value":false}],"since":[{"operator":"NOT EMPTY"}]}</value>
              </constructor-arg>
              <!-- value for query parameter 'search_scope' -->
              <property name="channel" value="print"/>
              <!-- value for query parameter 'search_locale' -->
              <property name="locale" value="de_AT"/>
            </bean>

            <!-- FilterExpression beans on top level are connected with OR (i.e. the union of the records returned by each of them is built) -->
            <bean class="ch.brix.akeneoConnector.config.FilterExpression">
             <constructor-arg name="expression">
                <value>{"in_shop":[{"operator":"=","value":true}]}</value>
              </constructor-arg>
            </bean>

          </util:list>
        </property>

        <property name="hierarchyLevelConfigurations">
          <util:list value-type="ch.brix.akeneoConnector.config.HierarchyLevelConfiguration">

            <!-- family -->
            <bean class="ch.brix.akeneoConnector.config.HierarchyLevelConfiguration">
              <constructor-arg name="level">
                <bean class="ch.brix.akeneoConnector.hierarchySync.model.hierarchyLevel.FamilyLevel" factory-method="getInstance"/>
              </constructor-arg>
            </bean>

            <!-- producer attribute -->
            <bean class="ch.brix.akeneoConnector.config.HierarchyLevelConfiguration">
              <constructor-arg name="level">
                <bean class="ch.brix.akeneoConnector.hierarchySync.model.hierarchyLevel.AttributeLevel" factory-method="getInstance">
                  <constructor-arg value="producer"/>
                </bean>
              </constructor-arg>
            </bean>

            <!-- product model level 1 -->
            <bean class="ch.brix.akeneoConnector.config.HierarchyLevelConfiguration">
              <constructor-arg name="level">
                <bean class="ch.brix.akeneoConnector.hierarchySync.model.hierarchyLevel.ProductModelLevel" factory-method="getInstance">
                  <constructor-arg value="1"/>
                </bean>
              </constructor-arg>
              <!-- custom LabelBuilder: Product model identifier followed by value of attribute 'product_name' separated by pipe -->
              <constructor-arg name="labelBuilder">
                <bean class="ch.brix.akeneoConnector.hierarchySync.labelBuilder.ConcatLabelBuilder">
                  <constructor-arg>
                    <util:list>
                      <bean class="ch.brix.akeneoConnector.hierarchySync.labelBuilder.IdLabelBuilder">
                        <property name="expandToAllLanguages" value="true"/>
                      </bean>
                      <bean class="ch.brix.akeneoConnector.hierarchySync.labelBuilder.EmbeddedLabelBuilder">
                        <constructor-arg name="prefix" value=" | "/>
                        <constructor-arg name="embeddedLabelBuilder">
                          <bean class="ch.brix.akeneoConnector.hierarchySync.labelBuilder.AttributeValueLabelBuilder">
                            <constructor-arg name="attributeCode" value="product_name"/>
                            <property name="fallback" value=""/>
                          </bean>
                        </constructor-arg>
                      </bean>
                    </util:list>
                  </constructor-arg>
                </bean>
              </constructor-arg>
            </bean>

            <!-- product model level 2 -->
            <bean class="ch.brix.akeneoConnector.config.HierarchyLevelConfiguration">
              <constructor-arg name="level">
                <bean class="ch.brix.akeneoConnector.hierarchySync.model.hierarchyLevel.ProductModelLevel" factory-method="getInstance">
                  <constructor-arg value="2"/>
                </bean>
              </constructor-arg>
            </bean>

            <!-- product -->
            <bean class="ch.brix.akeneoConnector.config.HierarchyLevelConfiguration">

              <constructor-arg name="level">
                <bean class="ch.brix.akeneoConnector.hierarchySync.model.hierarchyLevel.ProductLevel" factory-method="getInstance"/>
              </constructor-arg>

              <property name="defaultImportMappingsByAttributeCode">
                <util:map>
                  <entry key="ean" value="113"/>
                  <entry key="description" value="120"/>
                </util:map>
              </property>

              <property name="exportMappings">
                <util:list>

                  <!-- export asset ID to text attribute -->
                  <bean class="ch.brix.akeneoConnector.assetExport.exportMapping.AssetIdExportMapping">
                    <constructor-arg name="attributeCode" value="celum_asset_id"/>
                    <!-- overwrite attribute value instead of appending to current value -->
                    <property name="overwriteValue" value="true"/>
                  </bean>

                  <!-- export asset name to text attribute -->
                  <bean class="ch.brix.akeneoConnector.assetExport.exportMapping.AssetNameExportMapping">
                    <constructor-arg name="attributeCode" value="celum_asset_name"/>
                  </bean>

                  <!-- export Public URL to text attribute -->
                  <bean class="ch.brix.akeneoConnector.assetExport.exportMapping.PublicUrlExportMapping">
                    <constructor-arg name="attributeCode" value="celum_public_url"/>
                    <!-- CELUM Public URLs are identified by a description, provider and an optional provider instance -->
                    <constructor-arg name="urlDescription" value="video"/>
                    <constructor-arg name="providerFilter">
                      <bean class="com.celum.api.model.ProviderFilter">
                        <constructor-arg name="provider" value="youtube"/>
                        <!-- optional instance identifier -->
                        <constructor-arg name="instance" value="instance123"/>
                      </bean>
                    </constructor-arg>
                    <!-- delimit multiple values by line break instead of comma -->
                    <property name="delimiter" value="\n"/>
                  </bean>

                  <!-- export asset binary to media file attribute -->
                  <bean class="ch.brix.akeneoConnector.assetExport.exportMapping.MediaFileExportMapping">
                    <constructor-arg name="attributeCode" value="picture"/>
                    <constructor-arg name="binaryUrlProvider">
                      <bean class="ch.brix.akeneoConnector.assetExport.exportMapping.binaryUrlProvider.CustomFormatBinaryUrlProvider">
                        <constructor-arg name="rawDownloadFormatId" value="17"/>
                      </bean>
                    </constructor-arg>
                  </bean>

                  <!-- export asset binary to asset collection attribute -->
                  <bean class="ch.brix.akeneoConnector.assetExport.exportMapping.AkeneoAssetExportMapping">
                    <constructor-arg name="attributeCode" value="celum_assets"/>
                    <constructor-arg name="binaryUrlProvider">
                      <bean class="ch.brix.akeneoConnector.assetExport.exportMapping.binaryUrlProvider.StandardFormatBinaryUrlProvider">
                        <constructor-arg name="rawRepresentationTypeId" value="prvw"/>
                      </bean>
                    </constructor-arg>
                    <!-- optional export condition: Export only if condition holds -->
                    <property name="exportCondition">
                      <bean class="ch.brix.akeneoConnector.assetExport.exportMapping.condition.AssetNameExportCondition">
                        <constructor-arg name="regex" value="^[A-Z]{2}\d{2}-[A-Z0-9]\d{2}_\d{5}_front\.\w+$"/>
                        <constructor-arg name="caseSensitive" value="false"/>
                      </bean>
                    </property>
                    <!-- omit if attribute is not scoped by channel -->
                    <property name="channelCode" value="ecommerce"/>
                    <!-- omit if attribute is not scoped by locale -->
                    <property name="locale" value="en_US"/>
                  </bean>

                </util:list>
              </property>
            </bean>

          </util:list>
        </property>
      </bean>
    </entry>
  </util:map>

</beans>

4. Usage

  • Login into the CELUM
  • Start the hierarchy synchronisation task
  • The configured structure will be set up in CELUM
  • Add assets to the product hierarchy
  • The information will be synchronized to Akeneo

5. Recommended

brix extension Carbon Copy

If there are mappings of data from Akeneo to CELUM, they will be written on the information field of the product node. To copy them from the node to all assigned Assets, we recommend to use our extension Carbon Copy

brix extension Asset Marker

If you like to see in general which assets are used in Akeneo, we recommend to use our extension Asset Marker

Release Notes

1.0

Released 2018-11-01

  • Initial version
1.0.3

Released 2018-11-08

  • Add license property
1.1.0

Released 2018-11-29

  • Add timeout properties
  • Add properties for default country code per language
1.2.0

Released 2018-12-04

  • Add property for akeneo identifier attribute code
1.5.0

Released 2019-01-31

  • Moved hierarchy configuration to akeneo-hierarchy-config.xml
  • Implement export mapping for Akeneo asset collection attributes
  • Implement code to label resolver for dropdown values
1.6.0

Released 2019-02-20

  • Implement code to label resolver for reference data attributes
  • Add support to specify one or more filter expressions for product retrieval
1.7.0

Released 2019-03-05

  • Implement code to label resolver for category attributes
  • Support multiple import mappings per attribute
  • Implement family filter (only sync specified families)
1.8.0

Released 2019-03-13

  • Implement update and deletion handling for export mappings
  • Optionally consider asset availability in export mappings (enabled via configuration property)
1.8.9

Released 2019-03-25

  • Improved progress estimation for sync task
  • Optionally send notification mail if errors occur in sync task
1.9.0

Released 2019-04-11

  • Implement delta mode for sync task: Only considers products which have been updated in akeneo since the last task run
  • Implement new optional (configurable) feature "clear trigger field on move to trashbin"
1.10.0

Released 2019-05-14

  • Use Akeneo 3.x standard API in code to label resolver for reference data attributes