Skip to content

Commit 170cded

Browse files
authored
[feat] add java client library (#44)
This adds a java library that parallels the Go and Python libraries. Some small adjustments were made to existing code along the way.
1 parent 6ca7eba commit 170cded

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3810
-223
lines changed

.github/copilot-instructions.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ Use the same output file name (test_output.log) every time so that you don't lit
141141
### Errors
142142
- go errors should be tested with errors.Is(). String matching is only okay for external system errors.
143143

144+
## Test coverage
145+
- We want good test coverage
146+
- Best way: simplify so that there is less code to test
147+
- Next best: refactor so that more code can be covered by unit tests and then add unit tests
148+
- Worst: add integration tests
149+
144150
## Backawards compatibility
145151

146152
There are no current users of this library. No users of the production server.

.github/workflows/cloud_provider.yml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
name: Cloud Provider Tests
22

3-
on:
4-
push:
5-
branches: [ main ]
3+
on:
64
pull_request:
7-
branches: [ "**" ]
5+
branches: [main]
6+
push:
7+
branches: [main]
88

99
permissions:
1010
contents: read
@@ -133,6 +133,16 @@ jobs:
133133
env:
134134
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
135135

136+
- name: Upload Java coverage to Codecov
137+
uses: codecov/[email protected]
138+
with:
139+
files: ./java-coverage.xml
140+
flags: java,${{ matrix.name }}
141+
fail_ci_if_error: true
142+
name: ${{ matrix.name }}-java-coverage
143+
env:
144+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
145+
136146
- name: Clean up remote directory (Make)
137147
if: always()
138148
env:

.github/workflows/codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
push:
55
branches: [ main ]
66
pull_request:
7-
branches: [ "**" ]
7+
branches: [ main ]
88

99
permissions: # added using https://github.com/step-security/secure-workflows
1010
contents: read

.github/workflows/codeql-analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ on:
1616
branches: [ main ]
1717
pull_request:
1818
# The branches below must be a subset of the branches above
19-
branches: [ "**" ]
19+
branches: [ main ]
2020
schedule:
2121
- cron: '20 3 * * 4'
2222

.github/workflows/go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ on:
22
push:
33
branches: [ main ]
44
pull_request:
5-
branches: [ "**" ]
5+
branches: [ main ]
66

77
name: Go Unit Tests
88
permissions: # added using https://github.com/step-security/secure-workflows

.github/workflows/golangci-lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ on:
22
push:
33
branches: [ main ]
44
pull_request:
5-
branches: [ "**" ]
5+
branches: [ main ]
66

77
name: golangci-lint
88
permissions: # added using https://github.com/step-security/secure-workflows

.github/workflows/java.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: java
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build-test-coverage:
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 10
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Set up JDK 21
18+
uses: actions/setup-java@v4
19+
with:
20+
distribution: temurin
21+
java-version: '21'
22+
cache: maven
23+
24+
- name: Lint / formatting check
25+
run: make lint-java
26+
27+
- name: Run tests with coverage (mvn verify)
28+
working-directory: java
29+
run: mvn -q -DskipTests=false verify
30+
31+
- name: Show coverage summary
32+
if: always()
33+
run: |
34+
if [ -f java/target/site/jacoco/index.html ]; then
35+
echo "JaCoCo report generated"
36+
fi
37+
if [ -f java/target/site/jacoco/jacoco.xml ]; then
38+
grep -q '<report' java/target/site/jacoco/jacoco.xml && echo "jacoco.xml present"
39+
fi
40+
41+
- name: Upload Java coverage to Codecov
42+
uses: codecov/[email protected]
43+
with:
44+
files: java/target/site/jacoco/jacoco.xml
45+
flags: java
46+
fail_ci_if_error: true
47+
env:
48+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
push:
55
branches: [ main ]
66
pull_request:
7-
branches: [ "**" ]
7+
branches: [ main ]
88

99
permissions:
1010
contents: read

.gitignore

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,25 @@ Thumbs.db
156156
*.temp
157157
.temporary/
158158

159-
# Notes and personal files
160-
notes
161-
*.local
162-
163159
# local evelopment-specific copilot instructions
164160
.github/copilot-dev-guide.md
165161

166162
# Python-specific excludes
167163
python/LICENSE
164+
165+
# === Project-specific ephemeral artifacts ===
166+
*-log
167+
168+
# Coverage artifacts aggregated locally after remote download
169+
go-coverage.out
170+
python-coverage.xml
171+
java-coverage.xml
172+
go-coverage-*.out
173+
python-coverage-*.xml
174+
java-coverage-*.xml
175+
176+
# JaCoCo & Maven build outputs (Maven target/ already indirectly covered for python target but add explicit java target)
177+
java/target/
178+
179+
# test junk
180+
go/s2iam_test_server_info*.json

Makefile

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,34 @@ UNIQUE_DIR := dev-$(shell echo $$(( ( $(shell date +%s) / 60 ) % 3 + 1 )))
1313
endif
1414
export UNIQUE_DIR
1515

16-
.PHONY: help test test-local test-go-local test-python-local on-remote-test on-remote-test-go on-remote-test-python check-cloud-env check-host clean \
16+
.PHONY: help test \
17+
test-local test-go-local test-python-local test-local-java \
18+
on-remote-test on-remote-test-go on-remote-test-python on-remote-test-java \
19+
check-cloud-env check-host clean \
1720
dev-setup-ubuntu dev-setup-macos \
18-
dev-setup-ubuntu-go dev-setup-ubuntu-python dev-setup-macos-go dev-setup-macos-python \
19-
dev-setup-common lint lint-go lint-python format format-go format-python ssh-copy-to-remote ssh-run-remote-tests ssh-download-coverage ssh-download-coverage-go ssh-download-coverage-python ssh-cleanup-remote
21+
dev-setup-ubuntu-go dev-setup-ubuntu-python dev-setup-macos-go dev-setup-macos-python dev-setup-ubuntu-java dev-setup-macos-java \
22+
dev-setup-common \
23+
lint lint-go lint-python lint-java \
24+
format format-go format-python format-java \
25+
ssh-copy-to-remote ssh-run-remote-tests \
26+
ssh-download-coverage ssh-download-coverage-go ssh-download-coverage-python ssh-download-coverage-java \
27+
ssh-cleanup-remote
2028

2129
# Default target
2230
help:
2331
@echo "SingleStore Auth IAM Build System"
2432
@echo ""
2533
@echo "Local Testing:"
26-
@echo " make test Run all local tests (Go + Python)"
27-
@echo " make test-go-local Run Go local tests"
28-
@echo " make test-python-local Run Python local tests"
34+
@echo " make test Run all local tests (Go + Python + Java)"
35+
@echo " make test-local-go Run Go local tests"
36+
@echo " make test-local-python Run Python local tests"
37+
@echo " make test-local-java Run Java local tests"
2938
@echo ""
3039
@echo "Cloud Testing (run ON cloud VMs - these targets work when you're ON the cloud host):"
31-
@echo " make on-remote-test Run cloud tests (Go + Python)"
40+
@echo " make on-remote-test Run cloud tests (Go + Python + Java)"
3241
@echo " make on-remote-test-go Run Go cloud tests only"
3342
@echo " make on-remote-test-python Run Python cloud tests only"
43+
@echo " make on-remote-test-java Run Java cloud tests only"
3444
@echo ""
3545
@echo " SSH Operations (for advanced usage):"
3646
@echo " make ssh-copy-to-remote Copy code to remote HOST"
@@ -41,12 +51,14 @@ help:
4151
@echo " make ssh-cleanup-remote Clean up remote directory on HOST"
4252
@echo ""
4353
@echo "Development Setup:"
44-
@echo " make dev-setup-ubuntu Full dev setup Ubuntu/Debian (Go + Python)"
54+
@echo " make dev-setup-ubuntu Full dev setup Ubuntu/Debian (Go + Python + Java)"
4555
@echo " make dev-setup-ubuntu-go Ubuntu/Debian Go toolchain + linters"
4656
@echo " make dev-setup-ubuntu-python Ubuntu/Debian Python tooling + deps"
47-
@echo " make dev-setup-macos Full dev setup macOS (Go + Python)"
57+
@echo " make dev-setup-ubuntu-java Ubuntu/Debian Java development tooling (OpenJDK + Maven deps)"
58+
@echo " make dev-setup-macos Full dev setup macOS (Go + Python + Java)"
4859
@echo " make dev-setup-macos-go macOS Go toolchain + linters"
4960
@echo " make dev-setup-macos-python macOS Python tooling + deps"
61+
@echo " make dev-setup-macos-java macOS Java development tooling (Temurin/OpenJDK + Maven deps)"
5062
@echo ""
5163
@echo "Code Quality:"
5264
@echo " make lint Run all linters"
@@ -70,22 +82,43 @@ help:
7082
@echo " AZURE_POSITIVE_*, AZURE_NEGATIVE_*)"
7183
@echo ""
7284
@echo "Coverage files are automatically timestamped (e.g., go-coverage-20250807-143022.out)"
85+
@echo ""
86+
@echo "Helper Scripts (doodles/):"
87+
@echo " doodles/install-all Run dev-setup across all remote test hosts"
7388

7489
# Test targets
7590
test: test-local
7691
@echo "✓ All local tests completed"
7792

78-
test-local: test-go-local test-python-local
93+
.PHONY: test-local-patterns
94+
test-local-patterns:
7995
! git grep -i 'jwt[ _]token'
96+
@violations=$$(git grep -n 'S2IAM_TEST_' -- 'go/s2iam' 'go/internal' 'python/src' 'java/src/main' 2>/dev/null | grep -v '_test.go' | grep -v '/testhelp/' || true); \
97+
if [ -n "$$violations" ]; then \
98+
echo 'ERROR: S2IAM_TEST_ variables found in library (non-test) source:'; \
99+
echo "$$violations"; \
100+
exit 1; \
101+
fi
102+
103+
test-local: test-local-patterns test-local-go test-local-python test-local-java
80104
@echo "✓ All local tests passed"
81105

82-
test-go-local:
106+
test-local-go:
83107
@echo "Running Go local tests..."
84108
cd go && go test -v ./...
85109

86-
test-python-local:
110+
test-local-python:
87111
@echo "Running Python local tests..."
88-
cd python && python3 -m pytest tests/ -v
112+
cd python && python3 -m venv test-venv && \
113+
PIP_CACHE_DIR=$(HOME)/.cache/pip-test ./test-venv/bin/pip install -e '.[dev]' && \
114+
./test-venv/bin/python -m pytest tests/ -v; \
115+
EXIT_CODE=$$?; \
116+
rm -rf test-venv; \
117+
exit $$EXIT_CODE
118+
119+
test-local-java:
120+
@echo "Running Java local tests..."
121+
cd java && mvn -q -DskipTests=false test
89122

90123
check-cloud-env:
91124
@if [ -z "$$S2IAM_TEST_CLOUD_PROVIDER" ] && [ -z "$$S2IAM_TEST_CLOUD_PROVIDER_NO_ROLE" ] && [ -z "$$S2IAM_TEST_ASSUME_ROLE" ]; then \
@@ -102,10 +135,10 @@ ifndef HOST
102135
endif
103136

104137
on-remote-completed:
105-
@echo "✓ All tests completed successfully"
138+
@echo "ALL_TESTS_COMPLETED_OK"
106139

107140
# Cloud test targets (designed to run ON cloud VMs)
108-
on-remote-test: check-cloud-env on-remote-test-go on-remote-test-python
141+
on-remote-test: check-cloud-env on-remote-test-java on-remote-test-go on-remote-test-python
109142

110143
on-remote-test-go: check-cloud-env
111144
@echo "=== Running Go cloud tests ==="
@@ -122,10 +155,19 @@ on-remote-test-python: check-cloud-env
122155
# Add src to PYTHONPATH so tests can import s2iam without installation
123156
cd python && PYTHONPATH=src python3 -m pytest tests/ -v --tb=short --cov=src/s2iam --cov-report=xml:coverage.xml --cov-report=html:htmlcov
124157

125-
dev-setup-ubuntu: dev-setup-ubuntu-go dev-setup-ubuntu-python
158+
on-remote-test-java: check-cloud-env
159+
@echo "=== Running Java cloud tests ==="
160+
@echo "Environment: S2IAM_TEST_CLOUD_PROVIDER=$${S2IAM_TEST_CLOUD_PROVIDER:-<unset>}"
161+
@echo "Environment: S2IAM_TEST_CLOUD_PROVIDER_NO_ROLE=$${S2IAM_TEST_CLOUD_PROVIDER_NO_ROLE:-<unset>}"
162+
@echo "Environment: S2IAM_TEST_ASSUME_ROLE=$${S2IAM_TEST_ASSUME_ROLE:-<unset>}"
163+
cd java && mvn -q -DskipTests=false verify
164+
# Copy JaCoCo XML up one level for remote retrieval naming consistency
165+
@if [ -f java/target/site/jacoco/jacoco.xml ]; then cp java/target/site/jacoco/jacoco.xml java-coverage.xml || true; fi
166+
167+
dev-setup-ubuntu: dev-setup-ubuntu-go dev-setup-ubuntu-python dev-setup-ubuntu-java
126168
@echo "✓ Full Ubuntu/Debian development environment ready"
127169

128-
dev-setup-macos: dev-setup-macos-go dev-setup-macos-python
170+
dev-setup-macos: dev-setup-macos-go dev-setup-macos-python dev-setup-macos-java
129171
@echo "✓ Full macOS development environment ready"
130172

131173
dev-setup-common:
@@ -161,6 +203,27 @@ dev-setup-ubuntu-python: dev-setup-common
161203
cd python && pip install -e .[dev]
162204
@echo "✓ Ubuntu Python development environment ready (no virtualenv)"
163205

206+
dev-setup-ubuntu-java:
207+
@echo "Installing Java toolchain (OpenJDK 11 + Maven)..."
208+
sudo apt update
209+
sudo apt install -y openjdk-11-jdk maven
210+
@echo "Priming Maven dependency cache (offline build support)..."
211+
cd java && mvn -q -DskipTests dependency:go-offline || { echo "Maven dependency prefetch failed"; exit 1; }
212+
@echo "✓ Java development environment ready"
213+
214+
dev-setup-macos-java:
215+
@if ! command -v brew >/dev/null 2>&1; then \
216+
echo "ERROR: Homebrew not found. Install from https://brew.sh first."; \
217+
exit 1; \
218+
fi
219+
@echo "Installing Java toolchain (Temurin 11 + Maven + Spotless deps)..."
220+
brew install openjdk@11 maven || { echo "Failed to install Java tooling"; exit 1; }
221+
# Ensure JAVA_HOME is set for current shell usage note
222+
@echo "Add to shell profile if not present: export JAVA_HOME=\`/usr/libexec/java_home -v 11\`"
223+
@echo "Priming Maven dependency cache (offline build support)..."
224+
cd java && mvn -q -DskipTests dependency:go-offline || { echo "Maven dependency prefetch failed"; exit 1; }
225+
@echo "✓ macOS Java development environment ready"
226+
164227
dev-setup-macos-python:
165228
@if ! command -v brew >/dev/null 2>&1; then \
166229
echo "ERROR: Homebrew not found. Install from https://brew.sh first."; \
@@ -184,7 +247,7 @@ dev-setup-azure:
184247
dev-setup-gcp:
185248
@echo "GCP dependencies installed via python3-google-auth and python3-google-auth-oauthlib"
186249

187-
lint: lint-go lint-python
250+
lint: lint-go lint-python lint-java
188251

189252
lint-go:
190253
@echo "Running Go linters..."
@@ -212,7 +275,15 @@ lint-python:
212275
cd python && python3 -m black --check src/ tests/
213276
cd python && python3 -m isort --check-only src tests
214277

215-
format: format-go format-python
278+
format: format-go format-python format-java
279+
280+
lint-java:
281+
@echo "Running Java formatter check (Spotless)..."
282+
cd java && mvn -q spotless:check || { echo "Java formatting issues found (run make format-java)"; exit 1; }
283+
284+
format-java:
285+
@echo "Formatting Java code (Spotless)..."
286+
cd java && mvn -q spotless:apply
216287

217288
format-go:
218289
@echo "Formatting Go code..."
@@ -254,7 +325,7 @@ ssh-run-remote-tests: check-host
254325
ssh $(SSH_OPTS) $(HOST) \
255326
"cd $(REMOTE_BASE_DIR)/$(UNIQUE_DIR) && env $(ENV_VARS) make $(TEST_TARGET) on-remote-completed" \
256327
2>&1 | tee $(HOST)-log
257-
@if grep -q "✓ All tests completed successfully" $(HOST)-log; then \
328+
@if grep -q "ALL_TESTS_COMPLETED_OK" $(HOST)-log; then \
258329
echo "✓ Remote tests passed on $(HOST)"; \
259330
else \
260331
echo "✗ Remote tests failed on $(HOST) - check $(HOST)-log"; \
@@ -263,7 +334,7 @@ ssh-run-remote-tests: check-host
263334

264335
# Generic function to download coverage files
265336
# CI target - download coverage files from remote host
266-
ssh-download-coverage: ssh-download-coverage-go ssh-download-coverage-python
337+
ssh-download-coverage: ssh-download-coverage-go ssh-download-coverage-python ssh-download-coverage-java
267338
@echo "✓ All coverage files downloaded"
268339

269340
# CI target - download Go coverage from remote host
@@ -282,6 +353,13 @@ ssh-download-coverage-python: check-host
282353
if [ ! -s ./python-coverage-$$TIMESTAMP.xml ]; then echo "Python coverage file empty or missing"; exit 1; fi; \
283354
cp ./python-coverage-$$TIMESTAMP.xml python-coverage.xml
284355

356+
ssh-download-coverage-java: check-host
357+
@echo "Downloading Java coverage from $(HOST)..."
358+
TIMESTAMP=$$(date +%Y%m%d-%H%M%S); \
359+
scp $(SSH_OPTS) $(HOST):$(REMOTE_BASE_DIR)/$(UNIQUE_DIR)/java/java-coverage.xml ./java-coverage-$$TIMESTAMP.xml || scp $(SSH_OPTS) $(HOST):$(REMOTE_BASE_DIR)/$(UNIQUE_DIR)/java/target/site/jacoco/jacoco.xml ./java-coverage-$$TIMESTAMP.xml; \
360+
if [ ! -s ./java-coverage-$$TIMESTAMP.xml ]; then echo "Java coverage file empty or missing"; exit 1; fi; \
361+
cp ./java-coverage-$$TIMESTAMP.xml java-coverage.xml
362+
285363
# Generic function to cleanup remote directory
286364
# CI target - cleanup remote directory
287365
ssh-cleanup-remote: check-host

0 commit comments

Comments
 (0)