@@ -15,6 +15,7 @@ import (
15
15
"github.com/coder/coder/cli/clibase"
16
16
"github.com/coder/coder/cli/cliui"
17
17
"github.com/coder/coder/codersdk"
18
+ "github.com/google/uuid"
18
19
)
19
20
20
21
var jwtRegexp = regexp .MustCompile (`^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$` )
@@ -136,6 +137,76 @@ func validJWT(s string) error {
136
137
}
137
138
138
139
func (r * RootCmd ) licensesList () * clibase.Cmd {
140
+ type tableLicense struct {
141
+ ID int32 `table:"id,default_sort"`
142
+ UUID uuid.UUID `table:"uuid" format:"uuid"`
143
+ UploadedAt time.Time `table:"uploaded_at" format:"date-time"`
144
+ // Features is the formatted string for the license claims.
145
+ // Used for the table view.
146
+ Features string `table:"features"`
147
+ ExpiresAt time.Time `table:"expires_at" format:"date-time"`
148
+ Trial bool `table:"trial"`
149
+ }
150
+
151
+ formatter := cliui .NewOutputFormatter (
152
+ cliui .ChangeFormatterData (
153
+ cliui .TableFormat ([]tableLicense {}, []string {"UUID" , "Expires At" , "Uploaded At" , "Features" }),
154
+ func (data any ) (any , error ) {
155
+ list , ok := data .([]codersdk.License )
156
+ if ! ok {
157
+ return nil , xerrors .Errorf ("invalid data type %T" , data )
158
+ }
159
+ out := make ([]tableLicense , 0 , len (list ))
160
+ for _ , lic := range list {
161
+ var formattedFeatures string
162
+ features , err := lic .FeaturesClaims ()
163
+ if err != nil {
164
+ formattedFeatures = xerrors .Errorf ("invalid license: %w" , err ).Error ()
165
+ } else {
166
+ var strs []string
167
+ if lic .AllFeaturesClaim () {
168
+ // If all features are enabled, just include that
169
+ strs = append (strs , "all features" )
170
+ } else {
171
+ for k , v := range features {
172
+ if v > 0 {
173
+ // Only include claims > 0
174
+ strs = append (strs , fmt .Sprintf ("%s=%v" , k , v ))
175
+ }
176
+ }
177
+ }
178
+ formattedFeatures = strings .Join (strs , ", " )
179
+ }
180
+ // If this returns an error, a zero time is returned.
181
+ exp , _ := lic .ExpiresAt ()
182
+
183
+ out = append (out , tableLicense {
184
+ ID : lic .ID ,
185
+ UUID : lic .UUID ,
186
+ UploadedAt : lic .UploadedAt ,
187
+ Features : formattedFeatures ,
188
+ ExpiresAt : exp ,
189
+ Trial : lic .Trial (),
190
+ })
191
+ }
192
+ return out , nil
193
+ }),
194
+ cliui .ChangeFormatterData (cliui .JSONFormat (), func (data any ) (any , error ) {
195
+ list , ok := data .([]codersdk.License )
196
+ if ! ok {
197
+ return nil , xerrors .Errorf ("invalid data type %T" , data )
198
+ }
199
+ for i := range list {
200
+ humanExp , err := list [i ].ExpiresAt ()
201
+ if err == nil {
202
+ list [i ].Claims [codersdk .LicenseExpiryClaim + "_human" ] = humanExp .Format (time .RFC3339 )
203
+ }
204
+ }
205
+
206
+ return list , nil
207
+ }),
208
+ )
209
+
139
210
client := new (codersdk.Client )
140
211
cmd := & clibase.Cmd {
141
212
Use : "list" ,
@@ -155,19 +226,16 @@ func (r *RootCmd) licensesList() *clibase.Cmd {
155
226
licenses = make ([]codersdk.License , 0 )
156
227
}
157
228
158
- for i , license := range licenses {
159
- newClaims , err := convertLicenseExpireTime (license .Claims )
160
- if err != nil {
161
- return err
162
- }
163
- licenses [i ].Claims = newClaims
229
+ out , err := formatter .Format (inv .Context (), licenses )
230
+ if err != nil {
231
+ return err
164
232
}
165
233
166
- enc := json .NewEncoder (inv .Stdout )
167
- enc .SetIndent ("" , " " )
168
- return enc .Encode (licenses )
234
+ _ , err = fmt .Fprintln (inv .Stdout , out )
235
+ return err
169
236
},
170
237
}
238
+ formatter .AttachOptions (& cmd .Options )
171
239
return cmd
172
240
}
173
241
@@ -196,29 +264,3 @@ func (r *RootCmd) licenseDelete() *clibase.Cmd {
196
264
}
197
265
return cmd
198
266
}
199
-
200
- func convertLicenseExpireTime (licenseClaims map [string ]interface {}) (map [string ]interface {}, error ) {
201
- if licenseClaims ["license_expires" ] != nil {
202
- licenseExpiresNumber , ok := licenseClaims ["license_expires" ].(json.Number )
203
- if ! ok {
204
- return licenseClaims , xerrors .Errorf ("could not convert license_expires to json.Number" )
205
- }
206
-
207
- licenseExpires , err := licenseExpiresNumber .Int64 ()
208
- if err != nil {
209
- return licenseClaims , xerrors .Errorf ("could not convert license_expires to int64: %w" , err )
210
- }
211
-
212
- t := time .Unix (licenseExpires , 0 )
213
- rfc3339Format := t .Format (time .RFC3339 )
214
-
215
- claimsCopy := make (map [string ]interface {}, len (licenseClaims ))
216
- for k , v := range licenseClaims {
217
- claimsCopy [k ] = v
218
- }
219
-
220
- claimsCopy ["license_expires" ] = rfc3339Format
221
- return claimsCopy , nil
222
- }
223
- return licenseClaims , nil
224
- }
0 commit comments