diff --git a/artefactregistry.go b/artifactregistry.go similarity index 87% rename from artefactregistry.go rename to artifactregistry.go index 7b62d6d..7c028ec 100644 --- a/artefactregistry.go +++ b/artifactregistry.go @@ -11,7 +11,7 @@ import ( var _ ArtifactRegistry = (*InMemoryArtifactRegistry)(nil) type InMemoryArtifactRegistry struct { - ArtifactMap map[string][]string + ArtifactMap map[string][]ArtifactID mu sync.RWMutex // Mutex to protect concurrent access } @@ -19,32 +19,32 @@ func (r *InMemoryArtifactRegistry) Load() error { return nil } func (r *InMemoryArtifactRegistry) Save() error { return nil } -func (r *InMemoryArtifactRegistry) AddArtifact(processorName string, artifactName string) { +func (r *InMemoryArtifactRegistry) AddArtifact(processorName string, artifactID ArtifactID) { r.mu.Lock() defer r.mu.Unlock() if r.ArtifactMap == nil { - r.ArtifactMap = map[string][]string{} + r.ArtifactMap = map[string][]ArtifactID{} } if _, ok := r.ArtifactMap[processorName]; !ok { - r.ArtifactMap[processorName] = make([]string, 0) + r.ArtifactMap[processorName] = make([]ArtifactID, 0) } - r.ArtifactMap[processorName] = append(r.ArtifactMap[processorName], artifactName) + r.ArtifactMap[processorName] = append(r.ArtifactMap[processorName], artifactID) } -func (r *InMemoryArtifactRegistry) RemoveArtifact(processorName string, artifactName string) { +func (r *InMemoryArtifactRegistry) RemoveArtifact(processorName string, artifactID ArtifactID) { r.mu.Lock() defer r.mu.Unlock() if _, ok := r.ArtifactMap[processorName]; !ok { return } if r.ArtifactMap == nil { - r.ArtifactMap = map[string][]string{} + r.ArtifactMap = map[string][]ArtifactID{} } - var artifacts []string + var artifacts []ArtifactID for _, file := range r.ArtifactMap[processorName] { - if file != artifactName { + if file != artifactID { artifacts = append(artifacts, file) } } @@ -52,9 +52,9 @@ func (r *InMemoryArtifactRegistry) RemoveArtifact(processorName string, artifact r.ArtifactMap[processorName] = artifacts } -func (r *InMemoryArtifactRegistry) Artifacts(processorName string) []string { +func (r *InMemoryArtifactRegistry) Artifacts(processorName string) []ArtifactID { if r.ArtifactMap == nil { - r.ArtifactMap = map[string][]string{} + r.ArtifactMap = map[string][]ArtifactID{} } values, ok := r.ArtifactMap[processorName] @@ -67,6 +67,8 @@ func (r *InMemoryArtifactRegistry) Artifacts(processorName string) []string { // JSONArtifactRegistry implementation of a ArtifactRegistry that is saved as a JSON file. type JSONArtifactRegistry struct { + UseAbsolutePaths bool `json:"-"` + GeneratedAt time.Time `json:"generatedAt"` ArtifactMap map[string]*JSONArtifactRegistryProcessor `json:"files"` FilePath string `json:"-"` @@ -76,7 +78,7 @@ type JSONArtifactRegistry struct { } type JSONArtifactRegistryProcessor struct { - Artifacts []string `json:"files"` + Artifacts []ArtifactID `json:"files"` } // NewJSONArtifactRegistry returns a new artifact file registry. @@ -134,7 +136,7 @@ func (r *JSONArtifactRegistry) Save() error { return nil } -func (r *JSONArtifactRegistry) AddArtifact(processorName string, artifactName string) { +func (r *JSONArtifactRegistry) AddArtifact(processorName string, artifactID ArtifactID) { r.mu.Lock() defer r.mu.Unlock() @@ -145,10 +147,10 @@ func (r *JSONArtifactRegistry) AddArtifact(processorName string, artifactName st if _, ok := r.ArtifactMap[processorName]; !ok { r.ArtifactMap[processorName] = &JSONArtifactRegistryProcessor{} } - r.ArtifactMap[processorName].Artifacts = append(r.ArtifactMap[processorName].Artifacts, artifactName) + r.ArtifactMap[processorName].Artifacts = append(r.ArtifactMap[processorName].Artifacts, artifactID) } -func (r *JSONArtifactRegistry) RemoveArtifact(processorName string, artifactName string) { +func (r *JSONArtifactRegistry) RemoveArtifact(processorName string, artifactID ArtifactID) { r.mu.Lock() defer r.mu.Unlock() @@ -156,9 +158,9 @@ func (r *JSONArtifactRegistry) RemoveArtifact(processorName string, artifactName return } - var files []string + var files []ArtifactID for _, file := range r.ArtifactMap[processorName].Artifacts { - if file != artifactName { + if file != artifactID { files = append(files, file) } } @@ -166,7 +168,7 @@ func (r *JSONArtifactRegistry) RemoveArtifact(processorName string, artifactName r.ArtifactMap[processorName].Artifacts = files } -func (r *JSONArtifactRegistry) Artifacts(processorName string) []string { +func (r *JSONArtifactRegistry) Artifacts(processorName string) []ArtifactID { r.mu.RLock() defer r.mu.RUnlock() diff --git a/artefactregistry_test.go b/artifactregistry_test.go similarity index 86% rename from artefactregistry_test.go rename to artifactregistry_test.go index 0cdf581..b952073 100644 --- a/artefactregistry_test.go +++ b/artifactregistry_test.go @@ -32,7 +32,7 @@ func TestJSONArtifactRegistry_Load(t *testing.T) { expectedError: nil, expectedValue: &JSONArtifactRegistry{ GeneratedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), - ArtifactMap: map[string]*JSONArtifactRegistryProcessor{"processor1": {Artifacts: []string{"file1.txt"}}}, + ArtifactMap: map[string]*JSONArtifactRegistryProcessor{"processor1": {Artifacts: []ArtifactID{"file1.txt"}}}, }, }, }, @@ -106,9 +106,10 @@ func TestJSONArtifactRegistry_Save(t *testing.T) { { name: "Successful Save", given: &JSONArtifactRegistry{ + UseAbsolutePaths: false, ArtifactMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt"}, + Artifacts: []ArtifactID{"file1.txt"}, }, }, }, @@ -168,17 +169,17 @@ func TestJSONArtifactRegistry_AddArtifact(t *testing.T) { name string initialMap map[string]*JSONArtifactRegistryProcessor processorName string - artifactName string + artifactID ArtifactID expectedMap map[string]*JSONArtifactRegistryProcessor }{ { name: "Add New Artifact", initialMap: map[string]*JSONArtifactRegistryProcessor{}, processorName: "processor1", - artifactName: "file1.txt", + artifactID: "file1.txt", expectedMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt"}, + Artifacts: []ArtifactID{"file1.txt"}, }, }, }, @@ -186,14 +187,14 @@ func TestJSONArtifactRegistry_AddArtifact(t *testing.T) { name: "Add Artifact to Existing Processor", initialMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file2.txt"}, + Artifacts: []ArtifactID{"file2.txt"}, }, }, processorName: "processor1", - artifactName: "file1.txt", + artifactID: "file1.txt", expectedMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file2.txt", "file1.txt"}, + Artifacts: []ArtifactID{"file2.txt", "file1.txt"}, }, }, }, @@ -206,7 +207,7 @@ func TestJSONArtifactRegistry_AddArtifact(t *testing.T) { } // Act - registry.AddArtifact(tt.processorName, tt.artifactName) + registry.AddArtifact(tt.processorName, tt.artifactID) // Assert assert.Equal(t, tt.expectedMap, registry.ArtifactMap) @@ -219,21 +220,21 @@ func TestJSONArtifactRegistry_RemoveArtifact(t *testing.T) { name string initialMap map[string]*JSONArtifactRegistryProcessor processorName string - artifactName string + artifactID ArtifactID expectedMap map[string]*JSONArtifactRegistryProcessor }{ { name: "Remove Existing Artifact", initialMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt", "file2.txt"}, + Artifacts: []ArtifactID{"file1.txt", "file2.txt"}, }, }, processorName: "processor1", - artifactName: "file1.txt", + artifactID: "file1.txt", expectedMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file2.txt"}, + Artifacts: []ArtifactID{"file2.txt"}, }, }, }, @@ -241,14 +242,14 @@ func TestJSONArtifactRegistry_RemoveArtifact(t *testing.T) { name: "Remove Non-Existing Artifact", initialMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt"}, + Artifacts: []ArtifactID{"file1.txt"}, }, }, processorName: "processor1", - artifactName: "file2.txt", + artifactID: "file2.txt", expectedMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt"}, + Artifacts: []ArtifactID{"file1.txt"}, }, }, }, @@ -256,14 +257,14 @@ func TestJSONArtifactRegistry_RemoveArtifact(t *testing.T) { name: "Remove From Non-Existing Processor", initialMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt"}, + Artifacts: []ArtifactID{"file1.txt"}, }, }, processorName: "processor2", - artifactName: "file1.txt", + artifactID: "file1.txt", expectedMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt"}, + Artifacts: []ArtifactID{"file1.txt"}, }, }, }, @@ -276,7 +277,7 @@ func TestJSONArtifactRegistry_RemoveArtifact(t *testing.T) { } // Act - registry.RemoveArtifact(tt.processorName, tt.artifactName) + registry.RemoveArtifact(tt.processorName, tt.artifactID) // Assert assert.Equal(t, tt.expectedMap, registry.ArtifactMap) @@ -289,23 +290,23 @@ func TestJSONArtifactRegistry_Artifacts(t *testing.T) { name string initialMap map[string]*JSONArtifactRegistryProcessor processorName string - expectedArtifacts []string + expectedArtifacts []ArtifactID }{ { name: "Get Artifacts for Existing Processor", initialMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt", "file2.txt"}, + Artifacts: []ArtifactID{"file1.txt", "file2.txt"}, }, }, processorName: "processor1", - expectedArtifacts: []string{"file1.txt", "file2.txt"}, + expectedArtifacts: []ArtifactID{"file1.txt", "file2.txt"}, }, { name: "Get Artifacts for Non-Existing Processor", initialMap: map[string]*JSONArtifactRegistryProcessor{ "processor1": { - Artifacts: []string{"file1.txt"}, + Artifacts: []ArtifactID{"file1.txt"}, }, }, processorName: "processor2", diff --git a/depresolve.go b/depresolve.go index 665a4b8..5b45c65 100644 --- a/depresolve.go +++ b/depresolve.go @@ -20,12 +20,16 @@ import ( "strings" ) -const ResolvedDependencyContextArtifactName = "_resolved_dependencies" +const ResolvedDependenciesArtifactID = "_resolved_dependencies" // ResolvedDependencies represents an ordered list of Specification that should be processed in that specific order to avoid // unresolved types. type ResolvedDependencies SpecificationGroup +func (r ResolvedDependencies) ID() ArtifactID { + return ResolvedDependenciesArtifactID +} + type DependencyProvider interface { Supports(s Specification) bool Provide(s Specification) []SpecificationName @@ -68,16 +72,11 @@ func (p DependencyResolutionProcessor) Process(ctx ProcessingContext) ([]Artifac } ctx.Logger.Success("Dependencies resolved successfully.") - return []Artifact{ - { - Name: ResolvedDependencyContextArtifactName, - Value: deps, - }, - }, nil + return []Artifact{deps}, nil } func GetResolvedDependenciesFromContext(ctx ProcessingContext) ResolvedDependencies { - return GetContextArtifact[ResolvedDependencies](ctx, ResolvedDependencyContextArtifactName) + return GetContextArtifact[ResolvedDependencies](ctx, ResolvedDependenciesArtifactID) } type dependencySet map[SpecificationName]struct{} diff --git a/depresolve_test.go b/depresolve_test.go index b4e4ed4..6895dd5 100644 --- a/depresolve_test.go +++ b/depresolve_test.go @@ -35,41 +35,6 @@ func TestDependencyResolutionProcessor_Process(t *testing.T) { then ResolvedDependencies expectedError error }{ - { - name: "GIVEN no matching providers THEN returns resolved dependencies in the same order", - given: args{ - providers: []DependencyProvider{ - &MockDependencyProvider{ - supportFunc: func(s Specification) bool { - return false - }, - provideFunc: func(s Specification) []SpecificationName { - return nil - }, - }, - &MockDependencyProvider{ - supportFunc: func(s Specification) bool { - return false - }, - provideFunc: func(s Specification) []SpecificationName { - if s.Name() == "spec1" { - return []SpecificationName{"spec2"} - } - return nil - }, - }, - }, - specifications: SpecificationGroup{ - spec1, - spec2, - }, - }, - then: ResolvedDependencies{ - spec1, - spec2, - }, - expectedError: nil, - }, { name: "GIVEN no providers THEN returns nil", given: args{ @@ -182,7 +147,7 @@ func TestDependencyResolutionProcessor_Process(t *testing.T) { return } - artifact := ctx.Artifact(ResolvedDependencyContextArtifactName).Value + artifact := ctx.Artifact(ResolvedDependenciesArtifactID) graph := artifact.(ResolvedDependencies) require.NoError(t, err) @@ -191,6 +156,14 @@ func TestDependencyResolutionProcessor_Process(t *testing.T) { } } +type mockArtifact struct { + id ArtifactID +} + +func (m mockArtifact) ID() ArtifactID { + return m.id +} + func TestGetResolvedDependenciesFromContext(t *testing.T) { tests := []struct { name string @@ -201,11 +174,8 @@ func TestGetResolvedDependenciesFromContext(t *testing.T) { name: "GIVEN a context with resolved dependencies THEN return resolved dependencies", given: ProcessingContext{ Artifacts: []Artifact{ - { - Name: ResolvedDependencyContextArtifactName, - Value: ResolvedDependencies{ - NewGenericSpecification("name", "type", Source{}), - }, + ResolvedDependencies{ + NewGenericSpecification("name", "type", Source{}), }, }, }, @@ -217,10 +187,7 @@ func TestGetResolvedDependenciesFromContext(t *testing.T) { name: "GIVEN a context with resolved dependencies with wrong type THEN return nil", given: ProcessingContext{ Artifacts: []Artifact{ - { - Name: ResolvedDependencyContextArtifactName, - Value: "this is not the right value", - }, + mockArtifact{id: ResolvedDependenciesArtifactID}, }, }, want: nil, diff --git a/filesystem_test.go b/filesystem_test.go index 0cce15e..e7ace69 100644 --- a/filesystem_test.go +++ b/filesystem_test.go @@ -7,6 +7,7 @@ import ( "os" "runtime" "strings" + "sync" "testing" ) @@ -34,13 +35,14 @@ func (m mockFileInfo) Info() (fs.FileInfo, error) { return m, nil } -func (m mockFileInfo) Name() string { return m.name } +func (m mockFileInfo) ID() string { return m.name } func (m mockFileInfo) Size() int64 { return m.size } func (m mockFileInfo) Mode() os.FileMode { return m.mode } func (m mockFileInfo) IsDir() bool { return m.isDir } func (m mockFileInfo) Sys() interface{} { return nil } type mockFileSystem struct { + mu sync.RWMutex files map[string][]byte dirs map[string]bool @@ -58,6 +60,8 @@ func (m *mockFileSystem) Rel(basePath, targetPath string) (string, error) { } func (m *mockFileSystem) WriteFile(filePath string, data []byte, _ fs.FileMode) error { + m.mu.Lock() + defer m.mu.Unlock() if m.writeFileErr != nil { return m.writeFileErr } @@ -72,6 +76,9 @@ func (m *mockFileSystem) WriteFile(filePath string, data []byte, _ fs.FileMode) } func (m *mockFileSystem) Mkdir(dirPath string, _ fs.FileMode) error { + m.mu.Lock() + defer m.mu.Unlock() + if m.mkdirErr != nil { return m.mkdirErr } @@ -82,6 +89,9 @@ func (m *mockFileSystem) Mkdir(dirPath string, _ fs.FileMode) error { } func (m *mockFileSystem) Remove(path string) error { + m.mu.Lock() + defer m.mu.Unlock() + if m.rmErr != nil { return m.rmErr } @@ -93,6 +103,9 @@ func (m *mockFileSystem) Remove(path string) error { } func (m *mockFileSystem) Abs(location string) (string, error) { + m.mu.RLock() + defer m.mu.RUnlock() + if m.absErr != nil { return "", m.absErr } @@ -109,10 +122,13 @@ func (m *mockFileSystem) Abs(location string) (string, error) { if absPath, exists := absPaths[location]; exists && absPath { return location, nil } - return "", nil + return location, nil } func (m *mockFileSystem) StatPath(location string) (os.FileInfo, error) { + m.mu.RLock() + defer m.mu.RUnlock() + if m.statErr != nil { return nil, m.statErr } @@ -129,6 +145,9 @@ func (m *mockFileSystem) StatPath(location string) (os.FileInfo, error) { } func (m *mockFileSystem) WalkDir(dirPath string, f func(path string, d fs.DirEntry, err error) error) error { + m.mu.RLock() + defer m.mu.RUnlock() + if m.walkDirErr != nil { return m.walkDirErr } @@ -155,6 +174,9 @@ func (m *mockFileSystem) WalkDir(dirPath string, f func(path string, d fs.DirEnt } func (m *mockFileSystem) ReadFile(filePath string) ([]byte, error) { + m.mu.RLock() + defer m.mu.RUnlock() + if m.readFileErr != nil { return nil, m.readFileErr } diff --git a/linting.go b/linting.go index da74a7b..d92f8c8 100644 --- a/linting.go +++ b/linting.go @@ -7,7 +7,7 @@ import ( "unicode" ) -const LintingProcessingContextArtifactName = "_linting_processor_results" +const LinterResultArtifactID = "_linting_processor_results" // UndefinedSpecificationName constant used to test against undefined SpecificationName. const UndefinedSpecificationName SpecificationName = "" @@ -39,10 +39,7 @@ func (l LintingProcessor) Process(ctx ProcessingContext) (artifacts []Artifact, lr := linter.Lint(ctx.Specifications) - artifacts = append(artifacts, Artifact{ - Name: LintingProcessingContextArtifactName, - Value: lr, - }) + artifacts = append(artifacts, lr) if lr.HasWarnings() { for _, w := range lr.Warnings() { @@ -66,7 +63,7 @@ func (l LintingProcessor) Process(ctx ProcessingContext) (artifacts []Artifact, } func GetLintingResultsFromContext(ctx ProcessingContext) LinterResultSet { - return GetContextArtifact[LinterResultSet](ctx, LintingProcessingContextArtifactName) + return GetContextArtifact[LinterResultSet](ctx, LinterResultArtifactID) } type LinterResult struct { @@ -105,6 +102,10 @@ func (s LinterResultSet) Warnings() LinterResultSet { return warns } +func (s LinterResultSet) ID() ArtifactID { + return LinterResultArtifactID +} + // HasErrors returns if this result set has any result representing an error. func (s LinterResultSet) HasErrors() bool { return s.Errors().HasErrors() diff --git a/linting_test.go b/linting_test.go index 3a0f8c9..44dc565 100644 --- a/linting_test.go +++ b/linting_test.go @@ -481,10 +481,7 @@ func TestLintingProcessor_Process(t *testing.T) { }, }, then: []Artifact{ - { - Name: LintingProcessingContextArtifactName, - Value: LinterResultSet(nil), - }, + LinterResultSet(nil), }, expectedError: nil, }, @@ -502,10 +499,7 @@ func TestLintingProcessor_Process(t *testing.T) { }, }, then: []Artifact{ - { - Name: LintingProcessingContextArtifactName, - Value: LinterResultSet{{Severity: WarningSeverity, Message: "a warning"}}, - }, + LinterResultSet{{Severity: WarningSeverity, Message: "a warning"}}, }, }, { @@ -522,10 +516,7 @@ func TestLintingProcessor_Process(t *testing.T) { }, }, then: []Artifact{ - { - Name: LintingProcessingContextArtifactName, - Value: LinterResultSet{{Severity: ErrorSeverity, Message: assert.AnError.Error()}}, - }, + LinterResultSet{{Severity: ErrorSeverity, Message: assert.AnError.Error()}}, }, expectedError: assert.AnError, }, @@ -550,15 +541,12 @@ func TestLintingProcessor_Process(t *testing.T) { }, }, then: []Artifact{ - { - Name: LintingProcessingContextArtifactName, - Value: LinterResultSet{ - { - Severity: ErrorSeverity, Message: assert.AnError.Error(), - }, - { - Severity: WarningSeverity, Message: "a warning", - }, + LinterResultSet{ + { + Severity: ErrorSeverity, Message: assert.AnError.Error(), + }, + { + Severity: WarningSeverity, Message: "a warning", }, }, }, @@ -591,10 +579,7 @@ func TestGetLintingResultsFromContext(t *testing.T) { name: "GIVEN context with linting results THEN return linting results", given: ProcessingContext{ Artifacts: []Artifact{ - { - Name: LintingProcessingContextArtifactName, - Value: LinterResultSet{{Severity: WarningSeverity, Message: "a warning"}}, - }, + LinterResultSet{{Severity: WarningSeverity, Message: "a warning"}}, }, }, then: LinterResultSet{{Severity: WarningSeverity, Message: "a warning"}}, @@ -608,10 +593,7 @@ func TestGetLintingResultsFromContext(t *testing.T) { name: "GIVEN a context with wrong value for artifact name THEN return nil", given: ProcessingContext{ Artifacts: []Artifact{ - { - Name: LintingProcessingContextArtifactName, - Value: "this is not the right value", - }, + mockArtifact{LinterResultArtifactID}, }, }, then: LinterResultSet(nil), diff --git a/mkdirartfproc.go b/mkdirartfproc.go index 260bc3a..0f59a6a 100644 --- a/mkdirartfproc.go +++ b/mkdirartfproc.go @@ -15,6 +15,10 @@ type DirectoryArtifact struct { WriteMode WriteMode } +func (d DirectoryArtifact) ID() ArtifactID { + return ArtifactID(d.Path) +} + type MakeDirectoryArtifactsProcessor struct { FileSystem FileSystem } @@ -39,7 +43,7 @@ func (p MakeDirectoryArtifactsProcessor) Process(ctx ArtifactProcessingContext) return err } - dir, ok := artifact.Value.(DirectoryArtifact) + dir, ok := artifact.(DirectoryArtifact) if !ok { continue } @@ -48,7 +52,7 @@ func (p MakeDirectoryArtifactsProcessor) Process(ctx ArtifactProcessingContext) } wg.Add(1) - go func(ctx ArtifactProcessingContext, artifactName string, dir DirectoryArtifact) { + go func(ctx ArtifactProcessingContext, dir DirectoryArtifact) { defer wg.Done() dirPath, err := p.FileSystem.Abs(dir.Path) if err != nil { @@ -80,9 +84,9 @@ func (p MakeDirectoryArtifactsProcessor) Process(ctx ArtifactProcessingContext) } if dir.WriteMode != WriteOnceMode { - ctx.AddToRegistry(artifactName) + ctx.AddToRegistry(dir.ID()) } - }(ctx, artifact.Name, dir) + }(ctx, dir) } wg.Wait() @@ -103,9 +107,9 @@ func (p MakeDirectoryArtifactsProcessor) cleanRegistry(ctx ArtifactProcessingCon } wg.Add(1) - go func(ctx ArtifactProcessingContext, o string) { + go func(ctx ArtifactProcessingContext, o ArtifactID) { defer wg.Done() - if err := p.FileSystem.Remove(o); err != nil { + if err := p.FileSystem.Remove(string(o)); err != nil { if errors.Is(err, os.ErrNotExist) { return } diff --git a/mkdirartfproc_test.go b/mkdirartfproc_test.go index a66900c..f222493 100644 --- a/mkdirartfproc_test.go +++ b/mkdirartfproc_test.go @@ -21,14 +21,8 @@ func TestMakeDirectoryArtifactsProcessor_Process(t *testing.T) { dirs: make(map[string]bool), }, artifacts: []Artifact{ - { - Name: "dir1", - Value: DirectoryArtifact{Path: "/path/to/dir1", FileMode: 0755}, - }, - { - Name: "dir2", - Value: DirectoryArtifact{Path: "/path/to/dir2", FileMode: 0755}, - }, + DirectoryArtifact{Path: "/path/to/dir1", FileMode: 0755}, + DirectoryArtifact{Path: "/path/to/dir2", FileMode: 0755}, }, expectedDirs: []string{"/path/to/dir1", "/path/to/dir2"}, expectError: nil, @@ -39,14 +33,8 @@ func TestMakeDirectoryArtifactsProcessor_Process(t *testing.T) { dirs: make(map[string]bool), }, artifacts: []Artifact{ - { - Name: "dir1", - Value: DirectoryArtifact{Path: "/path/to/dir1", FileMode: 0755}, - }, - { - Name: "not_a_dir", - Value: "this is not a directory", - }, + DirectoryArtifact{Path: "/path/to/dir1", FileMode: 0755}, + mockArtifact{id: "not_a_dir"}, }, expectedDirs: []string{"/path/to/dir1"}, expectError: nil, @@ -58,10 +46,7 @@ func TestMakeDirectoryArtifactsProcessor_Process(t *testing.T) { mkdirErr: assert.AnError, }, artifacts: []Artifact{ - { - Name: "dir1", - Value: DirectoryArtifact{Path: "/path/to/dir1", FileMode: 0755}, - }, + DirectoryArtifact{Path: "/path/to/dir1", FileMode: 0755}, }, expectedDirs: []string{}, expectError: assert.AnError, @@ -74,10 +59,7 @@ func TestMakeDirectoryArtifactsProcessor_Process(t *testing.T) { }, }, artifacts: []Artifact{ - { - Name: "file1", - Value: DirectoryArtifact{Path: "/dir", WriteMode: WriteOnceMode}, - }, + DirectoryArtifact{Path: "/dir", WriteMode: WriteOnceMode}, }, expectedDirs: []string{}, expectError: nil, diff --git a/processing.go b/processing.go index 748f678..8771bc3 100644 --- a/processing.go +++ b/processing.go @@ -10,33 +10,32 @@ type ProcessingContext struct { } // Artifact returns the artifact associated with a given processor. -func (c ProcessingContext) Artifact(artifactName string) Artifact { +func (c ProcessingContext) Artifact(id ArtifactID) Artifact { for _, o := range c.Artifacts { - if o.Name == artifactName { + if o.ID() == id { return o } } - return Artifact{Name: artifactName, Value: nil} + return nil } +type ArtifactID string + // Artifact represents a result or output generated by a SpecificationProcessor. // An artifact is a unit of data or information produced as part of the processing workflow. // It can be a transient, in-memory object, or it might represent more permanent entities such as // files on disk, records in a database, deployment units, or other forms of data artifacts. -type Artifact struct { - // Name is a unique identifier of the artifact, which helps in distinguishing and referencing +type Artifact interface { + // ID is a unique identifier of the artifact, which helps in distinguishing and referencing // the artifact within a processing context. - Name string - - // Value is the actual content or data of the artifact. This can be any type, including but not - // limited to in-memory structures, file paths, database records, or serialized deployment units. - Value any + ID() ArtifactID } // SpecificationProcessor are services responsible for performing work using Specifications // and which can possibly generate artifacts. type SpecificationProcessor interface { - // Name returns the unique FilePath of this processor. + // Name returns the unique name of this processor. + // This name can appear in logs to report information about a given processor. Name() string // Process processes a group of specifications. @@ -60,18 +59,18 @@ type ArtifactRegistry interface { // error occurs, it should be returned to indicate the failure of the saving operation. Save() error - // AddArtifact registers an artifact name under a specific processor name. This method + // AddArtifact registers an ArtifactID under a specific processor name. This method // should ensure that the file path is associated with the given processor name // in the registry. - AddArtifact(processorName string, artifactName string) + AddArtifact(processorName string, artifactID ArtifactID) - // RemoveArtifact removes a given named artifact registration for a specific processor name. This + // RemoveArtifact removes a given ArtifactID artifact registration for a specific processor name. This // method should ensure that the file path is disassociated from the given // processor name in the registry. - RemoveArtifact(processorName string, artifactName string) + RemoveArtifact(processorName string, artifactID ArtifactID) // Artifacts returns the artifacts for a given processor. - Artifacts(processorName string) []string + Artifacts(processorName string) []ArtifactID } type NoopArtifactRegistry struct { @@ -85,11 +84,11 @@ func (n NoopArtifactRegistry) Save() error { return nil } -func (n NoopArtifactRegistry) AddArtifact(processorName string, artifactName string) {} +func (n NoopArtifactRegistry) AddArtifact(_ string, _ ArtifactID) {} -func (n NoopArtifactRegistry) RemoveArtifact(processorName string, artifactName string) {} +func (n NoopArtifactRegistry) RemoveArtifact(_ string, _ ArtifactID) {} -func (n NoopArtifactRegistry) Artifacts(processorName string) []string { +func (n NoopArtifactRegistry) Artifacts(_ string) []ArtifactID { return nil } @@ -103,15 +102,15 @@ type ArtifactProcessingContext struct { processorName string } -func (c *ArtifactProcessingContext) AddToRegistry(artifactName string) { - c.artifactRegistry.AddArtifact(c.processorName, artifactName) +func (c *ArtifactProcessingContext) AddToRegistry(artifactID ArtifactID) { + c.artifactRegistry.AddArtifact(c.processorName, artifactID) } -func (c *ArtifactProcessingContext) RemoveFromRegistry(artifactName string) { - c.artifactRegistry.RemoveArtifact(c.processorName, artifactName) +func (c *ArtifactProcessingContext) RemoveFromRegistry(artifactID ArtifactID) { + c.artifactRegistry.RemoveArtifact(c.processorName, artifactID) } -func (c *ArtifactProcessingContext) RegistryArtifacts() []string { +func (c *ArtifactProcessingContext) RegistryArtifacts() []ArtifactID { return c.artifactRegistry.Artifacts(c.processorName) } @@ -124,8 +123,8 @@ type ArtifactProcessor interface { Name() string } -func GetContextArtifact[T any](ctx ProcessingContext, name string) (v T) { - artifact := ctx.Artifact(name) - v, _ = artifact.Value.(T) +func GetContextArtifact[T Artifact](ctx ProcessingContext, id ArtifactID) T { + artifact := ctx.Artifact(id) + v, _ := artifact.(T) return v } diff --git a/processing_test.go b/processing_test.go index 42b788a..1f0e8c0 100644 --- a/processing_test.go +++ b/processing_test.go @@ -21,33 +21,33 @@ func (m *MockArtifactRegistry) Save() error { return args.Error(0) } -func (m *MockArtifactRegistry) AddArtifact(processorName string, artifactName string) { - m.Called(processorName, artifactName) +func (m *MockArtifactRegistry) AddArtifact(processorName string, artifactID ArtifactID) { + m.Called(processorName, artifactID) } -func (m *MockArtifactRegistry) RemoveArtifact(processorName string, artifactName string) { - m.Called(processorName, artifactName) +func (m *MockArtifactRegistry) RemoveArtifact(processorName string, artifactID ArtifactID) { + m.Called(processorName, artifactID) } -func (m *MockArtifactRegistry) Artifacts(processorName string) []string { +func (m *MockArtifactRegistry) Artifacts(processorName string) []ArtifactID { args := m.Called(processorName) - return args.Get(0).([]string) + return args.Get(0).([]ArtifactID) } func TestArtifactProcessingContext__AddToRegistry(t *testing.T) { // Arrange - mockRegistry := new(MockArtifactRegistry) + mockRegistry := &MockArtifactRegistry{} ctx := &ArtifactProcessingContext{ artifactRegistry: mockRegistry, processorName: "testProcessor", } - artifactName := "artifactFile.txt" + artifactID := ArtifactID("artifactFile.txt") - mockRegistry.On("AddArtifact", "testProcessor", artifactName).Return() + mockRegistry.On("AddArtifact", "testProcessor", artifactID).Return() // Act - ctx.AddToRegistry(artifactName) + ctx.AddToRegistry(artifactID) // Assert mockRegistry.AssertExpectations(t) @@ -61,12 +61,12 @@ func TestArtifactProcessingContext__RemoveFromRegistry(t *testing.T) { processorName: "testProcessor", } - artifactName := "artifactFile.txt" + artifactID := ArtifactID("artifactFile.txt") - mockRegistry.On("RemoveArtifact", "testProcessor", artifactName).Return() + mockRegistry.On("RemoveArtifact", "testProcessor", artifactID).Return() // Act - ctx.RemoveFromRegistry(artifactName) + ctx.RemoveFromRegistry(artifactID) // Assert mockRegistry.AssertExpectations(t) @@ -80,7 +80,7 @@ func TestArtifactProcessingContext__RegistryArtifacts(t *testing.T) { processorName: "testProcessor", } - expectedArtifacts := []string{"file1.txt", "file2.txt"} + expectedArtifacts := []ArtifactID{"file1.txt", "file2.txt"} mockRegistry.On("Artifacts", "testProcessor").Return(expectedArtifacts) diff --git a/specter.go b/specter.go index e4f628b..3f8a8fd 100644 --- a/specter.go +++ b/specter.go @@ -221,7 +221,7 @@ func (s Specter) ProcessSpecifications(ctx context.Context, specs []Specificatio s.Logger.Info(fmt.Sprintf("%d artifacts generated.", len(pctx.Artifacts))) for _, o := range pctx.Artifacts { - s.Logger.Info(fmt.Sprintf("-> %s", o.Name)) + s.Logger.Info(fmt.Sprintf("-> %s", o.ID())) } s.Logger.Success("Specifications processed successfully.") diff --git a/writefileartfproc.go b/writefileartfproc.go index 1f8ce2c..35d65e3 100644 --- a/writefileartfproc.go +++ b/writefileartfproc.go @@ -25,6 +25,10 @@ type FileArtifact struct { WriteMode WriteMode } +func (a FileArtifact) ID() ArtifactID { + return ArtifactID(a.Path) +} + // WriteFileArtifactProcessor is a processor responsible for writing Artifact referring to files. // To perform its work this processor looks at the processing context for any FileArtifact. type WriteFileArtifactProcessor struct { @@ -40,7 +44,7 @@ func (p WriteFileArtifactProcessor) Process(ctx ArtifactProcessingContext) error var files []FileArtifact for _, o := range ctx.Artifacts { - fo, ok := o.Value.(FileArtifact) + fo, ok := o.(FileArtifact) if !ok { continue } @@ -101,7 +105,7 @@ func (p WriteFileArtifactProcessor) Process(ctx ArtifactProcessingContext) error } if file.WriteMode != WriteOnceMode { - ctx.AddToRegistry(file.Path) + ctx.AddToRegistry(file.ID()) } }(ctx, file) @@ -121,9 +125,9 @@ func (p WriteFileArtifactProcessor) cleanRegistry(ctx ArtifactProcessingContext) var wg sync.WaitGroup for _, o := range ctx.RegistryArtifacts() { wg.Add(1) - go func(ctx ArtifactProcessingContext, o string) { + go func(ctx ArtifactProcessingContext, o ArtifactID) { defer wg.Done() - if err := p.FileSystem.Remove(o); err != nil { + if err := p.FileSystem.Remove(string(o)); err != nil { if errors.Is(err, os.ErrNotExist) { return } diff --git a/writefileartfproc_test.go b/writefileartfproc_test.go index 6ba18c1..70aefae 100644 --- a/writefileartfproc_test.go +++ b/writefileartfproc_test.go @@ -18,14 +18,8 @@ func TestWriteFileArtifactProcessor_Process(t *testing.T) { name: "GIVEN file artifacts THEN successful file creation", mockFS: &mockFileSystem{}, artifacts: []Artifact{ - { - Name: "file1", - Value: FileArtifact{Path: "/path/to/file1", Mode: 0755}, - }, - { - Name: "file2", - Value: FileArtifact{Path: "/path/to/file2", Mode: 0755}, - }, + FileArtifact{Path: "/path/to/file1", Mode: 0755}, + FileArtifact{Path: "/path/to/file2", Mode: 0755}, }, expectedFiles: []string{"/path/to/file1", "/path/to/file2"}, expectError: nil, @@ -34,14 +28,8 @@ func TestWriteFileArtifactProcessor_Process(t *testing.T) { name: "GIVEN non-file artifacts THEN skip and return no error", mockFS: &mockFileSystem{}, artifacts: []Artifact{ - { - Name: "file1", - Value: FileArtifact{Path: "/path/to/file1", Mode: 0755}, - }, - { - Name: "not_a_file", - Value: "this is not a file", - }, + FileArtifact{Path: "/path/to/file1", Mode: 0755}, + mockArtifact{}, }, expectedFiles: []string{"/path/to/file1"}, expectError: nil, @@ -52,10 +40,7 @@ func TestWriteFileArtifactProcessor_Process(t *testing.T) { writeFileErr: assert.AnError, }, artifacts: []Artifact{ - { - Name: "file1", - Value: FileArtifact{Path: "/path/to/file1", Mode: 0755}, - }, + FileArtifact{Path: "/path/to/file1", Mode: 0755}, }, expectedFiles: []string{}, expectError: assert.AnError, @@ -68,10 +53,7 @@ func TestWriteFileArtifactProcessor_Process(t *testing.T) { }, }, artifacts: []Artifact{ - { - Name: "file1", - Value: FileArtifact{Path: "/path/to/file1", Mode: 0755, WriteMode: WriteOnceMode}, - }, + FileArtifact{Path: "/path/to/file1", Mode: 0755, WriteMode: WriteOnceMode}, }, expectedFiles: []string{}, expectError: nil,