Merged in feature/mutable-metadata1 (pull request #221)
M1, M2 and M3 complete * M1, M2 and M3 complete * review changes * docs * docs
This commit is contained in:
@@ -82,15 +82,17 @@ func (s *Service) GetEnriched(ctx context.Context, id uuid.UUID, includeTextReco
|
||||
}
|
||||
|
||||
result := &DocumentEnriched{
|
||||
ID: docExternal.ID,
|
||||
ClientID: docExternal.Clientid,
|
||||
Hash: docExternal.Hash,
|
||||
HasTextRecord: hasTextRecord,
|
||||
FolderID: docEnriched.Folderid,
|
||||
Filename: docEnriched.Filename,
|
||||
OriginalPath: docEnriched.Originalpath,
|
||||
Labels: labels,
|
||||
FileSizeBytes: docEnriched.FileSizeBytes,
|
||||
ID: docExternal.ID,
|
||||
ClientID: docExternal.Clientid,
|
||||
Hash: docExternal.Hash,
|
||||
HasTextRecord: hasTextRecord,
|
||||
FolderID: docEnriched.Folderid,
|
||||
Filename: docEnriched.Filename,
|
||||
OriginalPath: docEnriched.Originalpath,
|
||||
Labels: labels,
|
||||
FileSizeBytes: docEnriched.FileSizeBytes,
|
||||
CustomSchemaID: docEnriched.CustomSchemaID,
|
||||
HasCustomMetadata: docEnriched.HasCustomMetadata,
|
||||
}
|
||||
|
||||
// 5. Optionally fetch full text record if requested and exists
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package document_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"queryorchestration/internal/database/repository"
|
||||
@@ -210,3 +211,110 @@ func TestGetEnriched(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
// TestGetEnriched_CustomSchemaFields covers Milestone 2.3a: the
|
||||
// DocumentEnriched struct surfaces CustomSchemaID and HasCustomMetadata
|
||||
// populated from the extended GetDocumentEnriched query in a single
|
||||
// round-trip. Three fixture shapes exercise every branch of the two new
|
||||
// fields.
|
||||
func TestGetEnriched_CustomSchemaFields(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
cfg := &TestConfig{}
|
||||
test.CreateDB(t, cfg)
|
||||
svc := document.New(cfg)
|
||||
|
||||
clientID := "custom-schema-enriched"
|
||||
// testcontainer pg instances are reused across runs; wipe the client's
|
||||
// rows before re-seeding so the test is idempotent.
|
||||
resetClientForEnrichedTest(t, ctx, cfg, clientID)
|
||||
require.NoError(t, cfg.GetDBQueries().CreateClient(ctx, &repository.CreateClientParams{
|
||||
Clientid: clientID,
|
||||
Name: "Custom Schema Enriched",
|
||||
}))
|
||||
|
||||
// Seed one active schema for this client via direct SQL (avoids a
|
||||
// hard dependency on the customschema package in the document tests).
|
||||
var schemaID uuid.UUID
|
||||
require.NoError(t, cfg.GetDBPool().QueryRow(ctx, `
|
||||
INSERT INTO client_metadata_schemas (client_id, name, schema_def, version, created_by)
|
||||
VALUES ($1, $2, $3::jsonb, 1, 'tester')
|
||||
RETURNING id
|
||||
`, clientID, "enriched-fields", `{"type":"object","additionalProperties":false}`).Scan(&schemaID))
|
||||
|
||||
t.Run("document with no schema bound", func(t *testing.T) {
|
||||
docID, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
|
||||
Clientid: clientID,
|
||||
Hash: "custom-no-schema",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := svc.GetEnriched(ctx, docID, false)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, result.CustomSchemaID, "unbound document must have CustomSchemaID = nil")
|
||||
assert.False(t, result.HasCustomMetadata, "unbound document must have HasCustomMetadata = false")
|
||||
})
|
||||
|
||||
t.Run("document with schema bound but no metadata", func(t *testing.T) {
|
||||
docID, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
|
||||
Clientid: clientID,
|
||||
Hash: "custom-schema-no-metadata",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = cfg.GetDBPool().Exec(ctx,
|
||||
`UPDATE documents SET custom_schema_id = $1 WHERE id = $2`, schemaID, docID)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := svc.GetEnriched(ctx, docID, false)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result.CustomSchemaID, "bound document must carry CustomSchemaID")
|
||||
assert.Equal(t, schemaID, *result.CustomSchemaID)
|
||||
assert.False(t, result.HasCustomMetadata, "bound document with no metadata must be false")
|
||||
})
|
||||
|
||||
t.Run("document with schema and at least one metadata row", func(t *testing.T) {
|
||||
docID, err := cfg.GetDBQueries().CreateDocument(ctx, &repository.CreateDocumentParams{
|
||||
Clientid: clientID,
|
||||
Hash: "custom-schema-with-metadata",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = cfg.GetDBPool().Exec(ctx,
|
||||
`UPDATE documents SET custom_schema_id = $1 WHERE id = $2`, schemaID, docID)
|
||||
require.NoError(t, err)
|
||||
_, err = cfg.GetDBPool().Exec(ctx, `
|
||||
INSERT INTO document_custom_metadata (document_id, metadata, version, created_by)
|
||||
VALUES ($1, '{"k":"v"}'::jsonb, 1, 'tester')
|
||||
`, docID)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := svc.GetEnriched(ctx, docID, false)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result.CustomSchemaID)
|
||||
assert.Equal(t, schemaID, *result.CustomSchemaID)
|
||||
assert.True(t, result.HasCustomMetadata, "document with metadata row must be true")
|
||||
})
|
||||
}
|
||||
|
||||
// resetClientForEnrichedTest wipes every row owned by clientID in the order
|
||||
// required by the FK graph so the test starts clean even when the
|
||||
// testcontainer Postgres volume is reused across runs. Mirrors the pattern
|
||||
// from internal/database/repository/milestone2_migrations_test.go.
|
||||
func resetClientForEnrichedTest(t testing.TB, ctx context.Context, cfg serviceconfig.ConfigProvider, clientID string) {
|
||||
t.Helper()
|
||||
pool := cfg.GetDBPool()
|
||||
stmts := []string{
|
||||
`DELETE FROM document_custom_metadata
|
||||
WHERE document_id IN (SELECT id FROM documents WHERE clientId = $1)`,
|
||||
`UPDATE documents SET custom_schema_id = NULL WHERE clientId = $1`,
|
||||
`DELETE FROM client_metadata_schemas WHERE client_id = $1`,
|
||||
`DELETE FROM documentEntries
|
||||
WHERE documentId IN (SELECT id FROM documents WHERE clientId = $1)`,
|
||||
`DELETE FROM documents WHERE clientId = $1`,
|
||||
`DELETE FROM folders WHERE clientId = $1`,
|
||||
`DELETE FROM clientCanSync WHERE clientId = $1`,
|
||||
`DELETE FROM clients WHERE clientId = $1`,
|
||||
}
|
||||
for _, stmt := range stmts {
|
||||
_, err := pool.Exec(ctx, stmt, clientID)
|
||||
require.NoErrorf(t, err, "reset stmt failed: %s", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,17 +26,26 @@ type DocumentExternal struct {
|
||||
// DocumentEnriched contains full document details including metadata, labels, and optionally text record.
|
||||
// Used by GET /document/{id} endpoint.
|
||||
// Note: Query functionality (Fields) has been removed. See remove_query_plan.md for details.
|
||||
//
|
||||
// Milestone 2.3a adds two fields for the mutable-metadata feature:
|
||||
// - CustomSchemaID is the bound schema id (nil when the document has no schema).
|
||||
// - HasCustomMetadata is true iff document_custom_metadata has >=1 row for the document.
|
||||
//
|
||||
// Both values are populated from the extended GetDocumentEnriched query in
|
||||
// one round-trip; no follow-up DB call is made.
|
||||
type DocumentEnriched struct {
|
||||
ID uuid.UUID
|
||||
ClientID string
|
||||
Hash string
|
||||
HasTextRecord bool
|
||||
FolderID *uuid.UUID
|
||||
Filename *string
|
||||
OriginalPath *string
|
||||
Labels []LabelRecord
|
||||
TextRecord *TextRecord // Only populated when includeTextRecord=true
|
||||
FileSizeBytes *int64 // File size in bytes (nil for legacy documents)
|
||||
ID uuid.UUID
|
||||
ClientID string
|
||||
Hash string
|
||||
HasTextRecord bool
|
||||
FolderID *uuid.UUID
|
||||
Filename *string
|
||||
OriginalPath *string
|
||||
Labels []LabelRecord
|
||||
TextRecord *TextRecord // Only populated when includeTextRecord=true
|
||||
FileSizeBytes *int64 // File size in bytes (nil for legacy documents)
|
||||
CustomSchemaID *uuid.UUID // Bound schema id (nil = no schema)
|
||||
HasCustomMetadata bool // True iff any document_custom_metadata row exists
|
||||
}
|
||||
|
||||
// LabelRecord represents a label applied to a document.
|
||||
|
||||
Reference in New Issue
Block a user