Skip to content

Commit c160fcf

Browse files
ryanpqewanharris
andauthored
feat: support start time parameter on read changes (#443)
* feat: support start time parameter on read changes feat: support start time parameter on read changes * Update README.md Co-authored-by: Ewan Harris <ewanharris93@gmail.com> * chore: bump go-sdk version to include start_time variable fix * chore: bump go-sdk version, update changelog feat: use RFC3339 in place of ISO8601 layout string fix: resolve linting error from newline docs: Update changelog with start time and prep for 0.6.4 release --------- Co-authored-by: Ewan Harris <ewanharris93@gmail.com>
1 parent 123afcc commit c160fcf

File tree

6 files changed

+68
-16
lines changed

6 files changed

+68
-16
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
#### [Unreleased](https://github.com/openfga/cli/compare/v0.6.3...HEAD) (2025-02-06)
4+
Added:
5+
- Support for start-time parameter in changes command (#443)
6+
37
#### [0.6.3](https://github.com/openfga/cli/compare/v0.6.2...v0.6.3) (2025-01-22)
48

59
Added:

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ type document
655655
| [Write Relationship Tuples](#write-relationship-tuples) | `write` | `--store-id`, `--model-id` | `fga tuple write user:anne can_view document:roadmap --store-id=01H0H015178Y2V4CX10C2KGHF4` |
656656
| [Delete Relationship Tuples](#delete-relationship-tuples) | `delete` | `--store-id`, `--model-id` | `fga tuple delete user:anne can_view document:roadmap --store-id=01H0H015178Y2V4CX10C2KGHF4` |
657657
| [Read Relationship Tuples](#read-relationship-tuples) | `read` | `--store-id`, `--model-id` | `fga tuple read --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1` |
658-
| [Read Relationship Tuple Changes (Watch)](#read-relationship-tuple-changes-watch) | `changes` | `--store-id`, `--type`, `--continuation-token`, | `fga tuple changes --store-id=01H0H015178Y2V4CX10C2KGHF4 --type=document --continuation-token=M3w=` |
658+
| [Read Relationship Tuple Changes (Watch)](#read-relationship-tuple-changes-watch) | `changes` | `--store-id`, `--type`, `--start-time`, `--continuation-token`, | `fga tuple changes --store-id=01H0H015178Y2V4CX10C2KGHF4 --type=document --start-time=2022-01-01T00:00:00Z --continuation-token=M3w=` |
659659
| [Import Relationship Tuples](#import-relationship-tuples) | `import` | `--store-id`, `--model-id`, `--file` | `fga tuple import --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1 --file tuples.json` |
660660

661661
##### Write Relationship Tuples
@@ -896,6 +896,7 @@ fga tuple **changes** --type <type> --store-id=<store-id>
896896
###### Parameters
897897
* `--store-id`: Specifies the store id
898898
* `--type`: Restrict to a specific type (optional)
899+
* `--start-time`: Return changes since a specified time (optional)
899900
* `--max-pages`: Max number of pages to retrieve (default: 20)
900901
* `--continuation-token`: Continuation token to start changes from
901902

cmd/tuple/changes.go

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package tuple
1919
import (
2020
"context"
2121
"fmt"
22+
"time"
2223

2324
openfga "github.com/openfga/go-sdk"
2425
"github.com/openfga/go-sdk/client"
@@ -32,15 +33,30 @@ import (
3233
var MaxReadChangesPagesLength = 20
3334

3435
func readChanges(
35-
fgaClient client.SdkClient, maxPages int, selectedType string, continuationToken string,
36+
fgaClient client.SdkClient, maxPages int, selectedType string, startTime string, continuationToken string,
3637
) (*openfga.ReadChangesResponse, error) {
3738
changes := []openfga.TupleChange{}
3839
pageIndex := 0
3940

41+
var startTimeObj *time.Time
42+
43+
if startTime != "" {
44+
parsedTime, err := time.Parse(time.RFC3339, startTime)
45+
if err != nil {
46+
return nil, fmt.Errorf("failed to parse startTime: %w", err)
47+
}
48+
49+
startTimeObj = &parsedTime
50+
}
51+
4052
for {
4153
body := &client.ClientReadChangesRequest{
4254
Type: selectedType,
4355
}
56+
if startTimeObj != nil {
57+
body.StartTime = *startTimeObj
58+
}
59+
4460
options := &client.ClientReadChangesOptions{
4561
ContinuationToken: &continuationToken,
4662
}
@@ -67,10 +83,11 @@ func readChanges(
6783

6884
// changesCmd represents the changes command.
6985
var changesCmd = &cobra.Command{
70-
Use: "changes",
71-
Short: "Read Relationship Tuple Changes (Watch)",
72-
Long: "Get a list of relationship tuple changes (Writes and Deletes) across time.",
73-
Example: "fga tuple changes --store-id=01H0H015178Y2V4CX10C2KGHF4 --type document --continuation-token=MXw=",
86+
Use: "changes",
87+
Short: "Read Relationship Tuple Changes (Watch)",
88+
Long: "Get a list of relationship tuple changes (Writes and Deletes) across time.",
89+
Example: `fga tuple changes --store-id=01H0H015178Y2V4CX10C2KGHF4 --type document
90+
--start-time 2022-01-01T00:00:00Z --continuation-token=MXw=`,
7491
RunE: func(cmd *cobra.Command, _ []string) error {
7592
clientConfig := cmdutils.GetClientConfig(cmd)
7693

@@ -89,12 +106,17 @@ var changesCmd = &cobra.Command{
89106
return fmt.Errorf("failed to get tuple changes due to %w", err)
90107
}
91108

109+
startTime, err := cmd.Flags().GetString("start-time")
110+
if err != nil {
111+
return fmt.Errorf("failed to get tuple changes due to %w", err)
112+
}
113+
92114
continuationToken, err := cmd.Flags().GetString("continuation-token")
93115
if err != nil {
94116
return fmt.Errorf("failed to get tuple changes due to %w", err)
95117
}
96118

97-
response, err := readChanges(fgaClient, maxPages, selectedType, continuationToken)
119+
response, err := readChanges(fgaClient, maxPages, selectedType, startTime, continuationToken)
98120
if err != nil {
99121
return err
100122
}
@@ -105,6 +127,7 @@ var changesCmd = &cobra.Command{
105127

106128
func init() {
107129
changesCmd.Flags().String("type", "", "Type to restrict the changes by.")
130+
changesCmd.Flags().String("start-time", "", "Time to return changes since.")
108131
changesCmd.Flags().Int("max-pages", MaxReadChangesPagesLength, "Max number of pages to get.")
109132
changesCmd.Flags().String("continuation-token", "", "Continuation token to start changes from.")
110133
}

cmd/tuple/changes_test.go

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func TestReadChangesError(t *testing.T) {
4444

4545
mockFgaClient.EXPECT().ReadChanges(context.Background()).Return(mockBody)
4646

47-
_, err := readChanges(mockFgaClient, 5, "document", "")
47+
_, err := readChanges(mockFgaClient, 5, "document", "", "")
4848
if err == nil {
4949
t.Error("Expect error but there is none")
5050
}
@@ -82,7 +82,7 @@ func TestReadChangesEmpty(t *testing.T) {
8282

8383
mockFgaClient.EXPECT().ReadChanges(context.Background()).Return(mockBody)
8484

85-
output, err := readChanges(mockFgaClient, 5, "document", "")
85+
output, err := readChanges(mockFgaClient, 5, "document", "", "")
8686
if err != nil {
8787
t.Error(err)
8888
}
@@ -139,14 +139,20 @@ func TestReadChangesSinglePage(t *testing.T) {
139139

140140
mockBody := mock_client.NewMockSdkClientReadChangesRequestInterface(mockCtrl)
141141

142+
sTime, err := time.Parse(time.RFC3339, "2022-01-01T00:00:00Z")
143+
if err != nil {
144+
t.Error(err)
145+
}
146+
142147
body := client.ClientReadChangesRequest{
143-
Type: "document",
148+
Type: "document",
149+
StartTime: sTime,
144150
}
145151
mockBody.EXPECT().Body(body).Return(mockRequest)
146152

147153
mockFgaClient.EXPECT().ReadChanges(context.Background()).Return(mockBody)
148154

149-
output, err := readChanges(mockFgaClient, 5, "document", "")
155+
output, err := readChanges(mockFgaClient, 5, "document", "2022-01-01T00:00:00Z", "")
150156
if err != nil {
151157
t.Error(err)
152158
}
@@ -232,8 +238,14 @@ func TestReadChangesMultiPages(t *testing.T) {
232238
mockBody1 := mock_client.NewMockSdkClientReadChangesRequestInterface(mockCtrl)
233239
mockBody2 := mock_client.NewMockSdkClientReadChangesRequestInterface(mockCtrl)
234240

241+
sTime, err := time.Parse(time.RFC3339, "2022-01-01T00:00:00Z")
242+
if err != nil {
243+
t.Error(err)
244+
}
245+
235246
body := client.ClientReadChangesRequest{
236-
Type: "document",
247+
Type: "document",
248+
StartTime: sTime,
237249
}
238250

239251
gomock.InOrder(
@@ -246,7 +258,7 @@ func TestReadChangesMultiPages(t *testing.T) {
246258
mockFgaClient.EXPECT().ReadChanges(context.Background()).Return(mockBody2),
247259
)
248260

249-
output, err := readChanges(mockFgaClient, 5, "document", "")
261+
output, err := readChanges(mockFgaClient, 5, "document", "2022-01-01T00:00:00Z", "")
250262
if err != nil {
251263
t.Error(err)
252264
}
@@ -303,14 +315,20 @@ func TestReadChangesMultiPagesLimit(t *testing.T) {
303315

304316
mockBody := mock_client.NewMockSdkClientReadChangesRequestInterface(mockCtrl)
305317

318+
sTime, err := time.Parse(time.RFC3339, "2022-01-01T00:00:00Z")
319+
if err != nil {
320+
t.Error(err)
321+
}
322+
306323
body := client.ClientReadChangesRequest{
307-
Type: "document",
324+
Type: "document",
325+
StartTime: sTime,
308326
}
309327
mockBody.EXPECT().Body(body).Return(mockRequest)
310328

311329
mockFgaClient.EXPECT().ReadChanges(context.Background()).Return(mockBody)
312330

313-
output, err := readChanges(mockFgaClient, 1, "document", "")
331+
output, err := readChanges(mockFgaClient, 1, "document", "2022-01-01T00:00:00Z", "")
314332
if err != nil {
315333
t.Error(err)
316334
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require (
1111
github.com/nwidger/jsoncolor v0.3.2
1212
github.com/oklog/ulid/v2 v2.1.0
1313
github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5
14-
github.com/openfga/go-sdk v0.6.4-0.20250107171931-2adebcc8c8bc
14+
github.com/openfga/go-sdk v0.6.5
1515
github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c
1616
github.com/openfga/openfga v1.8.4
1717
github.com/spf13/cobra v1.8.1

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,12 @@ github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5 h1:z9jaRoo+NIN1A
182182
github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5/go.mod h1:m74TNgnAAIJ03gfHcx+xaRWnr+IbQy3y/AVNwwCFrC0=
183183
github.com/openfga/go-sdk v0.6.4-0.20250107171931-2adebcc8c8bc h1:E7x5UZbNIbcCOhBRD3cqrmQz35qNNceMnZPb6UxZRnw=
184184
github.com/openfga/go-sdk v0.6.4-0.20250107171931-2adebcc8c8bc/go.mod h1:zui7pHE3eLAYh2fFmEMrWg9XbxYns2WW5Xr/GEgili4=
185+
github.com/openfga/go-sdk v0.6.4 h1:VAeH9exZuG1qaBt41+mjIK5RxTtuL9maS4d47YsYGZw=
186+
github.com/openfga/go-sdk v0.6.4/go.mod h1:zui7pHE3eLAYh2fFmEMrWg9XbxYns2WW5Xr/GEgili4=
187+
github.com/openfga/go-sdk v0.6.5-0.20250205144545-22df3ecba077 h1:o1s2Y5cjg/ALF9vN2BiTcF6ucrGDGfJKR7N8QdjKcR4=
188+
github.com/openfga/go-sdk v0.6.5-0.20250205144545-22df3ecba077/go.mod h1:zui7pHE3eLAYh2fFmEMrWg9XbxYns2WW5Xr/GEgili4=
189+
github.com/openfga/go-sdk v0.6.5 h1:2bxZkoLyphOahFETo9wPvls1AQ3IqbsygIyDkzRvx1k=
190+
github.com/openfga/go-sdk v0.6.5/go.mod h1:zui7pHE3eLAYh2fFmEMrWg9XbxYns2WW5Xr/GEgili4=
185191
github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c h1:1y84C0V4NRfPtRi4MqQ7+gnFtYgeBKPIeIAPLdVJ7j4=
186192
github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c/go.mod h1:12RMe/HuRNyOzS33RQa53jwdcxE2znr8ycXMlVbgQN4=
187193
github.com/openfga/openfga v1.8.4 h1:OqyRpuxMCxcS7irTFYFkhAIYzmAnczNwxUqjnuZOQyo=

0 commit comments

Comments
 (0)