55use Utopia \OpenAPI \Model \Operation ;
66use Utopia \OpenAPI \Model \Parameter ;
77use Utopia \OpenAPI \Model \Schema ;
8+ use Utopia \OpenAPI \Model \StringSchema ;
89use Utopia \OpenAPI \Model \Tag ;
910use Utopia \OpenAPI \Specification ;
1011use Override ;
@@ -514,6 +515,7 @@ protected function getCliHelpOptionOrder(): array
514515 protected function getCliHelpFunctions (): array
515516 {
516517 return [
518+ new TwigFunction ('cliExample ' , fn (Operation $ method , Tag $ service ): array => $ this ->getCliExample ($ method , $ service )),
517519 new TwigFunction ('cliHelpGroups ' , fn (): array => $ this ->getCliHelpGroups ()),
518520 new TwigFunction ('cliHelpSummaries ' , fn (string $ title ): array => $ this ->getCliHelpSummaries ($ title )),
519521 new TwigFunction ('cliHelpOptionOrder ' , fn (): array => $ this ->getCliHelpOptionOrder ()),
@@ -524,43 +526,92 @@ protected function getCliHelpFunctions(): array
524526 }
525527
526528 /**
527- * How a value is spelled on the command line, for the docs examples. This
528- * is a shell invocation, not source in either CLI's implementation
529- * language, so it belongs to the shared surface: the target language of the
530- * generated CLI cannot change how a user types an array of roles.
529+ * Render required spec parameters using the registered CLI option metadata.
531530 *
532- * A trait method wins over the inherited Go implementation, which renders
533- * Go literals; `--roles []string{}` is not a command anybody can run.
531+ * @return list<string>
532+ */
533+ protected function getCliExample (Operation $ method , Tag $ service ): array
534+ {
535+ $ arguments = [];
536+ foreach ($ this ->getOperationParameters ($ method ) as $ parameter ) {
537+ if (!$ parameter ->required ) {
538+ continue ;
539+ }
540+
541+ $ option = $ this ->getCliOption ($ parameter , $ service );
542+ $ flag = '-- ' . $ option ['flag ' ];
543+
544+ if ($ option ['register ' ] === 'StringArray ' ) {
545+ $ itemSchema = $ this ->getArraySchema ($ parameter ) ?? new StringSchema ();
546+ $ example = $ this ->getSchema ($ parameter )->example ;
547+ if (is_string ($ example )) {
548+ $ decoded = json_decode ($ example );
549+ if (json_last_error () === JSON_ERROR_NONE ) {
550+ $ example = $ decoded ;
551+ }
552+ }
553+
554+ $ items = is_array ($ example ) && $ example !== [] ? $ example : [$ itemSchema ->example ];
555+ foreach ($ items as $ item ) {
556+ $ arguments [] = $ flag . ' ' . $ this ->getParamExample ($ itemSchema , example: $ item );
557+ }
558+ continue ;
559+ }
560+
561+ $ separator = $ option ['register ' ] === 'Bool ' ? '= ' : ' ' ;
562+ $ arguments [] = $ flag . $ separator . $ this ->getParamExample ($ parameter );
563+ }
564+
565+ $ query = $ this ->getCliQueryConfig ($ method );
566+ if ($ query ['hasPagination ' ]) {
567+ $ arguments [] = '--limit 25 ' ;
568+ }
569+ if ($ this ->getMethodType ($ method ) === 'location ' ) {
570+ $ arguments [] = '--destination ./download ' ;
571+ }
572+
573+ return $ arguments ;
574+ }
575+
576+ protected function quoteCliExample (mixed $ value ): string
577+ {
578+ $ text = is_string ($ value )
579+ ? $ value
580+ : json_encode ($ value , JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
581+
582+ if ($ text !== '' && !preg_match ('/[^A-Za-z0-9_@%+=:,\.\/-]/ ' , $ text )) {
583+ return $ text ;
584+ }
585+
586+ return "' " . str_replace ("' " , "' \"' \"' " , $ text ) . "' " ;
587+ }
588+
589+ /**
590+ * Render a schema example as a shell-safe CLI value.
534591 */
535592 #[Override]
536- public function getParamExample (Schema |Parameter $ param , string $ lang = '' ): string
593+ public function getParamExample (Schema |Parameter $ param , string $ lang = '' , mixed $ example = null ): string
537594 {
538595 $ type = $ this ->getSchemaType ($ param );
539- $ example = $ this ->getSchemaExample ($ param );
540-
541- if (empty ($ example ) && $ example !== 0 && $ example !== false ) {
542- return match ($ type ) {
543- self ::TYPE_NUMBER , self ::TYPE_INTEGER , self ::TYPE_BOOLEAN => 'null ' ,
544- self ::TYPE_STRING => "'' " ,
545- self ::TYPE_ARRAY => 'one two three ' ,
546- self ::TYPE_OBJECT => '\'{ "key": "value" } \'' ,
547- self ::TYPE_FILE => "'path/to/file.png' " ,
548- default => '' ,
549- };
596+ $ example ??= $ this ->getSchema ($ param )->example ;
597+ if (in_array ($ type , [self ::TYPE_ARRAY , self ::TYPE_OBJECT ], true ) && is_string ($ example )) {
598+ $ decoded = json_decode ($ example );
599+ if (json_last_error () === JSON_ERROR_NONE ) {
600+ $ example = $ decoded ;
601+ }
602+ }
603+
604+ if ($ type === self ::TYPE_OBJECT && is_array ($ example )) {
605+ $ example = (object ) $ example ;
550606 }
551607
552608 return match ($ type ) {
553- self ::TYPE_ARRAY => (\str_contains ((string ) $ example , '[ ' ) && \str_contains ((string ) $ example , '] ' ))
554- ? \implode (' ' , \explode (', ' , \substr ((string ) $ example , 1 , -1 )))
555- : (string ) $ example ,
556- self ::TYPE_OBJECT => '\'{ "key": "value" } \'' ,
557- self ::TYPE_NUMBER , self ::TYPE_INTEGER => (string ) $ example ,
558- self ::TYPE_BOOLEAN => $ example ? 'true ' : 'false ' ,
559- self ::TYPE_STRING => \preg_match ('/[^A-Za-z0-9_@%+=:,\.\/-]/ ' , (string ) $ example )
560- ? "' " . \str_replace ("' " , "' \"' \"' " , (string ) $ example ) . "' "
561- : (string ) $ example ,
562- self ::TYPE_FILE => "'path/to/file.png' " ,
563- default => '' ,
609+ self ::TYPE_ARRAY => $ this ->quoteCliExample ($ example ?? []),
610+ self ::TYPE_OBJECT => $ this ->quoteCliExample ($ example ?? (object ) ['key ' => 'value ' ]),
611+ self ::TYPE_NUMBER , self ::TYPE_INTEGER => $ this ->quoteCliExample ($ example ?? 1 ),
612+ self ::TYPE_BOOLEAN => $ this ->quoteCliExample ($ example ?? false ),
613+ self ::TYPE_FILE => $ this ->quoteCliExample ('path/to/file.png ' ),
614+ default => $ this ->quoteCliExample ($ example ?? $ this ->getEnumSchema ($ param )->enum [0 ] ?? '<VALUE> ' ),
564615 };
565616 }
566617}
0 commit comments