nc-db 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
nc_db-0.1.3/PKG-INFO ADDED
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.3
2
+ Name: nc_db
3
+ Version: 0.1.3
4
+ Summary: A PostgresSql Database project with a fully generic infrastructure and design.
5
+ License: Proprietary
6
+ Author: Colt Hale
7
+ Author-email: colt@pixelthin.com
8
+ Requires-Python: >=3.12,<4.0
9
+ Classifier: License :: Other/Proprietary License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: psycopg-pool (>=3.2.6,<4.0.0)
14
+ Requires-Dist: psycopg[binary] (>=3.2.9,<4.0.0)
15
+ Requires-Dist: pydantic (>=2.11.7,<3.0.0)
16
+ Description-Content-Type: text/markdown
17
+
18
+ # nc_db
19
+ PostgreSQL schema, lightweight Python DB client, tests, and Kubernetes manifests for the Neural Context project.
20
+
21
+ This repository provides:
22
+ - A thin, typed Python client for JSONB-first tables (insert/update/select/delete) under a configurable schema.
23
+ - Test suite that spins up isolated schemas to validate behavior and edge cases.
24
+ - Kubernetes manifests (Kustomize) to run Postgres locally or in-cluster, plus a safe reset script.
25
+
26
+ ## Contents
27
+
28
+ ```
29
+ src/db/ # Python client: Db, DbConfig, SQL helpers
30
+ src/tests/ # Pytest suite and fixtures (isolated schemas)
31
+ k8s/ # K8s StatefulSet, init SQL, and ops scripts
32
+ pyproject.toml # Poetry + pytest configuration
33
+ .github/ # Issue and PR templates
34
+ README.md # This file (schema diagram below)
35
+ ```
36
+
37
+ ## Prerequisites
38
+
39
+ - Python 3.12+
40
+ - PostgreSQL 15+ available locally, or run via the provided K8s manifests
41
+
42
+ Default development credentials used in examples/tests:
43
+ `host=localhost, port=5432, db=nc-data, user=nc_admin, password=bob`.
44
+
45
+ ## Quick Start
46
+
47
+ 1) Install dependencies with Poetry:
48
+
49
+ ```bash
50
+ poetry install
51
+ ```
52
+
53
+ 2) Ensure Postgres is reachable using the defaults above. You can run it via Kubernetes (see below) or your local Postgres.
54
+
55
+ 3) Smoke test the Python client:
56
+
57
+ ```bash
58
+ poetry run python -c "from src.db.db import Db; print(Db().get('scg', ids=[]))"
59
+ ```
60
+
61
+ ## Running Tests
62
+
63
+ ```bash
64
+ kubectl apply -k k8s/postgres/overlays/ephemeral
65
+ kubectl -n nc_db wait --for=condition=ready pod -l app=nc-data --timeout=90s
66
+ kubectl -n nc_db port-forward svc/postgres 5432:5432
67
+ ```
68
+
69
+ ```bash
70
+ poetry run pytest -q
71
+ ```
72
+
73
+ - To run a single test: `poetry run pytest -k test_update_merge -q`
74
+ - Tests create per-test schemas and tables; they do not require pre-existing app tables.
75
+
76
+ ## Python Client Overview
77
+
78
+ The client favors tables shaped as `(id BIGSERIAL PRIMARY KEY, body JSONB NOT NULL)` and enforces `body.id == id` on writes/reads.
79
+
80
+ Basic usage:
81
+
82
+ ```python
83
+ from nc_db.db.db import Db
84
+ from nc_db.db.DbConfig import DbConfig
85
+
86
+ db = Db(schema="nc", config=DbConfig(
87
+ user="my_user", password="my_password", host="localhost", port=5432, dbname="my_database"
88
+ ))
89
+
90
+ # Insert with or without explicit id
91
+ id1 = db.insert("scg", {"body": {"name": "alpha"}})
92
+ id2 = db.insert("scg", {"body": {"id": 42, "name": "explicit"}})
93
+
94
+ # Update (merge JSON into existing body or replace entirely)
95
+ db.update("scg", ids=[id1], items=[{"body": {"name": "alpha-2"}}], mode="merge")
96
+
97
+ # Read (optionally map rows to models)
98
+ rows = db.get("scg") # [{"id": 1, "body": {...}}, ...]
99
+
100
+ # Delete
101
+ db.delete("scg", [id2])
102
+ ```
103
+
104
+ Key features:
105
+ - Insert trusts a provided id if present; otherwise uses the table sequence and normalizes `body.id`.
106
+ - Update supports `mode="merge"` (default) and `mode="replace"`, always ensuring `body.id` matches the row id.
107
+ - Get returns rows as dictionaries or via a mapper function to your own model types.
108
+ - Delete single or all rows, plus a helper to `TRUNCATE ... RESTART IDENTITY`.
109
+
110
+ ## Configuration
111
+
112
+ Use `DbConfig` or environment variables to control connection pooling and DSN; defaults are suitable for local development.
113
+
114
+ ```python
115
+ from nc_db.db.DbConfig import DbConfig
116
+
117
+ config = DbConfig(
118
+ user="your_username",
119
+ password="your_password",
120
+ host="localhost",
121
+ port=5432,
122
+ dbname="your_database",
123
+ )
124
+ ```
125
+ OR
126
+
127
+ ```bash
128
+ export PGUSER="your_username"
129
+ export PGPASSWORD="your_password"
130
+ export PGHOST=localhost
131
+ export PGPORT=5432
132
+ export PGDATABASE="youd_database"
133
+
134
+ ```
135
+
136
+ ## Kubernetes (Optional)
137
+
138
+ Run a local Postgres via Kustomize overlays.
139
+
140
+ Ephemeral (emptyDir) instance:
141
+
142
+ ```bash
143
+ kubectl apply -k k8s/postgres/overlays/ephemeral
144
+ kubectl -n nc_db wait --for=condition=ready pod -l app=nc-data --timeout=90s
145
+ kubectl -n nc_db port-forward svc/postgres 5432:5432
146
+ ```
147
+
148
+ Persistent storage overlay is also available under `k8s/postgres/overlays/persistent`.
149
+
150
+ To safely reset a StatefulSet (DANGEROUS — wipes data), use:
151
+
152
+ ```bash
153
+ python k8s/scripts/full_pod_reset.py --yes
154
+ ```
155
+
156
+ Review the script before use; it scales down, deletes the PVC (and optionally PV), and scales back up.
157
+
158
+ ## Development Conventions
159
+
160
+ - Python 3.12, type hints, PEP 8; keep imports sorted and code minimal.
161
+ - Modules: snake_case; Classes: CamelCase; variables/functions: snake_case.
162
+ - Tests live in `src/tests`; name files/functions `test_*` and focus on behavior.
163
+ - See `.github/PULL_REQUEST_TEMPLATE.md` for PR expectations.
164
+
165
+ ## Schema Diagram
166
+
167
+ Note: the init SQL currently provisions `scg` and `healthcheck`
168
+
169
+ ## Security & License
170
+
171
+ - Do not commit real secrets. The defaults in code and K8s manifests are for local development only.
172
+ - Use `DbConfig` or environment variables to specify connection details.
173
+ - License: Proprietary (see `pyproject.toml`).
174
+
@@ -0,0 +1,156 @@
1
+ # nc_db
2
+ PostgreSQL schema, lightweight Python DB client, tests, and Kubernetes manifests for the Neural Context project.
3
+
4
+ This repository provides:
5
+ - A thin, typed Python client for JSONB-first tables (insert/update/select/delete) under a configurable schema.
6
+ - Test suite that spins up isolated schemas to validate behavior and edge cases.
7
+ - Kubernetes manifests (Kustomize) to run Postgres locally or in-cluster, plus a safe reset script.
8
+
9
+ ## Contents
10
+
11
+ ```
12
+ src/db/ # Python client: Db, DbConfig, SQL helpers
13
+ src/tests/ # Pytest suite and fixtures (isolated schemas)
14
+ k8s/ # K8s StatefulSet, init SQL, and ops scripts
15
+ pyproject.toml # Poetry + pytest configuration
16
+ .github/ # Issue and PR templates
17
+ README.md # This file (schema diagram below)
18
+ ```
19
+
20
+ ## Prerequisites
21
+
22
+ - Python 3.12+
23
+ - PostgreSQL 15+ available locally, or run via the provided K8s manifests
24
+
25
+ Default development credentials used in examples/tests:
26
+ `host=localhost, port=5432, db=nc-data, user=nc_admin, password=bob`.
27
+
28
+ ## Quick Start
29
+
30
+ 1) Install dependencies with Poetry:
31
+
32
+ ```bash
33
+ poetry install
34
+ ```
35
+
36
+ 2) Ensure Postgres is reachable using the defaults above. You can run it via Kubernetes (see below) or your local Postgres.
37
+
38
+ 3) Smoke test the Python client:
39
+
40
+ ```bash
41
+ poetry run python -c "from src.db.db import Db; print(Db().get('scg', ids=[]))"
42
+ ```
43
+
44
+ ## Running Tests
45
+
46
+ ```bash
47
+ kubectl apply -k k8s/postgres/overlays/ephemeral
48
+ kubectl -n nc_db wait --for=condition=ready pod -l app=nc-data --timeout=90s
49
+ kubectl -n nc_db port-forward svc/postgres 5432:5432
50
+ ```
51
+
52
+ ```bash
53
+ poetry run pytest -q
54
+ ```
55
+
56
+ - To run a single test: `poetry run pytest -k test_update_merge -q`
57
+ - Tests create per-test schemas and tables; they do not require pre-existing app tables.
58
+
59
+ ## Python Client Overview
60
+
61
+ The client favors tables shaped as `(id BIGSERIAL PRIMARY KEY, body JSONB NOT NULL)` and enforces `body.id == id` on writes/reads.
62
+
63
+ Basic usage:
64
+
65
+ ```python
66
+ from nc_db.db.db import Db
67
+ from nc_db.db.DbConfig import DbConfig
68
+
69
+ db = Db(schema="nc", config=DbConfig(
70
+ user="my_user", password="my_password", host="localhost", port=5432, dbname="my_database"
71
+ ))
72
+
73
+ # Insert with or without explicit id
74
+ id1 = db.insert("scg", {"body": {"name": "alpha"}})
75
+ id2 = db.insert("scg", {"body": {"id": 42, "name": "explicit"}})
76
+
77
+ # Update (merge JSON into existing body or replace entirely)
78
+ db.update("scg", ids=[id1], items=[{"body": {"name": "alpha-2"}}], mode="merge")
79
+
80
+ # Read (optionally map rows to models)
81
+ rows = db.get("scg") # [{"id": 1, "body": {...}}, ...]
82
+
83
+ # Delete
84
+ db.delete("scg", [id2])
85
+ ```
86
+
87
+ Key features:
88
+ - Insert trusts a provided id if present; otherwise uses the table sequence and normalizes `body.id`.
89
+ - Update supports `mode="merge"` (default) and `mode="replace"`, always ensuring `body.id` matches the row id.
90
+ - Get returns rows as dictionaries or via a mapper function to your own model types.
91
+ - Delete single or all rows, plus a helper to `TRUNCATE ... RESTART IDENTITY`.
92
+
93
+ ## Configuration
94
+
95
+ Use `DbConfig` or environment variables to control connection pooling and DSN; defaults are suitable for local development.
96
+
97
+ ```python
98
+ from nc_db.db.DbConfig import DbConfig
99
+
100
+ config = DbConfig(
101
+ user="your_username",
102
+ password="your_password",
103
+ host="localhost",
104
+ port=5432,
105
+ dbname="your_database",
106
+ )
107
+ ```
108
+ OR
109
+
110
+ ```bash
111
+ export PGUSER="your_username"
112
+ export PGPASSWORD="your_password"
113
+ export PGHOST=localhost
114
+ export PGPORT=5432
115
+ export PGDATABASE="youd_database"
116
+
117
+ ```
118
+
119
+ ## Kubernetes (Optional)
120
+
121
+ Run a local Postgres via Kustomize overlays.
122
+
123
+ Ephemeral (emptyDir) instance:
124
+
125
+ ```bash
126
+ kubectl apply -k k8s/postgres/overlays/ephemeral
127
+ kubectl -n nc_db wait --for=condition=ready pod -l app=nc-data --timeout=90s
128
+ kubectl -n nc_db port-forward svc/postgres 5432:5432
129
+ ```
130
+
131
+ Persistent storage overlay is also available under `k8s/postgres/overlays/persistent`.
132
+
133
+ To safely reset a StatefulSet (DANGEROUS — wipes data), use:
134
+
135
+ ```bash
136
+ python k8s/scripts/full_pod_reset.py --yes
137
+ ```
138
+
139
+ Review the script before use; it scales down, deletes the PVC (and optionally PV), and scales back up.
140
+
141
+ ## Development Conventions
142
+
143
+ - Python 3.12, type hints, PEP 8; keep imports sorted and code minimal.
144
+ - Modules: snake_case; Classes: CamelCase; variables/functions: snake_case.
145
+ - Tests live in `src/tests`; name files/functions `test_*` and focus on behavior.
146
+ - See `.github/PULL_REQUEST_TEMPLATE.md` for PR expectations.
147
+
148
+ ## Schema Diagram
149
+
150
+ Note: the init SQL currently provisions `scg` and `healthcheck`
151
+
152
+ ## Security & License
153
+
154
+ - Do not commit real secrets. The defaults in code and K8s manifests are for local development only.
155
+ - Use `DbConfig` or environment variables to specify connection details.
156
+ - License: Proprietary (see `pyproject.toml`).
@@ -0,0 +1,8 @@
1
+ CREATE SCHEMA IF NOT EXISTS nc;
2
+
3
+ CREATE TABLE IF NOT EXISTS nc.scg (
4
+ id BIGSERIAL PRIMARY KEY,
5
+ body JSONB NOT NULL
6
+ );
7
+
8
+ CREATE INDEX IF NOT EXISTS idx_data_body_gin on nc.scg USING GIN (body);
@@ -0,0 +1,16 @@
1
+ -- Revoke overly broad PUBLIC defaults and grant to the application role
2
+
3
+ -- Revoke defaults for future objects
4
+ ALTER DEFAULT PRIVILEGES IN SCHEMA "nc" REVOKE ALL ON TABLES FROM PUBLIC;
5
+ ALTER DEFAULT PRIVILEGES IN SCHEMA "nc" REVOKE ALL ON SEQUENCES FROM PUBLIC;
6
+
7
+ -- Revoke on existing objects (no-op if none)
8
+ REVOKE ALL ON ALL TABLES IN SCHEMA nc FROM PUBLIC;
9
+ REVOKE ALL ON ALL SEQUENCES IN SCHEMA nc FROM PUBLIC;
10
+
11
+ -- Grant defaults to nc_app for future objects (owner-dependent)
12
+ ALTER DEFAULT PRIVILEGES IN SCHEMA "nc"
13
+ GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO nc_app;
14
+
15
+ ALTER DEFAULT PRIVILEGES IN SCHEMA "nc"
16
+ GRANT USAGE, SELECT ON SEQUENCES TO nc_app;
@@ -0,0 +1,10 @@
1
+ CREATE SCHEMA IF NOT EXISTS nc;
2
+
3
+ CREATE TABLE IF NOT EXISTS nc.healthcheck (
4
+ id int PRIMARY KEY,
5
+ ok boolean NOT NULL
6
+ );
7
+
8
+ INSERT INTO nc.healthcheck (id, ok)
9
+ VALUES (1, true)
10
+ ON CONFLICT (id) DO NOTHING;
@@ -0,0 +1,31 @@
1
+ apiVersion: kustomize.config.k8s.io/v1beta1
2
+ kind: Kustomization
3
+ namespace: nc_db
4
+
5
+ secretGenerator:
6
+ # Cluster/postgres superuser creds
7
+ - name: postgres-credentials-root
8
+ envs:
9
+ - secrets/postgres.root.env
10
+
11
+ # App/user creds (what your app uses to connect)
12
+ - name: postgres-credentials-app
13
+ envs:
14
+ - secrets/postgres.app.env
15
+
16
+ configMapGenerator:
17
+ - name: pg-init-sql
18
+ files:
19
+ - init/00-create-app-role.sql
20
+ - init/01-create-schema.sql
21
+ - init/02-grants.sql
22
+ - init/10-healthcheck.sql
23
+
24
+ # Keep the suffix hash enabled so rolling updates occur when content changes
25
+ generatorOptions:
26
+ disableNameSuffixHash: false
27
+
28
+ resources:
29
+ - namespace.yaml
30
+ - service.yaml
31
+ - statefulset.yaml
@@ -0,0 +1,4 @@
1
+ apiVersion: v1
2
+ kind: Namespace
3
+ metadata:
4
+ name: nc_db
@@ -0,0 +1,50 @@
1
+ apiVersion: v1
2
+ kind: Secret
3
+ metadata:
4
+ name: pg-app-bootstrap
5
+ namespace: nc_db
6
+ type: Opaque
7
+ stringData:
8
+ 20-create-app-user.sh: |
9
+ #!/bin/bash
10
+ set -euo pipefail
11
+
12
+ echo "[initdb] creating / ensuring application role ${APP_DB_USER}"
13
+
14
+ psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" <<-EOSQL
15
+ DO \$\$
16
+ BEGIN
17
+ IF NOT EXISTS (
18
+ SELECT 1 FROM pg_roles WHERE rolname = '${APP_DB_USER}'
19
+ ) THEN
20
+ EXECUTE format(
21
+ 'CREATE ROLE %I LOGIN PASSWORD %L',
22
+ '${APP_DB_USER}',
23
+ '${APP_DB_PASSWORD}'
24
+ );
25
+ END IF;
26
+ END
27
+ \$\$;
28
+
29
+ -- ensure schema exists
30
+ CREATE SCHEMA IF NOT EXISTS ${APP_DB_SCHEMA};
31
+
32
+ -- make sure app user can actually use the DB
33
+ GRANT CONNECT ON DATABASE "${APP_DB_NAME}" TO ${APP_DB_USER};
34
+
35
+ GRANT USAGE ON SCHEMA ${APP_DB_SCHEMA} TO ${APP_DB_USER};
36
+
37
+ GRANT SELECT, INSERT, UPDATE, DELETE
38
+ ON ALL TABLES IN SCHEMA ${APP_DB_SCHEMA}
39
+ TO ${APP_DB_USER};
40
+
41
+ GRANT USAGE, SELECT
42
+ ON ALL SEQUENCES IN SCHEMA ${APP_DB_SCHEMA}
43
+ TO ${APP_DB_USER};
44
+
45
+ ALTER DEFAULT PRIVILEGES IN SCHEMA ${APP_DB_SCHEMA}
46
+ GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ${APP_DB_USER};
47
+
48
+ ALTER DEFAULT PRIVILEGES IN SCHEMA ${APP_DB_SCHEMA}
49
+ GRANT USAGE, SELECT ON SEQUENCES TO ${APP_DB_USER};
50
+ EOSQL
@@ -0,0 +1,12 @@
1
+ apiVersion: v1
2
+ kind: Service
3
+ metadata:
4
+ name: postgres
5
+ spec:
6
+ type: ClusterIP
7
+ selector:
8
+ app: nc-data
9
+ ports:
10
+ - name: pg
11
+ port: 5432
12
+ targetPort: 5432
@@ -0,0 +1,88 @@
1
+ apiVersion: apps/v1
2
+ kind: StatefulSet
3
+ metadata:
4
+ name: postgres
5
+ namespace: nc_db
6
+ spec:
7
+ serviceName: postgres
8
+ replicas: 1
9
+ selector:
10
+ matchLabels:
11
+ app: nc-data
12
+ template:
13
+ metadata:
14
+ labels:
15
+ app: nc-data
16
+ spec:
17
+ containers:
18
+ - name: postgres
19
+ image: postgres:15
20
+ imagePullPolicy: IfNotPresent
21
+ ports:
22
+ - containerPort: 5432
23
+ name: pg
24
+
25
+ # Load secrets into env so init script can read them
26
+ envFrom:
27
+ - secretRef:
28
+ name: postgres-credentials-root # POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB
29
+ - secretRef:
30
+ name: postgres-credentials-app # APP_DB_USER, APP_DB_PASSWORD, APP_DB_NAME, APP_DB_SCHEMA
31
+
32
+ # Non-secret config
33
+ env:
34
+ - name: PGDATA
35
+ value: /var/lib/postgresql/data/pgdata
36
+
37
+ # NOTE: lifecycle: postStart was intentionally removed
38
+
39
+ readinessProbe:
40
+ tcpSocket:
41
+ port: 5432
42
+ initialDelaySeconds: 5
43
+ periodSeconds: 5
44
+
45
+ livenessProbe:
46
+ tcpSocket:
47
+ port: 5432
48
+ initialDelaySeconds: 10
49
+ periodSeconds: 10
50
+
51
+ volumeMounts:
52
+ - name: data
53
+ mountPath: /var/lib/postgresql/data
54
+
55
+ # merged init dir: configmap SQL + secret script
56
+ - name: init-bootstrap
57
+ mountPath: /docker-entrypoint-initdb.d
58
+ readOnly: true
59
+
60
+ volumes:
61
+ - name: init-bootstrap
62
+ projected:
63
+ sources:
64
+ - configMap:
65
+ name: pg-init-sql
66
+ items:
67
+ - key: 01-create-schema.sql
68
+ path: 01-create-schema.sql
69
+ - key: 02-grants.sql
70
+ path: 02-grants.sql
71
+ - key: 10-healthcheck.sql
72
+ path: 10-healthcheck.sql
73
+ - secret:
74
+ name: pg-app-bootstrap
75
+ items:
76
+ - key: 20-create-app-user.sh
77
+ path: 20-create-app-user.sh
78
+
79
+ volumeClaimTemplates:
80
+ - metadata:
81
+ name: data
82
+ spec:
83
+ accessModes:
84
+ - ReadWriteOnce
85
+ storageClassName: microk8s-hostpath
86
+ resources:
87
+ requests:
88
+ storage: 5Gi
@@ -0,0 +1,7 @@
1
+ apiVersion: kustomize.config.k8s.io/v1beta1
2
+ kind: Kustomization
3
+ namespace: nc_db
4
+ resources:
5
+ - ../../base
6
+ patches:
7
+ - path: patch-ephemeral-emptydir.yaml
@@ -0,0 +1,15 @@
1
+ apiVersion: apps/v1
2
+ kind: StatefulSet
3
+ metadata:
4
+ name: postgres
5
+ spec:
6
+ template:
7
+ spec:
8
+ volumes:
9
+ - name: init-sql
10
+ configMap:
11
+ name: pg-init-sql
12
+ defaultMode: 0444
13
+ - name: data
14
+ emptyDir: {}
15
+ volumeClaimTemplates: [] # remove PVCs for ephemeral runs
@@ -0,0 +1,7 @@
1
+ apiVersion: kustomize.config.k8s.io/v1beta1
2
+ kind: Kustomization
3
+ namespace: nc_db
4
+ resources:
5
+ - ../../base
6
+ patches:
7
+ - path: patch-persistent-storageclass.yaml
@@ -0,0 +1,17 @@
1
+ apiVersion: apps/v1
2
+ kind: StatefulSet
3
+ metadata:
4
+ name: postgres
5
+ spec:
6
+ volumeClaimTemplates: [] # disable PVC template from base
7
+ template:
8
+ spec:
9
+ volumes:
10
+ - name: data
11
+ hostPath:
12
+ path: /home/chale/nc_db/postgress-store/
13
+ type: DirectoryOrCreate
14
+ - name: init-sql
15
+ configMap:
16
+ name: pg-init-sql
17
+ defaultMode: 0444