@@ -296,6 +296,90 @@ impl Name {
296
296
}
297
297
}
298
298
299
+ /**
300
+ * Name for a built-in role
301
+ */
302
+ #[ derive(
303
+ Clone ,
304
+ Debug ,
305
+ DeserializeFromStr ,
306
+ Display ,
307
+ Eq ,
308
+ FromStr ,
309
+ Ord ,
310
+ PartialEq ,
311
+ PartialOrd ,
312
+ SerializeDisplay ,
313
+ ) ]
314
+ #[ display( "{resource_type}.{role_name}" ) ]
315
+ pub struct RoleName {
316
+ // "resource_type" is generally the String value of one of the
317
+ // `ResourceType` variants. We could store the parsed `ResourceType`
318
+ // instead, but it's useful to be able to represent RoleNames for resource
319
+ // types that we don't know about. That could happen if we happen to find
320
+ // them in the database, for example.
321
+ #[ from_str( regex = "[a-z-]+" ) ]
322
+ resource_type : String ,
323
+ #[ from_str( regex = "[a-z-]+" ) ]
324
+ role_name : String ,
325
+ }
326
+
327
+ impl RoleName {
328
+ pub fn new ( resource_type : & str , role_name : & str ) -> RoleName {
329
+ RoleName {
330
+ resource_type : String :: from ( resource_type) ,
331
+ role_name : String :: from ( role_name) ,
332
+ }
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Custom JsonSchema implementation to encode the constraints on Name
338
+ */
339
+ /* TODO see TODOs on Name above */
340
+ impl JsonSchema for RoleName {
341
+ fn schema_name ( ) -> String {
342
+ "RoleName" . to_string ( )
343
+ }
344
+ fn json_schema (
345
+ _gen : & mut schemars:: gen:: SchemaGenerator ,
346
+ ) -> schemars:: schema:: Schema {
347
+ schemars:: schema:: Schema :: Object ( schemars:: schema:: SchemaObject {
348
+ metadata : Some ( Box :: new ( schemars:: schema:: Metadata {
349
+ id : None ,
350
+ title : Some ( "A name for a built-in role" . to_string ( ) ) ,
351
+ description : Some (
352
+ "Role names consist of two string components \
353
+ separated by dot (\" .\" )."
354
+ . to_string ( ) ,
355
+ ) ,
356
+ default : None ,
357
+ deprecated : false ,
358
+ read_only : false ,
359
+ write_only : false ,
360
+ examples : vec ! [ ] ,
361
+ } ) ) ,
362
+ instance_type : Some ( schemars:: schema:: SingleOrVec :: Single (
363
+ Box :: new ( schemars:: schema:: InstanceType :: String ) ,
364
+ ) ) ,
365
+ format : None ,
366
+ enum_values : None ,
367
+ const_value : None ,
368
+ subschemas : None ,
369
+ number : None ,
370
+ string : Some ( Box :: new ( schemars:: schema:: StringValidation {
371
+ max_length : Some ( 63 ) ,
372
+ min_length : None ,
373
+ pattern : Some ( "[a-z-]+\\ .[a-z-]+" . to_string ( ) ) ,
374
+ } ) ) ,
375
+ array : None ,
376
+ object : None ,
377
+ reference : None ,
378
+ extensions : BTreeMap :: new ( ) ,
379
+ } )
380
+ }
381
+ }
382
+
299
383
/**
300
384
* A count of bytes, typically used either for memory or storage capacity
301
385
*
@@ -465,8 +549,22 @@ impl TryFrom<i64> for Generation {
465
549
/**
466
550
* Identifies a type of API resource
467
551
*/
468
- #[ derive( Clone , Copy , Debug , Deserialize , Eq , PartialEq , Serialize ) ]
552
+ #[ derive(
553
+ Clone ,
554
+ Copy ,
555
+ Debug ,
556
+ DeserializeFromStr ,
557
+ Display ,
558
+ Eq ,
559
+ FromStr ,
560
+ Ord ,
561
+ PartialEq ,
562
+ PartialOrd ,
563
+ SerializeDisplay ,
564
+ ) ]
565
+ #[ display( style = "kebab-case" ) ]
469
566
pub enum ResourceType {
567
+ Fleet ,
470
568
Organization ,
471
569
Project ,
472
570
Dataset ,
@@ -483,39 +581,11 @@ pub enum ResourceType {
483
581
RouterRoute ,
484
582
Oximeter ,
485
583
MetricProducer ,
584
+ Role ,
486
585
User ,
487
586
Zpool ,
488
587
}
489
588
490
- impl Display for ResourceType {
491
- fn fmt ( & self , f : & mut Formatter ) -> FormatResult {
492
- write ! (
493
- f,
494
- "{}" ,
495
- match self {
496
- ResourceType :: Organization => "organization" ,
497
- ResourceType :: Project => "project" ,
498
- ResourceType :: Dataset => "dataset" ,
499
- ResourceType :: Disk => "disk" ,
500
- ResourceType :: Instance => "instance" ,
501
- ResourceType :: NetworkInterface => "network interface" ,
502
- ResourceType :: Rack => "rack" ,
503
- ResourceType :: Sled => "sled" ,
504
- ResourceType :: SagaDbg => "saga_dbg" ,
505
- ResourceType :: Vpc => "vpc" ,
506
- ResourceType :: VpcFirewallRule => "vpc firewall rule" ,
507
- ResourceType :: VpcSubnet => "vpc subnet" ,
508
- ResourceType :: VpcRouter => "vpc router" ,
509
- ResourceType :: RouterRoute => "vpc router route" ,
510
- ResourceType :: Oximeter => "oximeter" ,
511
- ResourceType :: MetricProducer => "metric producer" ,
512
- ResourceType :: User => "user" ,
513
- ResourceType :: Zpool => "zpool" ,
514
- }
515
- )
516
- }
517
- }
518
-
519
589
pub async fn to_list < T , U > ( object_stream : ObjectStream < T > ) -> Vec < U >
520
590
where
521
591
T : Into < U > ,
@@ -1841,13 +1911,14 @@ pub struct NetworkInterface {
1841
1911
#[ cfg( test) ]
1842
1912
mod test {
1843
1913
use super :: {
1844
- ByteCount , L4Port , L4PortRange , Name , NetworkTarget ,
1914
+ ByteCount , L4Port , L4PortRange , Name , NetworkTarget , RoleName ,
1845
1915
VpcFirewallRuleAction , VpcFirewallRuleDirection , VpcFirewallRuleFilter ,
1846
1916
VpcFirewallRuleHostFilter , VpcFirewallRulePriority ,
1847
1917
VpcFirewallRuleProtocol , VpcFirewallRuleStatus , VpcFirewallRuleTarget ,
1848
1918
VpcFirewallRuleUpdate , VpcFirewallRuleUpdateParams ,
1849
1919
} ;
1850
1920
use crate :: api:: external:: Error ;
1921
+ use crate :: api:: external:: ResourceType ;
1851
1922
use std:: convert:: TryFrom ;
1852
1923
use std:: net:: IpAddr ;
1853
1924
use std:: net:: Ipv4Addr ;
@@ -1900,6 +1971,83 @@ mod test {
1900
1971
}
1901
1972
}
1902
1973
1974
+ #[ test]
1975
+ fn test_role_name_parse ( ) {
1976
+ // Error cases
1977
+ let bad_inputs = vec ! [
1978
+ // empty string is always worth testing
1979
+ "" ,
1980
+ // missing dot
1981
+ "project" ,
1982
+ // extra dot (or, illegal character in the second component)
1983
+ "project.admin.super" ,
1984
+ // missing resource type (or, another bogus resource type)
1985
+ ".admin" ,
1986
+ // missing role name
1987
+ "project." ,
1988
+ // illegal characters in role name
1989
+ "project.not_good" ,
1990
+ ] ;
1991
+
1992
+ for input in bad_inputs {
1993
+ eprintln ! ( "check name {:?} (expecting error)" , input) ;
1994
+ let result =
1995
+ input. parse :: < RoleName > ( ) . expect_err ( "unexpectedly succeeded" ) ;
1996
+ eprintln ! ( "(expected) error: {:?}" , result) ;
1997
+ }
1998
+
1999
+ eprintln ! ( "check name \" project.admin\" (expecting success)" ) ;
2000
+ let role_name =
2001
+ "project.admin" . parse :: < RoleName > ( ) . expect ( "failed to parse" ) ;
2002
+ assert_eq ! ( role_name. to_string( ) , "project.admin" ) ;
2003
+ assert_eq ! ( role_name. resource_type, "project" ) ;
2004
+ assert_eq ! ( role_name. role_name, "admin" ) ;
2005
+
2006
+ eprintln ! ( "check name \" barf.admin\" (expecting success)" ) ;
2007
+ let role_name =
2008
+ "barf.admin" . parse :: < RoleName > ( ) . expect ( "failed to parse" ) ;
2009
+ assert_eq ! ( role_name. to_string( ) , "barf.admin" ) ;
2010
+ assert_eq ! ( role_name. resource_type, "barf" ) ;
2011
+ assert_eq ! ( role_name. role_name, "admin" ) ;
2012
+
2013
+ eprintln ! ( "check name \" organization.super-user\" (expecting success)" ) ;
2014
+ let role_name = "organization.super-user"
2015
+ . parse :: < RoleName > ( )
2016
+ . expect ( "failed to parse" ) ;
2017
+ assert_eq ! ( role_name. to_string( ) , "organization.super-user" ) ;
2018
+ assert_eq ! ( role_name. resource_type, "organization" ) ;
2019
+ assert_eq ! ( role_name. role_name, "super-user" ) ;
2020
+ }
2021
+
2022
+ #[ test]
2023
+ fn test_resource_name_parse ( ) {
2024
+ let bad_inputs = vec ! [
2025
+ "bogus" ,
2026
+ "" ,
2027
+ "Project" ,
2028
+ "oRgAnIzAtIoN" ,
2029
+ "organisation" ,
2030
+ "vpc subnet" ,
2031
+ "vpc_subnet" ,
2032
+ ] ;
2033
+ for input in bad_inputs {
2034
+ eprintln ! ( "check resource type {:?} (expecting error)" , input) ;
2035
+ let result = input
2036
+ . parse :: < ResourceType > ( )
2037
+ . expect_err ( "unexpectedly succeeded" ) ;
2038
+ eprintln ! ( "(expected) error: {:?}" , result) ;
2039
+ }
2040
+
2041
+ assert_eq ! (
2042
+ ResourceType :: Project ,
2043
+ "project" . parse:: <ResourceType >( ) . unwrap( )
2044
+ ) ;
2045
+ assert_eq ! (
2046
+ ResourceType :: VpcSubnet ,
2047
+ "vpc-subnet" . parse:: <ResourceType >( ) . unwrap( )
2048
+ ) ;
2049
+ }
2050
+
1903
2051
#[ test]
1904
2052
fn test_name_parse_from_param ( ) {
1905
2053
let result = Name :: from_param ( String :: from ( "my-name" ) , "the_name" ) ;
0 commit comments