Skip to content

Commit

Permalink
Merge pull request #1 from swisscom/feature/OCERED-6931
Browse files Browse the repository at this point in the history
Initial version
  • Loading branch information
sabberworm authored Oct 16, 2024
2 parents c80e539 + 405a8ed commit 53fbf1b
Show file tree
Hide file tree
Showing 233 changed files with 23,380 additions and 4,498 deletions.
35 changes: 35 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": ["eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended", "prettier"],
"overrides": [],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["react", "react-hooks", "@typescript-eslint"],
"settings": {
"react": {
"createClass": "createReactClass", // Regex for Component Factory to use,
// default to "createReactClass"
"pragma": "React", // Pragma to use, default to "React"
"fragment": "Fragment", // Fragment to use (may be a property of <pragma>), default to "Fragment"
"version": "detect" // React version. "detect" automatically picks the version you have installed.
// You can also use `16.0`, `16.3`, etc, if you want to override the detected value.
// It will default to "latest" and warn if missing, and to "detect" in the future
},
"componentWrapperFunctions": [
// The name of any function used to wrap components, e.g. Mobx `observer` function. If this isn't set, components wrapped by these functions will be skipped.
"styled" // `property`
]
},
"ignorePatterns": ["/build/**"],
"rules": {
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-unused-vars": 0,
"react/prop-types": 0
}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,6 @@ node_modules/
*.iml
/gradle.properties
/.idea/
/.parcel-cache/
/.vscode/
/mock/*.woff2
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.svg
21 changes: 21 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"arrowParens": "avoid",
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"endOfLine": "lf",
"bracketSameLine": false,
"jsxSingleQuote": false,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"singleQuote": true,
"tabWidth": 8,
"trailingComma": "all",
"plugins": ["prettier-plugin-java", "@prettier/plugin-xml"],
"xmlWhitespaceSensitivity": "ignore",
"htmlWhitespaceSensitivity": "ignore",
"useTabs": true,
"semi": true,
"printWidth": 150,
"requirePragma": false
}
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ Use GitHub’s issues feature to report problems with and request enhancements t

Code contributions are highly appreciated. Bug fixes and small enhancements can be handed in as pull-requests. For larger changes and features, please open a GitHub issue with your proposal first so the solution can be discussed.

### Code Checks

The following checks must succeed before code can be merged:

- Java Compile
- Java Test
- Checkstyle
- PMD
- CPD
- Spotbugs
- OakPAL
- Prettier
- TypeScript compile
- ESLint

The `./gradlew check` command executes them all. To reformat code, use `./gradlew prettierFormat`.

### Commit Guidelines

All your commits must follow the [conventional commit message format](https://www.conventionalcommits.org/en/v1.0.0/#summary).
Expand Down
135 changes: 131 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# JCR Hopper

<img src="./docs/logo.svg" width=200 height=200 alt="JCR Hopper Navy Hat">

_Migrate AEM with Grace_

JCR Hopper is a migration and reporting tool for AEM’s content repository.
Expand All @@ -10,11 +12,84 @@ JCR Hopper also comes with a visual tool to create, preview and run the scripts.

## Installation

Download the package (.zip file) from the “Releases” section in GitHub and install it using package manager.

Alternatively, if you don’t want the script builder GUI, you can also only deploy the bundle (.jar file).

## Usage

### Use the Script Builder
### Java API

Running a script requires a script (either as a JSON-encoded string or as an instance of `com.swisscom.aem.tools.jcrhopper.config.Script`) and a runner (`com.swisscom.aem.tools.jcrhopper.Runner`).

#### Creating a script

JCR hopper scripts comprise the following:

- A log level that determines which log messages are sent to the run handler.
- A list of hop configs that are run against a given node. Hops can also have their own descendant pipelines of hop configs.
- A list of parameters that the script supports. Parameters always have a default value, thus passing arguments to the script runner is always optional. Each parameter also has a name, a script builder input type hint and an evaluation type that determines how the default values/arguments are to be interpreted.

#### Configuring a builder

Typically, a runner is created by means of a builder using `Runner#builder()`.

The builder offers various methods to configure the runner:

- `#addUtil(String, Object)`, `#addUtils(Map<String, Object>)`: Register namespaces for utility classes/objects that are available in JEXL expressions and script blocks.
- `#addVariable(String, Object)`, `#addVariables(Map<String, Object>)`: Make some variables known to the script ahead of time.
- `#addHop(Hop<?>)`, `#addHops(Collection<Hop<?>>)`: Make hop types known to the runner. Note: scripts in JSON format can only be parsed once the hop types they are using are registered.
- `#registerFile(String, Function<String, File>)`, `#registerFiles(Map<String, Function<String, File>>)`: Register file creators that allow scripts to aggregate information into output files.
- `#addDefaultUtils(boolean)`: if set to `true`, The script will know about the `arrays`, `stream`, `class`, and `collections` utility namespaces.
- `#runHandler(com.swisscom.aem.tools.jcrhopper.config.RunHandler)`: Set a listener that is informed about script output and events. The default run handler does nothing (but log messages are logged using Slf4j in any case).

Once the builder is configured, it can be turned into a runner with either `#build(Script)` or `#build(String)` (for JSON scripts).

#### Running a Script

A configured runner offers several overloads to run the script:

- `#run(javax.jcr.Node, boolean)`: Run the script on the given node. Save the session afterwards if the second argument is `true`.
- `#run(javax.jcr.Session, boolean)`: Same as above but use the root node of the given session.
- `#run(javax.jcr.Session, boolean, java.util.Map<String, String>)`, `#run(javax.jcr.Node, boolean, java.util.Map<String, String>)`: Same as above but with arguments to be passed to the script. Only arguments matching declared parameters on the script will be taken into account.

#### Using the OSGi Service

The OSGi service `com.swisscom.aem.tools.jcrhopper.osgi.RunnerService` is used to return pre-configured runner builders that already know about the default hops, utils and files.

#### Extending the OSGi Service

To extend runner builders returned by the OSGi service, create a component that provides the `com.swisscom.aem.tools.jcrhopper.osgi.RunnerBuilderExtension` service. Implement `#configure` to configure runner builders that are created via `RunnerService`.

### HTTP API

#### Use the Script Builder

![Script builder screenshot](./docs/script-builder.png)

The script builder is a visual tool designed to help you build, prototype and execute your scripts. It is available at `/apps/jcr-hopper/script-builder.html`.

#### Run the Script Manually

The `com.swisscom.aem.tools.impl.http.HopRunnerServlet` servlet listens at `/bin/servlets/jcr-hopper/run` (though that is configurable) for POST requests. Each request will trigger one script execution. The script, the commit flag and all script arguments are passed in the request.

The script will be executed with the JCR session that is associated with the privileges of the requesting HTTP user.

The servlet uses `RunnerService`, so any registered `RunnerBuilderExtension`s are available.

The response will be a new-line delimited list of JSON objects or strings.

To use cURL to execute a script, do the following:

### Run the Script Manually
```sh
# $DO_COMMIT is either "true" or "false"
# $AEM_INSTANCE is the base URL of your AEM instance
curl -X POST -F "_script=@/path/to/script.json" -F "_commit=$DO_COMMIT" "$AEM_INSTANCE/bin/servlets/jcr-hopper/run"
```

Additional arguments can also be passed with `-F`.

<small>Note: Usually, POST requests are subject to the CSRF filter, thus a `:cq_csrf_token` would need to be added. However, the default configuration allowlists the cURL user agent to not require that.</small>

## Expression Syntax

Expand All @@ -26,32 +101,84 @@ Consult the [JEXL syntax reference](https://commons.apache.org/proper/commons-je

### <img src="./docs/icons/arrow_curve_down.svg" width=20 height=20 alt=""> Get Child Nodes

Gets all child nodes of this node accessible through the current Session that match a given pattern. The pattern may be a full name, a partial name with one or more wildcard characters (\*), or a disjunction of those (using the | character).

For more information, see [Node#getNodes(String)](https://adobe.ly/2YrfG1G).

The nested hop pipeline will run on each matching child node.

### <img src="./docs/icons/pirate_flag.svg" width=20 height=20 alt=""> Copy Node

Copy the current node recursively to a new destination.

The nested hop pipeline will run on the copied node.

### <img src="./docs/icons/sparkler.svg" width=20 height=20 alt=""> Create Child Node

Create a new node. If the given path is relative, it is based on the current node’s path (usually used to create a child node).

The nested hop pipeline will run on the created node.

### <img src="./docs/icons/control_knobs.svg" width=20 height=20 alt=""> Declare Variables

Sets variables on the current scope, available in script runner hops and JEXL expressions of all subsequent hops and their nested pipelines.

This hop does not have a nested pipeline.

### <img src="./docs/icons/loop.svg" width=20 height=20 alt=""> Iterate

Loops over an arbitrary expression. Can be configured to assume the expression evaluates to an iterable of nodes, in which case that becomes the current node of the nested pipeline. Otherwise, the nested pipeline stays on the current node but the iteration value becomes usable.

### <img src="./docs/icons/hole.svg" width=20 height=20 alt=""> Filter

Runs the nested pipeline only if the given JEXL expression is matched.

### <img src="./docs/icons/desert_island.svg" width=20 height=20 alt=""> Move Node

### <img src="./docs/icons/magnifying_glass_left.svg" width=20 height=20 alt=""> Query JCR
Moves the current node to a new location. Must not be used on the root node.

This hop does not have a nested pipeline.

### <img src="./docs/icons/magnifying_glass_right.svg" width=20 height=20 alt=""> Query JCR

Runs an XPath or SQL2 query against the JCR.

Runs the nested pipeline for each node found.

### <img src="./docs/icons/speech_bubble.svg" width=20 height=20 alt=""> Rename Property

Changes the name of the given property on the current node.

This hop does not have a nested pipeline.

### <img src="./docs/icons/arrow_up_down.svg" width=20 height=20 alt=""> Reorder Node

Changes the order of the current node.

This hop does not have a nested pipeline.

### <img src="./docs/icons/heart_exclamation.svg" width=20 height=20 alt=""> Resolve Specific Node

Resolves a node at a specific location.

Runs the nested pipeline on the resolved node.

### <img src="./docs/icons/pink_potion.svg" width=20 height=20 alt=""> Run a Script

### <img src="./docs/icons/cookie.svg" width=20 height=20 alt=""> Set a Property
Runs a script with arbitrary code. Currently, JEXL is supported as well as all languages known to the `javax.script.ScriptEngineManager`.

This hop does not have a nested pipeline.

### <img src="./docs/icons/pencil.svg" width=20 height=20 alt=""> Set a Property

Sets a property on the current node.

This hop does not have a nested pipeline.

### <img src="./docs/icons/bang.svg" width=20 height=20 alt=""> Catch Thrown Errors

Catches exceptions thrown on the nested pipeline and ensures hop execution of the current pipeline continues.

## Acknowledgements

[Mutant Standard emoji](https://mutant.tech/) are licensed CC BY-NC-SA 4.0 International.
3 changes: 3 additions & 0 deletions bnd.bnd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Import-Package: javax.annotation;version=0.0.0,\
org.apache.commons.logging;version=0.0.0,\
*;provide:=false
Loading

0 comments on commit 53fbf1b

Please sign in to comment.