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
9 changes: 8 additions & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
format-check:
name: Check Code Formatting
runs-on: ubuntu-latest
env:
AKKA_REPO_URL: ${{ secrets.AKKA_REPO_URL }}

steps:
- uses: actions/checkout@v4
Expand All @@ -39,6 +41,8 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
env:
AKKA_REPO_URL: ${{ secrets.AKKA_REPO_URL }}

steps:
- uses: actions/checkout@v4
Expand All @@ -53,5 +57,8 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

- name: Build API with Gradle Wrapper
run: ./gradlew :cqp-api:build :cqp-api:publishToMavenLocal

- name: Build with Gradle Wrapper
run: ./gradlew build
run: ./gradlew build -x :cqp-api:build
6 changes: 3 additions & 3 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ The Chemical Query Platform (CQP) is an open-source framework for indexing and s

### cqp-api (Core API Module)

**Version**: 0.0.16
**Version**: 0.0.17

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

Expand Down Expand Up @@ -132,7 +132,7 @@ com.quantori.cqp.api/

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

**Version**: 0.0.14
**Version**: 0.0.17

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

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

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

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

Expand Down
14 changes: 14 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

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.

## Package Naming Strategy

**CQP Internal Code**: All code in this repository uses the `com.quantori.cqp.*` package hierarchy:
- `com.quantori.cqp.api.model.*` - Core data models (Property, PropertyType, PropertyValue, etc.)
- `com.quantori.cqp.core.*` - Akka actors and core framework
- `com.quantori.cqp.storage.elasticsearch.*` - Elasticsearch storage implementation
- `com.quantori.cqp.build.*` - Gradle build plugins

**External Indigo Library**: The external Indigo Toolkit library uses `com.epam.indigo.*`:
- `com.epam.indigo.Indigo` - Main Indigo class
- `com.epam.indigo.IndigoObject` - Indigo molecule/reaction objects
- `com.epam.indigo.IndigoException` - Indigo exceptions
- **Do not change** these package references - they belong to the external library

## Project Structure

This is a multi-module Gradle project with the following key modules:
Expand Down
13 changes: 12 additions & 1 deletion Devnotes.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
## Build instructions
* Download the repository
* Let your IDE import necessary dependencies automatically or fetch them manually
* For local repository publication Run `gradle build` task and then run `gradle publishToMavenLocal` tasks
* For local repository publication run the following tasks
* `gradlew :cqp-api:build :cqp-api:publishToMavenLocal`
* `gradle build -x :cqp-api:build`
* `gradle publishToMavenLocal -x :cqp-api:publishToMavenLocal`

## Repository configuration

* The shared plugin reads the Akka repository URL in this order and stops at the first match:
1. Gradle property `AKKA_REPO_URL` (set via `gradle.properties` or `-PAKKA_REPO_URL=...`)
2. Gradle property `akkaRepoUrl` (legacy camelCase fallback)
3. Environment variable `AKKA_REPO_URL`
* 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.
4 changes: 2 additions & 2 deletions cqp-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {

group = "com.quantori"
description = "Chem query platform. Storage API"
version = "0.0.16"
version = "0.0.17"

dependencies {
implementation("commons-codec:commons-codec:1.15")
Expand Down Expand Up @@ -34,4 +34,4 @@ dependencies {
testImplementation(libs.jackson)
testImplementation(libs.lombok)
testAnnotationProcessor(libs.lombok)
}
}
43 changes: 42 additions & 1 deletion cqp-api/src/main/java/com/quantori/cqp/api/model/Property.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,48 @@ public Property(String name, PropertyType type) {
this.type = type;
}

/**
* Defines the supported property kinds in Chem Query Platform. Existing values must remain
* unchanged to keep backward compatibility with previously persisted data.
*/
public enum PropertyType {
STRING, DECIMAL, DATE
STRING,
DECIMAL,
DATE,
/**
* Binary payload for storing images, PDFs or other small files. Intended size limit is 10 MB per
* value and the binary content is transferred as byte arrays.
*/
BINARY,
/**
* Timestamp value with timezone information preserved. Values are serialized as ISO-8601
* instants (e.g. {@code 2025-01-15T10:30:00Z}).
*/
DATE_TIME,
/**
* Ordered collection of string values. This allows preserving the order in which the user
* provided the values (e.g. {@code ["first", "second"]}).
*/
LIST,
/**
* Hyperlink that stores HTTP/HTTPS (and similar) URL references. Validation is applied on
* backend layers while the enum only marks the data type.
*/
HYPERLINK,
/**
* Chemical structure serialized as a SMILES string. Consumers rely on Indigo toolkit for
* validation.
*/
CHEMICAL_STRUCTURE,
/**
* Three dimensional molecular structure stored as MOL text block. This is typically used for 3D
* renderings and cannot be exported to legacy SDF files.
*/
STRUCTURE_3D,
/**
* Sanitized HTML fragment used for rich text property rendering. Scripts and unsafe tags are
* expected to be removed before persisting.
*/
HTML
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.quantori.cqp.api.model;

import java.time.Instant;
import java.time.LocalDate;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* Container for the value of a {@link Property}. Each concrete field maps to a {@link
* Property.PropertyType}. Only one field is expected to be non-null for a particular value.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PropertyValue {

/** String payload for {@link Property.PropertyType#STRING} values. */
private String stringValue;

/** Decimal payload for {@link Property.PropertyType#DECIMAL} values. */
private Double decimalValue;

/** Date payload for {@link Property.PropertyType#DATE} values. */
private LocalDate dateValue;

/**
* Binary content for {@link Property.PropertyType#BINARY} values. Typical use cases include
* storing images or PDF attachments (up to 10 MB).
*/
private byte[] binaryValue;

/** Timestamp payload for {@link Property.PropertyType#DATE_TIME} values. */
private Instant dateTimeValue;

/** Ordered collection of strings for {@link Property.PropertyType#LIST} values. */
private List<String> listValue;

/** URL string for {@link Property.PropertyType#HYPERLINK} values. */
private String hyperlinkValue;

/** SMILES payload for {@link Property.PropertyType#CHEMICAL_STRUCTURE} values. */
private String chemicalStructureValue;

/** MOL block payload for {@link Property.PropertyType#STRUCTURE_3D} values. */
private String structure3DValue;

/** Sanitized HTML fragment for {@link Property.PropertyType#HTML} values. */
private String htmlValue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.quantori.cqp.api.model;

import static org.assertj.core.api.Assertions.assertThat;

import java.time.Instant;
import java.time.LocalDate;
import java.util.EnumSet;
import java.util.List;
import org.junit.jupiter.api.Test;

class PropertyTypeTest {

@Test
void shouldContainExtendedTypes() {
EnumSet<Property.PropertyType> types = EnumSet.allOf(Property.PropertyType.class);

assertThat(types)
.contains(
Property.PropertyType.BINARY,
Property.PropertyType.DATE_TIME,
Property.PropertyType.LIST,
Property.PropertyType.HYPERLINK,
Property.PropertyType.CHEMICAL_STRUCTURE,
Property.PropertyType.STRUCTURE_3D,
Property.PropertyType.HTML);
}

@Test
void shouldAllowPropertyValueAccessors() {
byte[] binary = new byte[] {1, 2, 3};
Instant timestamp = Instant.parse("2024-01-01T10:15:30Z");
List<String> orderedList = List.of("first", "second");
LocalDate date = LocalDate.of(2024, 2, 29);

PropertyValue value =
PropertyValue.builder()
.stringValue("test")
.decimalValue(12.34d)
.dateValue(date)
.binaryValue(binary)
.dateTimeValue(timestamp)
.listValue(orderedList)
.hyperlinkValue("https://example.com")
.chemicalStructureValue("CCO")
.structure3DValue("3D-MOL-DATA")
.htmlValue("<p>html</p>")
.build();

assertThat(value.getStringValue()).isEqualTo("test");
assertThat(value.getDecimalValue()).isEqualTo(12.34d);
assertThat(value.getDateValue()).isEqualTo(date);
assertThat(value.getBinaryValue()).containsExactly(binary);
assertThat(value.getDateTimeValue()).isEqualTo(timestamp);
assertThat(value.getListValue()).containsExactlyElementsOf(orderedList);
assertThat(value.getHyperlinkValue()).isEqualTo("https://example.com");
assertThat(value.getChemicalStructureValue()).isEqualTo("CCO");
assertThat(value.getStructure3DValue()).isEqualTo("3D-MOL-DATA");
assertThat(value.getHtmlValue()).isEqualTo("<p>html</p>");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.quantori.cqp.build

import org.gradle.api.GradleException
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
import org.gradle.api.Project
Expand Down Expand Up @@ -28,10 +29,18 @@ class CqpJavaLibraryPlugin : Plugin<Project> {

project.repositories {
mavenLocal()
mavenCentral ()
mavenCentral()

val akkaRepoUrl =
(project.findProperty("AKKA_REPO_URL") as String?)
?: (project.findProperty("akkaRepoUrl") as String?)
?: System.getenv("AKKA_REPO_URL").takeIf { !it.isNullOrBlank() }
?: throw GradleException(
"AKKA_REPO_URL is not configured. Configure the secret/property to resolve Akka artifacts.")

maven {
name = "Akka"
url = project.uri("https://repo.akka.io/maven")
url = project.uri(akkaRepoUrl)
content {
includeGroup("com.typesafe.akka")
includeGroupByRegex("com\\.lightbend\\..*")
Expand Down Expand Up @@ -200,4 +209,4 @@ class CqpJavaLibraryPlugin : Plugin<Project> {
}
}
}
}
}
4 changes: 2 additions & 2 deletions cqp-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ plugins {
}

description = "Chem query platform. Compound quick search"
version = "0.0.15"
version = "0.0.17"

val akkaVersion: String = "2.9.0"
val lightbendVersion: String = "1.5.0"

dependencies {
implementation("com.quantori:cqp-api:0.0.16")
implementation("com.quantori:cqp-api:0.0.17")

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

Expand Down
4 changes: 2 additions & 2 deletions cqp-storage-elasticsearch/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {

group = "com.quantori"
description = "Chem query platform. Storage Elasticsearch"
version = "0.0.14"
version = "0.0.17"

tasks.named<Javadoc>("javadoc") {
exclude(
Expand All @@ -16,7 +16,7 @@ tasks.named<Javadoc>("javadoc") {
}

dependencies {
api("com.quantori:cqp-api:0.0.16")
api("com.quantori:cqp-api:0.0.17")
implementation("co.elastic.clients:elasticsearch-java:8.6.2")
implementation(libs.jackson)
implementation(libs.jackson.jsr310)
Expand Down
Loading
Loading