Skip to content

Commit db34479

Browse files
committed
Add PREMIS-related activities
Added activities to add objects, events, and agents to a PREMIS file.
1 parent b0b1966 commit db34479

11 files changed

Lines changed: 1270 additions & 6 deletions

File tree

ffvalidate/activity_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fmt/95,"PDF/A "
101101
{
102102
name: "Does nothing when no allowlist path configured",
103103
params: ffvalidate.Params{Path: validFormatsDir},
104-
wantLog: "INFO validate-file-formats: No allowlist path configured, skipping file format validation ActivityID 0 ActivityType validate-file-formats\n",
104+
wantLog: "V[1] Executing activity. ActivityID 0 ActivityType validate-file-formats\nINFO validate-file-formats: No allowlist path configured, skipping file format validation ActivityID 0 ActivityType validate-file-formats\n",
105105
},
106106
{
107107
name: "Errors when allowlist path doesn't exist",

go.mod

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
module github.com/artefactual-sdps/temporal-activities
22

3-
go 1.21.4
3+
go 1.22
4+
5+
toolchain go1.22.2
46

57
require (
68
github.com/artefactual-labs/bagit-gython v0.2.0
9+
github.com/beevik/etree v1.4.1
710
github.com/google/safeopen v0.0.0-20240125081138-66b54d5181c6
11+
github.com/google/uuid v1.6.0
812
github.com/mholt/archiver/v4 v4.0.0-alpha.8
913
github.com/nyudlts/go-bagit v0.3.0-alpha.0.20240515212815-8dab411c23af
1014
github.com/otiai10/copy v1.14.0
1115
github.com/richardlehane/siegfried v1.11.1
1216
github.com/tonglil/buflogr v1.1.1
13-
go.artefactual.dev/tools v0.14.0
17+
go.artefactual.dev/tools v0.17.0
1418
go.temporal.io/sdk v1.26.1
1519
gocloud.dev v0.39.0
1620
gotest.tools/v3 v3.5.1
@@ -31,7 +35,6 @@ require (
3135
github.com/golang/mock v1.6.0 // indirect
3236
github.com/golang/snappy v0.0.4 // indirect
3337
github.com/google/go-cmp v0.6.0 // indirect
34-
github.com/google/uuid v1.6.0 // indirect
3538
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
3639
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
3740
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudr
7373
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
7474
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
7575
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
76+
github.com/beevik/etree v1.4.1 h1:PmQJDDYahBGNKDcpdX8uPy1xRCwoCGVUiW669MEirVI=
77+
github.com/beevik/etree v1.4.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
7678
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
7779
github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=
7880
github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8=
@@ -279,8 +281,8 @@ github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o
279281
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
280282
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
281283
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
282-
go.artefactual.dev/tools v0.14.0 h1:ESLbemsnkdIPmYXtz0uZTcPqVnTUXIEZd9DSTRyTZqY=
283-
go.artefactual.dev/tools v0.14.0/go.mod h1:5RJ7ObocHZv/zQFYFv/zG9cW/UVRGPFywcJx/oQ+TG8=
284+
go.artefactual.dev/tools v0.17.0 h1:7X/qZYKyKT8RxVjBsksqvalQ8F4wcor6jcA0ewjc92M=
285+
go.artefactual.dev/tools v0.17.0/go.mod h1:lsu0JcKFEJanNdrf5/IFjjzxul4pazG1dDHnLX9Nkvs=
284286
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
285287
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
286288
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=

premis/agent/activity.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package premis_agent
2+
3+
import (
4+
"context"
5+
6+
"github.com/artefactual-sdps/temporal-activities/premis"
7+
)
8+
9+
const Name = "add-premis-agent"
10+
11+
type (
12+
Params struct {
13+
PREMISFilePath string
14+
Agent premis.Agent
15+
}
16+
17+
Result struct{}
18+
19+
Activity struct{}
20+
)
21+
22+
func New() *Activity {
23+
return &Activity{}
24+
}
25+
26+
func (a *Activity) Execute(
27+
ctx context.Context,
28+
params *Params,
29+
) (*Result, error) {
30+
doc, err := premis.ParseOrInitialize(params.PREMISFilePath)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
err = premis.AppendAgentXML(doc, params.Agent)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
err = premis.WriteIndentedToFile(doc, params.PREMISFilePath)
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
return &Result{}, nil
46+
}

premis/agent/activity_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package premis_agent_test
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
temporalsdk_activity "go.temporal.io/sdk/activity"
8+
temporalsdk_testsuite "go.temporal.io/sdk/testsuite"
9+
"gotest.tools/v3/assert"
10+
"gotest.tools/v3/fs"
11+
12+
"github.com/artefactual-sdps/temporal-activities/premis"
13+
premis_agent "github.com/artefactual-sdps/temporal-activities/premis/agent"
14+
)
15+
16+
const expectedPREMISWithAgent = `<?xml version="1.0" encoding="UTF-8"?>
17+
<premis:premis xmlns:premis="http://www.loc.gov/premis/v3" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/premis/v3 https://www.loc.gov/standards/premis/premis.xsd" version="3.0">
18+
<premis:agent>
19+
<premis:agentIdentifier>
20+
<premis:agentIdentifierType valueURI="http://id.loc.gov/vocabulary/identifiers/local">url</premis:agentIdentifierType>
21+
<premis:agentIdentifierValue>https://github.com/artefactual-sdps/preprocessing-sfa</premis:agentIdentifierValue>
22+
</premis:agentIdentifier>
23+
<premis:agentName>Enduro</premis:agentName>
24+
<premis:agentType>software</premis:agentType>
25+
</premis:agent>
26+
</premis:premis>
27+
`
28+
29+
func TestAddPREMISAgent(t *testing.T) {
30+
t.Parallel()
31+
32+
// Transfer that's been deleted (for execution expected to fail).
33+
transferDeleted := fs.NewDir(t, "")
34+
PREMISFilePathNonExistent := transferDeleted.Join("metadata", "premis.xml")
35+
transferDeleted.Remove()
36+
37+
tests := []struct {
38+
name string
39+
params premis_agent.Params
40+
result premis_agent.Result
41+
wantErr string
42+
wantPREMIS string
43+
}{
44+
{
45+
name: "Add PREMIS agent for normal content",
46+
params: premis_agent.Params{
47+
PREMISFilePath: fs.NewDir(t, "",
48+
fs.WithFile("something.txt", "1234567899"),
49+
fs.WithDir("metadata"),
50+
).Join("metadata", "premis.xml"),
51+
Agent: premis.AgentDefault(),
52+
},
53+
result: premis_agent.Result{},
54+
wantPREMIS: expectedPREMISWithAgent,
55+
},
56+
{
57+
name: "Add PREMIS agent for no content",
58+
params: premis_agent.Params{
59+
PREMISFilePath: fs.NewDir(t, "",
60+
fs.WithDir("metadata"),
61+
).Join("metadata", "premis.xml"),
62+
Agent: premis.AgentDefault(),
63+
},
64+
result: premis_agent.Result{},
65+
wantPREMIS: expectedPREMISWithAgent,
66+
},
67+
{
68+
name: "Add PREMIS agent for bad path",
69+
params: premis_agent.Params{
70+
PREMISFilePath: PREMISFilePathNonExistent,
71+
Agent: premis.AgentDefault(),
72+
},
73+
result: premis_agent.Result{},
74+
wantErr: "no such file or directory",
75+
},
76+
}
77+
for _, tt := range tests {
78+
t.Run(tt.name, func(t *testing.T) {
79+
t.Parallel()
80+
81+
ts := &temporalsdk_testsuite.WorkflowTestSuite{}
82+
env := ts.NewTestActivityEnvironment()
83+
env.RegisterActivityWithOptions(
84+
premis_agent.New().Execute,
85+
temporalsdk_activity.RegisterOptions{Name: premis_agent.Name},
86+
)
87+
88+
var res premis_agent.Result
89+
future, err := env.ExecuteActivity(premis_agent.Name, tt.params)
90+
91+
if tt.wantErr != "" {
92+
if err == nil {
93+
t.Errorf("error is nil, expecting: %q", tt.wantErr)
94+
} else {
95+
assert.ErrorContains(t, err, tt.wantErr)
96+
}
97+
98+
return
99+
}
100+
101+
assert.NilError(t, err)
102+
103+
future.Get(&res)
104+
assert.DeepEqual(t, res, tt.result)
105+
106+
// Compare PREMIS output to what's expected.
107+
if tt.wantPREMIS != "" {
108+
b, err := os.ReadFile(tt.params.PREMISFilePath)
109+
assert.NilError(t, err)
110+
assert.Equal(t, string(b), tt.wantPREMIS)
111+
}
112+
})
113+
}
114+
}

premis/event/activity.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package premis_event
2+
3+
import (
4+
"context"
5+
6+
"github.com/artefactual-sdps/temporal-activities/premis"
7+
)
8+
9+
const Name = "add-premis-event"
10+
11+
type (
12+
Params struct {
13+
PREMISFilePath string
14+
Agent premis.Agent
15+
Summary premis.EventSummary
16+
}
17+
18+
Result struct{}
19+
20+
Activity struct{}
21+
)
22+
23+
func New() *Activity {
24+
return &Activity{}
25+
}
26+
27+
func (a *Activity) Execute(
28+
ctx context.Context,
29+
params *Params,
30+
) (*Result, error) {
31+
doc, err := premis.ParseOrInitialize(params.PREMISFilePath)
32+
if err != nil {
33+
return nil, err
34+
}
35+
36+
err = premis.AppendEventXMLForEachObject(doc, params.Summary, params.Agent)
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
err = premis.WriteIndentedToFile(doc, params.PREMISFilePath)
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
return &Result{}, nil
47+
}

0 commit comments

Comments
 (0)