Skip to content

Commit dbf4419

Browse files
authored
[TW-4826] feat(scheduler): add extended flags, file input, and validation to configurations (#47)
1 parent d9b6c1b commit dbf4419

File tree

6 files changed

+1452
-102
lines changed

6 files changed

+1452
-102
lines changed

docs/commands/scheduler.md

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,94 @@ nylas scheduler configurations list --json
2424
nylas scheduler configurations show <config-id>
2525
nylas scheduler configs show <config-id>
2626

27-
# Create a new configuration
28-
nylas scheduler configurations create \\
29-
--name "30 Min Meeting" \\
30-
--duration 30 \\
31-
--interval 15
27+
# Create a simple configuration
28+
nylas scheduler configurations create \
29+
--name "30 Min Meeting" \
30+
--title "30 Min Meeting" \
31+
--participants [email protected] \
32+
--duration 30
33+
34+
# Create with availability settings
35+
nylas scheduler configurations create \
36+
--name "Product Demo" \
37+
--title "Product Demo" \
38+
--participants [email protected] \
39+
--duration 30 \
40+
--interval 15 \
41+
--buffer-before 5 \
42+
--buffer-after 10 \
43+
--conferencing-provider "Google Meet" \
44+
--min-booking-notice 120 \
45+
--available-days-in-future 30
46+
47+
# Create from a JSON file
48+
nylas scheduler configurations create --file config.json
49+
50+
# Create from file with flag overrides
51+
nylas scheduler configurations create --file config.json --duration 60
3252

3353
# Update a configuration
34-
nylas scheduler configurations update <config-id> \\
35-
--name "Updated Name" \\
36-
--duration 60
54+
nylas scheduler configurations update <config-id> \
55+
--name "Updated Name" \
56+
--duration 60 \
57+
--buffer-before 10
58+
59+
# Update from a JSON file
60+
nylas scheduler configurations update <config-id> --file update.json
3761

3862
# Delete a configuration
3963
nylas scheduler configurations delete <config-id>
40-
nylas scheduler configs delete <config-id> -f # Skip confirmation
64+
nylas scheduler configs delete <config-id> -y # Skip confirmation
65+
```
66+
67+
**Configuration Flags:**
68+
69+
| Flag | Type | Description |
70+
|------|------|-------------|
71+
| `--name` | string | Configuration name |
72+
| `--participants` | strings | Participant emails (comma-separated, first is organizer) |
73+
| `--duration` | int | Meeting duration in minutes (default: 30) |
74+
| `--title` | string | Event title |
75+
| `--description` | string | Event description |
76+
| `--location` | string | Event location |
77+
| `--interval` | int | Slot interval in minutes |
78+
| `--round-to` | int | Round start times to nearest N minutes |
79+
| `--availability-method` | string | `max-fairness` or `max-availability` |
80+
| `--buffer-before` | int | Buffer minutes before meetings |
81+
| `--buffer-after` | int | Buffer minutes after meetings |
82+
| `--timezone` | string | Event timezone (e.g., `America/New_York`) |
83+
| `--booking-type` | string | `booking` or `organizer-confirmation` |
84+
| `--conferencing-provider` | string | `Google Meet`, `Zoom`, or `Microsoft Teams` |
85+
| `--disable-emails` | bool | Disable email notifications |
86+
| `--reminder-minutes` | ints | Reminder minutes (e.g., `10,60`) |
87+
| `--min-booking-notice` | int | Minimum minutes before booking |
88+
| `--min-cancellation-notice` | int | Minimum minutes before cancellation |
89+
| `--confirmation-method` | string | `automatic` or `manual` |
90+
| `--available-days-in-future` | int | Days in advance bookings are available |
91+
| `--cancellation-policy` | string | Cancellation policy text |
92+
| `--file` | string | JSON config file (flags override file values) |
93+
| `--json` | bool | Output as JSON |
94+
95+
**File Input:**
96+
97+
The `--file` flag accepts a JSON file matching the API request structure. You can export an existing configuration with `--json`, edit it, and re-import:
98+
99+
```bash
100+
# Export → edit → recreate
101+
nylas scheduler configs show abc123 --json > meeting.json
102+
# Edit meeting.json...
103+
nylas scheduler configs create --file meeting.json
41104
```
42105

106+
When both `--file` and flags are provided, flags take precedence over file values.
107+
43108
**Configuration Features:**
44109
- Duration and interval settings
45110
- Availability rules and windows
46111
- Buffer times before/after meetings
112+
- Conferencing auto-creation (Google Meet, Zoom, Teams)
47113
- Booking limits and restrictions
114+
- Reminder notifications
48115
- Custom event settings
49116

50117
### Scheduler Sessions

internal/cli/scheduler/configurations.go

Lines changed: 75 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"strings"
88

99
"github.com/nylas/cli/internal/cli/common"
10-
"github.com/nylas/cli/internal/domain"
1110
"github.com/nylas/cli/internal/ports"
1211
"github.com/spf13/cobra"
1312
)
@@ -93,30 +92,7 @@ func newConfigShowCmd() *cobra.Command {
9392
return struct{}{}, json.NewEncoder(cmd.OutOrStdout()).Encode(config)
9493
}
9594

96-
_, _ = common.Bold.Printf("Configuration: %s\n", config.Name)
97-
fmt.Printf(" ID: %s\n", common.Cyan.Sprint(config.ID))
98-
fmt.Printf(" Slug: %s\n", common.Green.Sprint(config.Slug))
99-
fmt.Printf(" Duration: %d minutes\n", config.Availability.DurationMinutes)
100-
101-
if len(config.Participants) > 0 {
102-
fmt.Printf("\nParticipants (%d):\n", len(config.Participants))
103-
for i, p := range config.Participants {
104-
fmt.Printf(" %d. %s <%s>", i+1, p.Name, p.Email)
105-
if p.IsOrganizer {
106-
fmt.Printf(" %s", common.Green.Sprint("(Organizer)"))
107-
}
108-
fmt.Println()
109-
}
110-
}
111-
112-
fmt.Printf("\nEvent Booking:\n")
113-
fmt.Printf(" Title: %s\n", config.EventBooking.Title)
114-
if config.EventBooking.Description != "" {
115-
fmt.Printf(" Description: %s\n", config.EventBooking.Description)
116-
}
117-
if config.EventBooking.Location != "" {
118-
fmt.Printf(" Location: %s\n", config.EventBooking.Location)
119-
}
95+
formatConfigDetails(cmd.OutOrStdout(), config)
12096

12197
return struct{}{}, nil
12298
})
@@ -139,47 +115,56 @@ func newConfigCreateCmd() *cobra.Command {
139115
location string
140116
jsonOutput bool
141117
)
118+
flags := &configFlags{}
142119

143120
cmd := &cobra.Command{
144121
Use: "create",
145122
Short: "Create a scheduler configuration",
146-
Long: "Create a new scheduler configuration (meeting type).",
123+
Long: `Create a new scheduler configuration (meeting type).
124+
125+
Use flags for common settings, or --file for full JSON config input.
126+
When both are provided, flags override file values.`,
127+
Example: ` # Simple inline creation
128+
nylas scheduler configs create --name "Quick Chat" --title "Quick Chat" \
129+
--participants [email protected] --duration 15
130+
131+
# With availability settings
132+
nylas scheduler configs create --name "Product Demo" --title "Demo" \
133+
--participants [email protected] --duration 30 --interval 15 \
134+
--buffer-before 5 --buffer-after 10 --conferencing-provider "Google Meet"
135+
136+
# From a JSON file
137+
nylas scheduler configs create --file config.json
138+
139+
# File as base, override specific values
140+
nylas scheduler configs create --file config.json --duration 60`,
147141
RunE: func(cmd *cobra.Command, args []string) error {
148-
// Validate participants
149-
if len(participants) == 0 {
150-
return common.NewUserError("at least one participant email is required", "Use --participant to specify email addresses")
151-
}
152-
for i, p := range participants {
153-
p = strings.TrimSpace(p)
154-
if p == "" {
155-
return fmt.Errorf("participant email at position %d cannot be empty", i+1)
156-
}
157-
participants[i] = p
142+
if err := validateConfigFlags(flags); err != nil {
143+
return err
158144
}
159145

160-
_, err := common.WithClient(args, func(ctx context.Context, client ports.NylasClient, grantID string) (struct{}, error) {
161-
// Build participants list
162-
var participantsList []domain.ConfigurationParticipant
163-
for i, email := range participants {
164-
participantsList = append(participantsList, domain.ConfigurationParticipant{
165-
Email: email,
166-
IsOrganizer: i == 0, // First participant is organizer
167-
})
146+
if flags.file == "" {
147+
if len(participants) == 0 {
148+
return common.NewUserError("at least one participant email is required", "Use --participants to specify email addresses")
168149
}
169-
170-
req := &domain.CreateSchedulerConfigurationRequest{
171-
Name: name,
172-
Participants: participantsList,
173-
Availability: domain.AvailabilityRules{
174-
DurationMinutes: duration,
175-
},
176-
EventBooking: domain.EventBooking{
177-
Title: title,
178-
Description: description,
179-
Location: location,
180-
},
150+
for i, p := range participants {
151+
p = strings.TrimSpace(p)
152+
if p == "" {
153+
return fmt.Errorf("participant email at position %d cannot be empty", i+1)
154+
}
155+
participants[i] = p
181156
}
157+
}
158+
159+
req, err := buildCreateRequest(cmd, flags, name, participants, duration, title, description, location)
160+
if err != nil {
161+
return err
162+
}
163+
if err := validateCreateRequest(req); err != nil {
164+
return err
165+
}
182166

167+
_, err = common.WithClient(args, func(ctx context.Context, client ports.NylasClient, grantID string) (struct{}, error) {
183168
config, err := client.CreateSchedulerConfiguration(ctx, req)
184169
if err != nil {
185170
return struct{}{}, common.WrapCreateError("configuration", err)
@@ -201,19 +186,16 @@ func newConfigCreateCmd() *cobra.Command {
201186
},
202187
}
203188

204-
cmd.Flags().StringVar(&name, "name", "", "Configuration name (required)")
189+
cmd.Flags().StringVar(&name, "name", "", "Configuration name")
205190
cmd.Flags().StringSliceVar(&participants, "participants", []string{}, "Participant emails (comma-separated, first is organizer)")
206191
cmd.Flags().IntVar(&duration, "duration", 30, "Meeting duration in minutes")
207-
cmd.Flags().StringVar(&title, "title", "", "Event title (required)")
192+
cmd.Flags().StringVar(&title, "title", "", "Event title")
208193
cmd.Flags().StringVar(&description, "description", "", "Event description")
209194
cmd.Flags().StringVar(&location, "location", "", "Event location")
210-
211-
_ = cmd.MarkFlagRequired("name")
212-
_ = cmd.MarkFlagRequired("participants")
213-
_ = cmd.MarkFlagRequired("title")
214-
215195
cmd.Flags().BoolVar(&jsonOutput, "json", false, "Output as JSON")
216196

197+
registerConfigFlags(cmd, flags)
198+
217199
return cmd
218200
}
219201

@@ -225,38 +207,42 @@ func newConfigUpdateCmd() *cobra.Command {
225207
description string
226208
jsonOutput bool
227209
)
210+
flags := &configFlags{}
228211

229212
cmd := &cobra.Command{
230213
Use: "update <config-id>",
231214
Short: "Update a scheduler configuration",
232-
Long: "Update an existing scheduler configuration.",
233-
Args: cobra.ExactArgs(1),
234-
RunE: func(cmd *cobra.Command, args []string) error {
235-
configID := args[0]
236-
_, err := common.WithClient(args, func(ctx context.Context, client ports.NylasClient, grantID string) (struct{}, error) {
237-
req := &domain.UpdateSchedulerConfigurationRequest{}
215+
Long: `Update an existing scheduler configuration.
238216
239-
if name != "" {
240-
req.Name = &name
241-
}
217+
Use flags to set specific fields, or --file for full JSON update.
218+
When both are provided, flags override file values.`,
219+
Example: ` # Update specific fields
220+
nylas scheduler configs update abc123 --name "Updated Name" --duration 60
242221
243-
if cmd.Flags().Changed("duration") {
244-
req.Availability = &domain.AvailabilityRules{
245-
DurationMinutes: duration,
246-
}
247-
}
222+
# Add buffer times
223+
nylas scheduler configs update abc123 --buffer-before 5 --buffer-after 10
248224
249-
if cmd.Flags().Changed("title") || cmd.Flags().Changed("description") {
250-
eventBooking := &domain.EventBooking{}
251-
if cmd.Flags().Changed("title") {
252-
eventBooking.Title = title
253-
}
254-
if cmd.Flags().Changed("description") {
255-
eventBooking.Description = description
256-
}
257-
req.EventBooking = eventBooking
258-
}
225+
# From a JSON file
226+
nylas scheduler configs update abc123 --file update.json
227+
228+
# File as base, override specific values
229+
nylas scheduler configs update abc123 --file update.json --duration 45`,
230+
Args: cobra.ExactArgs(1),
231+
RunE: func(cmd *cobra.Command, args []string) error {
232+
if err := validateConfigFlags(flags); err != nil {
233+
return err
234+
}
259235

236+
configID := args[0]
237+
req, err := buildUpdateRequest(cmd, flags, name, duration, title, description)
238+
if err != nil {
239+
return err
240+
}
241+
if err := validateUpdateRequest(req); err != nil {
242+
return err
243+
}
244+
245+
_, err = common.WithClient(args, func(ctx context.Context, client ports.NylasClient, grantID string) (struct{}, error) {
260246
config, err := client.UpdateSchedulerConfiguration(ctx, configID, req)
261247
if err != nil {
262248
return struct{}{}, common.WrapUpdateError("configuration", err)
@@ -280,6 +266,8 @@ func newConfigUpdateCmd() *cobra.Command {
280266
cmd.Flags().StringVar(&description, "description", "", "Event description")
281267
cmd.Flags().BoolVar(&jsonOutput, "json", false, "Output as JSON")
282268

269+
registerConfigFlags(cmd, flags)
270+
283271
return cmd
284272
}
285273

0 commit comments

Comments
 (0)