Assisted Service Module (DRAFT)

Note: This feature is introduced with version 1.3 of the Spartacus libraries.

Overview

Assisted Service Module (ASM) enables customer service personnel to provide real-time customer sales and service support using the storefront. For more information, refer to the ASM section on SAP Help Portal

Spartacus now supports the ASM functionality that allows customer emulation by sales support agents through the Spartacus storefront.

Requirements

For more information on to setup and configure ASM on your SAP Commerce Cloud, refer to the ASM section on SAP Help Portal.

Here are a few setup steps that are important and/or specific to get ASM working with Spartacus:

ASM Back End Requirements

ASM in Spartacus requires SAP Commerce Cloud version 1905.5 or newer. The minimum version of 1905.5 is required to enable CORS in the assistedservicewebservices endpoints.

The ASM feature in Spartacus requires the following extensions:

  • assistedservicewebservices Extension
  • assistedservicestorefront AddOn

Granting asagentgroup CMS Permissions

The user group asagentgroup needs specific rights to read CMS data from OCC.

Option 1: Initialize from scratch with 1905.5

If you start from scratch and initialize your SAP Commerce Cloud system with version 1905.5 or newer, asagentgroup will get the required permissions to use cms data via Spartacus and OCC. There is no additional step to do.

Option 2: Manual import in impex console

If you upgrade from an earlier version than 1905.5, you need to grant the asagentgroup permissions by importing this impex data via the impex console:

# Access rights for asagentgroup
# - These are needed for rendering (cmsoccaddon).

$START_USERRIGHTS;;;;;;;;;
Type;UID;MemberOfGroups;Password;Target;read;change;create;remove;change_perm
UserGroup;asagentgroup;;;;;;;;

# general
;;;;Item;+;;;;;
;;;;Type;+;;;;;

# access rights for Products, Store, Site and Catalogs
;;;;Product;+;;;;;
;;;;Category;+;;;;;
;;;;VariantType;+;;;;;
;;;;BaseSite;+;;;;;
;;;;BaseStore;+;;;;;
;;;;Catalog;+;;;;;
;;;;CatalogVersion;+;;;;;
;;;;ContentCatalog;+;;;;;
;;;;MediaContainer;+;-;-;-;-;
;;;;MediaContext;+;-;-;-;-;
;;;;MediaFormat;+;-;-;-;-;
;;;;MediaFormatMapping;+;-;-;-;-;

# cms2 items
;;;;CMSItem;+;-;-;-;-;

$END_USERRIGHTS;;;;;

CORS Configuration

The assistedservicewebservices extension requires CORS configuration, which is possible since SAP Commerce Cloud version 1905.5.
The cors configurations for assistedservicewebservices have default values specified in the project.properties file of the assistedservicewebservices. At the time of writing these lines, the default values are:

corsfilter.assistedservicewebservices.allowedOrigins=http://localhost:4200 https://localhost:4200
corsfilter.assistedservicewebservices.allowedMethods=GET HEAD OPTIONS PATCH PUT POST DELETE
corsfilter.assistedservicewebservices.allowedHeaders=origin content-type accept authorization

Customizing CORS Configuration

CORS configurations are customized by overriding the default configuration via your local.properties file.

Since configurations are overridden in local.properties, if you want to add a configuration element without losing the default value, you need to add all the defaults in addition to the new element. For example, to add ‘my-new-header’ in the allowed header list in addition to the default ones, you need to add this in local.properties:

corsfilter.assistedservicewebservices.allowedHeaders=origin content-type accept authorization my-new-header.

To customize allowedMethods and allowedHeaders you should add to the default values. To customize allowedOrigins, you will need to override the value with one that is relevant for your environment.

allowedOrigins

You need to customize the allowedOrigins property for assistedservicewebservices with host names that are relevant to your environment. As mentioned above, this is done by adding the propery yout local.properties with a new value:

corsfilter.assistedservicewebservices.allowedOrigins=https://my-new-host:4200

For development purposes only, the value can be a wildcard:

corsfilter.assistedservicewebservices.allowedOrigins=*

Bear in mind this wildcard configuration is flexible for development environments but it is unsecured. A more restrictive configuration is required for production use.

Invoke the ASM UI in the storefront

To invoke the ASM UI in the storefront, add the ?asm=true suffix to the url. For example, with the sample store, you can invoke the ASM UI on the home page with this url.

https://{hostname}/electronics-spa/en/USD/?asm=true

Update custom services to support ASM.

If you use a custom service that extends one of these classes:

  • UserService
  • UserAddressService
  • UserConsentService
  • UserOrderService
  • UserPaymentService

You need to update their constructor in order to support ASM in your storefront.

If during customer emulation the storefront displays the error message: Cannot find user with propertyValue 'current', it is very likely that you use custom services that need to be updated to support ASM.

The update consists of adding a dependency to AuthService in the constructor and pass it down to super(). The constructors that don’t have AuthService are now deprecated.

Beyond the constructor update, you may need to update your custom functions to support ASM as well. See the section called How to write ASM compatible code for details.

Update UserService Subclasses

Custom service

export class CustomUserService extends UserService {
  constructor(store: Store<StateWithUser | StateWithProcess<void>>) {
    super(store);
  }
}

Custom service updated to support ASM.

export class CustomUserService extends UserService {
  constructor(
    store: Store<StateWithUser | StateWithProcess<void>>,
    authService: AuthService
  ) {
    super(store, authService);
  }
}

Update UserAddressService Subclasses

Custom service

export class CustomUserAddressService extends UserAddressService {
  constructor(protected store: Store<StateWithUser | StateWithProcess<void>>) {
    super(store);
  }
}

Custom service updated to support ASM.

export class CustomUserAddressService extends UserAddressService {
  constructor(
    protected store: Store<StateWithUser | StateWithProcess<void>>,
    protected authService: AuthService
  ) {
    super(store, authService);
  }
}

Update UserConsentService Subclasses

Custom service

export class CustomUserConsentService extends UserConsentService {
  constructor(protected store: Store<StateWithUser | StateWithProcess<void>>) {
    super(store);
  }
}

Custom service updated to support ASM.

export class CustomUserConsentService extends UserConsentService {
  constructor(
    protected store: Store<StateWithUser | StateWithProcess<void>>,
    protected authService: AuthService
  ) {
    super(store, authService);
  }
}

Update UserOrderService Subclasses

Custom service

export class CustomUserOrderService extends UserOrderService {
  constructor(protected store: Store<StateWithUser | StateWithProcess<void>>) {
    super(store);
  }
}

Custom service updated to support ASM.

export class CustomUserOrderService extends UserOrderService {
  constructor(
    protected store: Store<StateWithUser | StateWithProcess<void>>,
    protected authService: AuthService
  ) {
    super(store, authService);
  }
}

Update UserPaymentService Subclasses

Custom service

export class CustomUserPaymentService extends UserPaymentService {
  constructor(protected store: Store<StateWithUser | StateWithProcess<void>>) {
    super(store);
  }
}

Custom service updated to support ASM.

export class CustomUserPaymentService extends UserPaymentService {
  constructor(
    protected store: Store<StateWithUser | StateWithProcess<void>>,
    protected authService: AuthService
  ) {
    super(store, authService);
  }
}

How to write ASM compatible code.

Writing ASM compatible code is all about using the function getOccUserId() from the AuthService to determine the userId used in occ calls. This is typically done in a service that dispatches and action containing the userId in the payload.

Before ASM was released, the occ userId in requests on the behalf of an authenticated user was the special occ user “current”, represented by the constant OCC_USER_ID_CURRENT, like in this example:

  load(): void {
    this.store.dispatch(new UserActions.LoadUserDetails(OCC_USER_ID_CURRENT));
  }

Now that Spartacus supports ASM, the correct way to determine the occ userId is to call AuthService.getOccUserId(). When we update the previous example, we get:


  load(): void {
    this.authService
      .getOccUserId()
      .pipe(take(1))
      .subscribe(occUserId =>
        this.store.dispatch(new UserActions.LoadUserDetails(occUserId))
      );
  }

The rule of thumb is, if OCC_USER_ID_CURRENT is used directly in a service, it should likely be replaced by a call to getOccUserId().

In order to support ASM, and potentially other features in the future, the facade services can’t simply use the “current” special userId when calling various actions. There needs to be some logic applied to determine the correct OCC userId to pass down to actions that will trigger a backend call.

Therefore, the logic to determine the correct OCC userId given the context is centralized in the AuthService function getOccUserId().

Configuring

Some ASM behaviors can be configures through Spartacus.

asm.agentSessionTimer.startingDelayInSeconds

The start time for the customer support agent session timer has a default value of 600 seconds (10 minutes). This can be configured. Specify the number of seconds for the timer staring delay via the property asm.agentSessionTimer.startingDelayInSeconds like so ( using the B2cStorefrontModule as an example ):

B2cStorefrontModule.withConfig({
  asm: {
    agentSessionTimer: {
      startingDelayInSeconds: 600
    }
  }
});

asm.customeSearch.maxResults

The number of results in the asm customer search can be customized in spartacus via the property asm.customeSearch.maxResults. You define it like this ( using the B2cStorefrontModule as an example ):

B2cStorefrontModule.withConfig({
  asm: {
    customeSearch: {
      maxResults: 20
    }
  }
});

Extending

No special extensibility is available for this feature.