Repository: cloudflare/cloudflare-go
Pull request #1386 (2023-09-02 00:55:51)

adds stream create video param to tus initiate
## Description

`stream.StreamInitiateTUSVideoUpload` currently allows you create a video stream
for upload using TUS protocol. Due to lack of `maxDurationSeconds`, the streams
created (for `direct_user=true`) results in `maxDurationSeconds= 14400` which
takes the video billing quota until the video is uploaded. This PR adds supports to
pass additional params `MaxDurationSeconds` and `Expiry` in `TUSUploadMetadata`
to add appropriate functionality to the newly created stream/video.

## Has your change been tested?

Added unit test to verify `expiry` and `maxDurationSeconds` metadata values are
properly encoded. Manually tested these by making API calls to cloudflare api using
a test account and verifying these values are correctly set in newly created stream video.

## Types of changes

What sort of change does your code introduce/modify?

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.
- [ ] This change is using publicly documented in [cloudflare/api-schemas](https://github.com/cloudflare/api-schemas) 
      and relies on stable APIs.

[1]: https://help.github.com/articles/closing-issues-using-keywords/



Patch:
diff --git a/.changelog/1386.txt b/.changelog/1386.txt
new file mode 100644
index 000000000..64d0db476
--- /dev/null
+++ b/.changelog/1386.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+streams: adds support for stream create parameters for tus upload initiate
+```
diff --git a/stream.go b/stream.go
index a2cae8dd9..3a831bc1e 100644
--- a/stream.go
+++ b/stream.go
@@ -204,10 +204,12 @@ type StreamInitiateTUSUploadResponse struct {
 
 type TUSUploadMetadata struct {
 	Name                  string     `json:"name,omitempty"`
+	MaxDurationSeconds    int        `json:"maxDurationSeconds,omitempty"`
 	RequireSignedURLs     bool       `json:"requiresignedurls,omitempty"`
 	AllowedOrigins        string     `json:"allowedorigins,omitempty"`
 	ThumbnailTimestampPct float64    `json:"thumbnailtimestamppct,omitempty"`
 	ScheduledDeletion     *time.Time `json:"scheduledDeletion,omitempty"`
+	Expiry                *time.Time `json:"expiry,omitempty"`
 	Watermark             string     `json:"watermark,omitempty"`
 }
 
@@ -216,6 +218,9 @@ func (t TUSUploadMetadata) ToTUSCsv() (string, error) {
 	if t.Name != "" {
 		metadataValues = append(metadataValues, fmt.Sprintf("%s %s", "name", base64.StdEncoding.EncodeToString([]byte(t.Name))))
 	}
+	if t.MaxDurationSeconds != 0 {
+		metadataValues = append(metadataValues, fmt.Sprintf("%s %s", "maxDurationSeconds", base64.StdEncoding.EncodeToString([]byte(strconv.Itoa(t.MaxDurationSeconds)))))
+	}
 	if t.RequireSignedURLs {
 		metadataValues = append(metadataValues, "requiresignedurls")
 	}
@@ -228,6 +233,9 @@ func (t TUSUploadMetadata) ToTUSCsv() (string, error) {
 	if t.ScheduledDeletion != nil {
 		metadataValues = append(metadataValues, fmt.Sprintf("%s %s", "scheduledDeletion", base64.StdEncoding.EncodeToString([]byte(t.ScheduledDeletion.Format(time.RFC3339)))))
 	}
+	if t.Expiry != nil {
+		metadataValues = append(metadataValues, fmt.Sprintf("%s %s", "expiry", base64.StdEncoding.EncodeToString([]byte(t.Expiry.Format(time.RFC3339)))))
+	}
 	if t.Watermark != "" {
 		metadataValues = append(metadataValues, fmt.Sprintf("%s %s", "watermark", base64.StdEncoding.EncodeToString([]byte(t.Watermark))))
 	}


Test patch:
diff --git a/stream_test.go b/stream_test.go
index d42b941a4..c589a08ce 100644
--- a/stream_test.go
+++ b/stream_test.go
@@ -554,10 +554,21 @@ func TestStream_TUSUploadMetadataToTUSCsv(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "name dGVzdC5tcDQ=,requiresignedurls,allowedorigins ZXhhbXBsZS5jb20=,thumbnailtimestamppct MC41,scheduledDeletion MjAyMy0xMC0wMVQwMjoyMDowMFo=", csv)
 
+	expiry, _ := time.Parse(time.RFC3339, "2023-09-25T02:45:00Z")
+	md.Expiry = &expiry
+	csv, err = md.ToTUSCsv()
+	assert.NoError(t, err)
+	assert.Equal(t, "name dGVzdC5tcDQ=,requiresignedurls,allowedorigins ZXhhbXBsZS5jb20=,thumbnailtimestamppct MC41,scheduledDeletion MjAyMy0xMC0wMVQwMjoyMDowMFo=,expiry MjAyMy0wOS0yNVQwMjo0NTowMFo=", csv)
+
 	md.Watermark = "watermark-profile-uid"
 	csv, err = md.ToTUSCsv()
 	assert.NoError(t, err)
-	assert.Equal(t, "name dGVzdC5tcDQ=,requiresignedurls,allowedorigins ZXhhbXBsZS5jb20=,thumbnailtimestamppct MC41,scheduledDeletion MjAyMy0xMC0wMVQwMjoyMDowMFo=,watermark d2F0ZXJtYXJrLXByb2ZpbGUtdWlk", csv)
+	assert.Equal(t, "name dGVzdC5tcDQ=,requiresignedurls,allowedorigins ZXhhbXBsZS5jb20=,thumbnailtimestamppct MC41,scheduledDeletion MjAyMy0xMC0wMVQwMjoyMDowMFo=,expiry MjAyMy0wOS0yNVQwMjo0NTowMFo=,watermark d2F0ZXJtYXJrLXByb2ZpbGUtdWlk", csv)
+
+	md.MaxDurationSeconds = 300
+	csv, err = md.ToTUSCsv()
+	assert.NoError(t, err)
+	assert.Equal(t, "name dGVzdC5tcDQ=,maxDurationSeconds MzAw,requiresignedurls,allowedorigins ZXhhbXBsZS5jb20=,thumbnailtimestamppct MC41,scheduledDeletion MjAyMy0xMC0wMVQwMjoyMDowMFo=,expiry MjAyMy0wOS0yNVQwMjo0NTowMFo=,watermark d2F0ZXJtYXJrLXByb2ZpbGUtdWlk", csv)
 
 	// empty metadata should return empty string
 	md = TUSUploadMetadata{}