Broadleaf Microservices
  • v1.0.0-latest-prod

Admin Metadata Release Notes for 2.2.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

Notable Changes

Added support for restricting routes by matching all security scopes not just any.

  • The previous default behavior when matching a Route’s security scopes was to match any.

  • Route now has a scopeMatchType property that can be set to either ANY or ALL, with ANY as the default.

  • Related changes have been made to Metadata Common 2.0.7.

Example Usage
@Bean
public ComponentRouteLocator productMetadataRoutes(RoutesBuilder routesBuilder) {
    RoutesSpec routes = routesBuilder.routes()
            .route("/products", r -> r.componentId("catalog:porducts:browse")
                    .scope("PRODUCT")
                    .scope("CUSTOM_ADMIN_VIEW_SCOPE")
                    .allMatchScopes(); // <-- NEW, default is `.anyMatchScopes()`
    return routes.build();
}

Added endpoint to read multiple components metadata by ID

  • GET /api/metadata/component taking a comma-separated list of component IDs as the ids query parameter.

  • This is in support of allowing multiple Routes to be mapped to the same Route and differentiated by security scope. Security scopes are evaluated in the Admin App rather than on the backend when determining whether to render a route.

Added support for generating Business Type Routes and Components for multiple base views

Configuration properties were add to determine what routes and route components should be generated when a Business Type is created. Previously, this was hardcoded to assume single base versions of the product browse, create, and update views. With these changes, users can now configure an unlimited number of routes including all the granular details like path, parent component key, and required scopes. The currently hardcoded values are now the default values for the new properties.

Note
Also added was a property to configure the supported entity types for the MetadataProductBusinessTypeModifiedHandler instead of just hardcoding PRODUCT, using broadleaf.metadata.business-type.product.supported-entity-types.

The following property takes a list of RouteProperties POJOs: broadleaf.metadata.business-type.product.routes.

Example of how the default routes for Business Types are configured
broadleaf:
  metadata:
    business-type:
      product:
        routes:
          - pathTemplate: /products/{businessType}
            routeComponentType: BROWSE
            # routeComponentLabel: <optional>
            componentKeyTemplate: catalog:products:{businessType}
            parentComponentKey: catalog:products:browse,
            scopes:
              - PRODUCT
            # optional, default is ANY
            scopeMatchType: ANY
          - pathTemplate: /products/{businessType}/create
            routeComponentType: CREATE
            # routeComponentLabel: <optional>
            componentKeyTemplate: catalog:products:{businessType}
            parentComponentKey: catalog:products:{parentType}:create
            scopes:
              - PRODUCT
            # optional
            scopeMatchType: ANY
          - pathTemplate: /products/{businessType}/:id
            routeComponentType: UPDATE
            # routeComponentLabel: <optional>
            componentKeyTemplate: catalog:products:{businessType}
            parentComponentKey: catalog:products:{parentType}:update
            scopes:
              - PRODUCT
            # optional
            scopeMatchType: ANY

Example Use Case

It may be the case that users want their Admins to see different versions of a view based on their role, e.g., a Merchandiser’s vs a Super Admin’s view of Products. If these views are sufficiently different from each other, it may be easier to configure separate view components rather than adding conditionals to each group and field of the Super Admin’s view. Given that these views are distinct, they also must have different component IDs, and, therefore, different routes configured. However, the routes will have the same path and will only be differentiated by the required scopes to access them.

Take the following as an example where we set up different routes for the Merchandiser and Super Admin views of Products.

Java Metadata Configuration
@RequiredArgsConstructor
public class CustomProductMetadataRoutesAutoConfiguration {
    private final CatalogMetadataProperties properties;

    @Bean
    public ComponentRouteLocator productMetadataRoutes(RoutesBuilder routesBuilder) {
        RoutesSpec routes = routesBuilder.routes();

        addRoutes("catalog:products:%s", "SUPER_ADMIN_VIEW");
        addRoutes("merchandiser:catalog:products:%s", "MERCHANDISER_ADMIN_VIEW");

        return routes.build();
    }

    private void addRoutes(String componentId, String scope) {
        routes.route("/products", r -> r.componentId(componentId.formatted("browse"))
                    .scope(scope)
                    .scope(ProductScopes.PRODUCT)
                    .allMatchScopes());

            getAvailableProductTypes().forEach(type -> {
                routes.route("/products/%s/create".formatted(type.name()),
                        r -> r.componentId(componentId.formatted(type.name() + ":create"))
                                .scope(scope)
                                .scope(ProductScopes.PRODUCT)
                                .allMatchScopes());
                routes.route("/products/%s/:id".formatted(type.name()),
                        r -> r.componentId(componentId.formatted(type.name() + ":update"))
                                .scope(scope)
                                .scope(ProductScopes.PRODUCT)
                                .allMatchScopes());
            });
    }

    private List<DefaultProductType> getAvailableProductTypes() {
        return Arrays.stream(values())
                .filter(t -> properties.getActiveProductTypes().contains(t.name()))
                .collect(Collectors.toList());
    }
}

With this, there are now two different views for each view type for Products. Therefore, when a new Product Type (a.k.a., Business Type) is created, it needs to generate two different routes for each of the three view types rather than just one. Moreover, the routes need to be distinguished by their scopes so that the correct one is rendered based on the user’s role. broadleaf.metadata.business-type.product.routes will now allow for this.

Example configuring the new routes to generate for Business Types
broadleaf:
  metadata:
    business-type:
      product:
        routes:
          # First, override the default routes for the Super Admin
          - pathTemplate: /products/{businessType}
            routeComponentType: BROWSE
            routeComponentLabel: Super Admin
            componentKeyTemplate: catalog:products:{businessType}
            # We point to the base product views as the parents for the super admin
            parentComponentKey: catalog:products:browse
            # we add our custom scope to gate this version of the view
            scopes:
              - SUPER_ADMIN_VIEW
              - PRODUCT
            # This will require both our new scope and the default PRODUCT scope to be present
            scopeMatchType: ALL
          - pathTemplate: /products/{businessType}/create
            routeComponentType: CREATE
            # This will result in the Product Type form having actions for customizing the views
            # having a label of "Modify Super Admin Create View" and "Modify Super Admin Update View".
            # This will distinguish one view type from the other.
            # By default the labels are simply "Modify Create View" and "Modify Update View".
            routeComponentLabel: Super Admin
            componentKeyTemplate: catalog:products:{businessType}
            parentComponentKey: catalog:products:{parentType}:create
            scopes:
              - SUPER_ADMIN_VIEW
              - PRODUCT
            scopeMatchType: ALL
          - pathTemplate: /products/{businessType}/:id
            routeComponentType: UPDATE
            routeComponentLabel: Super Admin
            componentKeyTemplate: catalog:products:{businessType}
            parentComponentKey: catalog:products:{parentType}:update
            scopes:
              - SUPER_ADMIN_VIEW
              - PRODUCT
            scopeMatchType: ALL
          # Second, configure new routes for the Merchandiser admin
          - pathTemplate: /products/{businessType}
            routeComponentType: BROWSE
            routeComponentLabel: Merchandiser
            componentKeyTemplate: merchandiser:catalog:products:{businessType}
            # here is where we pick the base view to use that we configured in java above
            parentComponentKey: merchandiser:catalog:products:browse
            # we user the merchandiser's scope instead of the super admin's
            scopes:
              - MERCHANDISER_ADMIN_VIEW
              - PRODUCT
            scopeMatchType: ALL
          - pathTemplate: /products/{businessType}/create
            routeComponentType: CREATE
            # This will result in the Product Type form having actions for customizing the views
            # having a label of "Modify Merchandiser Create View" and "Modify Merchandiser Update View".
            routeComponentLabel: Merchandiser
            componentKeyTemplate: merchandiser:catalog:products:{businessType}
            parentComponentKey: merchandiser:catalog:products:{parentType}:create
            scopes:
              - MERCHANDISER_ADMIN_VIEW
              - PRODUCT
            scopeMatchType: ALL
          - pathTemplate: /products/{businessType}/:id
            routeComponentType: UPDATE
            routeComponentLabel: Merchandiser
            componentKeyTemplate: merchandiser:catalog:products:{businessType}
            parentComponentKey: merchandiser:catalog:products:{parentType}:update
            scopes:
              - MERCHANDISER_ADMIN_VIEW
              - PRODUCT
            scopeMatchType: ALL

Minor Enhancement

  • Updated logic for retrieving the augmentation key for business type components due to recently applied type-strictness checking

  • Introduced functionality for handling characteristics associated with BusinessType entities in the metadata service. Also, to support the creation and management of augmentations for characteristics.

Bug Fixes

  • Fixed an issue that occasionally caused new product creation from the admin to result in 404. The fix was to use a List instead of a Set when declaring routes for product business types.