Skip to content

Commit d57850e

Browse files
committed
chore: add misc proposals and todo docs for future reference
1 parent 9298eb1 commit d57850e

16 files changed

Lines changed: 1257 additions & 244 deletions

File tree

docs/plan-config-refactor.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Config Loading Refactor — TODO
2+
3+
## Current state
4+
5+
### Files involved
6+
7+
| File | Role |
8+
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
9+
| [jgit-proxy-server/.../JettyConfigurationLoader.java](../jgit-proxy-server/src/main/java/org/finos/gitproxy/jetty/config/JettyConfigurationLoader.java) | Loads YAML files, deep-merges them, applies `GITPROXY_*` env var overrides |
10+
| [jgit-proxy-server/.../JettyConfigurationBuilder.java](../jgit-proxy-server/src/main/java/org/finos/gitproxy/jetty/config/JettyConfigurationBuilder.java) | Casts raw `Map<String, Object>` tree into domain objects (providers, filters, push store, commit config) |
11+
| [jgit-proxy-server/.../resources/git-proxy.yml](../jgit-proxy-server/src/main/resources/git-proxy.yml) | Base defaults shipped with the JAR |
12+
| [jgit-proxy-server/.../resources/git-proxy-local.yml](../jgit-proxy-server/src/main/resources/git-proxy-local.yml) | Local dev overrides (not for production) |
13+
| [docker/git-proxy-local.yml](../docker/git-proxy-local.yml) | Docker-mounted override for local Gitea |
14+
| [docker/git-proxy-postgres.yml](../docker/git-proxy-postgres.yml) | Docker-mounted override for PostgreSQL |
15+
| [docker/git-proxy-mongo.yml](../docker/git-proxy-mongo.yml) | Docker-mounted override for MongoDB |
16+
| [jgit-proxy-core/.../config/CommitConfig.java](../jgit-proxy-core/src/main/java/org/finos/gitproxy/config/CommitConfig.java) | Domain object for commit validation rules — candidate for typed binding |
17+
| [jgit-proxy-core/.../config/GpgConfig.java](../jgit-proxy-core/src/main/java/org/finos/gitproxy/config/GpgConfig.java) | Domain object for GPG settings — candidate for typed binding |
18+
| [jgit-proxy-server/.../GitProxyJettyApplication.java](../jgit-proxy-server/src/main/java/org/finos/gitproxy/jetty/GitProxyJettyApplication.java) | Server entry point — constructs loader + builder |
19+
| [jgit-proxy-dashboard/.../GitProxyWithDashboardApplication.java](../jgit-proxy-dashboard/src/main/java/org/finos/gitproxy/dashboard/GitProxyWithDashboardApplication.java) | Dashboard entry point — also constructs loader + builder |
20+
21+
`JettyConfigurationLoader` is a hand-rolled 3-tier YAML loader:
22+
23+
1. `git-proxy.yml` (classpath base)
24+
2. `git-proxy-local.yml` (optional local overrides)
25+
3. `GITPROXY_*` env vars (highest priority)
26+
27+
`JettyConfigurationBuilder` then casts the resulting `Map<String, Object>` tree into domain objects.
28+
29+
### Pain points
30+
31+
- **Partial env var coverage** — only simple scalar keys (port, base-path, provider enabled flag). Complex structures (whitelist filters, regex patterns) cannot be overridden via env vars.
32+
- **No profiles** — no `git-proxy-prod.yml` / `git-proxy-dev.yml` activation mechanism.
33+
- **No type safety** — raw `Map<String, Object>` throughout; type mismatches surface at runtime.
34+
- **No validation** — bad config silently produces defaults or runtime exceptions; no fail-fast at startup.
35+
- **No cloud config integration** — no Vault, AWS SSM, or Kubernetes Secrets support.
36+
37+
---
38+
39+
## Recommended replacement: Gestalt
40+
41+
Replace `JettyConfigurationLoader` with [Gestalt](https://github.com/gestalt-config/gestalt).
42+
43+
Gestalt is a lightweight, container-free config library that covers everything the hand-rolled loader lacks:
44+
45+
| Feature | Today | Gestalt |
46+
| ------------------------------ | -------------- | ------------------------ |
47+
| YAML loading | ✅ (SnakeYAML) | ✅ (SnakeYAML module) |
48+
| File merging / layering | ✅ manual | ✅ native, ordinal-based |
49+
| Env var overrides | ⚠️ partial | ✅ full, any key |
50+
| Profile activation || ✅ native |
51+
| Type-safe POJO binding |||
52+
| Startup validation (JSR-380) |||
53+
| Hot-reload |||
54+
| Cloud config (Vault, SSM, K8s) || ✅ (extension modules) |
55+
56+
---
57+
58+
## Migration sketch
59+
60+
1. **Add Gestalt dependency** to [jgit-proxy-server/build.gradle](../jgit-proxy-server/build.gradle):
61+
62+
```groovy
63+
implementation 'com.github.gestalt-config:gestalt-core:0.x.x'
64+
implementation 'com.github.gestalt-config:gestalt-yaml:0.x.x'
65+
```
66+
67+
2. **Define typed config records/POJOs** to replace the `Map<String, Object>` casts in [JettyConfigurationBuilder.java](../jgit-proxy-server/src/main/java/org/finos/gitproxy/jetty/config/JettyConfigurationBuilder.java) (e.g., `ServerConfig`, `ProviderConfig`, `CommitConfig`, `DatabaseConfig`). Annotate with Bean Validation constraints where appropriate. Existing domain objects [CommitConfig.java](../jgit-proxy-core/src/main/java/org/finos/gitproxy/config/CommitConfig.java) and [GpgConfig.java](../jgit-proxy-core/src/main/java/org/finos/gitproxy/config/GpgConfig.java) in `jgit-proxy-core` can be augmented rather than replaced.
68+
69+
3. **Replace [JettyConfigurationLoader.java](../jgit-proxy-server/src/main/java/org/finos/gitproxy/jetty/config/JettyConfigurationLoader.java)** with a `GestaltBuilder` composition:
70+
71+
```
72+
classpath:git-proxy.yml (base defaults, stays as-is)
73+
→ classpath:git-proxy-{profile}.yml (profile override, if active)
74+
→ filesystem:git-proxy-local.yml (external local override, stays as-is)
75+
→ environment variables (highest priority, full key coverage)
76+
```
77+
78+
4. **Simplify [JettyConfigurationBuilder.java](../jgit-proxy-server/src/main/java/org/finos/gitproxy/jetty/config/JettyConfigurationBuilder.java)** — replace all `(Map) rawConfig.get(...)` casts with `gestalt.getConfig("git-proxy", GitProxyConfig.class)`.
79+
80+
5. **Update entry points** [GitProxyJettyApplication.java](../jgit-proxy-server/src/main/java/org/finos/gitproxy/jetty/GitProxyJettyApplication.java) and [GitProxyWithDashboardApplication.java](../jgit-proxy-dashboard/src/main/java/org/finos/gitproxy/dashboard/GitProxyWithDashboardApplication.java) — swap `new JettyConfigurationLoader()` for the Gestalt-backed equivalent.
81+
82+
6. **Profile activation** — read `GITPROXY_PROFILE` env var (or system property) to select the profile YAML file.
83+
84+
---
85+
86+
## Libraries considered and rejected
87+
88+
- **Apache Commons Configuration 2** — no native profile concept; POJO binding via Beanutils is not first-class.
89+
- **Owner** — last release 2019, effectively unmaintained, no YAML support.

docs/plan-db-layer-refactor.md

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Database Layer Refactor Plan
2+
3+
## Goals
4+
5+
- Remove raw JDBC boilerplate (`PreparedStatement`, `ResultSet`, manual `Connection` try/finally)
6+
- Remove manual MongoDB `Document` construction/reads
7+
- Add lightweight query harness without pulling in a full ORM or Spring Boot auto-config
8+
- Keep the existing `PushStore` interface contract unchanged — this is an internal implementation refactor only
9+
10+
---
11+
12+
## Current State
13+
14+
| Class | Storage | Problem |
15+
| ------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------- |
16+
| `JdbcPushStore` | H2 / SQLite / PostgreSQL | Raw JDBC: manual statement prep, positional `?` params, no transaction management, exception swallowing |
17+
| `MongoPushStore` | MongoDB | Manual `Document` key/value construction and extraction |
18+
| `PushRecordMapper` || Already close to a `RowMapper<T>`, just not implementing the interface |
19+
| `DataSourceFactory` | HikariCP | Already good — wraps `javax.sql.DataSource`, no changes needed |
20+
21+
---
22+
23+
## Recommendation 1 — Add `spring-jdbc` + `spring-tx` to `jgit-proxy-core`
24+
25+
These are standalone Spring modules with no Spring Boot, no Spring Data JPA, no auto-configuration. They give you:
26+
27+
- `NamedParameterJdbcTemplate` — named `:param` SQL with `MapSqlParameterSource`
28+
- `JdbcTemplate` — positional `?` fallback when needed
29+
- `TransactionTemplate` — programmatic transactions, no AOP or proxy magic
30+
- `DataSourceTransactionManager` — wires `DataSource` to the transaction abstraction
31+
- `RowMapper<T>` — interface for result set → object mapping
32+
33+
Add to `jgit-proxy-core/build.gradle`:
34+
35+
```gradle
36+
api 'org.springframework:spring-jdbc:6.2.6'
37+
api 'org.springframework:spring-tx:6.2.6'
38+
```
39+
40+
Version-aligned with the existing `spring-webmvc:6.2.6` used in the dashboard — no version drift.
41+
42+
---
43+
44+
## Recommendation 2 — Refactor `JdbcPushStore` to `NamedParameterJdbcTemplate`
45+
46+
Replace manual `PreparedStatement` / `ResultSet` / `Connection` lifecycle with template-based calls.
47+
48+
Named parameters (`:status`, `:pushId`) are preferable to positional `?` for column-heavy queries. The existing `find(PushQuery)` dynamic WHERE clause is a direct fit for `MapSqlParameterSource`.
49+
50+
Key construction change:
51+
52+
```java
53+
private final NamedParameterJdbcTemplate jdbc;
54+
private final TransactionTemplate tx;
55+
56+
public JdbcPushStore(DataSource ds) {
57+
this.jdbc = new NamedParameterJdbcTemplate(ds);
58+
this.tx = new TransactionTemplate(new DataSourceTransactionManager(ds));
59+
}
60+
```
61+
62+
`approve()`, `reject()`, and `cancel()` are the only methods that touch multiple tables (status update + attestation insert) and need `tx.execute(...)`. All read paths (`find`, `findById`) are plain queries with no transaction wrapper needed.
63+
64+
---
65+
66+
## Recommendation 3 — Promote `PushRecordMapper` to `RowMapper<PushRecord>`
67+
68+
The existing mapper class already does the right work. Implement the Spring interface so it composes cleanly with the template:
69+
70+
```java
71+
public class PushRecordMapper implements RowMapper<PushRecord> {
72+
@Override
73+
public PushRecord mapRow(ResultSet rs, int rowNum) throws SQLException { ... }
74+
}
75+
```
76+
77+
Create equivalents for the child entities:
78+
79+
- `PushStepMapper implements RowMapper<PushStep>`
80+
- `PushCommitMapper implements RowMapper<PushCommit>`
81+
- `AttestationMapper implements RowMapper<Attestation>`
82+
83+
Each mapper class is trivially unit-testable with a mocked `ResultSet`.
84+
85+
---
86+
87+
## Recommendation 4 — MongoDB: POJO Codec Registry (no `spring-data-mongodb`)
88+
89+
`spring-data-mongodb` gives `MongoTemplate` but drags in `spring-data-commons` (~800KB) and its annotation-driven mapping layer. The MongoDB Java driver already ships a **POJO codec registry** that handles marshalling with zero additional dependencies.
90+
91+
Register domain classes at startup:
92+
93+
```java
94+
CodecRegistry pojoRegistry = fromRegistries(
95+
MongoClientSettings.getDefaultCodecRegistry(),
96+
fromProviders(PojoCodecProvider.builder()
97+
.register(PushRecord.class, PushCommit.class, PushStep.class, Attestation.class)
98+
.automatic(true)
99+
.build())
100+
);
101+
```
102+
103+
Then `MongoPushStore` works with typed collections instead of raw `Document`:
104+
105+
```java
106+
MongoCollection<PushRecord> collection = mongoClient
107+
.getDatabase("gitproxy")
108+
.withCodecRegistry(pojoRegistry)
109+
.getCollection("pushes", PushRecord.class);
110+
111+
// clean reads/writes
112+
collection.find(eq("status", PushStatus.APPROVED)).into(new ArrayList<>());
113+
collection.findOneAndUpdate(eq("id", id),
114+
Updates.combine(Updates.set("status", APPROVED), ...),
115+
new FindOneAndUpdateOptions().returnDocument(AFTER));
116+
```
117+
118+
`Filters`, `Updates`, and `Sorts` from `com.mongodb.client.model` are the Mongo-native equivalent of `MapSqlParameterSource` — composable and readable without string `Document` keys everywhere.
119+
120+
Requires the domain model classes (`PushRecord`, `PushStep`, etc.) to have default constructors and standard getters/setters (or public fields). If they currently use builder/record patterns, a small codec customisation or `@BsonProperty` annotations bridge the gap.
121+
122+
---
123+
124+
## Recommendation 5 — Dashboard Wiring (no Boot, no auto-config)
125+
126+
The dashboard already registers beans manually in `SpringWebConfig`. Expose the JDBC helpers as Spring-managed beans so controllers and any future services receive them by injection rather than constructing templates themselves:
127+
128+
```java
129+
// in SpringWebConfig (or a new DataConfig @Configuration)
130+
@Bean
131+
public NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) {
132+
return new NamedParameterJdbcTemplate(dataSource);
133+
}
134+
135+
@Bean
136+
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
137+
return new DataSourceTransactionManager(dataSource);
138+
}
139+
```
140+
141+
`DataSource` is already constructed by `DataSourceFactory` and passed in externally — this just promotes it to a Spring bean so the wiring stays explicit and testable without any `@Autowired` magic on construction.
142+
143+
---
144+
145+
## What This Does Not Change
146+
147+
- `PushStore` interface — unchanged, no API break
148+
- `DataSourceFactory` / HikariCP setup — unchanged
149+
- `schema.sql` — raw SQL stays as the schema of record, no JPA `@Entity` annotations, no Flyway (unless added separately)
150+
- `PushStoreFactory` convenience builders — unchanged
151+
- The MongoDB document structure — POJO codec maps to the same document shape as the current `Document`-based code
152+
153+
---
154+
155+
## Summary
156+
157+
| Concern | Approach |
158+
| -------------------------- | ----------------------------------------------------------- |
159+
| SQL connection boilerplate | `NamedParameterJdbcTemplate` |
160+
| Transaction management | `TransactionTemplate` + `DataSourceTransactionManager` |
161+
| Row mapping | `RowMapper<T>` per entity |
162+
| MongoDB document mapping | POJO codec registry (driver-native) |
163+
| Spring Boot / JPA | Not needed — `spring-jdbc` + `spring-tx` are self-contained |
164+
| ORM | None — schema.sql is the source of truth |
165+
166+
New transitive deps (dashboard): `spring-jdbc` + `spring-tx` only. Both are already version-aligned with Spring MVC 6.2.6 in use today.

docs/plan-diffcheck-refactor.md

Lines changed: 0 additions & 74 deletions
This file was deleted.

0 commit comments

Comments
 (0)