Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: 17
distribution: 'temurin'
Expand All @@ -39,10 +39,10 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
Expand All @@ -42,9 +42,9 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Apache Maven Central
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ jobs:
core.setOutput('base_ref', pr.data.base.ref);
core.setOutput('head_sha', pr.data.head.sha);

- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
ref: ${{ steps.pr.outputs.head_sha }}
fetch-depth: 0

- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: 17
distribution: 'temurin'
Expand Down
50 changes: 47 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The `DotPathQL` is the core component of this project that allows you to extract

- 🎯 **Selective Property Extraction**: Extract only the properties you need
- 🚫 **Property Exclusion**: Exclude specific properties and return everything else
- 🔐 **Property Obfuscation**: Replace sensitive property values with "****" while preserving structure
- 🔍 **Deep Nested Support**: Navigate through multiple levels of object nesting
- 📋 **Collection Handling**: Process Lists, Arrays, and other Collections
- 🗺️ **Map Support**: Handle both simple and complex Map structures
Expand All @@ -25,10 +26,9 @@ The `DotPathQL` is the core component of this project that allows you to extract

## Quick Start

## Install
- Using the source code `mvn clean install`
- Adding as a dependency - Maven
### Library coordinates

Maven
```xml
<dependency>
<groupId>ca.trackerforce</groupId>
Expand All @@ -37,6 +37,11 @@ The `DotPathQL` is the core component of this project that allows you to extract
</dependency>
```

Gradle
```groovy
implementation 'ca.trackerforce:dot-path-ql:${dot-path-ql.version}'
```

### Filter Usage

```java
Expand All @@ -59,6 +64,17 @@ Map<String, Object> result = new DotPathQL().exclude(userObject, List.of(
));
```

### Obfuscate Usage

```java
// Obfuscate specific properties by replacing their values with "****"
Map<String, Object> result = new DotPathQL().obfuscate(userObject, List.of(
"password",
"ssn",
"creditCard.number"
));
```

## Supported Data Structures

- Simple Properties (primitive and object types)
Expand Down Expand Up @@ -178,6 +194,34 @@ List<String> reportFields = List.of(
);
```

### Data Obfuscation for Security
Mask sensitive information while maintaining data structure for logging, debugging, or sharing with third parties:

```java
// Obfuscate sensitive fields while keeping the structure intact
List<String> sensitiveFields = List.of(
"password",
"ssn",
"creditCard.number",
"bankAccount.accountNumber",
"personalInfo.phoneNumber"
);

Map<String, Object> obfuscatedData = doPathQl.obfuscate(userObject, sensitiveFields);

// Result preserves structure but replaces sensitive values with "****"
// {
// "username": "john_doe",
// "password": "****",
// "ssn": "****",
// "creditCard": {
// "number": "****",
// "expiryDate": "12/25"
// },
// "email": "john@example.com"
// }
```

## JSON Output

Convert your filtered or excluded results to JSON format using the built-in `toJson` method. This feature supports both pretty-formatted (indented) and compact (single-line) output.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>ca.trackerforce</groupId>
<artifactId>dot-path-ql</artifactId>
<version>1.2.0</version>
<version>1.2.1</version>

<name>dot-path-ql</name>
<description>dotPathQL allows object attribute filtering</description>
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/ca/trackerforce/DotPathQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class DotPathQL {

private final DotPath pathFilter;
private final DotPath pathExclude;
private final DotPath pathObfuscate;
private final DotPrinter pathPrinter;

/**
Expand All @@ -26,6 +27,7 @@ public class DotPathQL {
public DotPathQL() {
pathFilter = DotPathFactory.buildFilter();
pathExclude = DotPathFactory.buildExclude();
pathObfuscate = DotPathFactory.buildObfuscate();
pathPrinter = DotPathFactory.buildPrinter(2);
}

Expand Down Expand Up @@ -58,6 +60,20 @@ public <T> Map<String, Object> exclude(T source, List<String> excludePaths) {
return pathExclude.run(source, excludePaths);
}

/**
* Obfuscates the given source object based on the specified paths.
* The paths can include nested properties, collections, and arrays.
* Also supports grouped paths syntax like "parent[child1.prop,child2.prop]"
*
* @param <T> the type of the source object
* @param source the source object to obfuscate
* @param obfuscatePaths the list of paths to obfuscate
* @return a map containing the obfuscated properties
*/
public <T> Map<String, Object> obfuscate(T source, List<String> obfuscatePaths) {
return pathObfuscate.run(source, obfuscatePaths);
}

/**
* Adds default filter paths that will be included in every filtering operation.
*
Expand All @@ -76,6 +92,15 @@ public void addDefaultExcludePaths(List<String> paths) {
pathExclude.addDefaultPaths(paths);
}

/**
* Adds default obfuscate paths that will be included in every obfuscation operation.
*
* @param paths the list of default obfuscate paths to add
*/
public void addDefaultObfuscatePaths(List<String> paths) {
pathObfuscate.addDefaultPaths(paths);
}

/**
* Converts the source object to a map representation.
*
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/ca/trackerforce/DotUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public static List<Map<String, Object>> listFrom(Map<String, Object> source, Str
*
* @param source the source map
* @param property the property to extract or a dot-notated path for nested properties
* @param clazz the class type of the list elements
* @param <T> the type of the list elements
* @return the extracted list of maps or an empty list if not found
* @throws ClassCastException if the property is not a list of maps
*/
Expand Down Expand Up @@ -132,5 +134,3 @@ private static Object[] convertToObjectArray(Object array) {
return result;
}
}


11 changes: 11 additions & 0 deletions src/main/java/ca/trackerforce/path/DotPathFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ public static DotPath buildExclude() {
return new PathExclude();
}

/**
* Builds and returns a new instance of PathExclude configured for obfuscation mode.
*
* @return a new PathExclude instance with obfuscation enabled
*/
public static PathExclude buildObfuscate() {
PathExclude pathExclude = new PathExclude();
pathExclude.setObfuscateMode(true);
return pathExclude;
}

/**
* Builds and returns a new instance of PathPrinter with the specified indentation size.
*
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/ca/trackerforce/path/PathExclude.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ private enum SkipValue {
INSTANCE
}

private boolean obfuscateMode = false;

public void setObfuscateMode(boolean obfuscateMode) {
this.obfuscateMode = obfuscateMode;
}

public <T> Map<String, Object> execute(T source, List<String> excludePaths) {
Map<String, Object> result = new LinkedHashMap<>();
excludePaths.addAll(0, defaultPaths);
Expand Down Expand Up @@ -48,9 +54,16 @@ private void buildExcluding(Map<String, Object> target, Object source, String cu
return;
}

excludeFromNode(target, source, currentPath, node);
}

private void excludeFromNode(Map<String, Object> target, Object source, String currentPath, ExclusionNode node) {
for (String prop : getPropertyNames(source.getClass())) {
ExclusionNode childNode = node == null ? null : node.getChildren().get(prop);
if (childNode != null && childNode.isExcludeSelf() && childNode.getChildren().isEmpty()) {
if (obfuscateMode) {
target.put(prop, "****");
}
continue;
}

Expand All @@ -69,6 +82,9 @@ private void excludeFromMap(Map<String, Object> target, String currentPath, Excl
ExclusionNode childNode = node == null ? null : node.getChildren().get(key);

if (childNode != null && childNode.isExcludeSelf() && childNode.getChildren().isEmpty()) {
if (obfuscateMode) {
target.put(key, "****");
}
continue;
}

Expand Down
10 changes: 4 additions & 6 deletions src/test/java/ca/trackerforce/ExcludeTypeClassTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ void shouldExcludeAndNotReturnPrivateAttributes() {
var customer = Customer.of();

// When
var result = dotPathQL.exclude(customer, List.of("metadata.tags"));
var result = dotPathQL.exclude(customer, List.of("email"));

// Then
var metadata = DotUtils.mapFrom(result, "metadata");
assertEquals(0, metadata.size()); // No fields should remain after excluding tags and private password
var metadata = DotUtils.mapFrom(result, "metadata"); // private field
assertEquals(0, metadata.size());

var features = DotUtils.listFrom(result, "features");
assertEquals(2, features.size());
assertTrue(features.get(0).containsKey("isEnabled")); // checking boolean field
assertFalse(result.containsKey("email"));
}
}
Loading
Loading