@@ -1996,6 +1996,295 @@ def _taskView(task_id):
19961996 _generic_reply_handler (reply , _print_task_array )
19971997
19981998
1999+ # =============================================================================
2000+ # ---------------------------------------------------------- Schema Functions
2001+ # =============================================================================
2002+
2003+
2004+ @_cli .command (name = "schema" , cls = _AliasedGroup , help = "Schema commands." )
2005+ def _schema ():
2006+ pass
2007+
2008+
2009+ @_schema .command (name = "view" )
2010+ @click .argument ("schema_id" , metavar = "ID" )
2011+ @click .option (
2012+ "-r" , "--resolve" , is_flag = True , help = "Resolve schema references."
2013+ )
2014+ @_global_output_options
2015+ def _schemaView (schema_id , resolve ):
2016+ """
2017+ View schema information. Displays schema definition, description, owner,
2018+ and other administrative fields. ID is the schema identifier, optionally
2019+ including a version suffix (e.g. "my_schema:1").
2020+ """
2021+
2022+ reply = _capi .schemaView (_resolve_id (schema_id ), resolve = resolve )
2023+ _generic_reply_handler (reply , _print_schema )
2024+
2025+
2026+ @_schema .command (name = "create" )
2027+ @click .argument ("schema_id" , metavar = "ID" )
2028+ @click .option (
2029+ "-D" ,
2030+ "--definition" ,
2031+ type = str ,
2032+ required = False ,
2033+ help = "Inline JSON schema definition string." ,
2034+ )
2035+ @click .option (
2036+ "-F" ,
2037+ "--definition-file" ,
2038+ type = str ,
2039+ required = False ,
2040+ help = "Path to local JSON file containing schema definition." ,
2041+ )
2042+ @click .option ("-d" , "--description" , type = str , required = False , help = "Description text." )
2043+ @click .option (
2044+ "-p" , "--public" , is_flag = True , required = False , help = "Make schema publicly visible."
2045+ )
2046+ @click .option (
2047+ "-s" , "--system" , is_flag = True , required = False , help = "Create as a system schema."
2048+ )
2049+ @_global_output_options
2050+ def _schemaCreate (schema_id , definition , definition_file , description , public , system ):
2051+ """
2052+ Create a new metadata schema. A JSON schema definition is required and
2053+ may be provided inline via --definition or read from a file via
2054+ --definition-file. Cannot specify both.
2055+ """
2056+
2057+ if definition and definition_file :
2058+ raise Exception (
2059+ "Cannot specify both --definition and --definition-file options."
2060+ )
2061+
2062+ if not definition and not definition_file :
2063+ raise Exception ("Must specify either --definition or --definition-file." )
2064+
2065+ reply = _capi .schemaCreate (
2066+ schema_id ,
2067+ definition = definition ,
2068+ definition_file = definition_file ,
2069+ description = description ,
2070+ public = public ,
2071+ system = system ,
2072+ )
2073+ _generic_reply_handler (reply , _print_ack_reply )
2074+
2075+
2076+ @_schema .command (name = "revise" )
2077+ @click .argument ("schema_id" , metavar = "ID" )
2078+ @click .option (
2079+ "-D" ,
2080+ "--definition" ,
2081+ type = str ,
2082+ required = False ,
2083+ help = "Updated inline JSON schema definition string." ,
2084+ )
2085+ @click .option (
2086+ "-F" ,
2087+ "--definition-file" ,
2088+ type = str ,
2089+ required = False ,
2090+ help = "Path to local JSON file containing updated schema definition." ,
2091+ )
2092+ @click .option ("-d" , "--description" , type = str , required = False , help = "Description text." )
2093+ @click .option (
2094+ "-p" ,
2095+ "--public" ,
2096+ is_flag = True ,
2097+ default = None ,
2098+ required = False ,
2099+ help = "Make schema publicly visible." ,
2100+ )
2101+ @click .option (
2102+ "-s" ,
2103+ "--system" ,
2104+ is_flag = True ,
2105+ default = None ,
2106+ required = False ,
2107+ help = "Set as system schema." ,
2108+ )
2109+ @_global_output_options
2110+ def _schemaRevise (schema_id , definition , definition_file , description , public , system ):
2111+ """
2112+ Create a new revision of an existing schema. Any fields not provided are
2113+ carried forward from the current revision. The definition may be provided
2114+ inline or read from a file.
2115+ """
2116+
2117+ if definition and definition_file :
2118+ raise Exception (
2119+ "Cannot specify both --definition and --definition-file options."
2120+ )
2121+
2122+ reply = _capi .schemaRevise (
2123+ schema_id ,
2124+ definition = definition ,
2125+ definition_file = definition_file ,
2126+ description = description ,
2127+ public = public if public else None ,
2128+ system = system if system else None ,
2129+ )
2130+ _generic_reply_handler (reply , _print_ack_reply )
2131+
2132+
2133+ @_schema .command (name = "update" )
2134+ @click .argument ("schema_id" , metavar = "ID" )
2135+ @click .option ("-n" , "--new-id" , type = str , required = False , help = "Rename schema to new ID." )
2136+ @click .option (
2137+ "-D" ,
2138+ "--definition" ,
2139+ type = str ,
2140+ required = False ,
2141+ help = "Updated inline JSON schema definition string." ,
2142+ )
2143+ @click .option (
2144+ "-F" ,
2145+ "--definition-file" ,
2146+ type = str ,
2147+ required = False ,
2148+ help = "Path to local JSON file containing updated schema definition." ,
2149+ )
2150+ @click .option ("-d" , "--description" , type = str , required = False , help = "Description text." )
2151+ @click .option (
2152+ "-p" ,
2153+ "--public" ,
2154+ is_flag = True ,
2155+ default = None ,
2156+ required = False ,
2157+ help = "Make schema publicly visible." ,
2158+ )
2159+ @click .option (
2160+ "-s" ,
2161+ "--system" ,
2162+ is_flag = True ,
2163+ default = None ,
2164+ required = False ,
2165+ help = "Set as system schema." ,
2166+ )
2167+ @_global_output_options
2168+ def _schemaUpdate (
2169+ schema_id , new_id , definition , definition_file , description , public , system
2170+ ):
2171+ """
2172+ Update an existing schema in place without creating a new revision.
2173+ The definition may be provided inline or read from a file.
2174+ """
2175+
2176+ if definition and definition_file :
2177+ raise Exception (
2178+ "Cannot specify both --definition and --definition-file options."
2179+ )
2180+
2181+ reply = _capi .schemaUpdate (
2182+ schema_id ,
2183+ new_id = new_id ,
2184+ definition = definition ,
2185+ definition_file = definition_file ,
2186+ description = description ,
2187+ public = public if public else None ,
2188+ system = system if system else None ,
2189+ )
2190+ _generic_reply_handler (reply , _print_ack_reply )
2191+
2192+
2193+ @_schema .command (name = "delete" )
2194+ @click .option ("-f" , "--force" , is_flag = True , help = "Delete without confirmation." )
2195+ @click .argument ("schema_id" , metavar = "ID" )
2196+ def _schemaDelete (schema_id , force ):
2197+ """
2198+ Delete a schema by ID.
2199+ """
2200+
2201+ if not force :
2202+ if not _interactive :
2203+ raise Exception ("Cannot confirm deletion while running non-interactively." )
2204+
2205+ if not click .confirm ("Confirm delete schema?" ):
2206+ return
2207+
2208+ reply = _capi .schemaDelete (_resolve_id (schema_id ))
2209+ _generic_reply_handler (reply , _print_ack_reply )
2210+
2211+
2212+ @_schema .command (name = "search" )
2213+ @click .option ("--id" , "schema_id" , type = str , required = False , help = "Schema ID query text." )
2214+ @click .option ("--text" , type = str , required = False , help = "Text search in description." )
2215+ @click .option ("--owner" , type = str , required = False , help = "Filter by owner ID." )
2216+ @click .option (
2217+ "--sort" ,
2218+ type = str ,
2219+ required = False ,
2220+ help = "Sort option (id, title, owner, ct, ut, text)." ,
2221+ )
2222+ @click .option (
2223+ "--sort-rev" ,
2224+ is_flag = True ,
2225+ required = False ,
2226+ help = "Sort in reverse order (not available for text)." ,
2227+ )
2228+ @click .option ("-O" , "--offset" , default = 0 , help = "Start list at offset." )
2229+ @click .option ("-C" , "--count" , default = 20 , help = "Limit list to count results." )
2230+ @_global_output_options
2231+ def _schemaSearch (schema_id , text , owner , sort , sort_rev , offset , count ):
2232+ """
2233+ Search for schemas. At least one search option should be specified. Results
2234+ are returned as a listing of matching schemas.
2235+ """
2236+
2237+ reply = _capi .schemaSearch (
2238+ schema_id = schema_id ,
2239+ text = text ,
2240+ owner = owner ,
2241+ sort = sort ,
2242+ sort_rev = sort_rev if sort_rev else None ,
2243+ offset = offset ,
2244+ count = count ,
2245+ )
2246+ _generic_reply_handler (reply , _print_schema_listing )
2247+
2248+
2249+ @_schema .command (name = "validate" )
2250+ @click .argument ("schema_id" , metavar = "SCHEMA_ID" )
2251+ @click .option (
2252+ "-m" ,
2253+ "--metadata" ,
2254+ type = str ,
2255+ required = False ,
2256+ help = "Inline metadata JSON string to validate." ,
2257+ )
2258+ @click .option (
2259+ "-f" ,
2260+ "--metadata-file" ,
2261+ type = str ,
2262+ required = False ,
2263+ help = "Path to local JSON file containing metadata to validate." ,
2264+ )
2265+ @_global_output_options
2266+ def _schemaValidate (schema_id , metadata , metadata_file ):
2267+ """
2268+ Validate metadata against a schema without creating or modifying a record.
2269+ Useful for pre-checking metadata before using it with 'data create' or
2270+ 'data update' with --schema-enforce. The SCHEMA_ID is the schema to
2271+ validate against (format: "id:version").
2272+ """
2273+
2274+ if metadata and metadata_file :
2275+ raise Exception ("Cannot specify both --metadata and --metadata-file options." )
2276+
2277+ if not metadata and not metadata_file :
2278+ raise Exception ("Must specify either --metadata or --metadata-file." )
2279+
2280+ reply = _capi .metadataValidate (
2281+ schema_id ,
2282+ metadata = metadata ,
2283+ metadata_file = metadata_file ,
2284+ )
2285+ _generic_reply_handler (reply , _print_metadata_validate )
2286+
2287+
19992288# =============================================================================
20002289# ---------------------------------------------------------- Endpoint Functions
20012290# =============================================================================
@@ -2787,6 +3076,84 @@ def _print_query(message):
27873076 " {:<18} {}" .format ("Category: " , _arrayToDotted (message .query .cat_tags ))
27883077 )
27893078
3079+ def _print_schema_listing (message ):
3080+ if len (message .schema ) == 0 :
3081+ click .echo ("(no schemas)" )
3082+ return
3083+
3084+ df_idx = 1
3085+ global _list_items
3086+ _list_items = []
3087+
3088+ for s in message .schema :
3089+ _list_items .append (s .id )
3090+ pub_flag = " [pub]" if s .pub else ""
3091+ depr_flag = " [DEPRECATED]" if s .depr else ""
3092+ click .echo (
3093+ "{:2}. {:30} v{:<5} {:20}{}{}" .format (
3094+ df_idx ,
3095+ s .id ,
3096+ s .ver ,
3097+ s .own_nm if s .own_nm else s .own_id ,
3098+ pub_flag ,
3099+ depr_flag ,
3100+ )
3101+ )
3102+ df_idx += 1
3103+
3104+ if message .total > message .offset + message .count :
3105+ click .echo (
3106+ " [{}-{} of {}]" .format (
3107+ message .offset + 1 ,
3108+ min (message .offset + message .count , message .total ),
3109+ message .total ,
3110+ )
3111+ )
3112+
3113+
3114+ def _print_schema (message ):
3115+ for s in message .schema :
3116+ click .echo ("{:<15}{:<50}" .format ("ID: " , s .id ))
3117+ click .echo ("{:<15}{:<50}" .format ("Version: " , str (s .ver )))
3118+ click .echo ("{:<15}{:<50}" .format ("Owner: " , s .own_nm if s .own_nm else s .own_id ))
3119+ click .echo ("{:<15}{:<50}" .format ("Public: " , "Yes" if s .pub else "No" ))
3120+ click .echo ("{:<15}{:<50}" .format ("Deprecated: " , "Yes" if s .depr else "No" ))
3121+ click .echo ("{:<15}{:<50}" .format ("Ref Count: " , str (s .cnt )))
3122+
3123+ _wrap_text (s .desc if s .desc else "" , "Description:" , 15 )
3124+
3125+ if _verbosity == 2 :
3126+ schema_def = getattr (s , 'def' )
3127+ if schema_def :
3128+ click .echo ("Definition:\n " )
3129+ try :
3130+ json_obj = jsonlib .loads (schema_def )
3131+ _printJSON (json_obj , 2 , 2 )
3132+ click .echo ("\n " )
3133+ except Exception :
3134+ click .echo (" " + schema_def + "\n " )
3135+ else :
3136+ click .echo ("{:<15}{:<50}" .format ("Definition: " , "(none)" ))
3137+
3138+ if len (s .uses ):
3139+ click .echo ("Uses:" )
3140+ for ref in s .uses :
3141+ click .echo (" {} v{}" .format (ref .id , ref .ver ))
3142+
3143+ if len (s .used_by ):
3144+ click .echo ("Used By:" )
3145+ for ref in s .used_by :
3146+ click .echo (" {} v{}" .format (ref .id , ref .ver ))
3147+
3148+
3149+ def _print_metadata_validate (message ):
3150+ if message .errors :
3151+ if _output_mode == _OM_TEXT :
3152+ click .echo ("Validation FAILED:\n " )
3153+ _wrap_text (message .errors , "" , 2 )
3154+ else :
3155+ if _output_mode == _OM_TEXT :
3156+ click .echo ("Validation passed." )
27903157
27913158def _wrap_text (text , prefix , indent , compact = False ):
27923159 if len (text ) == 0 :
0 commit comments