@fluentcommerce/ai-skills 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +622 -0
- package/bin/cli.mjs +1973 -0
- package/content/cli/agents/fluent-cli/agent.json +149 -0
- package/content/cli/agents/fluent-cli.md +132 -0
- package/content/cli/skills/fluent-bootstrap/SKILL.md +181 -0
- package/content/cli/skills/fluent-cli-index/SKILL.md +63 -0
- package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +77 -0
- package/content/cli/skills/fluent-cli-reference/SKILL.md +1031 -0
- package/content/cli/skills/fluent-cli-retailer/SKILL.md +85 -0
- package/content/cli/skills/fluent-cli-settings/SKILL.md +106 -0
- package/content/cli/skills/fluent-connect/SKILL.md +886 -0
- package/content/cli/skills/fluent-module-deploy/SKILL.md +349 -0
- package/content/cli/skills/fluent-profile/SKILL.md +180 -0
- package/content/cli/skills/fluent-workflow/SKILL.md +310 -0
- package/content/dev/agents/fluent-dev/agent.json +88 -0
- package/content/dev/agents/fluent-dev.md +525 -0
- package/content/dev/reference-modules/catalog.json +4754 -0
- package/content/dev/skills/fluent-build/SKILL.md +192 -0
- package/content/dev/skills/fluent-connection-analysis/SKILL.md +386 -0
- package/content/dev/skills/fluent-custom-code/SKILL.md +895 -0
- package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +714 -0
- package/content/dev/skills/fluent-e2e-test/SKILL.md +394 -0
- package/content/dev/skills/fluent-event-api/SKILL.md +945 -0
- package/content/dev/skills/fluent-feature-explain/SKILL.md +603 -0
- package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +695 -0
- package/content/dev/skills/fluent-feature-plan/SKILL.md +227 -0
- package/content/dev/skills/fluent-job-batch/SKILL.md +138 -0
- package/content/dev/skills/fluent-mermaid-validate/SKILL.md +86 -0
- package/content/dev/skills/fluent-module-scaffold/SKILL.md +1928 -0
- package/content/dev/skills/fluent-module-validate/SKILL.md +775 -0
- package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1108 -0
- package/content/dev/skills/fluent-retailer-config/SKILL.md +1111 -0
- package/content/dev/skills/fluent-rule-scaffold/SKILL.md +385 -0
- package/content/dev/skills/fluent-scope-decompose/SKILL.md +1021 -0
- package/content/dev/skills/fluent-session-audit-export/SKILL.md +632 -0
- package/content/dev/skills/fluent-session-summary/SKILL.md +195 -0
- package/content/dev/skills/fluent-settings/SKILL.md +1058 -0
- package/content/dev/skills/fluent-source-onboard/SKILL.md +632 -0
- package/content/dev/skills/fluent-system-monitoring/SKILL.md +767 -0
- package/content/dev/skills/fluent-test-data/SKILL.md +513 -0
- package/content/dev/skills/fluent-trace/SKILL.md +1143 -0
- package/content/dev/skills/fluent-transition-api/SKILL.md +346 -0
- package/content/dev/skills/fluent-version-manage/SKILL.md +744 -0
- package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +959 -0
- package/content/dev/skills/fluent-workflow-builder/SKILL.md +319 -0
- package/content/dev/skills/fluent-workflow-deploy/SKILL.md +267 -0
- package/content/mcp-extn/agents/fluent-mcp.md +69 -0
- package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +461 -0
- package/content/mcp-official/agents/fluent-mcp-core.md +91 -0
- package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -0
- package/content/rfl/agents/fluent-rfl.md +56 -0
- package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -0
- package/docs/CAPABILITY_MAP.md +77 -0
- package/docs/CLI_COVERAGE.md +47 -0
- package/docs/DEV_WORKFLOW.md +802 -0
- package/docs/FLOW_RUN.md +142 -0
- package/docs/USE_CASES.md +404 -0
- package/metadata.json +156 -0
- package/package.json +51 -0
|
@@ -0,0 +1,1928 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fluent-module-scaffold
|
|
3
|
+
description: Scaffold a new Fluent Commerce extension module. Generates Maven project structure, module.json, build scripts, initial rule classes, test skeletons, and .gitignore. Triggers on "scaffold module", "new module", "create module", "initialize module".
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
|
|
6
|
+
argument-hint: <module-name> [--entity-types ORDER,FULFILMENT] [--rules RuleName1,RuleName2] [--account-prefix ACCT]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Module Scaffolder
|
|
10
|
+
|
|
11
|
+
Generate a complete, buildable Fluent Commerce extension module skeleton from a module name, entity scope, and optional rule list. The output is a directory under `accounts/<PROFILE>/SOURCE/` that passes `/fluent-module-validate` and `/fluent-build` on first run.
|
|
12
|
+
|
|
13
|
+
## Pre-Check: New Module or Extend Existing?
|
|
14
|
+
|
|
15
|
+
**ALWAYS run this decision tree before scaffolding. Do NOT skip.**
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
User asks: "I need a module for X" / "Build me a module" / "Create rules for X"
|
|
19
|
+
│
|
|
20
|
+
├── 1. Discover existing modules in workspace
|
|
21
|
+
│ Search: accounts/<PROFILE>/SOURCE/*/resources/module.json
|
|
22
|
+
│ Also check: accounts/<PROFILE>/analysis/custom-code/source-map.json
|
|
23
|
+
│ List found modules with their rules and entity types
|
|
24
|
+
│
|
|
25
|
+
├── 2. Check deployed modules in live environment
|
|
26
|
+
│ Use MCP tool: plugin.list (no filter — get all custom rules)
|
|
27
|
+
│ Look for <ACCOUNT>.* rules (not FLUENTRETAIL.*)
|
|
28
|
+
│ Cross-reference with local SOURCE/ to find modules with source code available
|
|
29
|
+
│
|
|
30
|
+
├── 3. Evaluate: does the new functionality fit an existing module?
|
|
31
|
+
│ ├── YES — functionality belongs in an existing module's domain
|
|
32
|
+
│ │ ├── Source code in SOURCE/? → Use /fluent-rule-scaffold to add rules
|
|
33
|
+
│ │ ├── No source code? → Ask user to clone the repo first
|
|
34
|
+
│ │ │ "Module <name> is deployed but source not found in accounts/<PROFILE>/SOURCE/"
|
|
35
|
+
│ │ │ "Please clone the repo: git clone <url> accounts/<PROFILE>/SOURCE/<repo>"
|
|
36
|
+
│ │ │ Then use /fluent-custom-code to analyze, then /fluent-rule-scaffold
|
|
37
|
+
│ │ └── STOP — do NOT scaffold a new module
|
|
38
|
+
│ │
|
|
39
|
+
│ ├── NO — genuinely new domain, no existing module covers it
|
|
40
|
+
│ │ └── Proceed with scaffolding below
|
|
41
|
+
│ │
|
|
42
|
+
│ └── UNCLEAR — could go either way
|
|
43
|
+
│ └── Ask user: "Should I add rules to existing module <X> or create a new module?"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Source Code Availability Check
|
|
47
|
+
|
|
48
|
+
Before extending an existing module, verify the source is accessible:
|
|
49
|
+
|
|
50
|
+
| Source State | Action |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `accounts/<PROFILE>/SOURCE/<repo>/` exists with `pom.xml` + `module.json` | Ready — use `/fluent-rule-scaffold` |
|
|
53
|
+
| Module deployed but no source in `SOURCE/` | Ask user to clone repo into `SOURCE/` |
|
|
54
|
+
| Source exists but no `source-map.json` analysis | Run `/fluent-custom-code` first to map packages, base classes, conventions |
|
|
55
|
+
| Source exists with stale `source-map.json` | Re-run `/fluent-custom-code` to refresh |
|
|
56
|
+
|
|
57
|
+
## Planning Gate
|
|
58
|
+
|
|
59
|
+
**After the pre-check determines a NEW module is needed, write a plan using the template from `PLAN_TEMPLATE.md` in the `fluent-feature-plan` skill.** Every table row must carry a Source column (NEW/EXISTING/MODIFIED/REUSED/OOTB).
|
|
60
|
+
|
|
61
|
+
**Module-scaffold specific emphasis — ensure these are covered:**
|
|
62
|
+
|
|
63
|
+
1. **Business Context (Section 1)** — why a new module (not extending existing), decision rationale from pre-check
|
|
64
|
+
2. **Entity relationship diagram (Section 3)** — Mermaid diagram showing entity types the module operates on and their edges. Validate syntax per `/fluent-mermaid-validate`
|
|
65
|
+
3. **Cross-entity flow (Section 3.2)** — Mermaid `sequenceDiagram` showing how the module's rules will be triggered and what events/mutations they produce. Validate syntax per `/fluent-mermaid-validate`
|
|
66
|
+
4. **Impacted workflows (Section 4.1)** — which workflow rulesets will use these rules (existing or new)
|
|
67
|
+
5. **Rulesets & rules (Section 4.3)** — initial rules table with entity type, trigger event, OOTB/custom, inline/scheduled, description, parameters
|
|
68
|
+
6. **Settings (Section 4.4)** — settings the rules will need, with context and expected format
|
|
69
|
+
7. **GraphQL operations (Section 4.7)** — queries and mutations the rules will execute
|
|
70
|
+
8. **Detailed Design (Section 5)** — Maven directory structure, SDK version, parent POM, dependencies, package naming convention
|
|
71
|
+
|
|
72
|
+
**Write the plan to:** `accounts/<PROFILE>/plans/<YYYY-MM-DD>-module-scaffold-<slug>.md`. Set `Status: PENDING`.
|
|
73
|
+
|
|
74
|
+
Present the full plan content to the user and wait for approval before generating any files. On approval, update the file to `Status: APPROVED`. If the user says "just do it", proceed directly (still write the file for audit trail).
|
|
75
|
+
|
|
76
|
+
## When to Use
|
|
77
|
+
|
|
78
|
+
- Creating a brand-new Fluent Commerce extension module from scratch
|
|
79
|
+
- Starting a greenfield project that needs a complete Maven + module.json + build scripts structure
|
|
80
|
+
- Bootstrapping a module for a new account or retailer where no custom code exists yet
|
|
81
|
+
- Generating an empty module shell to be populated incrementally with `/fluent-rule-scaffold`
|
|
82
|
+
|
|
83
|
+
## Ownership Boundary
|
|
84
|
+
|
|
85
|
+
This skill owns:
|
|
86
|
+
|
|
87
|
+
- Creating the complete module directory structure
|
|
88
|
+
- Generating the parent POM and all submodule POMs (types, util, rules)
|
|
89
|
+
- Generating `module.json` with correct Fluent Commerce manifest format
|
|
90
|
+
- Generating initial rule classes and test skeletons (if `--rules` specified)
|
|
91
|
+
- Generating build scripts (bash `.sh` and PowerShell `.ps1`)
|
|
92
|
+
- Generating `.gitignore`
|
|
93
|
+
- Account prefix detection for rule registration
|
|
94
|
+
|
|
95
|
+
This skill does **not** own:
|
|
96
|
+
|
|
97
|
+
- Adding rules to an existing module --> `/fluent-rule-scaffold`
|
|
98
|
+
- Building and packaging --> `/fluent-build`
|
|
99
|
+
- Validating module structure --> `/fluent-module-validate`
|
|
100
|
+
- Deploying modules --> `/fluent-module-deploy`
|
|
101
|
+
- Analyzing existing custom code --> `/fluent-custom-code`
|
|
102
|
+
|
|
103
|
+
## Inputs
|
|
104
|
+
|
|
105
|
+
| Parameter | Required | Default | Description |
|
|
106
|
+
|-----------|----------|---------|-------------|
|
|
107
|
+
| `module-name` | Yes | -- | Module identifier (e.g., `hm-returns`). Used for directory names, POM artifactId, module.json name. Must be lowercase alphanumeric with hyphens only. |
|
|
108
|
+
| `--entity-types` | No | `ORDER` | Comma-separated entity types the module's rules will operate on. Valid: `ORDER`, `FULFILMENT`, `FULFILMENT_OPTIONS`, `ARTICLE`, `CONSIGNMENT`, `WAVE`, `LOCATION`, `PRODUCT`, `VIRTUAL_CATALOGUE`, `INVENTORY_POSITION`, `BILLING_ACCOUNT`, `RETURN_ORDER` |
|
|
109
|
+
| `--rules` | No | -- | Comma-separated rule class names to scaffold (each gets a Java class + test). Names must be PascalCase. |
|
|
110
|
+
| `--account-prefix` | No | Auto-detect from profile | Account context prefix for rule registration in module.json (e.g., `HMDEV`). Used as the OSGi symbolic name prefix. |
|
|
111
|
+
| `--group-id` | No | `com.fluentcommerce` | Maven groupId for all POMs |
|
|
112
|
+
| `--initial-version` | No | `1.0.0-SNAPSHOT` | Initial POM and module.json version |
|
|
113
|
+
| `--profile` | No | Active profile | Fluent CLI profile for account prefix detection and module path resolution |
|
|
114
|
+
|
|
115
|
+
## Pre-Check: Module Already Exists?
|
|
116
|
+
|
|
117
|
+
Before creating anything, check if the target directory already exists:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-<module-name>/
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
If it exists, **abort** with message:
|
|
124
|
+
```
|
|
125
|
+
Module directory already exists: accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-<module-name>/
|
|
126
|
+
Use /fluent-rule-scaffold to add rules to an existing module, or choose a different module name.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Also check for name collisions against deployed modules:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
# Query deployed modules via Fluent CLI
|
|
133
|
+
fluent module list -p <PROFILE>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
If a module with the same name is already deployed, warn (but do not abort -- the user may be re-creating local source for an existing deployed module).
|
|
137
|
+
|
|
138
|
+
## Account Prefix Detection
|
|
139
|
+
|
|
140
|
+
The account prefix is used in `module.json` rule registration keys and the OSGi symbolic name. Detection order:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
1. If --account-prefix provided, use it directly
|
|
144
|
+
|
|
145
|
+
2. Else query registered rules via MCP:
|
|
146
|
+
plugin.list (compact: true)
|
|
147
|
+
→ Parse first custom rule key: "<ACCOUNT>.<context>.<RuleName>"
|
|
148
|
+
→ Extract <ACCOUNT> prefix (the part before the first dot that is NOT "FLUENTRETAIL")
|
|
149
|
+
|
|
150
|
+
3. Else derive from PROFILE name:
|
|
151
|
+
HMDEV → HMDEV
|
|
152
|
+
SAGIRISH → SAGIRISH
|
|
153
|
+
(uppercase the profile name)
|
|
154
|
+
|
|
155
|
+
4. Fallback: "UNKNOWN"
|
|
156
|
+
→ Warn user: "Could not detect account prefix. Update module.json manually."
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Module Alias Derivation
|
|
160
|
+
|
|
161
|
+
The `<module-alias>` is derived from `<module-name>` by removing the common prefix pattern. It is used for submodule directory names and artifact suffixes:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
module-name: hm-returns
|
|
165
|
+
module-alias: hm-returns
|
|
166
|
+
|
|
167
|
+
module-name: sagirish-extensions
|
|
168
|
+
module-alias: sagirish-extensions
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The alias is the same as the module name. It appears in paths like `plugins/rules/<module-alias>/`.
|
|
172
|
+
|
|
173
|
+
### OSGi Symbolic Name
|
|
174
|
+
|
|
175
|
+
Derived from the account prefix and module alias with special characters removed:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
account-prefix: HMDEV
|
|
179
|
+
module-alias: hm-returns
|
|
180
|
+
osgi-name: _.hmreturns
|
|
181
|
+
|
|
182
|
+
account-prefix: SAGIRISH
|
|
183
|
+
module-alias: sagirish-extensions
|
|
184
|
+
osgi-name: _.sagirishextensions
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Pattern: `_.<alias-with-hyphens-removed>`
|
|
188
|
+
|
|
189
|
+
## Generated Directory Structure
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-<module-name>/
|
|
193
|
+
+-- plugins/
|
|
194
|
+
| +-- pom.xml # Parent POM (multi-module, packaging=pom)
|
|
195
|
+
| +-- types/
|
|
196
|
+
| | +-- pom.xml # Types aggregator POM (packaging=pom)
|
|
197
|
+
| | +-- <module-alias>/
|
|
198
|
+
| | +-- pom.xml # Types implementation POM
|
|
199
|
+
| | +-- src/
|
|
200
|
+
| | +-- main/java/com/fluentcommerce/types/ # (empty, ready for DTOs)
|
|
201
|
+
| +-- util/
|
|
202
|
+
| | +-- pom.xml # Util aggregator POM (packaging=pom)
|
|
203
|
+
| | +-- <module-alias>/
|
|
204
|
+
| | +-- pom.xml # Util implementation POM
|
|
205
|
+
| | +-- src/
|
|
206
|
+
| | +-- main/java/com/fluentcommerce/util/ # (empty, ready for helpers)
|
|
207
|
+
| +-- rules/
|
|
208
|
+
| +-- pom.xml # Rules aggregator POM (packaging=pom)
|
|
209
|
+
| +-- <module-alias>/
|
|
210
|
+
| +-- pom.xml # Rules implementation POM (OSGi bundle)
|
|
211
|
+
| +-- src/
|
|
212
|
+
| +-- main/java/com/fluentcommerce/rule/
|
|
213
|
+
| | +-- <package>/ # Sub-package per entity type
|
|
214
|
+
| | +-- <RuleName1>.java # (if --rules specified)
|
|
215
|
+
| | +-- <RuleName2>.java
|
|
216
|
+
| +-- test/java/com/fluentcommerce/rule/
|
|
217
|
+
| +-- <package>/
|
|
218
|
+
| +-- <RuleName1>Test.java # (if --rules specified)
|
|
219
|
+
| +-- <RuleName2>Test.java
|
|
220
|
+
+-- resources/
|
|
221
|
+
| +-- module.json # Fluent Commerce module manifest
|
|
222
|
+
| +-- settings/ # (empty, ready for default settings JSON)
|
|
223
|
+
+-- global/ # (empty, for GraphQL schema.json)
|
|
224
|
+
+-- scripts/
|
|
225
|
+
| +-- build-module.sh # Bash build + packaging script
|
|
226
|
+
| +-- build-module.ps1 # PowerShell build + packaging script
|
|
227
|
+
+-- dist/ # (empty, output dir for ZIP artifacts)
|
|
228
|
+
+-- .gitignore
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### POM Hierarchy
|
|
232
|
+
|
|
233
|
+
The Maven project uses a 3-level POM hierarchy matching real Fluent Commerce modules:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
plugins/pom.xml (parent, packaging=pom)
|
|
237
|
+
+-- types/pom.xml (aggregator, packaging=pom)
|
|
238
|
+
| +-- types/<alias>/pom.xml (implementation)
|
|
239
|
+
+-- util/pom.xml (aggregator, packaging=pom)
|
|
240
|
+
| +-- util/<alias>/pom.xml (implementation)
|
|
241
|
+
+-- rules/pom.xml (aggregator, packaging=pom)
|
|
242
|
+
+-- rules/<alias>/pom.xml (implementation, OSGi bundle)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The rules implementation POM is the only one that produces a deployable JAR. It depends on the types and util artifacts.
|
|
246
|
+
|
|
247
|
+
## File Templates
|
|
248
|
+
|
|
249
|
+
### Parent POM (`plugins/pom.xml`)
|
|
250
|
+
|
|
251
|
+
```xml
|
|
252
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
253
|
+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
254
|
+
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
255
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
256
|
+
<modelVersion>4.0.0</modelVersion>
|
|
257
|
+
|
|
258
|
+
<groupId>${GROUP_ID}</groupId>
|
|
259
|
+
<artifactId>fc-module-${MODULE_NAME}-plugins</artifactId>
|
|
260
|
+
<version>${INITIAL_VERSION}</version>
|
|
261
|
+
<packaging>pom</packaging>
|
|
262
|
+
|
|
263
|
+
<name>FC Module ${MODULE_TITLE} - Plugins Parent</name>
|
|
264
|
+
<description>Parent POM for fc-module-${MODULE_NAME} plugin builds</description>
|
|
265
|
+
|
|
266
|
+
<modules>
|
|
267
|
+
<module>types</module>
|
|
268
|
+
<module>util</module>
|
|
269
|
+
<module>rules</module>
|
|
270
|
+
</modules>
|
|
271
|
+
|
|
272
|
+
<properties>
|
|
273
|
+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
274
|
+
<maven.compiler.source>1.8</maven.compiler.source>
|
|
275
|
+
<maven.compiler.target>1.8</maven.compiler.target>
|
|
276
|
+
<apollo-client-maven-plugin.version>1.0.0.15</apollo-client-maven-plugin.version>
|
|
277
|
+
</properties>
|
|
278
|
+
|
|
279
|
+
<build>
|
|
280
|
+
<pluginManagement>
|
|
281
|
+
<plugins>
|
|
282
|
+
<plugin>
|
|
283
|
+
<groupId>com.fluentcommerce</groupId>
|
|
284
|
+
<artifactId>apollo-client-maven-plugin</artifactId>
|
|
285
|
+
<version>${apollo-client-maven-plugin.version}</version>
|
|
286
|
+
<configuration>
|
|
287
|
+
<basePackage>com.fluentcommerce.graphql</basePackage>
|
|
288
|
+
<introspectionFile>${project.basedir}/../../../global/schema.json</introspectionFile>
|
|
289
|
+
</configuration>
|
|
290
|
+
<executions>
|
|
291
|
+
<execution>
|
|
292
|
+
<id>generate-classes</id>
|
|
293
|
+
<goals>
|
|
294
|
+
<goal>generate</goal>
|
|
295
|
+
</goals>
|
|
296
|
+
</execution>
|
|
297
|
+
<execution>
|
|
298
|
+
<id>replace-customtypes</id>
|
|
299
|
+
<goals>
|
|
300
|
+
<goal>replace</goal>
|
|
301
|
+
</goals>
|
|
302
|
+
</execution>
|
|
303
|
+
</executions>
|
|
304
|
+
<dependencies>
|
|
305
|
+
<dependency>
|
|
306
|
+
<groupId>com.squareup.okio</groupId>
|
|
307
|
+
<artifactId>okio</artifactId>
|
|
308
|
+
<version>1.17.6</version>
|
|
309
|
+
</dependency>
|
|
310
|
+
<dependency>
|
|
311
|
+
<groupId>com.squareup.okhttp3</groupId>
|
|
312
|
+
<artifactId>okhttp</artifactId>
|
|
313
|
+
<version>3.14.9</version>
|
|
314
|
+
</dependency>
|
|
315
|
+
</dependencies>
|
|
316
|
+
</plugin>
|
|
317
|
+
</plugins>
|
|
318
|
+
</pluginManagement>
|
|
319
|
+
</build>
|
|
320
|
+
|
|
321
|
+
</project>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Types Aggregator POM (`plugins/types/pom.xml`)
|
|
325
|
+
|
|
326
|
+
```xml
|
|
327
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
328
|
+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
329
|
+
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
330
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
331
|
+
<modelVersion>4.0.0</modelVersion>
|
|
332
|
+
|
|
333
|
+
<parent>
|
|
334
|
+
<groupId>${GROUP_ID}</groupId>
|
|
335
|
+
<artifactId>fc-module-${MODULE_NAME}-plugins</artifactId>
|
|
336
|
+
<version>${INITIAL_VERSION}</version>
|
|
337
|
+
</parent>
|
|
338
|
+
|
|
339
|
+
<artifactId>fc-module-${MODULE_NAME}-types</artifactId>
|
|
340
|
+
<packaging>pom</packaging>
|
|
341
|
+
|
|
342
|
+
<name>FC Module ${MODULE_TITLE} - Types</name>
|
|
343
|
+
<description>Parent POM for fc-module-${MODULE_NAME} types plugins</description>
|
|
344
|
+
|
|
345
|
+
<modules>
|
|
346
|
+
<module>${MODULE_ALIAS}</module>
|
|
347
|
+
</modules>
|
|
348
|
+
|
|
349
|
+
</project>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Types Implementation POM (`plugins/types/<module-alias>/pom.xml`)
|
|
353
|
+
|
|
354
|
+
```xml
|
|
355
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
356
|
+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
357
|
+
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
358
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
359
|
+
<modelVersion>4.0.0</modelVersion>
|
|
360
|
+
|
|
361
|
+
<parent>
|
|
362
|
+
<groupId>${GROUP_ID}</groupId>
|
|
363
|
+
<artifactId>fc-module-${MODULE_NAME}-types</artifactId>
|
|
364
|
+
<version>${INITIAL_VERSION}</version>
|
|
365
|
+
</parent>
|
|
366
|
+
|
|
367
|
+
<artifactId>fc-types-${MODULE_NAME}</artifactId>
|
|
368
|
+
|
|
369
|
+
<name>FC Module ${MODULE_TITLE} - Types Implementation</name>
|
|
370
|
+
<description>DTOs and data types for ${MODULE_TITLE} module</description>
|
|
371
|
+
|
|
372
|
+
<properties>
|
|
373
|
+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
374
|
+
<lombok.version>1.18.30</lombok.version>
|
|
375
|
+
<jackson.version>2.10.0</jackson.version>
|
|
376
|
+
</properties>
|
|
377
|
+
|
|
378
|
+
<dependencies>
|
|
379
|
+
<dependency>
|
|
380
|
+
<groupId>org.projectlombok</groupId>
|
|
381
|
+
<artifactId>lombok</artifactId>
|
|
382
|
+
<version>${lombok.version}</version>
|
|
383
|
+
<scope>provided</scope>
|
|
384
|
+
</dependency>
|
|
385
|
+
|
|
386
|
+
<dependency>
|
|
387
|
+
<groupId>com.fasterxml.jackson.core</groupId>
|
|
388
|
+
<artifactId>jackson-core</artifactId>
|
|
389
|
+
<version>${jackson.version}</version>
|
|
390
|
+
</dependency>
|
|
391
|
+
|
|
392
|
+
<dependency>
|
|
393
|
+
<groupId>com.fasterxml.jackson.core</groupId>
|
|
394
|
+
<artifactId>jackson-databind</artifactId>
|
|
395
|
+
<version>${jackson.version}</version>
|
|
396
|
+
</dependency>
|
|
397
|
+
|
|
398
|
+
<dependency>
|
|
399
|
+
<groupId>com.fasterxml.jackson.core</groupId>
|
|
400
|
+
<artifactId>jackson-annotations</artifactId>
|
|
401
|
+
<version>${jackson.version}</version>
|
|
402
|
+
</dependency>
|
|
403
|
+
|
|
404
|
+
<dependency>
|
|
405
|
+
<groupId>javax.annotation</groupId>
|
|
406
|
+
<artifactId>javax.annotation-api</artifactId>
|
|
407
|
+
<version>1.3.2</version>
|
|
408
|
+
<scope>provided</scope>
|
|
409
|
+
</dependency>
|
|
410
|
+
</dependencies>
|
|
411
|
+
|
|
412
|
+
<build>
|
|
413
|
+
<plugins>
|
|
414
|
+
<plugin>
|
|
415
|
+
<groupId>org.apache.maven.plugins</groupId>
|
|
416
|
+
<artifactId>maven-compiler-plugin</artifactId>
|
|
417
|
+
<version>3.11.0</version>
|
|
418
|
+
<configuration>
|
|
419
|
+
<target>1.8</target>
|
|
420
|
+
<source>1.8</source>
|
|
421
|
+
<fork>true</fork>
|
|
422
|
+
<compilerArgs>
|
|
423
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
|
424
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
|
|
425
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
|
|
426
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
|
427
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
|
|
428
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
|
|
429
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
|
|
430
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
|
431
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
|
432
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
|
|
433
|
+
</compilerArgs>
|
|
434
|
+
<annotationProcessorPaths>
|
|
435
|
+
<path>
|
|
436
|
+
<groupId>org.projectlombok</groupId>
|
|
437
|
+
<artifactId>lombok</artifactId>
|
|
438
|
+
<version>${lombok.version}</version>
|
|
439
|
+
</path>
|
|
440
|
+
</annotationProcessorPaths>
|
|
441
|
+
</configuration>
|
|
442
|
+
</plugin>
|
|
443
|
+
</plugins>
|
|
444
|
+
</build>
|
|
445
|
+
</project>
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Util Aggregator POM (`plugins/util/pom.xml`)
|
|
449
|
+
|
|
450
|
+
```xml
|
|
451
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
452
|
+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
453
|
+
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
454
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
455
|
+
<modelVersion>4.0.0</modelVersion>
|
|
456
|
+
|
|
457
|
+
<parent>
|
|
458
|
+
<groupId>${GROUP_ID}</groupId>
|
|
459
|
+
<artifactId>fc-module-${MODULE_NAME}-plugins</artifactId>
|
|
460
|
+
<version>${INITIAL_VERSION}</version>
|
|
461
|
+
</parent>
|
|
462
|
+
|
|
463
|
+
<artifactId>fc-module-${MODULE_NAME}-util</artifactId>
|
|
464
|
+
<packaging>pom</packaging>
|
|
465
|
+
|
|
466
|
+
<name>FC Module ${MODULE_TITLE} - Util</name>
|
|
467
|
+
<description>Parent POM for fc-module-${MODULE_NAME} util plugins</description>
|
|
468
|
+
|
|
469
|
+
<repositories>
|
|
470
|
+
<repository>
|
|
471
|
+
<id>maven-s3-public-repo</id>
|
|
472
|
+
<name>FluentCommerce Public Repo</name>
|
|
473
|
+
<url>https://public.maven.dev.fluentcommerce.com/releases</url>
|
|
474
|
+
</repository>
|
|
475
|
+
</repositories>
|
|
476
|
+
|
|
477
|
+
<pluginRepositories>
|
|
478
|
+
<pluginRepository>
|
|
479
|
+
<id>maven-s3-public-repo</id>
|
|
480
|
+
<name>FluentCommerce Public Repo</name>
|
|
481
|
+
<url>https://public.maven.dev.fluentcommerce.com/releases</url>
|
|
482
|
+
</pluginRepository>
|
|
483
|
+
</pluginRepositories>
|
|
484
|
+
|
|
485
|
+
<modules>
|
|
486
|
+
<module>${MODULE_ALIAS}</module>
|
|
487
|
+
</modules>
|
|
488
|
+
|
|
489
|
+
</project>
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Util Implementation POM (`plugins/util/<module-alias>/pom.xml`)
|
|
493
|
+
|
|
494
|
+
```xml
|
|
495
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
496
|
+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
497
|
+
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
498
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
499
|
+
<modelVersion>4.0.0</modelVersion>
|
|
500
|
+
|
|
501
|
+
<parent>
|
|
502
|
+
<groupId>${GROUP_ID}</groupId>
|
|
503
|
+
<artifactId>fc-module-${MODULE_NAME}-util</artifactId>
|
|
504
|
+
<version>${INITIAL_VERSION}</version>
|
|
505
|
+
</parent>
|
|
506
|
+
|
|
507
|
+
<artifactId>fc-util-${MODULE_NAME}</artifactId>
|
|
508
|
+
|
|
509
|
+
<name>FC Module ${MODULE_TITLE} - Utilities Implementation</name>
|
|
510
|
+
<description>Utilities and helper classes for ${MODULE_TITLE} module</description>
|
|
511
|
+
|
|
512
|
+
<properties>
|
|
513
|
+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
514
|
+
<rubix-plugin-base.version>1.2.0.679</rubix-plugin-base.version>
|
|
515
|
+
<fluent-api-client.version>1.2.0.118</fluent-api-client.version>
|
|
516
|
+
<fluent-apollo-client-plugin.version>1.0.0.15</fluent-apollo-client-plugin.version>
|
|
517
|
+
<apollo.graphql.version>0.4.2</apollo.graphql.version>
|
|
518
|
+
<lombok.version>1.18.30</lombok.version>
|
|
519
|
+
<commons-lang3.version>3.4</commons-lang3.version>
|
|
520
|
+
<jackson.version>2.10.0</jackson.version>
|
|
521
|
+
<google.guava.version>19.0</google.guava.version>
|
|
522
|
+
<commons.collections4.version>4.3</commons.collections4.version>
|
|
523
|
+
</properties>
|
|
524
|
+
|
|
525
|
+
<dependencies>
|
|
526
|
+
<!-- Module Dependencies -->
|
|
527
|
+
<dependency>
|
|
528
|
+
<groupId>${GROUP_ID}</groupId>
|
|
529
|
+
<artifactId>fc-types-${MODULE_NAME}</artifactId>
|
|
530
|
+
<version>${project.version}</version>
|
|
531
|
+
</dependency>
|
|
532
|
+
|
|
533
|
+
<!-- Fluent Commerce dependencies -->
|
|
534
|
+
<dependency>
|
|
535
|
+
<groupId>com.fluentcommerce</groupId>
|
|
536
|
+
<artifactId>rubix-plugin-sdk</artifactId>
|
|
537
|
+
<version>${rubix-plugin-base.version}</version>
|
|
538
|
+
<scope>provided</scope>
|
|
539
|
+
</dependency>
|
|
540
|
+
|
|
541
|
+
<dependency>
|
|
542
|
+
<groupId>com.fluentcommerce</groupId>
|
|
543
|
+
<artifactId>fluent-api-client</artifactId>
|
|
544
|
+
<version>${fluent-api-client.version}</version>
|
|
545
|
+
<scope>provided</scope>
|
|
546
|
+
</dependency>
|
|
547
|
+
|
|
548
|
+
<!-- 3rd party libraries -->
|
|
549
|
+
<dependency>
|
|
550
|
+
<groupId>org.projectlombok</groupId>
|
|
551
|
+
<artifactId>lombok</artifactId>
|
|
552
|
+
<version>${lombok.version}</version>
|
|
553
|
+
<scope>provided</scope>
|
|
554
|
+
</dependency>
|
|
555
|
+
|
|
556
|
+
<dependency>
|
|
557
|
+
<groupId>org.apache.commons</groupId>
|
|
558
|
+
<artifactId>commons-lang3</artifactId>
|
|
559
|
+
<version>${commons-lang3.version}</version>
|
|
560
|
+
<scope>provided</scope>
|
|
561
|
+
</dependency>
|
|
562
|
+
|
|
563
|
+
<dependency>
|
|
564
|
+
<groupId>com.google.guava</groupId>
|
|
565
|
+
<artifactId>guava</artifactId>
|
|
566
|
+
<version>${google.guava.version}</version>
|
|
567
|
+
<scope>provided</scope>
|
|
568
|
+
</dependency>
|
|
569
|
+
|
|
570
|
+
<dependency>
|
|
571
|
+
<groupId>com.fasterxml.jackson.core</groupId>
|
|
572
|
+
<artifactId>jackson-core</artifactId>
|
|
573
|
+
<version>${jackson.version}</version>
|
|
574
|
+
</dependency>
|
|
575
|
+
|
|
576
|
+
<dependency>
|
|
577
|
+
<groupId>com.fasterxml.jackson.core</groupId>
|
|
578
|
+
<artifactId>jackson-databind</artifactId>
|
|
579
|
+
<version>${jackson.version}</version>
|
|
580
|
+
</dependency>
|
|
581
|
+
|
|
582
|
+
<dependency>
|
|
583
|
+
<groupId>org.apache.commons</groupId>
|
|
584
|
+
<artifactId>commons-collections4</artifactId>
|
|
585
|
+
<version>${commons.collections4.version}</version>
|
|
586
|
+
</dependency>
|
|
587
|
+
|
|
588
|
+
<!-- Apollo GraphQL -->
|
|
589
|
+
<dependency>
|
|
590
|
+
<groupId>com.apollographql.apollo</groupId>
|
|
591
|
+
<artifactId>apollo-runtime</artifactId>
|
|
592
|
+
<version>${apollo.graphql.version}</version>
|
|
593
|
+
</dependency>
|
|
594
|
+
|
|
595
|
+
<dependency>
|
|
596
|
+
<groupId>com.fasterxml.jackson.module</groupId>
|
|
597
|
+
<artifactId>jackson-module-jsonSchema</artifactId>
|
|
598
|
+
<version>${jackson.version}</version>
|
|
599
|
+
</dependency>
|
|
600
|
+
</dependencies>
|
|
601
|
+
|
|
602
|
+
<build>
|
|
603
|
+
<plugins>
|
|
604
|
+
<plugin>
|
|
605
|
+
<groupId>com.fluentcommerce</groupId>
|
|
606
|
+
<artifactId>apollo-client-maven-plugin</artifactId>
|
|
607
|
+
</plugin>
|
|
608
|
+
<plugin>
|
|
609
|
+
<groupId>org.apache.maven.plugins</groupId>
|
|
610
|
+
<artifactId>maven-compiler-plugin</artifactId>
|
|
611
|
+
<version>3.11.0</version>
|
|
612
|
+
<configuration>
|
|
613
|
+
<target>1.8</target>
|
|
614
|
+
<source>1.8</source>
|
|
615
|
+
<fork>true</fork>
|
|
616
|
+
<compilerArgs>
|
|
617
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
|
618
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
|
|
619
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
|
|
620
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
|
621
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
|
|
622
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
|
|
623
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
|
|
624
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
|
625
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
|
626
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
|
|
627
|
+
</compilerArgs>
|
|
628
|
+
<annotationProcessorPaths>
|
|
629
|
+
<path>
|
|
630
|
+
<groupId>org.projectlombok</groupId>
|
|
631
|
+
<artifactId>lombok</artifactId>
|
|
632
|
+
<version>${lombok.version}</version>
|
|
633
|
+
</path>
|
|
634
|
+
</annotationProcessorPaths>
|
|
635
|
+
</configuration>
|
|
636
|
+
</plugin>
|
|
637
|
+
</plugins>
|
|
638
|
+
</build>
|
|
639
|
+
</project>
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### Rules Aggregator POM (`plugins/rules/pom.xml`)
|
|
643
|
+
|
|
644
|
+
```xml
|
|
645
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
646
|
+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
647
|
+
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
648
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
649
|
+
<modelVersion>4.0.0</modelVersion>
|
|
650
|
+
|
|
651
|
+
<parent>
|
|
652
|
+
<groupId>${GROUP_ID}</groupId>
|
|
653
|
+
<artifactId>fc-module-${MODULE_NAME}-plugins</artifactId>
|
|
654
|
+
<version>${INITIAL_VERSION}</version>
|
|
655
|
+
</parent>
|
|
656
|
+
|
|
657
|
+
<artifactId>fc-module-${MODULE_NAME}-rules</artifactId>
|
|
658
|
+
<packaging>pom</packaging>
|
|
659
|
+
|
|
660
|
+
<name>FC Module ${MODULE_TITLE} - Rules</name>
|
|
661
|
+
<description>Parent POM for fc-module-${MODULE_NAME} rules plugins</description>
|
|
662
|
+
|
|
663
|
+
<modules>
|
|
664
|
+
<module>${MODULE_ALIAS}</module>
|
|
665
|
+
</modules>
|
|
666
|
+
|
|
667
|
+
</project>
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### Rules Implementation POM (`plugins/rules/<module-alias>/pom.xml`)
|
|
671
|
+
|
|
672
|
+
This is the most important POM -- it produces the deployable OSGi bundle JAR.
|
|
673
|
+
|
|
674
|
+
```xml
|
|
675
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
676
|
+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
677
|
+
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
678
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
679
|
+
|
|
680
|
+
<modelVersion>4.0.0</modelVersion>
|
|
681
|
+
|
|
682
|
+
<parent>
|
|
683
|
+
<groupId>${GROUP_ID}</groupId>
|
|
684
|
+
<artifactId>fc-module-${MODULE_NAME}-rules</artifactId>
|
|
685
|
+
<version>${INITIAL_VERSION}</version>
|
|
686
|
+
</parent>
|
|
687
|
+
|
|
688
|
+
<artifactId>fc-module-${MODULE_NAME}</artifactId>
|
|
689
|
+
|
|
690
|
+
<pluginRepositories>
|
|
691
|
+
<pluginRepository>
|
|
692
|
+
<id>maven-s3-public-repo</id>
|
|
693
|
+
<name>FluentCommerce Public Repo</name>
|
|
694
|
+
<url>https://public.maven.dev.fluentcommerce.com/releases</url>
|
|
695
|
+
</pluginRepository>
|
|
696
|
+
</pluginRepositories>
|
|
697
|
+
|
|
698
|
+
<repositories>
|
|
699
|
+
<repository>
|
|
700
|
+
<id>maven-s3-public-repo</id>
|
|
701
|
+
<name>FluentCommerce Public Repo</name>
|
|
702
|
+
<url>https://public.maven.dev.fluentcommerce.com/releases</url>
|
|
703
|
+
</repository>
|
|
704
|
+
</repositories>
|
|
705
|
+
|
|
706
|
+
<properties>
|
|
707
|
+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
708
|
+
<rubix.artifact>${project.groupId}.${project.artifactId}</rubix.artifact>
|
|
709
|
+
<rubix.artifact.vendor>Fluent Commerce</rubix.artifact.vendor>
|
|
710
|
+
<rubix.osgi.symbolic.name>${OSGI_SYMBOLIC_NAME}</rubix.osgi.symbolic.name>
|
|
711
|
+
<rubix.osgi.activator/>
|
|
712
|
+
<rubix.osgi.export.package/>
|
|
713
|
+
<rubix.osgi.export.service/>
|
|
714
|
+
<rubix-plugin-base.version>1.2.0.679</rubix-plugin-base.version>
|
|
715
|
+
<fluent-api-client.version>1.2.0.118</fluent-api-client.version>
|
|
716
|
+
<fluent-apollo-client-plugin.version>1.0.0.15</fluent-apollo-client-plugin.version>
|
|
717
|
+
<slf4j.version>1.7.22</slf4j.version>
|
|
718
|
+
|
|
719
|
+
<!-- 3rd party dependency versions -->
|
|
720
|
+
<apollo.graphql.version>0.4.2</apollo.graphql.version>
|
|
721
|
+
<commons.collections4.version>4.3</commons.collections4.version>
|
|
722
|
+
<lombok.version>1.18.30</lombok.version>
|
|
723
|
+
<commons-lang3.version>3.4</commons-lang3.version>
|
|
724
|
+
<jackson.version>2.10.0</jackson.version>
|
|
725
|
+
<google.guava.version>19.0</google.guava.version>
|
|
726
|
+
<okhttp.version>3.14.9</okhttp.version>
|
|
727
|
+
|
|
728
|
+
<!-- test dependency versions -->
|
|
729
|
+
<junit-jupiter.version>5.11.0</junit-jupiter.version>
|
|
730
|
+
<mockito.version>4.11.0</mockito.version>
|
|
731
|
+
|
|
732
|
+
<!-- maven plugin versions -->
|
|
733
|
+
<lombok.maven.plugin.version>1.16.8.0</lombok.maven.plugin.version>
|
|
734
|
+
<maven.compiler.plugin.version>3.11.0</maven.compiler.plugin.version>
|
|
735
|
+
<maven.dependency.plugin.version>2.8</maven.dependency.plugin.version>
|
|
736
|
+
<maven.resources.plugin.version>2.6</maven.resources.plugin.version>
|
|
737
|
+
<maven.bundle.plugin.version>5.1.9</maven.bundle.plugin.version>
|
|
738
|
+
</properties>
|
|
739
|
+
|
|
740
|
+
<dependencies>
|
|
741
|
+
|
|
742
|
+
<!-- Module Dependencies -->
|
|
743
|
+
<dependency>
|
|
744
|
+
<groupId>${GROUP_ID}</groupId>
|
|
745
|
+
<artifactId>fc-types-${MODULE_NAME}</artifactId>
|
|
746
|
+
<version>${project.version}</version>
|
|
747
|
+
</dependency>
|
|
748
|
+
<dependency>
|
|
749
|
+
<groupId>${GROUP_ID}</groupId>
|
|
750
|
+
<artifactId>fc-util-${MODULE_NAME}</artifactId>
|
|
751
|
+
<version>${project.version}</version>
|
|
752
|
+
</dependency>
|
|
753
|
+
|
|
754
|
+
<!-- INCLUDED DEPENDENCIES -->
|
|
755
|
+
<dependency>
|
|
756
|
+
<groupId>com.fasterxml.jackson.module</groupId>
|
|
757
|
+
<artifactId>jackson-module-jsonSchema</artifactId>
|
|
758
|
+
<version>${jackson.version}</version>
|
|
759
|
+
</dependency>
|
|
760
|
+
|
|
761
|
+
<!-- PROVIDED DEPENDENCIES -->
|
|
762
|
+
<dependency>
|
|
763
|
+
<groupId>com.fluentcommerce</groupId>
|
|
764
|
+
<artifactId>rubix-plugin-sdk</artifactId>
|
|
765
|
+
<version>${rubix-plugin-base.version}</version>
|
|
766
|
+
<scope>provided</scope>
|
|
767
|
+
</dependency>
|
|
768
|
+
|
|
769
|
+
<dependency>
|
|
770
|
+
<groupId>org.slf4j</groupId>
|
|
771
|
+
<artifactId>slf4j-api</artifactId>
|
|
772
|
+
<version>${slf4j.version}</version>
|
|
773
|
+
<scope>provided</scope>
|
|
774
|
+
</dependency>
|
|
775
|
+
|
|
776
|
+
<dependency>
|
|
777
|
+
<groupId>org.slf4j</groupId>
|
|
778
|
+
<artifactId>slf4j-simple</artifactId>
|
|
779
|
+
<version>${slf4j.version}</version>
|
|
780
|
+
<scope>provided</scope>
|
|
781
|
+
</dependency>
|
|
782
|
+
|
|
783
|
+
<dependency>
|
|
784
|
+
<groupId>org.projectlombok</groupId>
|
|
785
|
+
<artifactId>lombok</artifactId>
|
|
786
|
+
<version>${lombok.version}</version>
|
|
787
|
+
<scope>provided</scope>
|
|
788
|
+
</dependency>
|
|
789
|
+
|
|
790
|
+
<dependency>
|
|
791
|
+
<groupId>org.apache.commons</groupId>
|
|
792
|
+
<artifactId>commons-lang3</artifactId>
|
|
793
|
+
<version>${commons-lang3.version}</version>
|
|
794
|
+
<scope>provided</scope>
|
|
795
|
+
</dependency>
|
|
796
|
+
|
|
797
|
+
<dependency>
|
|
798
|
+
<groupId>com.fluentcommerce</groupId>
|
|
799
|
+
<artifactId>fluent-api-client</artifactId>
|
|
800
|
+
<version>${fluent-api-client.version}</version>
|
|
801
|
+
<scope>provided</scope>
|
|
802
|
+
</dependency>
|
|
803
|
+
|
|
804
|
+
<dependency>
|
|
805
|
+
<groupId>com.google.guava</groupId>
|
|
806
|
+
<artifactId>guava</artifactId>
|
|
807
|
+
<version>${google.guava.version}</version>
|
|
808
|
+
<scope>provided</scope>
|
|
809
|
+
</dependency>
|
|
810
|
+
|
|
811
|
+
<dependency>
|
|
812
|
+
<groupId>com.apollographql.apollo</groupId>
|
|
813
|
+
<artifactId>apollo-api</artifactId>
|
|
814
|
+
<version>${apollo.graphql.version}</version>
|
|
815
|
+
<scope>provided</scope>
|
|
816
|
+
</dependency>
|
|
817
|
+
|
|
818
|
+
<!-- 3rd party libs -->
|
|
819
|
+
<dependency>
|
|
820
|
+
<groupId>com.fasterxml.jackson.core</groupId>
|
|
821
|
+
<artifactId>jackson-core</artifactId>
|
|
822
|
+
<version>${jackson.version}</version>
|
|
823
|
+
</dependency>
|
|
824
|
+
|
|
825
|
+
<dependency>
|
|
826
|
+
<groupId>com.fasterxml.jackson.core</groupId>
|
|
827
|
+
<artifactId>jackson-databind</artifactId>
|
|
828
|
+
<version>${jackson.version}</version>
|
|
829
|
+
</dependency>
|
|
830
|
+
|
|
831
|
+
<dependency>
|
|
832
|
+
<groupId>org.apache.commons</groupId>
|
|
833
|
+
<artifactId>commons-collections4</artifactId>
|
|
834
|
+
<version>${commons.collections4.version}</version>
|
|
835
|
+
</dependency>
|
|
836
|
+
|
|
837
|
+
<!-- TEST -->
|
|
838
|
+
<dependency>
|
|
839
|
+
<groupId>com.squareup.okio</groupId>
|
|
840
|
+
<artifactId>okio</artifactId>
|
|
841
|
+
<version>1.17.6</version>
|
|
842
|
+
<scope>test</scope>
|
|
843
|
+
</dependency>
|
|
844
|
+
|
|
845
|
+
<dependency>
|
|
846
|
+
<groupId>org.mockito</groupId>
|
|
847
|
+
<artifactId>mockito-core</artifactId>
|
|
848
|
+
<version>${mockito.version}</version>
|
|
849
|
+
<scope>test</scope>
|
|
850
|
+
</dependency>
|
|
851
|
+
|
|
852
|
+
<dependency>
|
|
853
|
+
<groupId>org.mockito</groupId>
|
|
854
|
+
<artifactId>mockito-inline</artifactId>
|
|
855
|
+
<version>${mockito.version}</version>
|
|
856
|
+
<scope>test</scope>
|
|
857
|
+
</dependency>
|
|
858
|
+
|
|
859
|
+
<dependency>
|
|
860
|
+
<groupId>org.mockito</groupId>
|
|
861
|
+
<artifactId>mockito-junit-jupiter</artifactId>
|
|
862
|
+
<version>${mockito.version}</version>
|
|
863
|
+
<scope>test</scope>
|
|
864
|
+
</dependency>
|
|
865
|
+
|
|
866
|
+
<dependency>
|
|
867
|
+
<groupId>com.fluentcommerce</groupId>
|
|
868
|
+
<artifactId>rubix-test-mockery</artifactId>
|
|
869
|
+
<version>${rubix-plugin-base.version}</version>
|
|
870
|
+
<scope>test</scope>
|
|
871
|
+
</dependency>
|
|
872
|
+
|
|
873
|
+
<dependency>
|
|
874
|
+
<groupId>org.junit.jupiter</groupId>
|
|
875
|
+
<artifactId>junit-jupiter</artifactId>
|
|
876
|
+
<version>${junit-jupiter.version}</version>
|
|
877
|
+
<scope>test</scope>
|
|
878
|
+
</dependency>
|
|
879
|
+
|
|
880
|
+
<dependency>
|
|
881
|
+
<groupId>org.junit.jupiter</groupId>
|
|
882
|
+
<artifactId>junit-jupiter-api</artifactId>
|
|
883
|
+
<version>${junit-jupiter.version}</version>
|
|
884
|
+
<scope>test</scope>
|
|
885
|
+
</dependency>
|
|
886
|
+
|
|
887
|
+
<dependency>
|
|
888
|
+
<groupId>org.junit.jupiter</groupId>
|
|
889
|
+
<artifactId>junit-jupiter-engine</artifactId>
|
|
890
|
+
<version>${junit-jupiter.version}</version>
|
|
891
|
+
<scope>test</scope>
|
|
892
|
+
</dependency>
|
|
893
|
+
|
|
894
|
+
</dependencies>
|
|
895
|
+
|
|
896
|
+
<build>
|
|
897
|
+
<plugins>
|
|
898
|
+
<plugin>
|
|
899
|
+
<groupId>org.projectlombok</groupId>
|
|
900
|
+
<artifactId>lombok-maven-plugin</artifactId>
|
|
901
|
+
<version>${lombok.maven.plugin.version}</version>
|
|
902
|
+
<executions>
|
|
903
|
+
<execution>
|
|
904
|
+
<phase>generate-sources</phase>
|
|
905
|
+
<goals>
|
|
906
|
+
<goal>delombok</goal>
|
|
907
|
+
</goals>
|
|
908
|
+
</execution>
|
|
909
|
+
</executions>
|
|
910
|
+
</plugin>
|
|
911
|
+
<plugin>
|
|
912
|
+
<groupId>org.apache.maven.plugins</groupId>
|
|
913
|
+
<artifactId>maven-compiler-plugin</artifactId>
|
|
914
|
+
<version>${maven.compiler.plugin.version}</version>
|
|
915
|
+
<configuration>
|
|
916
|
+
<target>1.8</target>
|
|
917
|
+
<source>1.8</source>
|
|
918
|
+
<fork>true</fork>
|
|
919
|
+
<compilerArgs>
|
|
920
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
|
921
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
|
|
922
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
|
|
923
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
|
924
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
|
|
925
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
|
|
926
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
|
|
927
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
|
928
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
|
929
|
+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
|
|
930
|
+
</compilerArgs>
|
|
931
|
+
<annotationProcessorPaths>
|
|
932
|
+
<path>
|
|
933
|
+
<groupId>org.projectlombok</groupId>
|
|
934
|
+
<artifactId>lombok</artifactId>
|
|
935
|
+
<version>${lombok.version}</version>
|
|
936
|
+
</path>
|
|
937
|
+
</annotationProcessorPaths>
|
|
938
|
+
</configuration>
|
|
939
|
+
</plugin>
|
|
940
|
+
<plugin>
|
|
941
|
+
<groupId>org.apache.maven.plugins</groupId>
|
|
942
|
+
<artifactId>maven-dependency-plugin</artifactId>
|
|
943
|
+
<version>${maven.dependency.plugin.version}</version>
|
|
944
|
+
</plugin>
|
|
945
|
+
<plugin>
|
|
946
|
+
<groupId>org.apache.maven.plugins</groupId>
|
|
947
|
+
<artifactId>maven-resources-plugin</artifactId>
|
|
948
|
+
<version>${maven.resources.plugin.version}</version>
|
|
949
|
+
</plugin>
|
|
950
|
+
<plugin>
|
|
951
|
+
<groupId>org.apache.felix</groupId>
|
|
952
|
+
<artifactId>maven-bundle-plugin</artifactId>
|
|
953
|
+
<version>${maven.bundle.plugin.version}</version>
|
|
954
|
+
<extensions>true</extensions>
|
|
955
|
+
<executions>
|
|
956
|
+
<execution>
|
|
957
|
+
<phase>generate-sources</phase>
|
|
958
|
+
<goals>
|
|
959
|
+
<goal>cleanVersions</goal>
|
|
960
|
+
</goals>
|
|
961
|
+
</execution>
|
|
962
|
+
<execution>
|
|
963
|
+
<id>bundle-manifest</id>
|
|
964
|
+
<phase>process-classes</phase>
|
|
965
|
+
<goals>
|
|
966
|
+
<goal>manifest</goal>
|
|
967
|
+
</goals>
|
|
968
|
+
</execution>
|
|
969
|
+
<execution>
|
|
970
|
+
<id>package-bundle</id>
|
|
971
|
+
<phase>package</phase>
|
|
972
|
+
<goals>
|
|
973
|
+
<goal>bundle</goal>
|
|
974
|
+
</goals>
|
|
975
|
+
</execution>
|
|
976
|
+
</executions>
|
|
977
|
+
<configuration>
|
|
978
|
+
<instructions>
|
|
979
|
+
<Bundle-Name>${project.name}</Bundle-Name>
|
|
980
|
+
<Bundle-SymbolicName>${rubix.osgi.symbolic.name}</Bundle-SymbolicName>
|
|
981
|
+
<Bundle-Activator>${rubix.osgi.activator}</Bundle-Activator>
|
|
982
|
+
<Bundle-Vendor>${rubix.artifact.vendor}</Bundle-Vendor>
|
|
983
|
+
<Bundle-Version>${project.version}</Bundle-Version>
|
|
984
|
+
<Export-Package>${rubix.osgi.export.package}</Export-Package>
|
|
985
|
+
<Export-Service>${rubix.osgi.export.service}</Export-Service>
|
|
986
|
+
<Import-Package>!android.*,!org.conscrypt,!org.openjsse.*,!okhttp3.*,!okio.*,*</Import-Package>
|
|
987
|
+
<Embed-Dependency>*;scope=compile|runtime;inline=true</Embed-Dependency>
|
|
988
|
+
<Bundle-ClassPath>.,{maven-dependencies}</Bundle-ClassPath>
|
|
989
|
+
<Embed-Transitive>true</Embed-Transitive>
|
|
990
|
+
<Rubix-Rules>$(classes;CONCRETE;ANNOTATION;com.fluentretail.rubix.rule.meta.RuleInfo)</Rubix-Rules>
|
|
991
|
+
<Rubix-Account>_</Rubix-Account>
|
|
992
|
+
</instructions>
|
|
993
|
+
</configuration>
|
|
994
|
+
</plugin>
|
|
995
|
+
</plugins>
|
|
996
|
+
</build>
|
|
997
|
+
</project>
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
### Module Manifest (`resources/module.json`)
|
|
1001
|
+
|
|
1002
|
+
```json
|
|
1003
|
+
{
|
|
1004
|
+
"_schema": "1.0.0",
|
|
1005
|
+
"name": "fluent-commerce/fc-module-${MODULE_NAME}",
|
|
1006
|
+
"version": "${INITIAL_VERSION}",
|
|
1007
|
+
"title": "Fluent Commerce - ${MODULE_TITLE} Module",
|
|
1008
|
+
"description": "${MODULE_TITLE} extension module providing custom rules and utilities for the Rubix platform",
|
|
1009
|
+
"authors": [
|
|
1010
|
+
{
|
|
1011
|
+
"name": "Fluent Commerce",
|
|
1012
|
+
"email": "support@fluentcommerce.com",
|
|
1013
|
+
"web": "https://fluentcommerce.com"
|
|
1014
|
+
}
|
|
1015
|
+
],
|
|
1016
|
+
"dependencies": [
|
|
1017
|
+
{
|
|
1018
|
+
"name": "fluent-commerce/core",
|
|
1019
|
+
"type": "module",
|
|
1020
|
+
"version": "2.x.x"
|
|
1021
|
+
}
|
|
1022
|
+
],
|
|
1023
|
+
"contracts": [],
|
|
1024
|
+
"rules": [
|
|
1025
|
+
"${RULE_NAME_1}",
|
|
1026
|
+
"${RULE_NAME_2}"
|
|
1027
|
+
],
|
|
1028
|
+
"compatibility": {
|
|
1029
|
+
"minRubixVersion": "1.2.0",
|
|
1030
|
+
"rubixPluginSdk": "1.2.0.679",
|
|
1031
|
+
"fluentApiClient": "1.2.0.118"
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
**Important:** If no `--rules` are provided, the `"rules"` array must be empty `[]`. Each entry is a simple string matching the `@RuleInfo(name = "...")` annotation value. Do NOT include package prefixes or account prefixes in this array.
|
|
1037
|
+
|
|
1038
|
+
### Config Prefix System
|
|
1039
|
+
|
|
1040
|
+
The Fluent CLI uses a prefix system in `module.config.json` to scope variable values to specific contexts. Understanding this system is essential for extension modules that include workflows and settings.
|
|
1041
|
+
|
|
1042
|
+
#### How Config Prefixes Work
|
|
1043
|
+
|
|
1044
|
+
Config keys follow the pattern: `<prefix>:<variable.name>`
|
|
1045
|
+
|
|
1046
|
+
The CLI filters keys based on the asset context being processed and selects the most specific match.
|
|
1047
|
+
|
|
1048
|
+
#### Available Prefixes
|
|
1049
|
+
|
|
1050
|
+
| Prefix | Applies To | Specificity | Example |
|
|
1051
|
+
|--------|-----------|-------------|---------|
|
|
1052
|
+
| `default:` | All assets (workflows, settings, data) | 0 (lowest, fallback) | `"default:network.ref": "NET_DEFAULT"` |
|
|
1053
|
+
| `workflow:` | Workflows only | 0 | `"workflow:notification.enabled": "true"` |
|
|
1054
|
+
| `workflow:<type>:` | Specific entity type workflows | 1 | `"workflow:order:network.ref": "NET_ORDER"` |
|
|
1055
|
+
| `workflow:<type>:<subtype>:` | Specific entity type + subtype | 2 (highest) | `"workflow:order:hd:carrier.ref": "HD_CARRIER"` |
|
|
1056
|
+
| `setting:` | Settings only | 0 | `"setting:api.timeout": "30000"` |
|
|
1057
|
+
|
|
1058
|
+
**Specificity rules:** More colons after the prefix = higher specificity = wins. For a workflow `ORDER::HD`:
|
|
1059
|
+
- `workflow:order:hd:network.ref` (2 levels) beats
|
|
1060
|
+
- `workflow:order:network.ref` (1 level) beats
|
|
1061
|
+
- `default:network.ref` (0 levels)
|
|
1062
|
+
|
|
1063
|
+
#### Context-Based Filtering
|
|
1064
|
+
|
|
1065
|
+
| Asset Folder | Recognized Prefixes |
|
|
1066
|
+
|-------------|---------------------|
|
|
1067
|
+
| `assets/workflows/` | `default:`, `workflow:`, `workflow:<type>:`, `workflow:<type>:<subtype>:` |
|
|
1068
|
+
| `assets/settings/` | `default:`, `setting:` |
|
|
1069
|
+
| All other asset folders | `default:` only |
|
|
1070
|
+
|
|
1071
|
+
#### Built-in Auto-Injected Variables
|
|
1072
|
+
|
|
1073
|
+
These variables are available in all asset files without defining them in `module.config.json`:
|
|
1074
|
+
- `account.id` -- Fluent account ID
|
|
1075
|
+
- `retailer.id` -- Target retailer numeric ID
|
|
1076
|
+
- `retailer.ref` -- Target retailer reference string
|
|
1077
|
+
- `retailer.name` -- Target retailer name
|
|
1078
|
+
|
|
1079
|
+
Note: `account.host` is NOT auto-injected. If needed, define it explicitly: `"default:account.host": "acme.sandbox.api.fluentretail.com"`.
|
|
1080
|
+
|
|
1081
|
+
#### Variable Syntax
|
|
1082
|
+
|
|
1083
|
+
Use `[[variable]]` tokens in any JSON or CSV asset file:
|
|
1084
|
+
|
|
1085
|
+
```json
|
|
1086
|
+
{
|
|
1087
|
+
"ref": "NET_HD_[[retailer.id]]",
|
|
1088
|
+
"networkRef": "[[network.ref]]",
|
|
1089
|
+
"retailerId": [[retailer.id]]
|
|
1090
|
+
}
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
**Important:** Unresolved `[[tokens]]` are left as-is -- the CLI does not error on missing substitutions. Always scan for unreplaced tokens before deploying:
|
|
1094
|
+
|
|
1095
|
+
```bash
|
|
1096
|
+
rg '\[\[.*\]\]' resources/assets/
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
#### Example module.config.json
|
|
1100
|
+
|
|
1101
|
+
When the scaffold creates `resources/module.config.json`, it generates an empty object `{}`. Populate it with prefixed keys as needed:
|
|
1102
|
+
|
|
1103
|
+
```json
|
|
1104
|
+
{
|
|
1105
|
+
"default:carrier.ref": "CARRIER_STD",
|
|
1106
|
+
"default:network.ref": "NET_DEFAULT",
|
|
1107
|
+
"workflow:order:hd:network.ref": "NET_HD",
|
|
1108
|
+
"workflow:order:hd:carrier.ref": "HD_CARRIER",
|
|
1109
|
+
"workflow:order:cc:network.ref": "NET_CC",
|
|
1110
|
+
"setting:webhook.order.url": "https://example.com/webhook/order"
|
|
1111
|
+
}
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
The build scripts (`scripts/build-module.sh` and `scripts/build-module.ps1`) already handle copying `module.config.json` into the distribution ZIP alongside `module.json`. When deploying with `fluent module install`, the CLI reads this file and substitutes `[[variable]]` tokens in all asset files before uploading.
|
|
1115
|
+
|
|
1116
|
+
### Rule Class Template
|
|
1117
|
+
|
|
1118
|
+
```java
|
|
1119
|
+
package com.fluentcommerce.rule.${PACKAGE};
|
|
1120
|
+
|
|
1121
|
+
import com.fluentcommerce.common.BaseRule;
|
|
1122
|
+
import com.fluentcommerce.common.ContextWrapper;
|
|
1123
|
+
import com.fluentretail.rubix.rule.meta.ParamString;
|
|
1124
|
+
import com.fluentretail.rubix.rule.meta.RuleInfo;
|
|
1125
|
+
import lombok.extern.slf4j.Slf4j;
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* ${RULE_DESCRIPTION}
|
|
1129
|
+
*/
|
|
1130
|
+
@RuleInfo(
|
|
1131
|
+
name = "${RULE_NAME}",
|
|
1132
|
+
description = "${RULE_DESCRIPTION}"
|
|
1133
|
+
)
|
|
1134
|
+
@Slf4j
|
|
1135
|
+
public class ${RULE_NAME} extends BaseRule {
|
|
1136
|
+
|
|
1137
|
+
private static final String CLASS_NAME = ${RULE_NAME}.class.getSimpleName();
|
|
1138
|
+
|
|
1139
|
+
@Override
|
|
1140
|
+
public void run(ContextWrapper context) {
|
|
1141
|
+
String accountId = context.getEvent().getAccountId();
|
|
1142
|
+
|
|
1143
|
+
log.info("[{}] [{}] - Processing event: {}", accountId, CLASS_NAME,
|
|
1144
|
+
context.getEvent().getName());
|
|
1145
|
+
context.addLog("Processing " + CLASS_NAME);
|
|
1146
|
+
|
|
1147
|
+
try {
|
|
1148
|
+
// === Rule logic here ===
|
|
1149
|
+
|
|
1150
|
+
context.addLog(CLASS_NAME + " completed successfully");
|
|
1151
|
+
} catch (Exception e) {
|
|
1152
|
+
log.error("[{}] [{}] - Error: {}", accountId, CLASS_NAME, e.getMessage(), e);
|
|
1153
|
+
context.addLog("Error in " + CLASS_NAME + ": " + e.getMessage());
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
**Fallback rule class (when no BaseRule/ContextWrapper exists in the module):**
|
|
1160
|
+
|
|
1161
|
+
If this is a brand-new module without an existing `BaseRule` class, use the direct Rubix SDK API instead:
|
|
1162
|
+
|
|
1163
|
+
```java
|
|
1164
|
+
package com.fluentcommerce.rule.${PACKAGE};
|
|
1165
|
+
|
|
1166
|
+
import com.fluentretail.rubix.event.Event;
|
|
1167
|
+
import com.fluentretail.rubix.rule.meta.RuleInfo;
|
|
1168
|
+
import com.fluentretail.rubix.v2.context.Context;
|
|
1169
|
+
import com.fluentretail.rubix.v2.rule.Rule;
|
|
1170
|
+
import lombok.extern.slf4j.Slf4j;
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* ${RULE_DESCRIPTION}
|
|
1174
|
+
*/
|
|
1175
|
+
@RuleInfo(
|
|
1176
|
+
name = "${RULE_NAME}",
|
|
1177
|
+
description = "${RULE_DESCRIPTION}"
|
|
1178
|
+
)
|
|
1179
|
+
@Slf4j
|
|
1180
|
+
public class ${RULE_NAME} implements Rule {
|
|
1181
|
+
|
|
1182
|
+
private static final String CLASS_NAME = ${RULE_NAME}.class.getSimpleName();
|
|
1183
|
+
|
|
1184
|
+
@Override
|
|
1185
|
+
public void run(Context context) {
|
|
1186
|
+
Event event = context.getEvent();
|
|
1187
|
+
String accountId = event.getAccountId();
|
|
1188
|
+
|
|
1189
|
+
log.info("[{}] [{}] - Processing event: {} for entity {}",
|
|
1190
|
+
accountId, CLASS_NAME, event.getName(), event.getEntityRef());
|
|
1191
|
+
|
|
1192
|
+
try {
|
|
1193
|
+
// === Rule logic here ===
|
|
1194
|
+
|
|
1195
|
+
log.info("[{}] [{}] - Completed successfully", accountId, CLASS_NAME);
|
|
1196
|
+
} catch (Exception e) {
|
|
1197
|
+
log.error("[{}] [{}] - Error: {}", accountId, CLASS_NAME, e.getMessage(), e);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
**Which template to use:** For new modules with no shared `BaseRule`, use the fallback (`implements Rule`). For modules that already have a `BaseRule` and `ContextWrapper` in their `common` package (or in a util dependency), use the primary template (`extends BaseRule`). The scaffolder should check for the existence of `BaseRule.java` under `plugins/util/` or `plugins/rules/` and choose accordingly. Default for greenfield: use the fallback.
|
|
1204
|
+
|
|
1205
|
+
### Test Class Template
|
|
1206
|
+
|
|
1207
|
+
```java
|
|
1208
|
+
package com.fluentcommerce.rule.${PACKAGE};
|
|
1209
|
+
|
|
1210
|
+
import com.fluentretail.rubix.event.Event;
|
|
1211
|
+
import com.fluentretail.rubix.rule.meta.RuleInfo;
|
|
1212
|
+
import com.fluentretail.rubix.v2.context.Context;
|
|
1213
|
+
import com.fluentretail.rubix.v2.action.Action;
|
|
1214
|
+
import com.fluentretail.rubix.v2.action.MutationAction;
|
|
1215
|
+
import com.fluentretail.rubix.v2.action.EventAction;
|
|
1216
|
+
import org.junit.jupiter.api.BeforeEach;
|
|
1217
|
+
import org.junit.jupiter.api.Test;
|
|
1218
|
+
import org.junit.jupiter.api.DisplayName;
|
|
1219
|
+
import org.mockito.Mock;
|
|
1220
|
+
import org.mockito.MockitoAnnotations;
|
|
1221
|
+
|
|
1222
|
+
import java.util.HashMap;
|
|
1223
|
+
import java.util.Map;
|
|
1224
|
+
|
|
1225
|
+
import static org.junit.jupiter.api.Assertions.*;
|
|
1226
|
+
import static org.mockito.Mockito.*;
|
|
1227
|
+
|
|
1228
|
+
class ${RULE_NAME}Test {
|
|
1229
|
+
|
|
1230
|
+
private ${RULE_NAME} rule;
|
|
1231
|
+
|
|
1232
|
+
@Mock private Context context;
|
|
1233
|
+
@Mock private Event event;
|
|
1234
|
+
@Mock private Action action;
|
|
1235
|
+
@Mock private MutationAction mutationAction;
|
|
1236
|
+
@Mock private EventAction eventAction;
|
|
1237
|
+
|
|
1238
|
+
@BeforeEach
|
|
1239
|
+
void setUp() {
|
|
1240
|
+
MockitoAnnotations.openMocks(this);
|
|
1241
|
+
rule = new ${RULE_NAME}();
|
|
1242
|
+
|
|
1243
|
+
when(context.getEvent()).thenReturn(event);
|
|
1244
|
+
when(context.action()).thenReturn(action);
|
|
1245
|
+
when(action.mutation()).thenReturn(mutationAction);
|
|
1246
|
+
when(action.eventAction()).thenReturn(eventAction);
|
|
1247
|
+
when(event.getAccountId()).thenReturn("TEST_ACCOUNT");
|
|
1248
|
+
when(event.getName()).thenReturn("TestEvent");
|
|
1249
|
+
when(event.getEntityRef()).thenReturn("TEST-001");
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
@Test
|
|
1253
|
+
@DisplayName("Rule has correct @RuleInfo annotation")
|
|
1254
|
+
void hasRuleInfo() {
|
|
1255
|
+
RuleInfo info = ${RULE_NAME}.class.getAnnotation(RuleInfo.class);
|
|
1256
|
+
assertNotNull(info);
|
|
1257
|
+
assertEquals("${RULE_NAME}", info.name());
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
@Test
|
|
1261
|
+
@DisplayName("Processes event successfully")
|
|
1262
|
+
void processesSuccessfully() {
|
|
1263
|
+
// Should not throw
|
|
1264
|
+
assertDoesNotThrow(() -> rule.run(context));
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
@Test
|
|
1268
|
+
@DisplayName("Handles exception gracefully")
|
|
1269
|
+
void handlesExceptionGracefully() {
|
|
1270
|
+
when(event.getAccountId()).thenThrow(new RuntimeException("Simulated failure"));
|
|
1271
|
+
|
|
1272
|
+
// Rule should not propagate the exception
|
|
1273
|
+
assertDoesNotThrow(() -> rule.run(context));
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
### Build Script - Bash (`scripts/build-module.sh`)
|
|
1279
|
+
|
|
1280
|
+
```bash
|
|
1281
|
+
#!/bin/bash
|
|
1282
|
+
set -euo pipefail
|
|
1283
|
+
|
|
1284
|
+
VERBOSE=false
|
|
1285
|
+
BUILD_PLUGINS=true
|
|
1286
|
+
BUILD_MODULE=true
|
|
1287
|
+
|
|
1288
|
+
parse_options() {
|
|
1289
|
+
while getopts "mpx" opt; do
|
|
1290
|
+
case $opt in
|
|
1291
|
+
m) BUILD_MODULE=false ;;
|
|
1292
|
+
p) BUILD_PLUGINS=false ;;
|
|
1293
|
+
x) VERBOSE=true ;;
|
|
1294
|
+
*)
|
|
1295
|
+
print_usage
|
|
1296
|
+
exit 1
|
|
1297
|
+
;;
|
|
1298
|
+
esac
|
|
1299
|
+
done
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
print_usage() {
|
|
1303
|
+
echo "Usage: $0 [-m] [-p] [-x]"
|
|
1304
|
+
echo " -m Skip module zip creation"
|
|
1305
|
+
echo " -p Skip plugin build and package existing artifacts"
|
|
1306
|
+
echo " -x Enable verbose mode"
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
log() {
|
|
1310
|
+
if [ "$VERBOSE" = true ]; then
|
|
1311
|
+
echo "$@"
|
|
1312
|
+
fi
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
require_command() {
|
|
1316
|
+
local command_name=$1
|
|
1317
|
+
if ! command -v "$command_name" >/dev/null 2>&1; then
|
|
1318
|
+
echo "Error: required command '$command_name' was not found in PATH"
|
|
1319
|
+
exit 1
|
|
1320
|
+
fi
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
clean_folder() {
|
|
1324
|
+
local dist_dir=$1
|
|
1325
|
+
if [ -d "$dist_dir" ]; then
|
|
1326
|
+
log "Directory $dist_dir exist, clearing..."
|
|
1327
|
+
rm -rf "${dist_dir:?}/"
|
|
1328
|
+
fi
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
copy_assets() {
|
|
1332
|
+
local resources_dir=$1
|
|
1333
|
+
local dist_dir=$2
|
|
1334
|
+
local dist_assets_dir=$3
|
|
1335
|
+
|
|
1336
|
+
log "Resources Dir: '$resources_dir', Destination Dir: '$dist_dir'"
|
|
1337
|
+
|
|
1338
|
+
if [ ! -d "$resources_dir" ]; then
|
|
1339
|
+
echo "Error: Resources directory '$resources_dir' does not exist"
|
|
1340
|
+
exit 1
|
|
1341
|
+
fi
|
|
1342
|
+
|
|
1343
|
+
if [ -e "$resources_dir/module.json" ]; then
|
|
1344
|
+
cp "$resources_dir/module.json" "$dist_dir/"
|
|
1345
|
+
else
|
|
1346
|
+
log "No module.json found, skipping."
|
|
1347
|
+
fi
|
|
1348
|
+
|
|
1349
|
+
if [ -e "$resources_dir/module.config.json" ]; then
|
|
1350
|
+
cp "$resources_dir/module.config.json" "$dist_dir/"
|
|
1351
|
+
else
|
|
1352
|
+
log "No module.config.json found, skipping."
|
|
1353
|
+
fi
|
|
1354
|
+
|
|
1355
|
+
mkdir -p "$dist_assets_dir"
|
|
1356
|
+
for entry in "$resources_dir"/*; do
|
|
1357
|
+
[ -e "$entry" ] || continue
|
|
1358
|
+
entry_name=$(basename "$entry")
|
|
1359
|
+
case "$entry_name" in
|
|
1360
|
+
module.json|module.config.json|README.md|CHANGELOG.md|LICENSE.md|workflows-backup)
|
|
1361
|
+
log "Skipping non-asset entry: '$entry_name'"
|
|
1362
|
+
continue
|
|
1363
|
+
;;
|
|
1364
|
+
esac
|
|
1365
|
+
cp -r "$entry" "$dist_assets_dir/"
|
|
1366
|
+
done
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
copy_jar_files() {
|
|
1370
|
+
local plugin_folder=$1
|
|
1371
|
+
local destination=$2
|
|
1372
|
+
|
|
1373
|
+
if [ -d "$plugin_folder" ]; then
|
|
1374
|
+
log "Plugin folder exists, copying plugin files from '$plugin_folder' to '$destination'"
|
|
1375
|
+
mkdir -p "$destination"
|
|
1376
|
+
else
|
|
1377
|
+
log "No plugins to copy from: '$plugin_folder'"
|
|
1378
|
+
return 0
|
|
1379
|
+
fi
|
|
1380
|
+
|
|
1381
|
+
for TARGET_FOLDER in "$plugin_folder"/*/target; do
|
|
1382
|
+
log "TARGET_FOLDER: '$TARGET_FOLDER'"
|
|
1383
|
+
if [ -d "$TARGET_FOLDER" ]; then
|
|
1384
|
+
for JAR_FILE in "$TARGET_FOLDER"/*.jar; do
|
|
1385
|
+
log "JAR FILE: '$JAR_FILE'"
|
|
1386
|
+
if [ -f "$JAR_FILE" ]; then
|
|
1387
|
+
echo "Copying $JAR_FILE to $destination"
|
|
1388
|
+
cp "$JAR_FILE" "$destination"
|
|
1389
|
+
fi
|
|
1390
|
+
done
|
|
1391
|
+
fi
|
|
1392
|
+
done
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
zip_module() {
|
|
1396
|
+
local dist_dir=$1
|
|
1397
|
+
local module_name=$2
|
|
1398
|
+
|
|
1399
|
+
if [ -f "$dist_dir/$module_name.zip" ]; then
|
|
1400
|
+
rm "$dist_dir/$module_name.zip"
|
|
1401
|
+
fi
|
|
1402
|
+
|
|
1403
|
+
local zip_file="$module_name.zip"
|
|
1404
|
+
(cd "$dist_dir/$module_name" && zip -r "$zip_file" . -q)
|
|
1405
|
+
mv "$dist_dir/$module_name/$zip_file" "$dist_dir"
|
|
1406
|
+
|
|
1407
|
+
log "Contents of '$dist_dir' zipped into '$zip_file'."
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
main() {
|
|
1411
|
+
parse_options "$@"
|
|
1412
|
+
require_command sed
|
|
1413
|
+
require_command cp
|
|
1414
|
+
require_command basename
|
|
1415
|
+
if [ "$BUILD_PLUGINS" = true ]; then
|
|
1416
|
+
require_command mvn
|
|
1417
|
+
fi
|
|
1418
|
+
if [ "$BUILD_MODULE" = true ]; then
|
|
1419
|
+
require_command zip
|
|
1420
|
+
fi
|
|
1421
|
+
|
|
1422
|
+
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
|
1423
|
+
MODULE_BASE_DIR="$SCRIPT_DIR/.."
|
|
1424
|
+
PLUGIN_DIR="$SCRIPT_DIR/../plugins"
|
|
1425
|
+
PLUGIN_RULES_DIR="$PLUGIN_DIR/rules"
|
|
1426
|
+
DIST_DIR="$MODULE_BASE_DIR/dist"
|
|
1427
|
+
RESOURCES_DIR="$MODULE_BASE_DIR/resources"
|
|
1428
|
+
|
|
1429
|
+
log "Script Directory: $SCRIPT_DIR"
|
|
1430
|
+
log "Module Directory: $MODULE_BASE_DIR"
|
|
1431
|
+
|
|
1432
|
+
if [ $BUILD_PLUGINS = true ]; then
|
|
1433
|
+
log "Building plugins..."
|
|
1434
|
+
cd "$PLUGIN_DIR" || exit 1
|
|
1435
|
+
mvn clean package || {
|
|
1436
|
+
echo "Maven build failed"
|
|
1437
|
+
exit 1
|
|
1438
|
+
}
|
|
1439
|
+
else
|
|
1440
|
+
log "Skipping building plugins..."
|
|
1441
|
+
fi
|
|
1442
|
+
|
|
1443
|
+
module_name=$(sed -n 's/.*"name": *"\([^"]*\)".*/\1/p' "$RESOURCES_DIR/module.json" | head -n 1)
|
|
1444
|
+
sanitized_name=$(echo "$module_name" | sed 's|/|-|g')
|
|
1445
|
+
module_version=$(sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' "$RESOURCES_DIR/module.json" | head -n 1)
|
|
1446
|
+
combined_name="$sanitized_name-$module_version"
|
|
1447
|
+
module_dist_dir="$DIST_DIR/$combined_name"
|
|
1448
|
+
module_dist_assets_dir="$module_dist_dir/assets"
|
|
1449
|
+
module_dist_plugins_dir="$module_dist_assets_dir/rules"
|
|
1450
|
+
|
|
1451
|
+
clean_folder "$DIST_DIR"
|
|
1452
|
+
mkdir -p "$module_dist_dir"
|
|
1453
|
+
|
|
1454
|
+
for f in CHANGELOG.md README.md LICENSE.md; do
|
|
1455
|
+
if [ -e "$MODULE_BASE_DIR/$f" ]; then
|
|
1456
|
+
cp "$MODULE_BASE_DIR/$f" "$module_dist_dir/"
|
|
1457
|
+
else
|
|
1458
|
+
log "No $f found, skipping."
|
|
1459
|
+
fi
|
|
1460
|
+
done
|
|
1461
|
+
|
|
1462
|
+
copy_assets "$RESOURCES_DIR" "$module_dist_dir" "$module_dist_assets_dir"
|
|
1463
|
+
copy_jar_files "$PLUGIN_RULES_DIR" "$module_dist_plugins_dir"
|
|
1464
|
+
|
|
1465
|
+
if [ $BUILD_MODULE = true ]; then
|
|
1466
|
+
log "Building module..."
|
|
1467
|
+
zip_module "$DIST_DIR" "$combined_name"
|
|
1468
|
+
else
|
|
1469
|
+
log "Skipping building module zip..."
|
|
1470
|
+
fi
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
main "$@"
|
|
1474
|
+
```
|
|
1475
|
+
|
|
1476
|
+
### Build Script - PowerShell (`scripts/build-module.ps1`)
|
|
1477
|
+
|
|
1478
|
+
```powershell
|
|
1479
|
+
#!/usr/bin/env pwsh
|
|
1480
|
+
|
|
1481
|
+
<#
|
|
1482
|
+
.SYNOPSIS
|
|
1483
|
+
Build module script for FluentCommerce modules
|
|
1484
|
+
|
|
1485
|
+
.DESCRIPTION
|
|
1486
|
+
This script builds rule plugins and packages the module assets into a distributable zip file.
|
|
1487
|
+
|
|
1488
|
+
.PARAMETER SkipModule
|
|
1489
|
+
Skip zipping the module
|
|
1490
|
+
|
|
1491
|
+
.PARAMETER SkipPlugins
|
|
1492
|
+
Skip building rule plugins
|
|
1493
|
+
|
|
1494
|
+
.PARAMETER VerboseLogging
|
|
1495
|
+
Enable verbose logging
|
|
1496
|
+
|
|
1497
|
+
.EXAMPLE
|
|
1498
|
+
.\build-module.ps1 -VerboseLogging
|
|
1499
|
+
|
|
1500
|
+
.EXAMPLE
|
|
1501
|
+
.\build-module.ps1 -SkipPlugins
|
|
1502
|
+
#>
|
|
1503
|
+
|
|
1504
|
+
[CmdletBinding()]
|
|
1505
|
+
param(
|
|
1506
|
+
[Alias('m')]
|
|
1507
|
+
[switch]$SkipModule,
|
|
1508
|
+
|
|
1509
|
+
[Alias('p')]
|
|
1510
|
+
[switch]$SkipPlugins,
|
|
1511
|
+
|
|
1512
|
+
[Alias('x')]
|
|
1513
|
+
[switch]$VerboseLogging
|
|
1514
|
+
)
|
|
1515
|
+
|
|
1516
|
+
$Script:BuildPlugins = -not $SkipPlugins
|
|
1517
|
+
$Script:BuildModule = -not $SkipModule
|
|
1518
|
+
$Script:VerboseMode = $VerboseLogging
|
|
1519
|
+
|
|
1520
|
+
function Write-Log {
|
|
1521
|
+
param([string]$Message)
|
|
1522
|
+
if ($Script:VerboseMode) {
|
|
1523
|
+
Write-Host $Message
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
function Remove-FolderIfExists {
|
|
1528
|
+
param([string]$Path)
|
|
1529
|
+
if (Test-Path $Path) {
|
|
1530
|
+
Write-Log "Directory $Path exists, clearing..."
|
|
1531
|
+
Remove-Item -Path $Path -Recurse -Force
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
function Copy-Assets {
|
|
1536
|
+
param(
|
|
1537
|
+
[string]$ResourcesDir,
|
|
1538
|
+
[string]$DistDir,
|
|
1539
|
+
[string]$DistAssetsDir
|
|
1540
|
+
)
|
|
1541
|
+
Write-Log "Resources Dir: '$ResourcesDir', Destination Dir: '$DistDir'"
|
|
1542
|
+
|
|
1543
|
+
if (-not (Test-Path $ResourcesDir)) {
|
|
1544
|
+
Write-Error "Resources directory '$ResourcesDir' does not exist"
|
|
1545
|
+
exit 1
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
$moduleJsonPath = Join-Path $ResourcesDir "module.json"
|
|
1549
|
+
if (Test-Path $moduleJsonPath) {
|
|
1550
|
+
Copy-Item $moduleJsonPath $DistDir
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
$moduleConfigPath = Join-Path $ResourcesDir "module.config.json"
|
|
1554
|
+
if (Test-Path $moduleConfigPath) {
|
|
1555
|
+
Copy-Item $moduleConfigPath $DistDir
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
if (-not (Test-Path $DistAssetsDir)) {
|
|
1559
|
+
New-Item -ItemType Directory -Path $DistAssetsDir -Force | Out-Null
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
$excludedEntries = @("module.json", "module.config.json", "README.md", "CHANGELOG.md", "LICENSE.md", "workflows-backup")
|
|
1563
|
+
Get-ChildItem -Path $ResourcesDir -Force | ForEach-Object {
|
|
1564
|
+
if ($excludedEntries -contains $_.Name) {
|
|
1565
|
+
Write-Log "Skipping non-asset entry: '$($_.Name)'"
|
|
1566
|
+
} else {
|
|
1567
|
+
Copy-Item -Path $_.FullName -Destination $DistAssetsDir -Recurse -Force
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
function Copy-JarFiles {
|
|
1573
|
+
param(
|
|
1574
|
+
[string]$PluginFolder,
|
|
1575
|
+
[string]$Destination
|
|
1576
|
+
)
|
|
1577
|
+
if (Test-Path $PluginFolder) {
|
|
1578
|
+
Write-Log "Plugin folder exists, copying from '$PluginFolder' to '$Destination'"
|
|
1579
|
+
New-Item -ItemType Directory -Path $Destination -Force | Out-Null
|
|
1580
|
+
} else {
|
|
1581
|
+
Write-Log "No plugins to copy from: '$PluginFolder'"
|
|
1582
|
+
return
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
$targetFolders = Get-ChildItem -Path $PluginFolder -Directory | Where-Object { Test-Path (Join-Path $_.FullName "target") }
|
|
1586
|
+
foreach ($folder in $targetFolders) {
|
|
1587
|
+
$targetFolder = Join-Path $folder.FullName "target"
|
|
1588
|
+
if (Test-Path $targetFolder) {
|
|
1589
|
+
$jarFiles = Get-ChildItem -Path $targetFolder -Filter "*.jar"
|
|
1590
|
+
foreach ($jarFile in $jarFiles) {
|
|
1591
|
+
Write-Host "Copying $($jarFile.FullName) to $Destination"
|
|
1592
|
+
Copy-Item $jarFile.FullName $Destination
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
function New-ModuleZip {
|
|
1599
|
+
param(
|
|
1600
|
+
[string]$DistDir,
|
|
1601
|
+
[string]$ModuleName
|
|
1602
|
+
)
|
|
1603
|
+
$zipPath = Join-Path $DistDir "$ModuleName.zip"
|
|
1604
|
+
if (Test-Path $zipPath) {
|
|
1605
|
+
Remove-Item $zipPath -Force
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
$sourceDir = Join-Path $DistDir $ModuleName
|
|
1609
|
+
Compress-Archive -Path "$sourceDir\*" -DestinationPath $zipPath -Force
|
|
1610
|
+
Write-Log "Contents zipped into '$ModuleName.zip'."
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
function Get-JsonValue {
|
|
1614
|
+
param(
|
|
1615
|
+
[string]$FilePath,
|
|
1616
|
+
[string]$PropertyName
|
|
1617
|
+
)
|
|
1618
|
+
if (-not (Test-Path $FilePath)) { return $null }
|
|
1619
|
+
try {
|
|
1620
|
+
$json = Get-Content $FilePath -Raw | ConvertFrom-Json
|
|
1621
|
+
return $json.$PropertyName
|
|
1622
|
+
} catch {
|
|
1623
|
+
Write-Log "Failed to parse JSON from $FilePath"
|
|
1624
|
+
return $null
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
function Main {
|
|
1629
|
+
if ($PSScriptRoot) {
|
|
1630
|
+
$scriptDir = $PSScriptRoot
|
|
1631
|
+
} elseif ($MyInvocation.MyCommand.Path) {
|
|
1632
|
+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
1633
|
+
} else {
|
|
1634
|
+
$scriptDir = (Get-Location).Path
|
|
1635
|
+
}
|
|
1636
|
+
$moduleBaseDir = Split-Path -Parent $scriptDir
|
|
1637
|
+
$pluginDir = Join-Path $moduleBaseDir "plugins"
|
|
1638
|
+
$pluginRulesDir = Join-Path $pluginDir "rules"
|
|
1639
|
+
$distDir = Join-Path $moduleBaseDir "dist"
|
|
1640
|
+
$resourcesDir = Join-Path $moduleBaseDir "resources"
|
|
1641
|
+
|
|
1642
|
+
if ($Script:BuildPlugins) {
|
|
1643
|
+
Write-Log "Building plugins..."
|
|
1644
|
+
Push-Location $pluginDir
|
|
1645
|
+
try {
|
|
1646
|
+
& mvn clean package
|
|
1647
|
+
if ($LASTEXITCODE -ne 0) {
|
|
1648
|
+
Write-Error "Maven build failed"
|
|
1649
|
+
exit 1
|
|
1650
|
+
}
|
|
1651
|
+
} finally {
|
|
1652
|
+
Pop-Location
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
$moduleJsonPath = Join-Path $resourcesDir "module.json"
|
|
1657
|
+
$moduleName = Get-JsonValue $moduleJsonPath "name"
|
|
1658
|
+
$moduleVersion = Get-JsonValue $moduleJsonPath "version"
|
|
1659
|
+
if (-not $moduleName -or -not $moduleVersion) {
|
|
1660
|
+
Write-Error "Could not extract module name or version from module.json"
|
|
1661
|
+
exit 1
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
$sanitizedName = $moduleName -replace "/", "-"
|
|
1665
|
+
$combinedName = "$sanitizedName-$moduleVersion"
|
|
1666
|
+
$moduleDistDir = Join-Path $distDir $combinedName
|
|
1667
|
+
$moduleDistAssetsDir = Join-Path $moduleDistDir "assets"
|
|
1668
|
+
$moduleDistPluginsDir = Join-Path $moduleDistAssetsDir "rules"
|
|
1669
|
+
|
|
1670
|
+
Remove-FolderIfExists $distDir
|
|
1671
|
+
New-Item -ItemType Directory -Path $moduleDistDir -Force | Out-Null
|
|
1672
|
+
|
|
1673
|
+
$rootFiles = @("CHANGELOG.md", "README.md", "LICENSE.md")
|
|
1674
|
+
foreach ($file in $rootFiles) {
|
|
1675
|
+
$sourcePath = Join-Path $moduleBaseDir $file
|
|
1676
|
+
if (Test-Path $sourcePath) {
|
|
1677
|
+
Copy-Item $sourcePath $moduleDistDir
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
Copy-Assets $resourcesDir $moduleDistDir $moduleDistAssetsDir
|
|
1682
|
+
Copy-JarFiles $pluginRulesDir $moduleDistPluginsDir
|
|
1683
|
+
|
|
1684
|
+
if ($Script:BuildModule) {
|
|
1685
|
+
Write-Log "Building module..."
|
|
1686
|
+
New-ModuleZip $distDir $combinedName
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
Write-Host "Build completed successfully!"
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
Main
|
|
1693
|
+
```
|
|
1694
|
+
|
|
1695
|
+
### `.gitignore`
|
|
1696
|
+
|
|
1697
|
+
```gitignore
|
|
1698
|
+
# Maven build output
|
|
1699
|
+
target/
|
|
1700
|
+
*.class
|
|
1701
|
+
|
|
1702
|
+
# IDE files
|
|
1703
|
+
.idea/
|
|
1704
|
+
*.iml
|
|
1705
|
+
.project
|
|
1706
|
+
.classpath
|
|
1707
|
+
.settings/
|
|
1708
|
+
.vscode/
|
|
1709
|
+
|
|
1710
|
+
# Distribution output
|
|
1711
|
+
dist/
|
|
1712
|
+
|
|
1713
|
+
# OS files
|
|
1714
|
+
.DS_Store
|
|
1715
|
+
Thumbs.db
|
|
1716
|
+
|
|
1717
|
+
# Logs
|
|
1718
|
+
*.log
|
|
1719
|
+
|
|
1720
|
+
# Dependencies (should be resolved by Maven)
|
|
1721
|
+
node_modules/
|
|
1722
|
+
|
|
1723
|
+
# Schema (downloaded separately)
|
|
1724
|
+
global/schema.json
|
|
1725
|
+
```
|
|
1726
|
+
|
|
1727
|
+
## Entity Type to Package Mapping
|
|
1728
|
+
|
|
1729
|
+
When `--entity-types` is specified, rule classes are placed in sub-packages that match the entity type:
|
|
1730
|
+
|
|
1731
|
+
| Entity Type | Java Package | Sub-package name |
|
|
1732
|
+
|-------------|-------------|------------------|
|
|
1733
|
+
| `ORDER` | `com.fluentcommerce.rule.order` | `order` |
|
|
1734
|
+
| `FULFILMENT` | `com.fluentcommerce.rule.fulfilment` | `fulfilment` |
|
|
1735
|
+
| `FULFILMENT_OPTIONS` | `com.fluentcommerce.rule.fulfilmentoption` | `fulfilmentoption` |
|
|
1736
|
+
| `ARTICLE` | `com.fluentcommerce.rule.article` | `article` |
|
|
1737
|
+
| `CONSIGNMENT` | `com.fluentcommerce.rule.consignment` | `consignment` |
|
|
1738
|
+
| `WAVE` | `com.fluentcommerce.rule.wave` | `wave` |
|
|
1739
|
+
| `LOCATION` | `com.fluentcommerce.rule.location` | `location` |
|
|
1740
|
+
| `PRODUCT` | `com.fluentcommerce.rule.variantproduct` | `variantproduct` |
|
|
1741
|
+
| `VIRTUAL_CATALOGUE` | `com.fluentcommerce.rule.catalogue` | `catalogue` |
|
|
1742
|
+
| `INVENTORY_POSITION` | `com.fluentcommerce.rule.inventory` | `inventory` |
|
|
1743
|
+
| `RETURN_ORDER` | `com.fluentcommerce.rule.returnorder` | `returnorder` |
|
|
1744
|
+
| (default/common) | `com.fluentcommerce.rule.common` | `common` |
|
|
1745
|
+
|
|
1746
|
+
When multiple entity types are specified, place each rule in the package matching its primary entity type. If a rule applies to multiple entity types, use `common`.
|
|
1747
|
+
|
|
1748
|
+
## Execution Flow
|
|
1749
|
+
|
|
1750
|
+
```
|
|
1751
|
+
1. VALIDATE inputs
|
|
1752
|
+
a. module-name format: must match /^[a-z][a-z0-9-]*$/ (lowercase, hyphens allowed, no leading hyphen)
|
|
1753
|
+
b. rule names (if --rules): must match /^[A-Z][a-zA-Z0-9]*$/ (PascalCase)
|
|
1754
|
+
c. entity types (if --entity-types): must be in the valid set above
|
|
1755
|
+
|
|
1756
|
+
2. PRE-CHECK: Module already exists?
|
|
1757
|
+
a. Check accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-<module-name>/ exists
|
|
1758
|
+
b. If exists → ABORT with guidance
|
|
1759
|
+
c. Optionally check deployed modules via: fluent module list -p <PROFILE>
|
|
1760
|
+
|
|
1761
|
+
3. DETECT account prefix
|
|
1762
|
+
a. If --account-prefix provided → use it
|
|
1763
|
+
b. Else try MCP: plugin.list (compact: true)
|
|
1764
|
+
→ Extract <ACCOUNT> from first non-FLUENTRETAIL rule key
|
|
1765
|
+
c. Else derive from PROFILE name (uppercase)
|
|
1766
|
+
d. Fallback: "UNKNOWN" with warning
|
|
1767
|
+
|
|
1768
|
+
4. DETECT SDK versions (optional)
|
|
1769
|
+
a. Check if another module exists under accounts/<PROFILE>/SOURCE/
|
|
1770
|
+
b. If found, read its rules/*/pom.xml for rubix-plugin-base.version and fluent-api-client.version
|
|
1771
|
+
c. If not found, use defaults: rubix-plugin-base=1.2.0.679, fluent-api-client=1.2.0.118
|
|
1772
|
+
|
|
1773
|
+
5. COMPUTE derived values
|
|
1774
|
+
a. MODULE_ALIAS = <module-name> (e.g., hm-returns)
|
|
1775
|
+
b. MODULE_TITLE = title-case of module-name with hyphens as spaces (e.g., Hm Returns)
|
|
1776
|
+
c. OSGI_SYMBOLIC_NAME = "_." + <module-alias with hyphens removed> (e.g., _.hmreturns)
|
|
1777
|
+
d. PACKAGE = entity type sub-package from mapping table above
|
|
1778
|
+
|
|
1779
|
+
6. CREATE directory structure
|
|
1780
|
+
a. Create all directories listed in the structure section
|
|
1781
|
+
b. Ensure all parent directories exist before writing files
|
|
1782
|
+
|
|
1783
|
+
7. GENERATE files (use Write tool, not bash echo)
|
|
1784
|
+
a. plugins/pom.xml — parent POM with all variable substitutions
|
|
1785
|
+
b. plugins/types/pom.xml — types aggregator
|
|
1786
|
+
c. plugins/types/<alias>/pom.xml — types implementation
|
|
1787
|
+
d. plugins/util/pom.xml — util aggregator
|
|
1788
|
+
e. plugins/util/<alias>/pom.xml — util implementation
|
|
1789
|
+
f. plugins/rules/pom.xml — rules aggregator
|
|
1790
|
+
g. plugins/rules/<alias>/pom.xml — rules implementation (OSGi bundle)
|
|
1791
|
+
h. resources/module.json — module manifest
|
|
1792
|
+
i. scripts/build-module.sh — bash build script (set executable: chmod +x)
|
|
1793
|
+
j. scripts/build-module.ps1 — powershell build script
|
|
1794
|
+
k. .gitignore
|
|
1795
|
+
|
|
1796
|
+
8. FOR EACH rule in --rules (if specified):
|
|
1797
|
+
a. Determine target package from entity type
|
|
1798
|
+
b. Generate Java rule class using the appropriate template (BaseRule or Rule)
|
|
1799
|
+
c. Generate test class
|
|
1800
|
+
d. Add rule name to module.json rules[] array
|
|
1801
|
+
|
|
1802
|
+
9. CREATE empty placeholder directories
|
|
1803
|
+
a. resources/settings/
|
|
1804
|
+
b. global/
|
|
1805
|
+
c. dist/
|
|
1806
|
+
d. Empty src/main/java/ trees for types and util (if no rules specified)
|
|
1807
|
+
|
|
1808
|
+
10. MAKE build script executable
|
|
1809
|
+
bash: chmod +x scripts/build-module.sh
|
|
1810
|
+
|
|
1811
|
+
11. VERIFY structure (optional, if Maven is available)
|
|
1812
|
+
cd plugins/ && mvn validate
|
|
1813
|
+
This confirms POM structure is valid without actually building.
|
|
1814
|
+
|
|
1815
|
+
12. REPORT generated files
|
|
1816
|
+
List all files created with their full paths.
|
|
1817
|
+
Print next steps:
|
|
1818
|
+
- "Module scaffolded at: accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-<module-name>/"
|
|
1819
|
+
- "Next: Add rules with /fluent-rule-scaffold or build with /fluent-build"
|
|
1820
|
+
- "Validate structure: /fluent-module-validate"
|
|
1821
|
+
```
|
|
1822
|
+
|
|
1823
|
+
## SDK Version Detection
|
|
1824
|
+
|
|
1825
|
+
The `rubix-plugin-base.version` and `fluent-api-client.version` determine Fluent Commerce SDK compatibility. Detection order:
|
|
1826
|
+
|
|
1827
|
+
```
|
|
1828
|
+
1. Check existing modules under accounts/<PROFILE>/SOURCE/
|
|
1829
|
+
→ Read any rules/*/pom.xml for <rubix-plugin-base.version> and <fluent-api-client.version>
|
|
1830
|
+
→ Reuse the same versions for consistency
|
|
1831
|
+
|
|
1832
|
+
2. Check deployed modules via MCP (environment.discover with modules section)
|
|
1833
|
+
→ Extract compatibility block versions from deployed module metadata
|
|
1834
|
+
|
|
1835
|
+
3. Fall back to known stable versions:
|
|
1836
|
+
rubix-plugin-base.version = 1.2.0.679
|
|
1837
|
+
fluent-api-client.version = 1.2.0.118
|
|
1838
|
+
apollo-client-maven-plugin.version = 1.0.0.15
|
|
1839
|
+
```
|
|
1840
|
+
|
|
1841
|
+
## Integration with Other Skills
|
|
1842
|
+
|
|
1843
|
+
| Skill | Relationship |
|
|
1844
|
+
|-------|-------------|
|
|
1845
|
+
| `/fluent-rule-scaffold` | For adding rules to an EXISTING module. After module-scaffold creates the module structure, use rule-scaffold to add individual rules incrementally. Rule-scaffold handles: OOTB-vs-custom decision, BaseRule vs Rule template selection, module.json wiring, and test generation. |
|
|
1846
|
+
| `/fluent-build` | Immediately usable after scaffold. Run `mvn clean install` from `plugins/`, then `scripts/build-module.sh` (or `.ps1`) for ZIP packaging. |
|
|
1847
|
+
| `/fluent-module-validate` | Should pass structural validation on the scaffolded output with 0 FAILs. Expected WARNs: no GraphQL schema (not yet downloaded), no distribution (not yet built). |
|
|
1848
|
+
| `/fluent-module-deploy` | Deploy the built ZIP to a target retailer after build succeeds. |
|
|
1849
|
+
| `/fluent-custom-code` | Analyzes the generated source for behavior mapping and constraint extraction. |
|
|
1850
|
+
|
|
1851
|
+
### Post-Scaffold Workflow
|
|
1852
|
+
|
|
1853
|
+
```
|
|
1854
|
+
/fluent-module-scaffold hm-returns --entity-types ORDER,RETURN_ORDER --rules CreateReturnFromOrder,ProcessReturnApproval
|
|
1855
|
+
|
|
|
1856
|
+
v
|
|
1857
|
+
/fluent-rule-scaffold AddReturnNotes --entity-type RETURN_ORDER --package returnorder
|
|
1858
|
+
|
|
|
1859
|
+
v
|
|
1860
|
+
/fluent-module-validate accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-hm-returns/
|
|
1861
|
+
|
|
|
1862
|
+
v
|
|
1863
|
+
/fluent-build build
|
|
1864
|
+
|
|
|
1865
|
+
v
|
|
1866
|
+
/fluent-module-deploy
|
|
1867
|
+
```
|
|
1868
|
+
|
|
1869
|
+
## Edge Cases
|
|
1870
|
+
|
|
1871
|
+
### Module name conflicts
|
|
1872
|
+
|
|
1873
|
+
Check `accounts/<PROFILE>/SOURCE/` for any directory containing the module name:
|
|
1874
|
+
|
|
1875
|
+
```bash
|
|
1876
|
+
ls accounts/<PROFILE>/SOURCE/ | grep -i "<module-name>"
|
|
1877
|
+
```
|
|
1878
|
+
|
|
1879
|
+
If found, abort. Do NOT overwrite an existing module.
|
|
1880
|
+
|
|
1881
|
+
### No rules specified
|
|
1882
|
+
|
|
1883
|
+
When `--rules` is omitted:
|
|
1884
|
+
- `module.json` has `"rules": []`
|
|
1885
|
+
- No Java classes are generated
|
|
1886
|
+
- Empty `src/main/java/com/fluentcommerce/rule/` directory is created
|
|
1887
|
+
- Empty `src/test/java/com/fluentcommerce/rule/` directory is created
|
|
1888
|
+
- The module is still buildable (produces an empty JAR)
|
|
1889
|
+
|
|
1890
|
+
### Windows path handling
|
|
1891
|
+
|
|
1892
|
+
- All POM `<module>` references use forward slashes (Maven standard)
|
|
1893
|
+
- Build scripts have both `.sh` (Git Bash, WSL, macOS, Linux) and `.ps1` (PowerShell) variants
|
|
1894
|
+
- Use Node.js for cross-platform directory creation when bash `mkdir -p` is unavailable
|
|
1895
|
+
- Use `path.join()` semantics in documentation, not hardcoded separators
|
|
1896
|
+
|
|
1897
|
+
### Module name validation
|
|
1898
|
+
|
|
1899
|
+
Reject module names that:
|
|
1900
|
+
- Start with a digit or hyphen
|
|
1901
|
+
- Contain uppercase letters (convention: lowercase only)
|
|
1902
|
+
- Contain spaces or special characters beyond hyphens
|
|
1903
|
+
- Are empty or longer than 50 characters
|
|
1904
|
+
|
|
1905
|
+
### GraphQL schema not available
|
|
1906
|
+
|
|
1907
|
+
The `global/schema.json` file is required for Apollo codegen (types submodule) but may not be available at scaffold time. The scaffolder:
|
|
1908
|
+
- Creates the `global/` directory empty
|
|
1909
|
+
- Notes in the output that the schema must be downloaded before a full build
|
|
1910
|
+
- The types submodule will fail codegen without it, but `mvn validate` will pass
|
|
1911
|
+
|
|
1912
|
+
### Duplicate rule names
|
|
1913
|
+
|
|
1914
|
+
If `--rules` contains duplicates, deduplicate silently and warn:
|
|
1915
|
+
|
|
1916
|
+
```
|
|
1917
|
+
Warning: Duplicate rule name 'MyRule' removed from list.
|
|
1918
|
+
```
|
|
1919
|
+
|
|
1920
|
+
## Gotchas
|
|
1921
|
+
|
|
1922
|
+
- **OSGi symbolic name** must be unique per account. Convention: `_.<aliasNoHyphens>` (e.g., `_.hmreturns`). If two modules share the same symbolic name, deployment will fail with a cryptic OSGi error.
|
|
1923
|
+
- **`<Rubix-Account>_</Rubix-Account>`** in the bundle plugin config means "register to the deploying account." Do not change this to a hardcoded account name.
|
|
1924
|
+
- **Java source level 1.8** is required by the Rubix runtime. Do not use Java 11+ features in rule code even if the build JDK is newer.
|
|
1925
|
+
- **`module.json` `name` field** uses the format `fluent-commerce/fc-module-<name>` by convention (with the `fluent-commerce/` prefix). This is the canonical identifier used by `fluent module install`.
|
|
1926
|
+
- **`module.json` `rules` array** contains simple rule names (e.g., `"MyRule"`), NOT fully-qualified class names or account-prefixed keys. The OSGi bundle plugin discovers `@RuleInfo` annotations at build time.
|
|
1927
|
+
- **Apollo codegen** requires Node.js 18 for the Maven plugin. On Windows with nvm-windows (nvm4w), use: `nvm use 18.x.x` (full version number required).
|
|
1928
|
+
- **Intermediate POM hierarchy** matters. The parent POM declares `<module>types</module>`, which references `plugins/types/pom.xml` (an aggregator), which in turn declares `<module>${MODULE_ALIAS}</module>` pointing to the implementation POM. Skipping the aggregator level will break the build.
|