Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 28 additions & 8 deletions internal/api/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions internal/validate/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ func JobPayload(data []byte) (api.JobCreation, error) {
if err := json.Unmarshal(data, &payload); err != nil {
return api.JobCreation{}, clierr.Validation("job payload is not valid JSON: "+err.Error(), "Run nexrender schema createJob --json to inspect the request schema.")
}
if strings.TrimSpace(payload.Template.Id) == "" {
return api.JobCreation{}, clierr.Validation("template.id is required", "Select a template with nexrender template list --json.")
id := trimmed(payload.Template.Id)
src := trimmed(payload.Template.Src)
switch {
case id == "" && src == "":
return api.JobCreation{}, clierr.Validation("template.id or template.src is required", "Select an uploaded template with nexrender template list --json, or put a template URL in template.src.")
case id != "" && src != "":
return api.JobCreation{}, clierr.Validation("template.id and template.src cannot be used together", "Keep template.id for an uploaded template, or template.src for a template URL.")
case src != "" && trimmed(payload.Template.Name) == "":
return api.JobCreation{}, clierr.Validation("template.name is required with template.src", "Set it to the .aep or .mogrt file name, or to the .aep path inside a .zip.")
}
if payload.Preview != nil && *payload.Preview && payload.Settings != nil {
return api.JobCreation{}, clierr.Validation("preview and settings cannot be used together", "Remove settings for a preview job, or set preview to false.")
Expand All @@ -31,3 +38,10 @@ func JobPayload(data []byte) (api.JobCreation, error) {
}
return payload, nil
}

func trimmed(value *string) string {
if value == nil {
return ""
}
return strings.TrimSpace(*value)
}
23 changes: 23 additions & 0 deletions internal/validate/job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,26 @@ func TestJobPayloadRules(t *testing.T) {
t.Fatal("expected short codec to fail")
}
}

func TestJobPayloadTemplateSource(t *testing.T) {
cases := []struct {
name string
payload string
valid bool
}{
{"uploaded template", `{"template":{"id":"tpl","composition":"Main"}}`, true},
{"template URL", `{"template":{"src":"https://nx1-assets-eu.nexrender.com/example/Test%20AEP.zip","name":"LTExample.aep","composition":"Final 2"}}`, true},
{"neither", `{"template":{"composition":"Main"}}`, false},
{"both", `{"template":{"id":"tpl","src":"https://nx1-assets-eu.nexrender.com/example/Test%20AEP.zip","name":"LTExample.aep","composition":"Main"}}`, false},
{"src without name", `{"template":{"src":"https://nx1-assets-eu.nexrender.com/example/Test%20AEP.zip","composition":"Final 2"}}`, false},
}
for _, tc := range cases {
_, err := JobPayload([]byte(tc.payload))
if tc.valid && err != nil {
t.Errorf("%s: unexpected error %v", tc.name, err)
}
if !tc.valid && err == nil {
t.Errorf("%s: expected a validation error", tc.name)
}
}
}
13 changes: 7 additions & 6 deletions openapi/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -425,26 +425,27 @@
},
"JobTemplate": {
"type": "object",
"description": "Template configuration defining the After Effects project and composition to render",
"description": "Template configuration defining the After Effects project and composition to render. Provide exactly one of `id` (an uploaded template) or `src` (a template URL); sending both, or neither, is rejected.",
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Z0-9]{26}$",
"description": "Unique template identifier in ULID format (26 characters, uppercase alphanumeric)"
"description": "Unique template identifier in ULID format (26 characters, uppercase alphanumeric). Use instead of `src`."
},
"src": {
"type": "string",
"description": "Source URL to the After Effects template (.aep, .mogrt, or .zip file)"
"description": "Use instead of `id`. Public HTTP(S), native s3:/gs:, inline data:, or a file: URL inside the current job work directory for an After Effects template (.aep, .mogrt, or .zip file). The worker rejects paths and symlinks escaping that directory and non-public network destinations. Requires `name`."
},
"name": {
"type": "string",
"description": "Project file name. Required with `src`: for a .zip, the path of the .aep inside the archive (for example `project/main.aep`); otherwise the file name of the .aep or .mogrt. With `id`, it defaults to the name detected when the template was uploaded."
},
"composition": {
"type": "string",
"description": "Name of the specific composition within the template to render (optional for .mogrt)",
"nullable": true
}
},
"required": [
"id"
],
"additionalProperties": true
},
"JobCreation": {
Expand Down
Loading