Milesoft Liquid SDK (liquid-sdk)

1. Overview

The Liquid SDK (liquid-sdk) provides a strongly-typed programmatic interface to the Liquid compilation and resolution microservice of the Milesoft platform. It supports high-performance rendering of dynamic text, email layouts, and JSON payloads based on the Liquid template language. It also manages account-level template layouts, global app-level libraries, and custom text filters with automated context lazy-loading.


2. Initialization

2.1 Dependency Installation

The liquid-sdk package is hosted in our secure Google Cloud Artifact Registry repository.

To consume liquid-sdk in your Spring Boot application, apply the Google Cloud Artifact Registry plugin and declare our secure repository in your build.gradle:

plugins {
    id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.5"
}

repositories {
    mavenCentral()
    maven { url "artifactregistry://us-west2-maven.pkg.dev/milesoft-repo/repo-maven" }
}

dependencies {
    // Declaring the standard liquid-sdk library dependency
    implementation "io.milesoft:liquid-sdk:2.1.0"
}

2.2 Central Spring Boot Configuration

Configure the underlying clients and LiquidService helper class inside your Spring configuration layer.

import io.milesoft.clients.PlatformClient;
import io.milesoft.clients.strategy.PlatformTokenProvider;
import io.milesoft.liquid.client.v2.LibraryClient;
import io.milesoft.liquid.client.v2.LiquidClient;
import io.milesoft.liquid.client.v2.TemplateClient;
import io.milesoft.liquid.services.LiquidService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LiquidSdkConfig {

    private final LiquidClient liquidClient;
    private final LibraryClient libraryClient;
    private final TemplateClient templateClient;
    private final LiquidService liquidService;

    @Autowired
    public LiquidSdkConfig(PlatformClient platformClient,
                        @Value("${milesoft.liquid.baseUrl}") String baseUrl) {

       PlatformTokenProvider tokenProvider = new PlatformTokenProvider(platformClient);

       this.liquidClient = new LiquidClient(baseUrl, tokenProvider);
       this.libraryClient = new LibraryClient(baseUrl, tokenProvider);
       this.templateClient = new TemplateClient(baseUrl, tokenProvider);
       this.liquidService = new LiquidService(liquidClient);
    }

    @Bean
    public LiquidClient liquidClient() { return liquidClient; }

    @Bean
    public LibraryClient libraryClient() { return libraryClient; }

    @Bean
    public TemplateClient templateClient() { return templateClient; }

    @Bean
    public LiquidService liquidService() { return liquidService; }
}

3. Core SDK Clients & Method Specifications

Programmatic execution is managed across logical clients designed to completely abstract the underlying REST boundaries.

3.1 TemplateClient

Manages account-specific template configurations.

  • Templates list(String cursor, Integer limit, Actor actor): Lists active account templates.
  • Templates loadBulk(Ids payload, Actor actor): Batch resolves templates by ID lists.
  • Template create(SaveTemplate payload, Actor actor): Registers a template.
  • Template update(String templateId, SaveTemplate payload, Actor actor): Updates template content or metadata.
  • Template copy(String templateId, Actor actor): Clones an existing template within the account.
  • Template installLibrary(String libraryId, Actor actor): Clones an app-level library template into the account portfolio.
  • void delete(String templateId, Actor actor): Purges templates.

3.2 LibraryClient

Handles global app-level templates.

  • Templates listLibrary(String cursor, Integer limit, Actor actor): Lists app-level library templates.
  • Templates loadBulkLibrary(Ids payload, Actor actor): Batch resolves library templates.
  • Template createLibrary(SaveTemplate payload, Actor actor): Registers global templates.
  • Template updateLibrary(String id, SaveTemplate payload, Actor actor): Updates global templates.
  • Template cloneLibrary(String id, Actor actor): Clones library templates.
  • void deleteLibrary(String id, Actor actor): Purges library templates.

3.3 LiquidClient

Performs compilation and resolution of Liquid layouts.

  • Resolved resolve(Resolve payload, Actor actor): Compiles raw Liquid strings using merge variables.
  • ResolvedTemplate resolveSaved(String templateId, MergeData payload, Actor actor): Resolves database templates using merge data.
  • ResolvedBulk resolveBulk(ResolveBulk payload, Actor actor): Compiles multiple raw Liquid strings in a single batch request.

3.4 LiquidService

A high-level convenience service designed for backend applications. It internally extracts the active Actor and handles direct compilation with the minimum boilerplate.

  • String resolve(String template, MergeData data): Directly compiles raw template text.

4. Custom Liquid Filters & Preloaded Contexts

4.1 Custom Template Filters

The Liquid service implements custom formatting filters directly inside the Shopify Liquid compilation engine:

Filter Name Class Example Usage Output / Behavior
json EscapeJsonFilter {{ value | json }} Escapes literal characters safely for raw JSON embedding.
money MoneyFilter {{ 1234.5 | money }} Formats numbers to currency string ($1,234.50).
iso IsoDateTimeFilter {{ created | iso }} Formats date/time, local date, or local time objects to ISO-8601 strings.
full FullDateTimeFilter {{ created | full }} Formats localized datetime using FormatStyle.FULL.
long LongDateTimeFilter {{ created | long }} Formats localized datetime using FormatStyle.LONG.
medium MediumDateTimeFilter {{ created | medium }} Formats localized datetime using FormatStyle.MEDIUM.
short ShortDateTimeFilter {{ created | short }} Formats localized datetime using FormatStyle.SHORT.
pretty PrettyDateTimeFilter {{ created | pretty }} Formats localized datetime using human-friendly pattern eee, d MMM h:mm a.
custom CustomDateTimeFilter {{ created | custom: "yyyy-MM-dd" }} Formats localized datetime using custom format patterns.
range DateTimeRangeFilter {{ start_time | range: stop_time }} Merges two localized datetimes into an optimized localized range span string.

4.2 Lazy-Loaded Context Variables

Render requests leverage lazy-loading mechanisms, fetching metadata from database repositories only if referenced inside the template text:

  1. today (TodayModel): Localized ZonedDateTime, LocalDate, or LocalTime.
  2. app (AppModel): Active application configurations (id, title, url, logo_url, version).
  3. account (AccountModel): Active subscriber metadata, branding colors, and company URLs.
  4. profile (ProfileModel): Profile details of the active operator (id, name, first_name, last_name, email, phone).
  5. user (UserModel): Evaluated if userId is passed in context. Exposes target user profile fields.
  6. contact (ContactModel): Evaluated if contactId is passed in context. Exposes contact directory fields (names, phones, emails, addresses, dates, tags).
  7. users (UsersModel): Plural map of users resolved via userQualifiers.
  8. contacts (ContactsModel): Plural map of contacts resolved via contactQualifiers.

5. Code Examples

5.1 Rendering a Welcome Document

import io.milesoft.liquid.services.LiquidService;
import io.milesoft.primitives.merge.MergeData;
import io.milesoft.primitives.merge.Model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;

@Service
public class DocumentRenderer {

    private final LiquidService liquidService;

    @Autowired
    public DocumentRenderer(LiquidService liquidService) {
        this.liquidService = liquidService;
    }

    public String generateWelcomeEmail(String userId, String contactId, String organizationName) {
        // Construct custom dataset models
        Model companyModel = Model.builder()
            .key("organization")
            .data(Map.of("name", organizationName))
            .build();

        // Populate context merge data
        MergeData mergeData = MergeData.builder()
            .userId(userId)
            .contactId(contactId)
            .models(List.of(companyModel))
            .build();

        String rawTemplate = "Dear {{ contact.first_name }},\n\n"
            + "Our customer success agent, {{ user.name }}, welcomes you on behalf of {{ organization.name }}! "
            + "Your account was successfully provisioned on {{ today.date | pretty }}.\n\n"
            + "Warm regards,\n{{ app.title }} Support";

        // Resolve compiled output using the high-level service helper
        return liquidService.resolve(rawTemplate, mergeData);
    }
}

6. SDK Data Transfer Objects (DTOs)

SaveTemplate

Represents request payload to write or modify a template.

Field Type Description / Constraints
name String Name of the template. Required / Not Blank.
content String Raw Liquid template body. Required / Not Blank.
extra Map<String, String> Optional auxiliary template content map.
order Integer Ordinal sorting value. Required / Min(0).
metadata Map<String, Object> Optional custom metadata.

Template

Represents a persisted template model.

Field Type Description / Constraints
id String Unique identifier. Required.
name String Template name. Required.
content String Raw Liquid template body. Required.
extra Map<String, String> Auxiliary content map.
order Integer Sort order. Required / Min(0).
metadata Map<String, Object> Custom metadata.
created ZonedDateTime Creation timestamp. Required.
updated ZonedDateTime Last update timestamp. Required.

Templates

A paged container of templates.

Field Type Description / Constraints
templates List