Skip to content
Open
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
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# CHANGELOG

## version [1.0.0] - 25-05-2026
### Fixed
- **IntelliJ Indexing and Project Structure Boundaries:** Resolved a local environment issue where Maven build lifecycle mappings were hidden and source files were displayed in monochromatic grey tones. Fixed by executing a targeted `Reload All Maven Projects` configuration cycle from the right sidebar tool window to re-index dependencies.
- **Maven Compilation and Context-Test Roadblocks:** Resolved a fatal test-phase runtime failure during the artifact packaging execution (`./mvnw clean package`) caused by disconnected context data layers.

## version [0.0.3] - 24-05-2026
### Added
- **Automated Data Auditing Layer:** Integrated automated database logging utilizing Spring Data JPA and Hibernate ORM to automatically persist every incoming sequence stream into the `SEQUENCE_HISTORY` table in real-time.
- **Project Documentation & Repository Guidelines:** Created a highly detailed `README.md` file including environment variables, prerequisites, and a complete step-by-step developer guide for the final Codeline submission branch validation and Pull Request (PR) workflow.

## version [0.0.2] - 23-05-2026
### Changed
- **Database Schema Auto-Synchronization:** Migrated datasource configurations in `application.properties` to utilize `spring.jpa.hibernate.ddl-auto=update` for dynamic Oracle table structuralization at startup, eliminating manual SQL setup scripts.
- **Context Testing Isolation Configuration:** Refactored `OraclequantapiApplicationTests.java` by injecting hardcoded connection configurations directly via `@SpringBootTest(properties = {...})` to isolate context evaluation boundaries from environmental container failures during compilation.

## version [0.0.1] - 21-05-2026
### Added
- **Initial Release of Measurement Conversion API:** Setup the baseline Spring Boot 3 enterprise structure using Java 17 and Maven project lifecycles.
- **Core Sequence Parser Logic:** Implemented the measurement parsing sequence engine to cleanly decode custom string streams into structured standard numeric arrays.
- **REST Controller Infrastructure:** Exposed a clean `SequenceController` component serving incoming web requests via the `GET /api/convert-measurements` endpoint.
- **Oracle Database Container Integration:** Setup a containerized Oracle Express Edition (XE) database engine running locally via Docker Desktop, mapped to standard database port `1521` with customized credential keys (`29999login`).
- **Live System Administration Mapping:** Established an active telemetry connection workspace inside DbVisualizer pointing to the local database schema to track data persistence and schema structures.
121 changes: 60 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,60 @@
## Submission Instructions

To submit your Oracle JAVA Spring Boot Maven project as a solution, please follow these steps:

### Step 1: Install git on your PC
- Install "git" as shown in this tutorial: [How to install git](https://youtu.be/iYkLrXobBbA?si=_l0haibv_X9NpIjJ)
- Open command prompt and run
```bash
git version
```
- If you see the version, then git is successfully installed.

### Step 2: Fork the Repository
- Navigate to [this repository](https://github.com/CodelineAtyab/oraclequantapi) provided by Codeline.
- Click on the "Fork" button at the top-right corner of the page to create a copy of the repository under your own GitHub account.

### Step 3: Clone the Forked Repository
- Open your terminal or command prompt.
- Clone the forked repository to your local machine using the following command:
```bash
git clone https://github.com/your-username/repo-name.git
```

### Step 4: Create a new branch
- Navigate to the cloned repository directory
```bash
cd repo-name
```
- Create a new branch for your code submissions (Replace your-name with your name in your-name-submission-branch):
```bash
git checkout -b your-name-submission-branch
```


### Step 5: Add Your Code
- Implement the API

### Step 6: Commit your changes
- Run the following commands in order to commit your changes:
```bash
git add *
git commit -m "Meaningful commit message here"
```

### Step 7: Push Your Branch to GitHub
- Run the following commands to upload the changes to the forked github repository (Replace your-name with your name in your-name-submission-branch):
```bash
git push origin your-name-submission-branch
```

### Step 8: Create a Pull Request
- Go to your forked repository on GitHub.
- You should see a prompt to create a pull request. Click on "Compare & pull request".
- Provide a title and description for your pull request, then click "Create pull request".

### Step 9: Notify Codeline
- Notify on slack that you have created a PR for your solution.

## Note: If you face any issues in the process above, Please do the following:
- Watch [this youtube tutorial](https://www.youtube.com/watch?v=a_FLqX3vGR4)
- Contact Ikhlas or Atyab.
# Oracle Quant ERP API — Measurement Converter

A robust, enterprise-grade Backend RESTful API built using Spring Boot 3 and Java 17. This service is designed as an evaluation module for ERP system integration, specialized in parsing, decoding, and processing complex measurement sequence strings into standard numeric arrays. It features fully automated transaction logging, persisting all operations into an Oracle Database via Hibernate ORM.

---

## Tech Stack & Ecosystem

- **Language:** Java 17 (JDK 17)
- **Framework:** Spring Boot 3.5.14
- **Build Tool:** Maven
- **Database:** Oracle Database Express Edition (XE) via Docker Container
- **Data Access Layer:** Spring Data JPA / Hibernate ORM
- **Testing & Administration:** Postman & DbVisualizer

---

## Prerequisites

Ensure you have the following environment components installed and running before starting the application:
1. Java 17 JDK configured in your system path.
2. Docker Desktop running locally to host the containerized database.
3. Postman for endpoint verification and API testing.
4. DbVisualizer (or equivalent database tool) to monitor transactional tables.

---

## Local Setup & Execution Guide

### 1. Initialize Oracle Database via Docker
Run the following command in your terminal to pull and boot up the Oracle XE database container configured for this project:

- docker run -d --name oracle-db -p 1521:1521 -e ORACLE_PASSWORD=29999login gvenzl/oracle-xe

### 2. Configure Environment Properties
The live application properties profile located at src/main/resources/application.properties is configured as follows to map the container environment:

- spring.application.name=oraclequantapi
- spring.datasource.url=jdbc:oracle:thin:@localhost:1521/XEPDB1
- spring.datasource.username=system
- spring.datasource.password=29999login
- spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
- spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
- spring.jpa.hibernate.ddl-auto=update
- spring.jpa.show-sql=true

### 3. Build & Compile the Executable Artifact (JAR)
To clean previous builds and package the application into a standalone runnable artifact while ensuring database context tests pass cleanly, execute:

- ./mvnw clean package

**Output Destination:** The executable file will be successfully generated inside the /target directory as oraclequantapi-0.0.1-SNAPSHOT.jar.

### 4. Run the Application Server
To boot up the embedded web application server directly via the terminal interface, run:

- ./mvnw spring-boot:run

Upon successful initialization, the console log will confirm the listener port:
Tomcat started on port 8080 (http) with context path '/'
25 changes: 25 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,42 @@
<properties>
<java.version>17</java.version>
</properties>

<dependencies>

<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Oracle JDBC -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<version>23.4.0.24.05</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- H2 for tests -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.oraclequantapi.oraclequantapi.controller;

import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.util.List;
import com.oraclequantapi.oraclequantapi.model.Sequence;
import com.oraclequantapi.oraclequantapi.service.SequenceService;

@RestController
@RequestMapping("/api")
public class SequenceController {

private final SequenceService sequenceService;

public SequenceController(SequenceService sequenceService) {
this.sequenceService = sequenceService;
}

@GetMapping("/convert-measurements")
public ResponseEntity<List<Integer>> GET(@RequestParam("input") String input) {
Sequence currentSequence = sequenceService.get_sequence(input);

if (!currentSequence.is_valid()) {
return ResponseEntity.badRequest().build();
}

List<Integer> decodedList = sequenceService.process_sequence(currentSequence);
return ResponseEntity.ok(decodedList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.oraclequantapi.oraclequantapi.model;

import java.util.ArrayList;
import java.util.List;


public class Sequence {

private List<String> value = new ArrayList<>();

public Sequence() {}

public void set_value(List<String> value) {
this.value = (value != null) ? value : new ArrayList<>();
}

public String get_value_as_str() {
if (value == null || value.isEmpty()) return "";
return String.join("", value);
}

public boolean is_valid() {
if (value == null || value.isEmpty()) return false;
for (String element : value) {
if (element == null || element.length() != 1) return false;
char ch = element.charAt(0);
if (!((ch >= 'a' && ch <= 'z') || ch == '_')) return false;
}
return true;
}

public List<String> get_value() {
return this.value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.oraclequantapi.oraclequantapi.model;

import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "SEQUENCE_HISTORY")
public class SequenceHistory {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_hist_gen")
@SequenceGenerator(name = "seq_hist_gen", sequenceName = "SEQ_HIST_SEQ", allocationSize = 1)
@Column(name = "ID")
private Long id;

@Column(name = "TIMESTAMP_COL")
private LocalDateTime timestamp;

@Column(name = "SOURCE_IP", length = 45)
private String sourceIpAddress;

@Column(name = "INPUT_COL", length = 4000)
private String inputColumn;

@Column(name = "OUTPUT_COL", length = 4000)
private String outputColumn;

@Transient
private List<Sequence> list_of_seq = new ArrayList<>();

protected SequenceHistory() {}

public boolean save_curr_seq(Sequence sequence) {
if (sequence == null) return false;
this.list_of_seq.add(sequence);
this.timestamp = LocalDateTime.now();
this.inputColumn = sequence.get_value_as_str();
return true;
}

public List<Sequence> get_history() { return this.list_of_seq; }

public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime t) { this.timestamp = t; }
public String getSourceIpAddress() { return sourceIpAddress; }
public void setSourceIpAddress(String ip) { this.sourceIpAddress = ip; }
public String getInputColumn() { return inputColumn; }
public void setInputColumn(String s) { this.inputColumn = s; }
public String getOutputColumn() { return outputColumn; }
public void setOutputColumn(String s) { this.outputColumn = s; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.oraclequantapi.oraclequantapi.repo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.oraclequantapi.oraclequantapi.model.SequenceHistory;

@Repository
public interface SequenceHistoryRepository extends JpaRepository<SequenceHistory, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.oraclequantapi.oraclequantapi.service;

import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.oraclequantapi.oraclequantapi.model.Sequence;

@Service
public class SequenceService {

public Sequence get_sequence(String str) {
Sequence sequence = new Sequence();
if (str != null && !str.isEmpty()) {
sequence.set_value(Arrays.asList(str.split("")));
} else {
sequence.set_value(new ArrayList<>());
}
return sequence;
}

// Now contains the full decoding algorithm
public List<Integer> process_sequence(Sequence sequence) {
List<String> chars = sequence.get_value();
List<Integer> results = new ArrayList<>();
int pointer = 0;

while (pointer < chars.size()) {

// Step 1: read counter — z chains into next char (26 + next)
int[] counter = readChained(chars, pointer);
int counterValue = counter[0];
pointer = counter[1];

// Step 2: read exactly counterValue value-slots, sum them
int slotSum = 0;
for (int i = 0; i < counterValue; i++) {
int[] slot = readChained(chars, pointer);
slotSum += slot[0];
pointer = slot[1];
}

results.add(slotSum);
}

return results;
}

// Handles z-chaining: z + next = 26 + value(next), recursively
private int[] readChained(List<String> chars, int pos) {
String ch = chars.get(pos);
if ("z".equals(ch)) {
int[] next = readChained(chars, pos + 1);
return new int[]{ 26 + next[0], next[1] };
}
return new int[]{ charValue(ch), pos + 1 };
}

// a=1, b=2,.. z=26, _=0
private int charValue(String ch) {
if ("_".equals(ch)) return 0;
return ch.charAt(0) - 'a' + 1;
}
}
Loading