diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e2ae33c..aaf4b56 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: super_ref: - description: 'brimdata/super branch, tag, or SHA to test against' + description: 'superdb/super branch, tag, or SHA to test against' default: main required: false repository_dispatch: @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/checkout@v4 with: - repository: brimdata/super + repository: superdb/super ref: ${{ inputs.super_ref || 'main' }} path: super - uses: actions/setup-go@v5 @@ -33,7 +33,7 @@ jobs: working-directory: super - name: Start super db serve (no auth) run: | - $GITHUB_WORKSPACE/bin/super db -db $(mktemp -d) serve -l localhost:9867 & + $GITHUB_WORKSPACE/bin/super db serve -l localhost:9867 $(mktemp -d) & for i in $(seq 1 30); do curl -sf http://localhost:9867/status 2>/dev/null && break sleep 1 @@ -51,7 +51,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/checkout@v4 with: - repository: brimdata/super + repository: superdb/super ref: ${{ inputs.super_ref || 'main' }} path: super - uses: actions/setup-go@v5 @@ -65,12 +65,13 @@ jobs: working-directory: super - name: Start super db serve (with auth) run: | - $GITHUB_WORKSPACE/bin/super db -db $(mktemp -d) serve -l localhost:9867 \ + $GITHUB_WORKSPACE/bin/super db serve -l localhost:9867 \ -auth.enabled=t \ -auth.audience=a \ -auth.clientid=c \ -auth.domain=d \ - -auth.jwkspath=$GITHUB_WORKSPACE/super/service/testdata/auth-public-jwks.json & + -auth.jwkspath=$GITHUB_WORKSPACE/super/service/testdata/auth-public-jwks.json \ + $(mktemp -d) & for i in $(seq 1 30); do curl -sf http://localhost:9867/status 2>/dev/null && break sleep 1 @@ -81,7 +82,7 @@ jobs: -audience a -domain d -keyid testkey \ -privatekeyfile $GITHUB_WORKSPACE/super/service/testdata/auth-private-key \ -tenantid t -userid u) - $GITHUB_WORKSPACE/bin/super db auth store -access "$token" -db http://localhost:9867 + $GITHUB_WORKSPACE/bin/super db attach -q -token "$token" http://localhost:9867 - uses: actions/setup-python@v5 with: python-version: '3.x' diff --git a/README.md b/README.md index c2a7119..031fe74 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ dependencies, and start a local SuperDB service: python3 -m venv .venv source .venv/bin/activate pip3 install -e '.[test]' -super db -db $(mktemp -d) serve +super db serve $(mktemp -d) ``` Then in another shell (with the virtual environment activated): diff --git a/superdb.py b/superdb.py index 7cfc18e..5aa02b9 100755 --- a/superdb.py +++ b/superdb.py @@ -32,21 +32,21 @@ def __get_auth_token(self, config_dir): return creds['services'][self.base_url]['access'] return None - def create_pool(self, name, layout={'order': 'desc', 'keys': [['ts']]}, - thresh=0): - r = self.session.post(self.base_url + '/pool', json={ + def create_table(self, name, layout={'order': 'desc', 'keys': [['ts']]}, + thresh=0): + r = self.session.post(self.base_url + '/table', json={ 'name': name, 'layout': layout, 'thresh': thresh, }) self.__raise_for_status(r) - def load(self, pool_name_or_id, data, branch_name='main', + def load(self, table_name_or_id, data, branch_name='main', commit_author=getpass.getuser(), commit_body='', mime_type=None): - pool = urllib.parse.quote(pool_name_or_id, safe='') + table = urllib.parse.quote(table_name_or_id, safe='') branch = urllib.parse.quote(branch_name, safe='') - url = self.base_url + '/pool/' + pool + '/branch/' + branch + url = self.base_url + '/table/' + table + '/branch/' + branch commit_message = {'author': commit_author, 'body': commit_body} headers = {'SuperDB-Commit': json.dumps(commit_message)} if mime_type is not None: @@ -54,9 +54,9 @@ def load(self, pool_name_or_id, data, branch_name='main', r = self.session.post(url, headers=headers, data=data) self.__raise_for_status(r) - def delete_pool(self, pool_name_or_id): - pool = urllib.parse.quote(pool_name_or_id, safe='') - r = self.session.delete(self.base_url + '/pool/' + pool) + def delete_table(self, table_name_or_id): + table = urllib.parse.quote(table_name_or_id, safe='') + r = self.session.delete(self.base_url + '/table/' + table) self.__raise_for_status(r) def query(self, query, safe=True): @@ -96,7 +96,7 @@ def query(self, query, safe=True): try: reader = pa.ipc.open_stream(r.raw) except pa.lib.ArrowInvalid as e: - # An empty response body (no schema) means either the pool has no + # An empty response body (no schema) means either the table has no # data or the data contains a type the Arrow encoder can't handle # (e.g. an empty record). Both cases are indistinguishable at the # HTTP level when streaming, so both are silently treated as an diff --git a/test_auth.py b/test_auth.py index 8179f16..f386dc8 100644 --- a/test_auth.py +++ b/test_auth.py @@ -32,16 +32,16 @@ def test_authenticated_client_can_query(): client = Client() name = 'test_auth_' + uuid.uuid4().hex[:8] - client.create_pool(name) + client.create_table(name) try: client.load(name, b'{a: 1}', mime_type='application/x-sup') assert list(client.query(f'from {name}')) == [{'a': 1}] finally: - client.delete_pool(name) + client.delete_table(name) def test_unauthenticated_client_raises_request_error(): # A client with no credentials (config_dir='') should be rejected by an # auth-enabled service on any request. with pytest.raises(RequestError): - Client(config_dir='').create_pool('x') + Client(config_dir='').create_table('x') diff --git a/test_superdb.py b/test_superdb.py index eb9f80e..ffe90a1 100644 --- a/test_superdb.py +++ b/test_superdb.py @@ -7,7 +7,7 @@ the service is not reachable. Start a local instance with: - super db serve + super db serve $(mktemp -d) """ import datetime @@ -75,17 +75,17 @@ def client(): @pytest.fixture(scope='session') -def pool(client): +def table(client): name = 'test_types_' + uuid.uuid4().hex[:8] - client.create_pool(name) + client.create_table(name) client.load(name, _SUP_ALL_TYPES.encode(), mime_type='application/x-sup') yield name - client.delete_pool(name) + client.delete_table(name) @pytest.fixture(scope='session') -def record(client, pool): - results = list(client.query(f'from {pool}')) +def record(client, table): + results = list(client.query(f'from {table}')) assert len(results) == 1 return results[0] @@ -235,7 +235,7 @@ def test_multiple_record_types_raises_mixed_types_error(client): # With safe=True (default), query() runs a pre-flight check and raises # MixedTypesError before requesting Arrow data, preventing silent truncation. name = 'test_multi_' + uuid.uuid4().hex[:8] - client.create_pool(name) + client.create_table(name) try: client.load(name, b'{a: 1}\n{b: "hello"}', mime_type='application/x-sup') @@ -243,7 +243,7 @@ def test_multiple_record_types_raises_mixed_types_error(client): list(client.query(f'from {name}')) assert exc_info.value.type_count == 2 finally: - client.delete_pool(name) + client.delete_table(name) def test_multiple_record_types_safe_false_returns_partial(client): @@ -251,40 +251,40 @@ def test_multiple_record_types_safe_false_returns_partial(client): # silently truncates at the first schema change, so callers that opt out # must know their data is homogeneous. name = 'test_multi_unsafe_' + uuid.uuid4().hex[:8] - client.create_pool(name) + client.create_table(name) try: client.load(name, b'{a: 1}\n{b: "hello"}', mime_type='application/x-sup') results = list(client.query(f'from {name}', safe=False)) assert len(results) < 2 finally: - client.delete_pool(name) + client.delete_table(name) def test_non_record_values_raises_non_record_error(client): # Data loaded in line format is stored as top-level strings, not records. # With safe=True (default), query() detects this and raises NonRecordError. name = 'test_nonrec_' + uuid.uuid4().hex[:8] - client.create_pool(name) + client.create_table(name) try: client.load(name, b'hello\nworld', mime_type='application/x-line') with pytest.raises(NonRecordError) as exc_info: list(client.query(f'from {name}')) assert exc_info.value.kinds == ['primitive'] finally: - client.delete_pool(name) + client.delete_table(name) def test_non_record_values_safe_false_returns_empty(client): # With safe=False, non-record values are not detected. Arrow cannot encode # non-record top-level values, so the server returns an empty stream. name = 'test_nonrec_unsafe_' + uuid.uuid4().hex[:8] - client.create_pool(name) + client.create_table(name) try: client.load(name, b'hello\nworld', mime_type='application/x-line') assert list(client.query(f'from {name}', safe=False)) == [] finally: - client.delete_pool(name) + client.delete_table(name) def test_empty_record_returns_no_results(client): @@ -292,25 +292,25 @@ def test_empty_record_returns_no_results(client): # empty stream rather than an error, so the client silently returns no # results. See the README for details. name = 'test_empty_' + uuid.uuid4().hex[:8] - client.create_pool(name) + client.create_table(name) try: client.load(name, b'{}', mime_type='application/x-sup') assert list(client.query(f'from {name}')) == [] finally: - client.delete_pool(name) + client.delete_table(name) -def test_duplicate_pool_raises_request_error(client): +def test_duplicate_table_raises_request_error(client): name = 'test_dup_' + uuid.uuid4().hex[:8] - client.create_pool(name) + client.create_table(name) try: with pytest.raises(RequestError): - client.create_pool(name) + client.create_table(name) finally: - client.delete_pool(name) + client.delete_table(name) -def test_query_nonexistent_pool_raises_request_error(client): - name = 'test_nopool_' + uuid.uuid4().hex[:8] +def test_query_nonexistent_table_raises_request_error(client): + name = 'test_notable_' + uuid.uuid4().hex[:8] with pytest.raises(RequestError): list(client.query(f'from {name}'))