Skip to content

Commit ff48cc5

Browse files
authored
Merge pull request #49 from quantori/feature/data-types-task0.1
feat: add extended property types
2 parents dff1c85 + 038bbd8 commit ff48cc5

15 files changed

Lines changed: 312 additions & 41 deletions

File tree

.github/workflows/build-and-test.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
format-check:
1818
name: Check Code Formatting
1919
runs-on: ubuntu-latest
20+
env:
21+
AKKA_REPO_URL: ${{ secrets.AKKA_REPO_URL }}
2022

2123
steps:
2224
- uses: actions/checkout@v4
@@ -39,6 +41,8 @@ jobs:
3941
runs-on: ubuntu-latest
4042
permissions:
4143
contents: read
44+
env:
45+
AKKA_REPO_URL: ${{ secrets.AKKA_REPO_URL }}
4246

4347
steps:
4448
- uses: actions/checkout@v4
@@ -53,5 +57,8 @@ jobs:
5357
- name: Setup Gradle
5458
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
5559

60+
- name: Build API with Gradle Wrapper
61+
run: ./gradlew :cqp-api:build :cqp-api:publishToMavenLocal
62+
5663
- name: Build with Gradle Wrapper
57-
run: ./gradlew build
64+
run: ./gradlew build -x :cqp-api:build

ARCHITECTURE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ The Chemical Query Platform (CQP) is an open-source framework for indexing and s
9191

9292
### cqp-api (Core API Module)
9393

94-
**Version**: 0.0.16
94+
**Version**: 0.0.17
9595

9696
**Purpose**: Defines interfaces, models, and abstractions for chemical structure storage and search.
9797

@@ -132,7 +132,7 @@ com.quantori.cqp.api/
132132

133133
### cqp-storage-elasticsearch (Elasticsearch Implementation)
134134

135-
**Version**: 0.0.14
135+
**Version**: 0.0.17
136136

137137
**Purpose**: Elasticsearch implementation of CQP storage interfaces.
138138

@@ -155,7 +155,7 @@ com.quantori.cqp.storage.elasticsearch/
155155
└── ReactionMapper.java # Domain ↔ Elasticsearch
156156
```
157157

158-
**Dependencies**: cqp-api (0.0.16), Elasticsearch Java Client (8.6.2)
158+
**Dependencies**: cqp-api (0.0.17), Elasticsearch Java Client (8.6.2)
159159

160160
### cqp-core (Akka Actors Module - Currently Missing)
161161

CONTEXT.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
66

77
CQP is an open-source framework for indexing and searching within cheminformatics applications (molecules & reactions). It's built on the Akka Actors framework for scalability and supports multiple storage engines including PostgreSQL, Elasticsearch, and Apache Solr.
88

9+
## Package Naming Strategy
10+
11+
**CQP Internal Code**: All code in this repository uses the `com.quantori.cqp.*` package hierarchy:
12+
- `com.quantori.cqp.api.model.*` - Core data models (Property, PropertyType, PropertyValue, etc.)
13+
- `com.quantori.cqp.core.*` - Akka actors and core framework
14+
- `com.quantori.cqp.storage.elasticsearch.*` - Elasticsearch storage implementation
15+
- `com.quantori.cqp.build.*` - Gradle build plugins
16+
17+
**External Indigo Library**: The external Indigo Toolkit library uses `com.epam.indigo.*`:
18+
- `com.epam.indigo.Indigo` - Main Indigo class
19+
- `com.epam.indigo.IndigoObject` - Indigo molecule/reaction objects
20+
- `com.epam.indigo.IndigoException` - Indigo exceptions
21+
- **Do not change** these package references - they belong to the external library
22+
923
## Project Structure
1024

1125
This is a multi-module Gradle project with the following key modules:

Devnotes.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
## Build instructions
22
* Download the repository
33
* Let your IDE import necessary dependencies automatically or fetch them manually
4-
* For local repository publication Run `gradle build` task and then run `gradle publishToMavenLocal` tasks
4+
* For local repository publication run the following tasks
5+
* `gradlew :cqp-api:build :cqp-api:publishToMavenLocal`
6+
* `gradle build -x :cqp-api:build`
7+
* `gradle publishToMavenLocal -x :cqp-api:publishToMavenLocal`
8+
9+
## Repository configuration
10+
11+
* The shared plugin reads the Akka repository URL in this order and stops at the first match:
12+
1. Gradle property `AKKA_REPO_URL` (set via `gradle.properties` or `-PAKKA_REPO_URL=...`)
13+
2. Gradle property `akkaRepoUrl` (legacy camelCase fallback)
14+
3. Environment variable `AKKA_REPO_URL`
15+
* If none of the above is supplied, the build fails immediately. Obtain the secure URL/token from https://account.akka.io/ and set one of the properties/variables before running Gradle.

cqp-api/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66

77
group = "com.quantori"
88
description = "Chem query platform. Storage API"
9-
version = "0.0.16"
9+
version = "0.0.17"
1010

1111
dependencies {
1212
implementation("commons-codec:commons-codec:1.15")
@@ -34,4 +34,4 @@ dependencies {
3434
testImplementation(libs.jackson)
3535
testImplementation(libs.lombok)
3636
testAnnotationProcessor(libs.lombok)
37-
}
37+
}

cqp-api/src/main/java/com/quantori/cqp/api/model/Property.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,48 @@ public Property(String name, PropertyType type) {
2121
this.type = type;
2222
}
2323

24+
/**
25+
* Defines the supported property kinds in Chem Query Platform. Existing values must remain
26+
* unchanged to keep backward compatibility with previously persisted data.
27+
*/
2428
public enum PropertyType {
25-
STRING, DECIMAL, DATE
29+
STRING,
30+
DECIMAL,
31+
DATE,
32+
/**
33+
* Binary payload for storing images, PDFs or other small files. Intended size limit is 10 MB per
34+
* value and the binary content is transferred as byte arrays.
35+
*/
36+
BINARY,
37+
/**
38+
* Timestamp value with timezone information preserved. Values are serialized as ISO-8601
39+
* instants (e.g. {@code 2025-01-15T10:30:00Z}).
40+
*/
41+
DATE_TIME,
42+
/**
43+
* Ordered collection of string values. This allows preserving the order in which the user
44+
* provided the values (e.g. {@code ["first", "second"]}).
45+
*/
46+
LIST,
47+
/**
48+
* Hyperlink that stores HTTP/HTTPS (and similar) URL references. Validation is applied on
49+
* backend layers while the enum only marks the data type.
50+
*/
51+
HYPERLINK,
52+
/**
53+
* Chemical structure serialized as a SMILES string. Consumers rely on Indigo toolkit for
54+
* validation.
55+
*/
56+
CHEMICAL_STRUCTURE,
57+
/**
58+
* Three dimensional molecular structure stored as MOL text block. This is typically used for 3D
59+
* renderings and cannot be exported to legacy SDF files.
60+
*/
61+
STRUCTURE_3D,
62+
/**
63+
* Sanitized HTML fragment used for rich text property rendering. Scripts and unsafe tags are
64+
* expected to be removed before persisting.
65+
*/
66+
HTML
2667
}
2768
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.quantori.cqp.api.model;
2+
3+
import java.time.Instant;
4+
import java.time.LocalDate;
5+
import java.util.List;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Data;
9+
import lombok.NoArgsConstructor;
10+
11+
/**
12+
* Container for the value of a {@link Property}. Each concrete field maps to a {@link
13+
* Property.PropertyType}. Only one field is expected to be non-null for a particular value.
14+
*/
15+
@Data
16+
@NoArgsConstructor
17+
@AllArgsConstructor
18+
@Builder
19+
public class PropertyValue {
20+
21+
/** String payload for {@link Property.PropertyType#STRING} values. */
22+
private String stringValue;
23+
24+
/** Decimal payload for {@link Property.PropertyType#DECIMAL} values. */
25+
private Double decimalValue;
26+
27+
/** Date payload for {@link Property.PropertyType#DATE} values. */
28+
private LocalDate dateValue;
29+
30+
/**
31+
* Binary content for {@link Property.PropertyType#BINARY} values. Typical use cases include
32+
* storing images or PDF attachments (up to 10 MB).
33+
*/
34+
private byte[] binaryValue;
35+
36+
/** Timestamp payload for {@link Property.PropertyType#DATE_TIME} values. */
37+
private Instant dateTimeValue;
38+
39+
/** Ordered collection of strings for {@link Property.PropertyType#LIST} values. */
40+
private List<String> listValue;
41+
42+
/** URL string for {@link Property.PropertyType#HYPERLINK} values. */
43+
private String hyperlinkValue;
44+
45+
/** SMILES payload for {@link Property.PropertyType#CHEMICAL_STRUCTURE} values. */
46+
private String chemicalStructureValue;
47+
48+
/** MOL block payload for {@link Property.PropertyType#STRUCTURE_3D} values. */
49+
private String structure3DValue;
50+
51+
/** Sanitized HTML fragment for {@link Property.PropertyType#HTML} values. */
52+
private String htmlValue;
53+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.quantori.cqp.api.model;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.time.Instant;
6+
import java.time.LocalDate;
7+
import java.util.EnumSet;
8+
import java.util.List;
9+
import org.junit.jupiter.api.Test;
10+
11+
class PropertyTypeTest {
12+
13+
@Test
14+
void shouldContainExtendedTypes() {
15+
EnumSet<Property.PropertyType> types = EnumSet.allOf(Property.PropertyType.class);
16+
17+
assertThat(types)
18+
.contains(
19+
Property.PropertyType.BINARY,
20+
Property.PropertyType.DATE_TIME,
21+
Property.PropertyType.LIST,
22+
Property.PropertyType.HYPERLINK,
23+
Property.PropertyType.CHEMICAL_STRUCTURE,
24+
Property.PropertyType.STRUCTURE_3D,
25+
Property.PropertyType.HTML);
26+
}
27+
28+
@Test
29+
void shouldAllowPropertyValueAccessors() {
30+
byte[] binary = new byte[] {1, 2, 3};
31+
Instant timestamp = Instant.parse("2024-01-01T10:15:30Z");
32+
List<String> orderedList = List.of("first", "second");
33+
LocalDate date = LocalDate.of(2024, 2, 29);
34+
35+
PropertyValue value =
36+
PropertyValue.builder()
37+
.stringValue("test")
38+
.decimalValue(12.34d)
39+
.dateValue(date)
40+
.binaryValue(binary)
41+
.dateTimeValue(timestamp)
42+
.listValue(orderedList)
43+
.hyperlinkValue("https://example.com")
44+
.chemicalStructureValue("CCO")
45+
.structure3DValue("3D-MOL-DATA")
46+
.htmlValue("<p>html</p>")
47+
.build();
48+
49+
assertThat(value.getStringValue()).isEqualTo("test");
50+
assertThat(value.getDecimalValue()).isEqualTo(12.34d);
51+
assertThat(value.getDateValue()).isEqualTo(date);
52+
assertThat(value.getBinaryValue()).containsExactly(binary);
53+
assertThat(value.getDateTimeValue()).isEqualTo(timestamp);
54+
assertThat(value.getListValue()).containsExactlyElementsOf(orderedList);
55+
assertThat(value.getHyperlinkValue()).isEqualTo("https://example.com");
56+
assertThat(value.getChemicalStructureValue()).isEqualTo("CCO");
57+
assertThat(value.getStructure3DValue()).isEqualTo("3D-MOL-DATA");
58+
assertThat(value.getHtmlValue()).isEqualTo("<p>html</p>");
59+
}
60+
}

cqp-build/src/main/kotlin/com/quantori/cqp/build/CqpJavaLibraryPlugin.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.quantori.cqp.build
22

3+
import org.gradle.api.GradleException
34
import org.gradle.api.JavaVersion
45
import org.gradle.api.Plugin
56
import org.gradle.api.Project
@@ -28,10 +29,18 @@ class CqpJavaLibraryPlugin : Plugin<Project> {
2829

2930
project.repositories {
3031
mavenLocal()
31-
mavenCentral ()
32+
mavenCentral()
33+
34+
val akkaRepoUrl =
35+
(project.findProperty("AKKA_REPO_URL") as String?)
36+
?: (project.findProperty("akkaRepoUrl") as String?)
37+
?: System.getenv("AKKA_REPO_URL").takeIf { !it.isNullOrBlank() }
38+
?: throw GradleException(
39+
"AKKA_REPO_URL is not configured. Configure the secret/property to resolve Akka artifacts.")
40+
3241
maven {
3342
name = "Akka"
34-
url = project.uri("https://repo.akka.io/maven")
43+
url = project.uri(akkaRepoUrl)
3544
content {
3645
includeGroup("com.typesafe.akka")
3746
includeGroupByRegex("com\\.lightbend\\..*")
@@ -200,4 +209,4 @@ class CqpJavaLibraryPlugin : Plugin<Project> {
200209
}
201210
}
202211
}
203-
}
212+
}

cqp-core/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ plugins {
55
}
66

77
description = "Chem query platform. Compound quick search"
8-
version = "0.0.15"
8+
version = "0.0.17"
99

1010
val akkaVersion: String = "2.9.0"
1111
val lightbendVersion: String = "1.5.0"
1212

1313
dependencies {
14-
implementation("com.quantori:cqp-api:0.0.16")
14+
implementation("com.quantori:cqp-api:0.0.17")
1515

1616
implementation("com.typesafe:config:1.4.2")
1717

0 commit comments

Comments
 (0)