diff --git a/README.md b/README.md index 6fa4bb7..36d10e3 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ All commands of Jagabata.psm are added "Ansible" prefix by default, like `Get-Ho > ```powershell > Import-Module Jagabata.psm -Prefix Awx > ``` -> All commands of Jagabata.psm will be added "Awx" prefix instead of "Ansibl", like `Get-Host` -> `Get-AwxHost`. +> All commands of Jagabata.psm will be added "Awx" prefix instead of "Ansible", like `Get-Host` -> `Get-AwxHost`. ### 3. Get Poersonal Access Token diff --git a/docs/en-US/CredentialType.md b/docs/en-US/CredentialType.md new file mode 100644 index 0000000..e13c2e9 --- /dev/null +++ b/docs/en-US/CredentialType.md @@ -0,0 +1,201 @@ +# Creating CredentialType + +To create CredentialType, we need to define `inputs` and `injectors` data. + +## Custom Credential Type Inputs + +The specification: https://github.com/ansible/awx/blob/devel/docs/credentials/custom_credential_types.md#defining-custom-credential-type-inputs + +> A `Credential Type` specifies an `inputs` schema which defines a set of ordered fields for that type: +> ```yaml +> "inputs": { +> "fields": [{ +> "id": "api_token", # required - a unique name used to +> # reference the field value +> +> "label": "API Token", # required - a unique label for the +> # field +> +> "help_text": "User-facing short text describing the field.", +> +> "type": ("string" | "boolean") # defaults to 'string' +> +> "format": "ssh_private_key" # optional, can be used to enforce data +> # format validity for SSH private key +> # data (only applicable to `type=string`) +> +> "secret": true, # if true, the field value will be encrypted +> +> "multiline": false # if true, the field should be rendered +> # as multi-line for input entry +> # (only applicable to `type=string`) +> +> "default": "default value" # optional, can be used to provide a +> # default value if the field is left empty; +> # when creating a credential of this type, +> # credential forms will use this value +> # as a prefill when making credentials of +> # this type +> },{ +> # field 2... +> },{ +> # field 3... +> }] +> "required": ["api_token"] # optional; one or more fields can be marked as required +> }, +> ``` +> When `type=string`, fields can optionally specify multiple choice options: +> ```yaml +> "inputs": { +> "fields": [{ +> "id": "api_token", # required - a unique name used to reference the field value +> "label": "API Token", # required - a unique label for the field +> "type": "string", +> "choices": ["A", "B", "C"] +> }] +> }, +> ``` + +There are helper classes that make the generation of these `fields` and `required` properties a little easier. + +### Classes +Namespace: `Jagabata.CredentialType`: +- `Jagabata.CredentialType.FieldList`: container for `fields` and `required` properties +- `Jagabata.CredentialType.BoolField`: boolean field (rendered as Checkbox) +- `Jagabata.CredentialType.StringField`: string input field (rendered as Input box) + - `Jagabata.CredentialType.SecretField`: confidential information input field (`secret=true`) + - `Jagabata.CredentialType.UrlField`: URL input field (`format=url`) + - `Jagabata.CredentialType.SshPrivateKeyField`: input field for SSH key (`secret=true`, `multiline=true`, `format=ssh_private_key`) +- `Jagabata.CredentialType.ChoiceField`: multiple choice field (rendered as Select box) + +### Example +```powershell +$list = [Jagabata.CredentialType.FieldList]::new(); + +# Add normal input field +$list.Add([Jagabata.CredentialType.StringField]@{ Id = "username"; Label = "User Name"; Required = $true }) + +# Add password input field +$list.Add([Jagabata.CredentialType.SecretField]@{ Id = "password"; Label = "Password"; Required = $true }) + +New-AnsibleCredentialType -Name "Demo-1" -Kind cloud -Inputs $list -WhatIf +``` + +```Output +What if: Performing the operation "New-AnsibleCredentialType" on target "{ + "name": "Demo 1", + "kind": "cloud", + "injectors": {}, + "inputs": { + "fields": [ + { + "id": "username", + "label": "User Name", + "type": "string" + }, + { + "secret": true, + "id": "password", + "label": "Password", + "type": "string" + } + ], + "required": [ + "username", + "password" + ] + } +}". +``` + +### Class Diagram + +```mermaid +classDiagram + FieldList -- FieldListConverter + FieldList o-- "*" FieldBase + FieldBase <|-- StringField + FieldBase <|-- ChoiceField + FieldBase <|-- BoolField + + StringField <|-- SecretField + StringField <|-- UrlField + StringField <|-- SshPrivateKeyField + + class FieldListConverter { + <> + ~read() + ~write() + } + class FieldList { + <> + +this[i] + } + class FieldBase { + <> + +string Id + +string Label + +FieldType Type = "string" + +string HelpText + +bool Required + } + class StringField { + +FieldType Type = "string" + +string? Default + +FieldFormat? Format + +bool? Secret + +bool? Multiline + } + class SecretField { + +bool Secret = true + } + class UrlField { + +FieldFormat? Format = "Url" + } + class SshPrivateKeyField { + +FieldFormat Format = "SshPrivateKey" + +bool Secret = true + +bool Multiline = true + } + class ChoiceField { + +FieldType Type = "string" + +string? Default + +string[] Choices + } + class BoolField { + +FieldType Type = "boolean" + +bool? Default + } + +``` +This is only an overview diagram and differs from the actual class diagram. + +## Custom Credential Type Injectors + +The specification: https://github.com/ansible/awx/blob/devel/docs/credentials/custom_credential_types.md#defining-custom-credential-type-injectors + +### Classes + +Namespace: `Jagabata.CredentialType`: +- `Jagabata.CredentialType.Injectors`: container for `env`, `extra_vars` or `file` properties + +```powershell +$injectors = [Jagabata.CredentialType.Injectors]::new(); +$injectors.Env = @{ + MY_CLOUD_INI_FILE = "{{ tower.filename }}" +} +$injectors.File = @{ + template = @( + "[mycloud]", + "token={{ api_token }}" + ) -join "`n" +} +$injectors.ExtraVars = @{ + some_extra_var = "{{ username }}:{{ password }}"; + auth = @{ + username = "{{ username }}"; + password = "{{ password }}"; + } +} +``` + diff --git a/docs/en-US/cmdlets/Find-AnsibleAccessList.md b/docs/en-US/cmdlets/Find-AnsibleAccessList.md index 1267063..72e1cde 100644 --- a/docs/en-US/cmdlets/Find-AnsibleAccessList.md +++ b/docs/en-US/cmdlets/Find-AnsibleAccessList.md @@ -20,7 +20,7 @@ Find-AnsibleAccessList [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleAccessList -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -176,7 +176,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleActivityStream.md b/docs/en-US/cmdlets/Find-AnsibleActivityStream.md index 02bbf98..e5dcfb9 100644 --- a/docs/en-US/cmdlets/Find-AnsibleActivityStream.md +++ b/docs/en-US/cmdlets/Find-AnsibleActivityStream.md @@ -26,7 +26,7 @@ Find-AnsibleActivityStream [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleActivityStream -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -207,7 +207,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleAdHocCommandJob.md b/docs/en-US/cmdlets/Find-AnsibleAdHocCommandJob.md index 81694f2..5096922 100644 --- a/docs/en-US/cmdlets/Find-AnsibleAdHocCommandJob.md +++ b/docs/en-US/cmdlets/Find-AnsibleAdHocCommandJob.md @@ -26,7 +26,7 @@ Find-AnsibleAdHocCommandJob [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleAdHocCommandJob -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -174,7 +174,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleApplication.md b/docs/en-US/cmdlets/Find-AnsibleApplication.md index b8e4907..5b1555a 100644 --- a/docs/en-US/cmdlets/Find-AnsibleApplication.md +++ b/docs/en-US/cmdlets/Find-AnsibleApplication.md @@ -26,7 +26,7 @@ Find-AnsibleApplication [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleApplication -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -173,7 +173,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleCredential.md b/docs/en-US/cmdlets/Find-AnsibleCredential.md index 63f3dd1..276f9cd 100644 --- a/docs/en-US/cmdlets/Find-AnsibleCredential.md +++ b/docs/en-US/cmdlets/Find-AnsibleCredential.md @@ -14,22 +14,23 @@ Retrieve Credentials. ### All (Default) ``` -Find-AnsibleCredential [-Kind ] [-Galaxy] [-OrderBy ] [-Search ] - [-Filter ] [-Count ] [-Page ] [-All] [] +Find-AnsibleCredential [-CredentialTypeKind ] [-CredentialTypeNamespace ] + [-Galaxy] [-OrderBy ] [-Search ] [-Filter ] [-Count ] + [-Page ] [-All] [] ``` ### AssociatedWith ``` -Find-AnsibleCredential [-Type] [-Id] [-Kind ] [-Galaxy] [-OrderBy ] - [-Search ] [-Filter ] [-Count ] [-Page ] [-All] - [] +Find-AnsibleCredential [-Type] [-Id] [-CredentialTypeKind ] + [-CredentialTypeNamespace ] [-Galaxy] [-OrderBy ] [-Search ] + [-Filter ] [-Count ] [-Page ] [-All] [] ``` ### PipelineInput ``` -Find-AnsibleCredential [-Resource] [-Kind ] [-Galaxy] [-OrderBy ] - [-Search ] [-Filter ] [-Count ] [-Page ] [-All] - [] +Find-AnsibleCredential -Resource [-CredentialTypeKind ] + [-CredentialTypeNamespace ] [-Galaxy] [-OrderBy ] [-Search ] + [-Filter ] [-Count ] [-Page ] [-All] [] ``` ## DESCRIPTION @@ -107,6 +108,49 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -CredentialTypeKind +Filter with CredentialType's `kind` field. +This parameter is the same as `-Filter credential_type__kind__in=...`. + +```yaml +Type: CredentialTypeKind[] +Parameter Sets: (All) +Aliases: Kind +Accepted values: ssh, vault, net, scm, cloud, registry, token, insights, external, kubernetes, galaxy, cryptography + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CredentialTypeNamespace +Filter with `kind` field. +This parameter is the same as `-Filter credential_type__namespace__in=...`. + +Examples.) +`ssh`, `scm`, `vault`, `net`, `awx`, `openstack`, `vmware`, `satellite6`, `gce`, `azure_rm`, +`github_token`, `gitlab_token`, `insights`, `rhv`, `controller`, `kubernetes_bearer_token`, +`registry`, `galaxy_api_token`, `gpg_public_key`, `aim`, `aws_secretsmanager_credential`, +`azure_kv`, `centrify_vault_kv`, `conjur`, `hashivault_kv`, `hashivault_ssh`, `thycotic_dsv`, +`thycotic_tss` + +> [!NOTE] +> The `kind` field of a Credential corresponds to `namespace` field of a CredentialType. + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: Namespace + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Filter Filtering various fields. @@ -161,31 +205,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -Kind -Filter with `kind` field. - -Examples.) -`ssh`, `scm`, `vault`, `net`, `awx`, `openstack`, `vmware`, `satellite6`, `gce`, `azure_rm`, -`github_token`, `gitlab_token`, `insights`, `rhv`, `controller`, `kubernetes_bearer_token`, -`registry`, `galaxy_api_token`, `gpg_public_key`, `aim`, `aws_secretsmanager_credential`, -`azure_kv`, `centrify_vault_kv`, `conjur`, `hashivault_kv`, `hashivault_ssh`, `thycotic_dsv`, -`thycotic_tss` - -> [!NOTE] -> The `kind` field of a Credential corresponds to `namespace` field of a CredentialType. - -```yaml -Type: String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -OrderBy Retrieve list in the specified orders. Use `!` prefix to sort in reverse. @@ -242,7 +261,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleGroup.md b/docs/en-US/cmdlets/Find-AnsibleGroup.md index ef60b62..38ea46f 100644 --- a/docs/en-US/cmdlets/Find-AnsibleGroup.md +++ b/docs/en-US/cmdlets/Find-AnsibleGroup.md @@ -27,7 +27,7 @@ Find-AnsibleGroup [-Type] [-Id] [-OnlyRoot] [-OnlyParnet ### PipelineInput ``` -Find-AnsibleGroup [-Resource] [-OnlyRoot] [-OnlyParnets] [-OrderBy ] [-Search ] +Find-AnsibleGroup -Resource [-OnlyRoot] [-OnlyParnets] [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -227,7 +227,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleHost.md b/docs/en-US/cmdlets/Find-AnsibleHost.md index ccbbdcc..e7958da 100644 --- a/docs/en-US/cmdlets/Find-AnsibleHost.md +++ b/docs/en-US/cmdlets/Find-AnsibleHost.md @@ -27,7 +27,7 @@ Find-AnsibleHost [-Type] [-Id] [-OnlyChildren] [-OrderBy ### PipelineInput ``` -Find-AnsibleHost [-Resource] [-OnlyChildren] [-OrderBy ] [-Search ] +Find-AnsibleHost -Resource [-OnlyChildren] [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -201,7 +201,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleInstanceGroup.md b/docs/en-US/cmdlets/Find-AnsibleInstanceGroup.md index acb9c5c..8f3a3f4 100644 --- a/docs/en-US/cmdlets/Find-AnsibleInstanceGroup.md +++ b/docs/en-US/cmdlets/Find-AnsibleInstanceGroup.md @@ -26,7 +26,7 @@ Find-AnsibleInstanceGroup [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleInstanceGroup -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -179,7 +179,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleInventory.md b/docs/en-US/cmdlets/Find-AnsibleInventory.md index fdd51f8..90abd0a 100644 --- a/docs/en-US/cmdlets/Find-AnsibleInventory.md +++ b/docs/en-US/cmdlets/Find-AnsibleInventory.md @@ -27,9 +27,8 @@ Find-AnsibleInventory [-Type] [-Id] [-Kind [-Kind ] [-OrderBy ] - [-Search ] [-Filter ] [-Count ] [-Page ] [-All] - [] +Find-AnsibleInventory -Resource [-Kind ] [-OrderBy ] [-Search ] + [-Filter ] [-Count ] [-Page ] [-All] [] ``` ## DESCRIPTION @@ -189,7 +188,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleInventorySource.md b/docs/en-US/cmdlets/Find-AnsibleInventorySource.md index 594b939..c3b3e31 100644 --- a/docs/en-US/cmdlets/Find-AnsibleInventorySource.md +++ b/docs/en-US/cmdlets/Find-AnsibleInventorySource.md @@ -26,7 +26,7 @@ Find-AnsibleInventorySource [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleInventorySource -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -173,7 +173,7 @@ Parameter Sets: PipelineVariable Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleInventoryUpdateJob.md b/docs/en-US/cmdlets/Find-AnsibleInventoryUpdateJob.md index 79364c4..e5b480a 100644 --- a/docs/en-US/cmdlets/Find-AnsibleInventoryUpdateJob.md +++ b/docs/en-US/cmdlets/Find-AnsibleInventoryUpdateJob.md @@ -26,7 +26,7 @@ Find-AnsibleInventoryUpdateJob [-Type] [-Id] [-OrderBy < ### PipelineVariable ``` -Find-AnsibleInventoryUpdateJob [-Resource] [-OrderBy ] [-Search ] +Find-AnsibleInventoryUpdateJob -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -169,7 +169,7 @@ Parameter Sets: PipelineVariable Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleJobEvent.md b/docs/en-US/cmdlets/Find-AnsibleJobEvent.md index ab82362..d11a8e5 100644 --- a/docs/en-US/cmdlets/Find-AnsibleJobEvent.md +++ b/docs/en-US/cmdlets/Find-AnsibleJobEvent.md @@ -21,7 +21,7 @@ Find-AnsibleJobEvent [-Type] [-Id] [-AdHocCommandEvent] ### PipelineInput ``` -Find-AnsibleJobEvent [-Resource] [-AdHocCommandEvent] [-OrderBy ] [-Search ] +Find-AnsibleJobEvent -Resource [-AdHocCommandEvent] [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -193,7 +193,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleJobHostSummary.md b/docs/en-US/cmdlets/Find-AnsibleJobHostSummary.md index 82f8486..2524de7 100644 --- a/docs/en-US/cmdlets/Find-AnsibleJobHostSummary.md +++ b/docs/en-US/cmdlets/Find-AnsibleJobHostSummary.md @@ -20,7 +20,7 @@ Find-AnsibleJobHostSummary [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleJobHostSummary -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -166,7 +166,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleJobTemplate.md b/docs/en-US/cmdlets/Find-AnsibleJobTemplate.md index 524be07..5a4cdc5 100644 --- a/docs/en-US/cmdlets/Find-AnsibleJobTemplate.md +++ b/docs/en-US/cmdlets/Find-AnsibleJobTemplate.md @@ -27,7 +27,7 @@ Find-AnsibleJobTemplate [-Type] [-Id] [[-Name] [[-Name] ] [-OrderBy ] [-Search ] +Find-AnsibleJobTemplate -Resource [[-Name] ] [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -189,7 +189,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleLabel.md b/docs/en-US/cmdlets/Find-AnsibleLabel.md index dd79ae0..bd2d70b 100644 --- a/docs/en-US/cmdlets/Find-AnsibleLabel.md +++ b/docs/en-US/cmdlets/Find-AnsibleLabel.md @@ -26,7 +26,7 @@ Find-AnsibleLabel [-Type] [-Id] [-OrderBy ] [- ### PipelineInput ``` -Find-AnsibleLabel [-Resource] [-OrderBy ] [-Search ] +Find-AnsibleLabel -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -174,7 +174,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleNotification.md b/docs/en-US/cmdlets/Find-AnsibleNotification.md index 1a2b8f4..0c5fa15 100644 --- a/docs/en-US/cmdlets/Find-AnsibleNotification.md +++ b/docs/en-US/cmdlets/Find-AnsibleNotification.md @@ -26,7 +26,7 @@ Find-AnsibleNotification [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleNotification -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -179,7 +179,7 @@ Parameter Sets: PipelineVariable Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForError.md b/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForError.md index 7e44a11..7003926 100644 --- a/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForError.md +++ b/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForError.md @@ -21,7 +21,7 @@ Find-AnsibleNotificationTemplateForError [-Type] [-Id] [ ### PipelineInput ``` -Find-AnsibleNotificationTemplateForError [-Resource] [-OrderBy ] [-Search ] +Find-AnsibleNotificationTemplateForError -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -167,7 +167,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForStarted.md b/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForStarted.md index 86fce59..055d3a9 100644 --- a/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForStarted.md +++ b/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForStarted.md @@ -21,7 +21,7 @@ Find-AnsibleNotificationTemplateForStarted [-Type] [-Id] ### PipelineInput ``` -Find-AnsibleNotificationTemplateForStarted [-Resource] [-OrderBy ] [-Search ] +Find-AnsibleNotificationTemplateForStarted -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -167,7 +167,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForSuccess.md b/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForSuccess.md index cb272aa..da354da 100644 --- a/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForSuccess.md +++ b/docs/en-US/cmdlets/Find-AnsibleNotificationTemplateForSuccess.md @@ -21,7 +21,7 @@ Find-AnsibleNotificationTemplateForSuccess [-Type] [-Id] ### PipelineInput ``` -Find-AnsibleNotificationTemplateForSuccess [-OrderBy ] [-Resource] [-Search ] +Find-AnsibleNotificationTemplateForSuccess [-OrderBy ] -Resource [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -167,7 +167,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleObjectRole.md b/docs/en-US/cmdlets/Find-AnsibleObjectRole.md index c9560fa..1bc2ab7 100644 --- a/docs/en-US/cmdlets/Find-AnsibleObjectRole.md +++ b/docs/en-US/cmdlets/Find-AnsibleObjectRole.md @@ -20,7 +20,7 @@ Find-AnsibleObjectRole [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleObjectRole -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -169,7 +169,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleProject.md b/docs/en-US/cmdlets/Find-AnsibleProject.md index 9720904..4cfff4f 100644 --- a/docs/en-US/cmdlets/Find-AnsibleProject.md +++ b/docs/en-US/cmdlets/Find-AnsibleProject.md @@ -26,7 +26,7 @@ Find-AnsibleProject [-Type] [-Id] [-OrderBy ] ### PipelineVariable ``` -Find-AnsibleProject [-Resource] [-OrderBy ] [-Search ] +Find-AnsibleProject -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -171,7 +171,7 @@ Parameter Sets: PipelineVariable Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleRole.md b/docs/en-US/cmdlets/Find-AnsibleRole.md index 773d4c3..acf4d88 100644 --- a/docs/en-US/cmdlets/Find-AnsibleRole.md +++ b/docs/en-US/cmdlets/Find-AnsibleRole.md @@ -26,7 +26,7 @@ Find-AnsibleRole [-Type] [-Id] [-OrderBy ] [-S ### PipelineInput ``` -Find-AnsibleRole [-Resource] [-OrderBy ] [-Search ] +Find-AnsibleRole -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -169,7 +169,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleSchedule.md b/docs/en-US/cmdlets/Find-AnsibleSchedule.md index 7675950..8f2c928 100644 --- a/docs/en-US/cmdlets/Find-AnsibleSchedule.md +++ b/docs/en-US/cmdlets/Find-AnsibleSchedule.md @@ -26,7 +26,7 @@ Find-AnsibleSchedule [-Type] [-Id] [-OrderBy ] ### PipelineInput ``` -Find-AnsibleSchedule [-Resource] [-OrderBy ] [-Search ] +Find-AnsibleSchedule -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -175,7 +175,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleTeam.md b/docs/en-US/cmdlets/Find-AnsibleTeam.md index 3a45006..cea9f8d 100644 --- a/docs/en-US/cmdlets/Find-AnsibleTeam.md +++ b/docs/en-US/cmdlets/Find-AnsibleTeam.md @@ -26,7 +26,7 @@ Find-AnsibleTeam [-Type] [-Id] [-OrderBy ] [-S ### PipelineInput ``` -Find-AnsibleTeam [-Resource] [-OrderBy ] [-Search ] +Find-AnsibleTeam -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -175,7 +175,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleToken.md b/docs/en-US/cmdlets/Find-AnsibleToken.md index 71344d1..74dbe34 100644 --- a/docs/en-US/cmdlets/Find-AnsibleToken.md +++ b/docs/en-US/cmdlets/Find-AnsibleToken.md @@ -27,7 +27,7 @@ Find-AnsibleToken [-Type] [-Id] [-TokenType ### PipelineInput ``` -Find-AnsibleToken [-Resource] [-TokenType ] [-OrderBy ] [-Search ] +Find-AnsibleToken -Resource [-TokenType ] [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -170,7 +170,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleUnifiedJob.md b/docs/en-US/cmdlets/Find-AnsibleUnifiedJob.md index 22de60c..6759925 100644 --- a/docs/en-US/cmdlets/Find-AnsibleUnifiedJob.md +++ b/docs/en-US/cmdlets/Find-AnsibleUnifiedJob.md @@ -26,7 +26,7 @@ Find-AnsibleUnifiedJob [-Type] [-Id] [-OrderBy [-OrderBy ] [-Search ] +Find-AnsibleUnifiedJob -Resource [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -181,7 +181,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleUser.md b/docs/en-US/cmdlets/Find-AnsibleUser.md index 831fcb3..1349a76 100644 --- a/docs/en-US/cmdlets/Find-AnsibleUser.md +++ b/docs/en-US/cmdlets/Find-AnsibleUser.md @@ -27,7 +27,7 @@ Find-AnsibleUser [-Type] [-Id] [[-UserName] ] ### PipelineInput ``` -Find-AnsibleUser [-Resource] [[-UserName] ] [[-Email] ] [-OrderBy ] +Find-AnsibleUser -Resource [[-UserName] ] [[-Email] ] [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] [-All] [] ``` @@ -192,7 +192,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/Find-AnsibleWorkflowJob.md b/docs/en-US/cmdlets/Find-AnsibleWorkflowJob.md index c1effd5..0f6a7ac 100644 --- a/docs/en-US/cmdlets/Find-AnsibleWorkflowJob.md +++ b/docs/en-US/cmdlets/Find-AnsibleWorkflowJob.md @@ -28,9 +28,9 @@ Find-AnsibleWorkflowJob [-Type] [-Id] [-Name ] ### PipelineInput ``` -Find-AnsibleWorkflowJob [-Resource] [-Name ] [-Status ] - [-LaunchType ] [-OrderBy ] [-Search ] [-Filter ] - [-Count ] [-Page ] [-All] [] +Find-AnsibleWorkflowJob -Resource [-Name ] [-Status ] [-LaunchType ] + [-OrderBy ] [-Search ] [-Filter ] [-Count ] [-Page ] + [-All] [] ``` ## DESCRIPTION @@ -206,7 +206,7 @@ Parameter Sets: PipelineInput Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByValue) Accept wildcard characters: False diff --git a/docs/en-US/cmdlets/New-AnsibleCredentialType.md b/docs/en-US/cmdlets/New-AnsibleCredentialType.md index 65abe27..78073ef 100644 --- a/docs/en-US/cmdlets/New-AnsibleCredentialType.md +++ b/docs/en-US/cmdlets/New-AnsibleCredentialType.md @@ -50,6 +50,9 @@ Accept wildcard characters: False ``` ### -Injectors +Custom Credential Type Injectors object which raw Dictionary or `Jagabata.CredentialType.Injectors`. + +See: https://github.com/teramako/Jagabata.psm/blob/develop/docs/en-US/CredentialType.md ```yaml Type: IDictionary @@ -64,6 +67,9 @@ Accept wildcard characters: False ``` ### -Inputs +Custom Credential Type Inputs object which raw Dictionary or `Jagabata.CredentialType.FieldList`. + +See: https://github.com/teramako/Jagabata.psm/blob/develop/docs/en-US/CredentialType.md ```yaml Type: IDictionary diff --git a/docs/en-US/cmdlets/New-AnsibleUser.md b/docs/en-US/cmdlets/New-AnsibleUser.md index 1d5fed1..e169251 100644 --- a/docs/en-US/cmdlets/New-AnsibleUser.md +++ b/docs/en-US/cmdlets/New-AnsibleUser.md @@ -47,14 +47,14 @@ Input UserName and Password via prompts. ### Example 2 ```powershell -PS C:\> New-AnsibleUser -Credential (Microsoft.PowerShell.Security\Get-AnsibleCredential -UserName user2) +PS C:\> New-AnsibleUser -Credential (Get-Credential -UserName user2) PowerShell credential request Enter your credentials. Password for user user2: ******** ``` -Input UserName and Password via `Microsoft.PowerShell.Security\Get-AnsibleCredential`. +Input UserName and Password via `Microsoft.PowerShell.Security\Get-Credential`. ### Example 3 ```powershell @@ -71,7 +71,7 @@ Input Password via a prompt. ### -Credential Credential (UserName and Password) -See: `Get-AnsibleHelp Microsoft.PowerShell.Security\Get-AnsibleCredential`. +See: `Get-Help Get-Credential`. ```yaml Type: PSCredential diff --git a/docs/en-US/cmdlets/Update-AnsibleCredentialType.md b/docs/en-US/cmdlets/Update-AnsibleCredentialType.md index 2f34323..6f01a1b 100644 --- a/docs/en-US/cmdlets/Update-AnsibleCredentialType.md +++ b/docs/en-US/cmdlets/Update-AnsibleCredentialType.md @@ -63,6 +63,9 @@ Accept wildcard characters: False ``` ### -Injectors +Custom Credential Type Injectors object which raw Dictionary or `Jagabata.CredentialType.Injectors`. + +See: https://github.com/teramako/Jagabata.psm/blob/develop/docs/en-US/CredentialType.md ```yaml Type: IDictionary @@ -77,6 +80,9 @@ Accept wildcard characters: False ``` ### -Inputs +Custom Credential Type Inputs object which raw Dictionary or `Jagabata.CredentialType.FieldList`. + +See: https://github.com/teramako/Jagabata.psm/blob/develop/docs/en-US/CredentialType.md ```yaml Type: IDictionary diff --git a/docs/en-US/cmdlets/Update-AnsibleUser.md b/docs/en-US/cmdlets/Update-AnsibleUser.md index 4cb1f70..6aa0b1a 100644 --- a/docs/en-US/cmdlets/Update-AnsibleUser.md +++ b/docs/en-US/cmdlets/Update-AnsibleUser.md @@ -124,7 +124,7 @@ Accept wildcard characters: False ``` ### -Password -Specify `SecureString` object. generated by commands such as `ConvertTo-SecureString` or `(Microsoft.PowerShell.Security\Get-AnsibleCredential).Password`. +Specify `SecureString` object. generated by commands such as `ConvertTo-SecureString` or `(Get-Credential).Password`. ```yaml Type: SecureString diff --git a/src/Jagabata/Cmdlets/ActivityStreamCommand.cs b/src/Jagabata/Cmdlets/ActivityStreamCommand.cs index b13998f..b061d94 100644 --- a/src/Jagabata/Cmdlets/ActivityStreamCommand.cs +++ b/src/Jagabata/Cmdlets/ActivityStreamCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -47,7 +48,7 @@ public class FindActivityStreamCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.OAuth2Application, ResourceType.OAuth2AccessToken, @@ -71,6 +72,7 @@ public class FindActivityStreamCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "timestamp", "operation", "changes", "object1", "object2", "action_node"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void BeginProcessing() @@ -94,7 +96,7 @@ protected override void ProcessRecord() ResourceType.Project => $"{Project.PATH}{Id}/activity_stream/", ResourceType.Team => $"{Team.PATH}{Id}/activity_stream/", ResourceType.Credential => $"{Credential.PATH}{Id}/activity_stream/", - ResourceType.CredentialType => $"{CredentialType.PATH}{Id}/activity_stream/", + ResourceType.CredentialType => $"{Resources.CredentialType.PATH}{Id}/activity_stream/", ResourceType.Inventory => $"{Inventory.PATH}{Id}/activity_stream/", ResourceType.InventorySource => $"{InventorySource.PATH}{Id}/activity_stream/", ResourceType.Group => $"{Group.PATH}{Id}/activity_stream/", diff --git a/src/Jagabata/Cmdlets/AdHocCommandCommand.cs b/src/Jagabata/Cmdlets/AdHocCommandCommand.cs index 80fcf8a..4c1783c 100644 --- a/src/Jagabata/Cmdlets/AdHocCommandCommand.cs +++ b/src/Jagabata/Cmdlets/AdHocCommandCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Collections; using System.Management.Automation; @@ -30,7 +31,7 @@ public class FindAdHocCommandJobCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Inventory, ResourceType.Host, @@ -39,6 +40,12 @@ public class FindAdHocCommandJobCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "launch_type", "status", "execution_environment", + "failed", "started", "finished", "canceled_on", "elapsed", "job_explanation", + "execution_node", "controller_node", "work_unit_id", "job_type", "inventory", "limit", + "credential", "module_name", "module_args", "forks", "verbosity", "become_enabled", + "diff_mode", "hosts", "organization", "schedule", "created_by", "modified_by", + "instance_group", "labels"])] public override string[] OrderBy { get; set; } = ["!id"]; diff --git a/src/Jagabata/Cmdlets/ApplicationCommand.cs b/src/Jagabata/Cmdlets/ApplicationCommand.cs index d6f3e07..ead5f72 100644 --- a/src/Jagabata/Cmdlets/ApplicationCommand.cs +++ b/src/Jagabata/Cmdlets/ApplicationCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -30,7 +31,7 @@ public class FindApplicationCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.User @@ -38,6 +39,8 @@ public class FindApplicationCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "name", "description", "client_type", "redirect_uris", + "authorization_grant_type", "skip_authorization", "organization", "user"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/ArgumentTransformation/DictionaryTransformation.cs b/src/Jagabata/Cmdlets/ArgumentTransformation/DictionaryTransformation.cs new file mode 100644 index 0000000..c0d96e5 --- /dev/null +++ b/src/Jagabata/Cmdlets/ArgumentTransformation/DictionaryTransformation.cs @@ -0,0 +1,38 @@ +using System.Collections; +using System.Management.Automation; +using System.Text.Json; + +namespace Jagabata.Cmdlets.ArgumentTransformation; + +internal class DictionaryTransformationAttribute : ArgumentTransformationAttribute +{ + public DictionaryTransformationAttribute(params Type[] types) + { + Types = types; + } + + protected Type[] Types { get; init; } + + public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + { + if (inputData is PSObject pso && pso.BaseObject is not null) + { + inputData = pso.BaseObject; + } + if (inputData is IDictionary dict) + { + return dict; + } + foreach (var t in Types) + { + if (inputData.GetType() != t) + { + continue; + } + var jsonElm = JsonSerializer.SerializeToElement(inputData, t, Json.SerializeOptions); + return JsonSerializer.Deserialize>(jsonElm, Json.DeserializeOptions) + ?? throw new ArgumentException("result is null"); + } + throw new ArgumentException($"{nameof(inputData)} should be one of [IDictionary, {string.Join(", ", Types.Select(static t => t.Name))}]"); + } +} diff --git a/src/Jagabata/Cmdlets/ArgumentTransformation/ExtraVarsArgumentTransformation.cs b/src/Jagabata/Cmdlets/ArgumentTransformation/ExtraVarsArgumentTransformation.cs index 8249aff..ade6efd 100644 --- a/src/Jagabata/Cmdlets/ArgumentTransformation/ExtraVarsArgumentTransformation.cs +++ b/src/Jagabata/Cmdlets/ArgumentTransformation/ExtraVarsArgumentTransformation.cs @@ -4,7 +4,7 @@ namespace Jagabata.Cmdlets.ArgumentTransformation { - public class ExtraVarsArgumentTransformationAttribute : ArgumentTransformationAttribute + internal class ExtraVarsArgumentTransformationAttribute : ArgumentTransformationAttribute { public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) { diff --git a/src/Jagabata/Cmdlets/ArgumentTransformation/FilterArgumentTransformation.cs b/src/Jagabata/Cmdlets/ArgumentTransformation/FilterArgumentTransformation.cs index 0aad60a..7c1637c 100644 --- a/src/Jagabata/Cmdlets/ArgumentTransformation/FilterArgumentTransformation.cs +++ b/src/Jagabata/Cmdlets/ArgumentTransformation/FilterArgumentTransformation.cs @@ -40,7 +40,7 @@ namespace Jagabata.Cmdlets.ArgumentTransformation /// /// /// - public class FilterArgumentTransformationAttribute : ArgumentTransformationAttribute + internal class FilterArgumentTransformationAttribute : ArgumentTransformationAttribute { public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) { @@ -66,9 +66,10 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input } continue; case string str: - foreach (var f in GetQueries(HttpUtility.ParseQueryString(str))) + foreach (var kv in str.Split('&')) { - queries.Add(f.GetKey(), f.GetValue()); + var filter = Filter.Parse(kv); + queries.Add(filter.GetKey(), filter.GetValue()); } continue; default: diff --git a/src/Jagabata/Cmdlets/ArgumentTransformation/ResourceTransformation.cs b/src/Jagabata/Cmdlets/ArgumentTransformation/ResourceTransformation.cs index e250ea2..f236394 100644 --- a/src/Jagabata/Cmdlets/ArgumentTransformation/ResourceTransformation.cs +++ b/src/Jagabata/Cmdlets/ArgumentTransformation/ResourceTransformation.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Globalization; using System.Management.Automation; using Jagabata.Resources; @@ -18,7 +19,7 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input return TransformToId(inputData); } } - private IList TransformList(IList list) + private List TransformList(IList list) { var arr = new List(); foreach (var inputItem in list) @@ -30,19 +31,18 @@ private IList TransformList(IList list) private ulong TransformToId(object inputData) { if (inputData is PSObject pso) + { inputData = pso.BaseObject; + } switch (inputData) { case int: case long: - if (ulong.TryParse($"{inputData}", out var id)) - return id; - throw new ArgumentException(); + return ulong.Parse($"{inputData}", CultureInfo.InvariantCulture); case uint: case ulong: - id = (ulong)inputData; - return id; + return (ulong)inputData; } var resource = TransformToResource(inputData); @@ -94,19 +94,26 @@ protected IList TransformToList(IList list) protected IResource TransformToResource(object inputData) { if (inputData is PSObject pso && pso.BaseObject is not PSCustomObject) + { inputData = pso.BaseObject; + } (ResourceType Type, ulong Id) resourceData = (ResourceType.None, 0); switch (inputData) { + case string str: + return Resource.Parse(str, CultureInfo.InvariantCulture); case IResource resource: return Validate(resource); case IDictionary dict: foreach (var key in dict.Keys) { - var strKey = key as string; - if (strKey is null) continue; + if (key is not string strKey) + { + continue; + } + switch (strKey.ToLowerInvariant()) { case "type": @@ -123,7 +130,9 @@ protected IResource TransformToResource(object inputData) break; } if (resourceData.Id > 0 && resourceData.Type > 0) + { break; + } } break; case PSObject ps: @@ -145,7 +154,9 @@ protected IResource TransformToResource(object inputData) break; } if (resourceData.Id > 0 && resourceData.Type > 0) + { break; + } } break; default: @@ -172,7 +183,9 @@ protected IResource TransformToResource(object inputData) break; } if (resourceData.Id > 0 && resourceData.Type > 0) + { break; + } } } break; diff --git a/src/Jagabata/Cmdlets/Completer/OrderByCompleter.cs b/src/Jagabata/Cmdlets/Completer/OrderByCompleter.cs new file mode 100644 index 0000000..485b95e --- /dev/null +++ b/src/Jagabata/Cmdlets/Completer/OrderByCompleter.cs @@ -0,0 +1,33 @@ +using System.Collections; +using System.Management.Automation; +using System.Management.Automation.Language; + +namespace Jagabata.Cmdlets.Completer; + +internal class OrderByCompleter : IArgumentCompleter +{ + public OrderByCompleter(params string[] keys) + { + Keys = keys; + } + public string[] Keys { get; init; } + public IEnumerable CompleteArgument(string commandName, string parameterName, + string wordToComplete, CommandAst commandAst, + IDictionary fakeBoundParameters) + { + var word = wordToComplete.StartsWith('!') + ? wordToComplete[1..].ToLowerInvariant() + : wordToComplete.ToLowerInvariant(); + foreach (var key in Keys) + { + if (!key.StartsWith(word, StringComparison.InvariantCulture)) + { + continue; + } + + yield return new CompletionResult(key, key, CompletionResultType.Keyword, $"Order by {key} ascending"); + var descendingProp = '!' + key; + yield return new CompletionResult(descendingProp, descendingProp, CompletionResultType.Keyword, $"Order by {key} descending"); + } + } +} diff --git a/src/Jagabata/Cmdlets/Completer/OrderByCompletionAttribute.cs b/src/Jagabata/Cmdlets/Completer/OrderByCompletionAttribute.cs new file mode 100644 index 0000000..7eea5b4 --- /dev/null +++ b/src/Jagabata/Cmdlets/Completer/OrderByCompletionAttribute.cs @@ -0,0 +1,12 @@ +using System.Management.Automation; + +namespace Jagabata.Cmdlets.Completer; + +internal class OrderByCompletionAttribute : ArgumentCompleterAttribute, IArgumentCompleterFactory +{ + public string[] Keys { get; init; } = []; + public IArgumentCompleter Create() + { + return new OrderByCompleter(Keys); + } +} diff --git a/src/Jagabata/Cmdlets/CredentialCommand.cs b/src/Jagabata/Cmdlets/CredentialCommand.cs index 578572a..9da621b 100644 --- a/src/Jagabata/Cmdlets/CredentialCommand.cs +++ b/src/Jagabata/Cmdlets/CredentialCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Collections; using System.Management.Automation; @@ -41,7 +42,7 @@ public class FindCredentialCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.User, @@ -58,12 +59,24 @@ public class FindCredentialCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] - public string? Kind { get; set; } + [Alias("Kind")] + public CredentialTypeKind[]? CredentialTypeKind { get; set; } + + [Parameter()] + [ArgumentCompletions("aim", "aws", "aws_secretsmanager_credential", "azure_kv", "azure_rm", "centrify_vault_kv", + "conjur", "controller", "galaxy_api_token", "gce", "github_token", "gitlab_token", + "gpg_public_key", "hashivault_kv", "hashivault_ssh", "insights", "kubernetes_bearer_token", + "net", "openstack", "registry", "rhv", "satellite6", "scm", "ssh", "thycotic_dsv", + "thycotic_tss", "vault", "vmware")] + [Alias("Namespace")] + public string[]? CredentialTypeNamespace { get; set; } /// /// Only affected for an Organization /// [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", + "credential_type", "managed", "created_by", "modified_by"])] public SwitchParameter Galaxy { get; set; } [Parameter()] @@ -71,9 +84,13 @@ public class FindCredentialCommand : FindCommandBase protected override void BeginProcessing() { - if (Kind is not null) + if (CredentialTypeKind is not null) + { + Query.Add("credential_type__kind__in", string.Join(',', CredentialTypeKind)); + } + if (CredentialTypeNamespace is not null) { - Query.Add("chain__credential_type__namespace__icontains", Kind); + Query.Add("credential_type__namespace__in", string.Join(',', CredentialTypeNamespace)); } SetupCommonQuery(); } @@ -90,7 +107,7 @@ protected override void ProcessRecord() ResourceType.Organization => $"{Organization.PATH}{Id}/" + (Galaxy ? "galaxy_credentials/" : "credentials/"), ResourceType.User => $"{User.PATH}{Id}/credentials/", ResourceType.Team => $"{Team.PATH}{Id}/credentials/", - ResourceType.CredentialType => $"{CredentialType.PATH}{Id}/credentials/", + ResourceType.CredentialType => $"{Resources.CredentialType.PATH}{Id}/credentials/", ResourceType.InventorySource => $"{InventorySource.PATH}{Id}/credentials/", ResourceType.InventoryUpdate => $"{InventoryUpdateJob.PATH}{Id}/credentials/", ResourceType.JobTemplate => $"{JobTemplate.PATH}{Id}/credentials/", @@ -191,7 +208,7 @@ public class RegisterCredentialCommand : RegistrationCommandBase ResourceType.Schedule, ResourceType.WorkflowJobTemplateNode ])] - public IResource To { get; set; } = new Resource(0 ,0); + public IResource To { get; set; } = new Resource(0, 0); protected override void ProcessRecord() { diff --git a/src/Jagabata/Cmdlets/CredentialInputSourceCommand.cs b/src/Jagabata/Cmdlets/CredentialInputSourceCommand.cs index acebcac..ba56def 100644 --- a/src/Jagabata/Cmdlets/CredentialInputSourceCommand.cs +++ b/src/Jagabata/Cmdlets/CredentialInputSourceCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -29,6 +30,8 @@ public class FindCredentialInputSourceCommand : FindCommandBase public ulong Credential { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "description", "input_field_name", + "metadata", "target_credential", "source_credential"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/CredentialTypeCommand.cs b/src/Jagabata/Cmdlets/CredentialTypeCommand.cs index 8a7f340..75c951c 100644 --- a/src/Jagabata/Cmdlets/CredentialTypeCommand.cs +++ b/src/Jagabata/Cmdlets/CredentialTypeCommand.cs @@ -1,4 +1,6 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; +using Jagabata.CredentialType; using Jagabata.Resources; using System.Collections; using System.Management.Automation; @@ -6,8 +8,8 @@ namespace Jagabata.Cmdlets { [Cmdlet(VerbsCommon.Get, "CredentialType")] - [OutputType(typeof(CredentialType))] - public class GetCredentialTypeCommand : GetCommandBase + [OutputType(typeof(Resources.CredentialType))] + public class GetCredentialTypeCommand : GetCommandBase { protected override ResourceType AcceptType => ResourceType.CredentialType; @@ -22,13 +24,15 @@ protected override void EndProcessing() } [Cmdlet(VerbsCommon.Find, "CredentialType")] - [OutputType(typeof(CredentialType))] + [OutputType(typeof(Resources.CredentialType))] public class FindCredentialTypeCommand : FindCommandBase { [Parameter()] public CredentialTypeKind[]? Kind { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "kind", "namespace", + "managed", "inputs", "injectors", "created_by", "modified_by"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -41,13 +45,13 @@ protected override void BeginProcessing() } protected override void ProcessRecord() { - Find(CredentialType.PATH); + Find(Resources.CredentialType.PATH); } } [Cmdlet(VerbsCommon.New, "CredentialType", SupportsShouldProcess = true)] - [OutputType(typeof(CredentialType))] - public class NewCredentialTypeCommand : NewCommandBase + [OutputType(typeof(Resources.CredentialType))] + public class NewCredentialTypeCommand : NewCommandBase { [Parameter(Mandatory = true, Position = 0)] public string Name { get; set; } = string.Empty; @@ -61,9 +65,11 @@ public class NewCredentialTypeCommand : NewCommandBase public string Kind { get; set; } = string.Empty; [Parameter()] + [DictionaryTransformation(typeof(FieldList))] public IDictionary Inputs { get; set; } = new Hashtable(); [Parameter()] + [DictionaryTransformation(typeof(Injectors))] public IDictionary Injectors { get; set; } = new Hashtable(); protected override Dictionary CreateSendData() @@ -75,8 +81,11 @@ protected override Dictionary CreateSendData() { "inputs", Inputs }, { "injectors", Injectors } }; + if (Description is not null) + { sendData.Add("description", Description); + } return sendData; } @@ -92,7 +101,7 @@ protected override void ProcessRecord() [Cmdlet(VerbsCommon.Remove, "CredentialType", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] [OutputType(typeof(void))] - public class RemoveCredentialTypeCommand : RemoveCommandBase + public class RemoveCredentialTypeCommand : RemoveCommandBase { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] [ResourceIdTransformation(AcceptableTypes = [ResourceType.CredentialType])] @@ -105,8 +114,8 @@ protected override void ProcessRecord() } [Cmdlet(VerbsData.Update, "CredentialType", SupportsShouldProcess = true)] - [OutputType(typeof(CredentialType))] - public class UpdateCredentialTypeCommand : UpdateCommandBase + [OutputType(typeof(Resources.CredentialType))] + public class UpdateCredentialTypeCommand : UpdateCommandBase { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] [ResourceIdTransformation(AcceptableTypes = [ResourceType.CredentialType])] @@ -124,9 +133,11 @@ public class UpdateCredentialTypeCommand : UpdateCommandBase public string? Kind { get; set; } [Parameter()] + [DictionaryTransformation(typeof(FieldList))] public IDictionary? Inputs { get; set; } [Parameter()] + [DictionaryTransformation(typeof(Injectors))] public IDictionary? Injectors { get; set; } protected override Dictionary CreateSendData() diff --git a/src/Jagabata/Cmdlets/ExecutionEnvironmentCommand.cs b/src/Jagabata/Cmdlets/ExecutionEnvironmentCommand.cs index fbead76..587d8fb 100644 --- a/src/Jagabata/Cmdlets/ExecutionEnvironmentCommand.cs +++ b/src/Jagabata/Cmdlets/ExecutionEnvironmentCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -28,6 +29,8 @@ public class FindExecutionEnvironmentCommand : FindCommandBase public ulong Organization { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", + "image", "managed", "credential", "pull"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/GroupCommand.cs b/src/Jagabata/Cmdlets/GroupCommand.cs index 8720723..eb75687 100644 --- a/src/Jagabata/Cmdlets/GroupCommand.cs +++ b/src/Jagabata/Cmdlets/GroupCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -34,7 +35,7 @@ public class FindGroupCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceIdTransformation(AcceptableTypes = [ ResourceType.Inventory, ResourceType.Group, @@ -60,6 +61,8 @@ public class FindGroupCommand : FindCommandBase public SwitchParameter OnlyParnets { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "inventory", "variables", + "parents", "created_by", "modified_by", "children", "hosts"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/HostCommand.cs b/src/Jagabata/Cmdlets/HostCommand.cs index 218f031..5e6d489 100644 --- a/src/Jagabata/Cmdlets/HostCommand.cs +++ b/src/Jagabata/Cmdlets/HostCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -33,7 +34,7 @@ public class FindHostCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Inventory, ResourceType.InventorySource, @@ -50,6 +51,9 @@ public class FindHostCommand : FindCommandBase public SwitchParameter OnlyChildren { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "inventory", "enabled", + "instance_id", "variables", "last_job", "last_job_host_summary", + "ansible_facts_modified", "groups", "created_by", "modified_by"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/HostMetricsCommand.cs b/src/Jagabata/Cmdlets/HostMetricsCommand.cs index 73876d1..c022152 100644 --- a/src/Jagabata/Cmdlets/HostMetricsCommand.cs +++ b/src/Jagabata/Cmdlets/HostMetricsCommand.cs @@ -1,3 +1,4 @@ +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -24,6 +25,8 @@ protected override void EndProcessing() public class FindHostMetricCommand : FindCommandBase { [Parameter()] + [OrderByCompletion(Keys = ["id", "hostname", "first_automation", "last_automation", "last_deleted", + "automated_counter", "deleted_counter", "deleted", "used_in_inventories"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/InstanceCommand.cs b/src/Jagabata/Cmdlets/InstanceCommand.cs index 969c696..9495419 100644 --- a/src/Jagabata/Cmdlets/InstanceCommand.cs +++ b/src/Jagabata/Cmdlets/InstanceCommand.cs @@ -1,3 +1,4 @@ +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -27,6 +28,10 @@ public class FindInstanceCommand : FindCommandBase public ulong InstanceGroup { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "hostname", "uuid", "created", "modified", "last_seen", "health_check_started", + "last_health_check", "errors", "capacity_adjustment", "version", "capacity", "cpu", + "memory", "cpu_capacity", "mem_capacity", "enabled", "managed_by_policy", "node_type", + "node_state", "ip_address", "listener_port", "peers_from_control_nodes"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/InstanceGroupCommand.cs b/src/Jagabata/Cmdlets/InstanceGroupCommand.cs index cfff4da..6ca632b 100644 --- a/src/Jagabata/Cmdlets/InstanceGroupCommand.cs +++ b/src/Jagabata/Cmdlets/InstanceGroupCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -36,7 +37,7 @@ public class FindInstanceGroupCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Instance, ResourceType.Organization, @@ -49,6 +50,9 @@ public class FindInstanceGroupCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "name", "created", "modified", "max_concurrent_jobs", "max_forks", + "is_container_group", "credential", "policy_instance_percentage", + "policy_instance_minimum", "policy_instance_list"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/InventoryCommand.cs b/src/Jagabata/Cmdlets/InventoryCommand.cs index 4b1cf2c..3a3b2a6 100644 --- a/src/Jagabata/Cmdlets/InventoryCommand.cs +++ b/src/Jagabata/Cmdlets/InventoryCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -33,7 +34,7 @@ public class FindInventoryCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.Inventory, @@ -45,6 +46,11 @@ public class FindInventoryCommand : FindCommandBase public InventoryKind Kind { get; set; } = InventoryKind.All; [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", "kind", + "host_filter", "variables", "has_active_failures", "total_hosts", + "hosts_with_active_failures", "total_groups", "has_inventory_sources", + "total_inventory_sources", "inventory_sources_with_failures", "pending_deletion", + "prevent_instance_group_fallback", "created_by", "modified_by"])] public override string[] OrderBy { get; set; } = ["id"]; public enum InventoryKind diff --git a/src/Jagabata/Cmdlets/InventorySourceCommand.cs b/src/Jagabata/Cmdlets/InventorySourceCommand.cs index b26fad4..bfa1c54 100644 --- a/src/Jagabata/Cmdlets/InventorySourceCommand.cs +++ b/src/Jagabata/Cmdlets/InventorySourceCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -33,7 +34,7 @@ public class FindInventorySourceCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineVariable", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineVariable", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Project, ResourceType.Inventory, @@ -43,6 +44,11 @@ public class FindInventorySourceCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "source", "source_path", + "source_vars", "scm_branch", "enabled_var", "enabled_value", "host_filter", "overwrite", + "overwrite_vars", "timeout", "verbosity", "limit", "last_job_run", "last_job_failed", + "next_job_run", "status", "execution_environment", "inventory", "update_on_launch", + "update_cache_timeout", "source_project"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/InventoryUpdateCommand.cs b/src/Jagabata/Cmdlets/InventoryUpdateCommand.cs index 5ff56e2..55512e1 100644 --- a/src/Jagabata/Cmdlets/InventoryUpdateCommand.cs +++ b/src/Jagabata/Cmdlets/InventoryUpdateCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -28,7 +29,7 @@ public class FindInventoryUpdateJobCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineVariable", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineVariable", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.ProjectUpdate, ResourceType.InventorySource @@ -36,6 +37,12 @@ public class FindInventoryUpdateJobCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "unified_job_template", "launch_type", "status", "execution_environment", "failed", + "started", "finished", "canceled_on", "elapsed", "job_explanation", "execution_node", + "controller_node", "work_unit_id", "source", "source_path", "source_vars", "scm_branch", + "enabled_var", "enabled_value", "enabled_value", "overwrite", "overwrite_vars", + "timeout", "verbosity", "limit", "inventory", "inventory_source", "license_error", + "org_host_limit_error", "source_project_update", "instance_group", "scm_revision"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/JobCommand.cs b/src/Jagabata/Cmdlets/JobCommand.cs index 46ea23b..70f8666 100644 --- a/src/Jagabata/Cmdlets/JobCommand.cs +++ b/src/Jagabata/Cmdlets/JobCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -37,6 +38,15 @@ public class FindJobTemplateJobCommand : FindCommandBase public string[]? LaunchType { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "unified_job_template", + "launch_type", "status", "execution_environment", "failed", "started", "finished", + "canceled_on", "elapsed", "job_explanation", "execution_node", "controller_node", + "work_unit_id", "job_type", "inventory", "project", "playbook", "scm_branch", "forks", + "limit", "verbosity", "job_tags", "force_handlers", "skip_tags", "start_at_task", + "timeout", "use_fact_cache", "organization", "job_template", "allow_simultaneous", + "artifacts", "scm_revision", "instance_group", "diff_mode", "job_slice_number", + "job_slice_count", "webhook_service", "webhook_credential", "webhook_credential", + "schedule", "created_by", "modified_by", "labels"])] public override string[] OrderBy { get; set; } = ["!id"]; diff --git a/src/Jagabata/Cmdlets/JobEventCommand.cs b/src/Jagabata/Cmdlets/JobEventCommand.cs index f700f0c..4fe731a 100644 --- a/src/Jagabata/Cmdlets/JobEventCommand.cs +++ b/src/Jagabata/Cmdlets/JobEventCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -21,7 +22,7 @@ public class FindJobEventCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } = 0; - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Job, ResourceType.ProjectUpdate, @@ -37,6 +38,8 @@ public class FindJobEventCommand : FindCommandBase public SwitchParameter AdHocCommandEvent { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "event", "counter", "event_data", "failed", "changed", + "uuid", "stdout", "start_line", "end_line", "verbosity"])] public override string[] OrderBy { get; set; } = ["counter"]; protected override void ProcessRecord() diff --git a/src/Jagabata/Cmdlets/JobHostSummaryCommand.cs b/src/Jagabata/Cmdlets/JobHostSummaryCommand.cs index 4c17a54..3821768 100644 --- a/src/Jagabata/Cmdlets/JobHostSummaryCommand.cs +++ b/src/Jagabata/Cmdlets/JobHostSummaryCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -29,7 +30,7 @@ public class FindJobHostSummaryCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput")] [ResourceTransformation(AcceptableTypes = [ ResourceType.Job, ResourceType.Host, @@ -38,6 +39,9 @@ public class FindJobHostSummaryCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "job", "host", "constructed_host", "host_name", + "changed", "dark", "failures", "ok", "processed", "skipped", "failed", + "ignored", "rescued"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void ProcessRecord() diff --git a/src/Jagabata/Cmdlets/JobLogCommand.cs b/src/Jagabata/Cmdlets/JobLogCommand.cs index 9403710..b42d88d 100644 --- a/src/Jagabata/Cmdlets/JobLogCommand.cs +++ b/src/Jagabata/Cmdlets/JobLogCommand.cs @@ -1,6 +1,8 @@ using Jagabata.Cmdlets.ArgumentTransformation; using Jagabata.Resources; +using System.Collections; using System.Collections.Specialized; +using System.Globalization; using System.Management.Automation; using System.Reflection; using System.Text; @@ -107,14 +109,18 @@ private void GetJobsFromWorkflowJob(ulong id) protected override void BeginProcessing() { if (Id > 0 && Type > 0) + { Job = new Resource(Type, Id); + } if (Download is null) { if (CommandRuntime.Host?.UI.SupportsVirtualTerminal ?? false) { if (Format == JobLogFormat.NotSpecified) + { Format = JobLogFormat.ansi; + } } else if (Format == JobLogFormat.ansi) { @@ -141,12 +147,14 @@ protected override void BeginProcessing() } } Query.Add("format", $"{Format}"); - Query.Add("dark", (Dark ? "1" : "0")); + Query.Add("dark", Dark ? "1" : "0"); } protected override void ProcessRecord() { if (Job.Id == 0) + { return; + } switch (Job.Type) { @@ -218,7 +226,7 @@ private IEnumerable StdoutLogs(IEnumerable jobs) } private IEnumerable DownloadLogs(DirectoryInfo dir) { - var unifiedJobsTask = UnifiedJob.Get(_jobs.Select(job => job.Id).ToArray()); + var unifiedJobsTask = UnifiedJob.Get(_jobs.Select(static job => job.Id).ToArray()); unifiedJobsTask.Wait(); foreach (var unifiedJob in unifiedJobsTask.Result) { @@ -240,7 +248,7 @@ private IEnumerable DownloadLogs(DirectoryInfo dir) yield return WriteLogAsHtml(dir, unifiedJob); break; default: - throw new Exception($"Unkown format: {Format}"); + throw new InvalidOperationException($"Unkown format: {Format}"); } } } @@ -256,46 +264,62 @@ private FileInfo WriteLogAsJson(DirectoryInfo dir, IUnifiedJob unifiedJob) private FileInfo WriteSystemLog(DirectoryInfo dir, ISystemJob systemJob) { FileInfo fileInfo = new(Path.Combine(dir.FullName, $"{systemJob.Id}.txt")); - using FileStream fileStream = fileInfo.OpenWrite(); + using FileStream fileStream = fileInfo.Open(fileInfo.Exists ? FileMode.Truncate : FileMode.CreateNew, + FileAccess.Write); var txtLog = systemJob.ResultStdout; using var ws = new StreamWriter(fileStream, Encoding.UTF8); ws.WriteLine("-----"); var props = typeof(ISystemJob).GetProperties(BindingFlags.Public); - var maxLength = props.Select(p => p.Name.Length).Max(); + var maxLength = props.Select(static p => p.Name.Length).Max(); var format = $"{{0,{maxLength}}}: {{1}}"; foreach (var prop in props) { - ws.WriteLine(format, prop.Name, prop.GetValue(systemJob)); + var value = prop.GetValue(systemJob); + var val = value switch + { + IList or IDictionary => Json.Stringify(value), + _ => value + }; + ws.WriteLine(format, prop.Name, val); } ws.WriteLine("-----"); ws.WriteLine(txtLog); + ws.Close(); return fileInfo; } private FileInfo WriteLogAsText(DirectoryInfo dir, IUnifiedJob unifiedJob) { FileInfo fileInfo = new(Path.Combine(dir.FullName, $"{unifiedJob.Id}.txt")); - using FileStream fileStream = fileInfo.OpenWrite(); + using FileStream fileStream = fileInfo.Open(fileInfo.Exists ? FileMode.Truncate : FileMode.CreateNew, + FileAccess.Write); var path = GetStdoutPath(unifiedJob.Id, unifiedJob.Type); var txtLog = GetResource($"{path}?{Query}", AcceptType.Text); using var ws = new StreamWriter(fileStream, Encoding.UTF8); ws.WriteLine("-----"); var props = GetJobProperties(unifiedJob).ToArray(); - var maxLength = props.Select(tuple => tuple.Key.Length).Max(); + var maxLength = props.Select(static tuple => tuple.Key.Length).Max(); var format = $"{{0,{maxLength}}}: {{1}}"; foreach (var (key, value) in props) { - ws.WriteLine(format, key, value); + var val = value switch + { + IList or IDictionary => Json.Stringify(value), + _ => value + }; + ws.WriteLine(format, key, val); } ws.WriteLine("-----"); ws.WriteLine(txtLog); + ws.Close(); return fileInfo; } private FileInfo WriteLogAsHtml(DirectoryInfo dir, IUnifiedJob unifiedJob) { FileInfo fileInfo = new(Path.Combine(dir.FullName, $"{unifiedJob.Id}.html")); - using FileStream fileStream = fileInfo.OpenWrite(); + using FileStream fileStream = fileInfo.Open(fileInfo.Exists ? FileMode.Truncate : FileMode.CreateNew, + FileAccess.Write); var path = GetStdoutPath(unifiedJob.Id, unifiedJob.Type); var htmlLog = GetResource($"{path}?{Query}", AcceptType.Html); var title = $"{unifiedJob.Id} - {HttpUtility.HtmlEncode(unifiedJob.Name)}"; @@ -307,18 +331,38 @@ private FileInfo WriteLogAsHtml(DirectoryInfo dir, IUnifiedJob unifiedJob) jobInfo.AppendLine(""); foreach ((string key, object? value) in GetJobProperties(unifiedJob)) { - jobInfo.AppendLine(string.Format(format, key, value)); + var val = value switch + { + IList or IDictionary => Json.Stringify(value), + _ => value + }; + jobInfo.AppendFormat(CultureInfo.CurrentCulture, format, key, HttpUtility.HtmlEncode(val)) + .AppendLine(); } jobInfo.AppendLine("
Job Info
"); // Write Log to a fileStream as HTML using StreamWriter ws = new(fileStream, Encoding.UTF8); + if (htmlLog is not null) + { + int bodyTagStart = htmlLog.IndexOf(" 0) + { + int bodyTagEnd = htmlLog.IndexOf('>', bodyTagStart) + 1; + ws.WriteLine(htmlLog[..bodyTagEnd].Replace("Type", $"{title}")); + ws.WriteLine(jobInfo.ToString()); + ws.WriteLine(htmlLog[bodyTagEnd..]); + ws.Close(); + return fileInfo; + } + } ws.WriteLine(""); ws.WriteLine($"{title}"); ws.WriteLine(""); ws.WriteLine(jobInfo.ToString()); ws.WriteLine("

Ooops, Missing log data :(

"); ws.WriteLine(""); + ws.Close(); return fileInfo; } private static IEnumerable<(string Key, object? Value)> GetJobProperties(IUnifiedJob job) @@ -333,8 +377,13 @@ private FileInfo WriteLogAsHtml(DirectoryInfo dir, IUnifiedJob unifiedJob) IProjectUpdateJob => typeof(IProjectUpdateJob), IInventoryUpdateJob => typeof(IInventoryUpdateJob), ISystemJob => typeof(ISystemJob), - _ => throw new NotFiniteNumberException() + IAdHocCommand => typeof(IAdHocCommand), + _ => null }; + if (type is null) + { + yield break; + } foreach (var prop in type.GetProperties()) { yield return (prop.Name, prop.GetValue(job)); diff --git a/src/Jagabata/Cmdlets/JobTemplateCommand.cs b/src/Jagabata/Cmdlets/JobTemplateCommand.cs index 6a2e82d..adc029b 100644 --- a/src/Jagabata/Cmdlets/JobTemplateCommand.cs +++ b/src/Jagabata/Cmdlets/JobTemplateCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Cmdlets.Utilities; using Jagabata.Resources; using System.Management.Automation; @@ -37,7 +38,7 @@ public class FindJobTemplateCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.Inventory @@ -48,6 +49,17 @@ public class FindJobTemplateCommand : FindCommandBase public string[]? Name { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "job_type", "inventory", "project", + "playbook", "scm_branch", "forks", "limit", "verbosity", "job_tags", "force_handlers", + "skip_tags", "start_at_task", "timeout", "use_fact_cache", "organization", "last_job_run", + "last_job_failed", "next_job_run", "status", "execution_environment", "ask_scm_branch_on_launch", + "ask_diff_mode_on_launch", "ask_variables_on_launch", "ask_limit_on_launch", "ask_tags_on_launch", + "ask_skip_tags_on_launch", "ask_job_type_on_launch", "ask_verbosity_on_launch", + "ask_inventory_on_launch", "ask_credential_on_launch", "ask_execution_environment_on_launch", + "ask_labels_on_launch", "ask_forks_on_launch", "ask_job_slice_count_on_launch", + "ask_timeout_on_launch", "ask_instance_groups_on_launch", "survey_enabled", "become_enabled", + "diff_mode", "allow_simultaneous", "custom_virtualenv", "job_slice_count", "webhook_service", + "webhook_credential", "prevent_instance_group_fallback"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/LabelCommand.cs b/src/Jagabata/Cmdlets/LabelCommand.cs index 2887f57..01bcc09 100644 --- a/src/Jagabata/Cmdlets/LabelCommand.cs +++ b/src/Jagabata/Cmdlets/LabelCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -37,7 +38,7 @@ public class FindLabelCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Inventory, ResourceType.JobTemplate, @@ -51,6 +52,7 @@ public class FindLabelCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "organization"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/NotificationCommand.cs b/src/Jagabata/Cmdlets/NotificationCommand.cs index b90ba07..4dd4e15 100644 --- a/src/Jagabata/Cmdlets/NotificationCommand.cs +++ b/src/Jagabata/Cmdlets/NotificationCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -37,7 +38,7 @@ public class FindNotificationCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineVariable", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineVariable", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.NotificationTemplate, ResourceType.Job, @@ -50,6 +51,8 @@ public class FindNotificationCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "notification_template", "error", "status", + "notifications_sent", "notification_type", "recipients", "subject", "body"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/NotificationTemplateCommand.cs b/src/Jagabata/Cmdlets/NotificationTemplateCommand.cs index dd0f61f..a68e1bb 100644 --- a/src/Jagabata/Cmdlets/NotificationTemplateCommand.cs +++ b/src/Jagabata/Cmdlets/NotificationTemplateCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Collections; using System.Management.Automation; @@ -30,6 +31,8 @@ public class FindNotificationTemplateCommand : FindCommandBase public ulong Organization { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", + "notification_type", "messages"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -66,6 +69,8 @@ public class FindNotificationTemplateForApprovalCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", + "notification_type", "messages"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -106,7 +111,7 @@ public class FindNotificationTemplateForErrorCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.Project, @@ -118,6 +123,8 @@ public class FindNotificationTemplateForErrorCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", + "notification_type", "messages"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -162,7 +169,7 @@ public class FindNotificationTemplateForStartedCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.Project, @@ -174,6 +181,8 @@ public class FindNotificationTemplateForStartedCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", + "notification_type", "messages"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -219,9 +228,11 @@ public class FindNotificationTemplateForSuccessCommand : FindCommandBase public ulong Id { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", + "notification_type", "messages"])] public override string[] OrderBy { get; set; } = ["id"]; - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.Project, diff --git a/src/Jagabata/Cmdlets/OrganizationCommand.cs b/src/Jagabata/Cmdlets/OrganizationCommand.cs index 1edaae9..8ea7a9b 100644 --- a/src/Jagabata/Cmdlets/OrganizationCommand.cs +++ b/src/Jagabata/Cmdlets/OrganizationCommand.cs @@ -1,5 +1,6 @@ using System.Management.Automation; using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; namespace Jagabata.Cmdlets @@ -35,6 +36,8 @@ public class FindOrganizationCommand : FindCommandBase public string[]? Name { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", + "max_hosts", "default_environment"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -96,7 +99,7 @@ protected override void ProcessRecord() } } } - + [Cmdlet(VerbsData.Update, "Organization", SupportsShouldProcess = true)] [OutputType(typeof(Organization))] public class UpdateOrganizationCommand : UpdateCommandBase diff --git a/src/Jagabata/Cmdlets/ProjectCommand.cs b/src/Jagabata/Cmdlets/ProjectCommand.cs index 41dba22..c74a311 100644 --- a/src/Jagabata/Cmdlets/ProjectCommand.cs +++ b/src/Jagabata/Cmdlets/ProjectCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -33,7 +34,7 @@ public class FindProjectCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineVariable", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineVariable", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.User, @@ -42,6 +43,12 @@ public class FindProjectCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "local_path", "scm_type", + "scm_url", "scm_branch", "scm_refspec", "scm_clean", "scm_track_submodules", + "scm_delete_on_update", "credential", "timeout", "scm_revision", "last_job_run", + "last_job_failed", "next_job_run", "status", "organization", "scm_update_on_launch", + "scm_update_cache_timeout", "allow_override", "default_environment", + "signature_validation_credential", "last_update_failed", "last_updated"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/ProjectUpdateCommand.cs b/src/Jagabata/Cmdlets/ProjectUpdateCommand.cs index 7aaa375..44c8edb 100644 --- a/src/Jagabata/Cmdlets/ProjectUpdateCommand.cs +++ b/src/Jagabata/Cmdlets/ProjectUpdateCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -32,6 +33,11 @@ public class FindProjectUpdateJobCommand : FindCommandBase public string[]? Status { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "launch_type", "status", + "execution_environment", "failed", "started", "finished", "canceled_on", "elapsed", + "job_explanation", "execution_node", "work_unit_id", "local_path", "scm_type", + "scm_url", "scm_branch", "scm_refspec", "scm_clean", "scm_track_submodules", + "scm_delete_on_update", "credential", "timeout", "scm_revision", "project"])] public override string[] OrderBy { get; set; } = ["!id"]; diff --git a/src/Jagabata/Cmdlets/RoleCommand.cs b/src/Jagabata/Cmdlets/RoleCommand.cs index 31a8a8b..5080877 100644 --- a/src/Jagabata/Cmdlets/RoleCommand.cs +++ b/src/Jagabata/Cmdlets/RoleCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -32,7 +33,7 @@ public class FindRoleCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput")] [ResourceTransformation(AcceptableTypes = [ ResourceType.User, ResourceType.Team @@ -40,6 +41,8 @@ public class FindRoleCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "name", "description", "parents", "parents", "content_type", + "ancestors", "descendents", "children"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -82,7 +85,7 @@ public class FindObjectRoleCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput")] [ResourceTransformation(AcceptableTypes = [ ResourceType.InstanceGroup, ResourceType.Organization, @@ -96,6 +99,8 @@ public class FindObjectRoleCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "name", "description", "parents", "parents", "content_type", + "ancestors", "descendents", "children"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/ScheduleCommand.cs b/src/Jagabata/Cmdlets/ScheduleCommand.cs index 315b23c..4ce58f4 100644 --- a/src/Jagabata/Cmdlets/ScheduleCommand.cs +++ b/src/Jagabata/Cmdlets/ScheduleCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -35,7 +36,7 @@ public class FindScheduleCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Project, ResourceType.InventorySource, @@ -46,6 +47,10 @@ public class FindScheduleCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["rrule", "id", "created", "modified", "name", "description", "extra_data", + "inventory", "execution_environment", "unified_job_template", "enabled", + "dtstart", "dtend", "next_run", "created_by", "modified_by", "credentials", + "instance_groups", "labels"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/SystemJobCommand.cs b/src/Jagabata/Cmdlets/SystemJobCommand.cs index e4ebc0b..ac74bda 100644 --- a/src/Jagabata/Cmdlets/SystemJobCommand.cs +++ b/src/Jagabata/Cmdlets/SystemJobCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -29,6 +30,11 @@ public class FindSystemJobCommand : FindCommandBase public string[]? Status { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "unified_job_template", + "launch_type", "status", "execution_environment", "failed", "started", "finished", + "canceled_on", "elapsed", "job_explanation", "execution_node", "work_unit_id", + "system_job_template", "job_type", "schedule", "notifications", "created_by", + "modified_by", "dependent_jobs", "labels", "instance_group"])] public override string[] OrderBy { get; set; } = ["!id"]; diff --git a/src/Jagabata/Cmdlets/SystemJobTemplateCommand.cs b/src/Jagabata/Cmdlets/SystemJobTemplateCommand.cs index a331b88..c4d8aa7 100644 --- a/src/Jagabata/Cmdlets/SystemJobTemplateCommand.cs +++ b/src/Jagabata/Cmdlets/SystemJobTemplateCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Collections; using System.Management.Automation; @@ -27,6 +28,12 @@ protected override void EndProcessing() public class FindSystemJobTemplateCommand : FindCommandBase { [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "last_job_run", + "last_job_failed", "next_job_run", "status", "execution_environment", + "job_type", "notification_templates_error", "notification_templates_success", + "notification_templates_started", "unifiedjob_unified_jobs", "last_job", + "organization", "schedules", "jobs", "created_by", "credentials", "current_job", + "modified_by", "instance_groups", "labels", "next_schedule"])] public override string[] OrderBy { get; set; } = ["id"]; diff --git a/src/Jagabata/Cmdlets/TeamCommand.cs b/src/Jagabata/Cmdlets/TeamCommand.cs index 2908c47..ffc25f1 100644 --- a/src/Jagabata/Cmdlets/TeamCommand.cs +++ b/src/Jagabata/Cmdlets/TeamCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -35,7 +36,7 @@ public class FindTeamCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.User, @@ -46,6 +47,8 @@ public class FindTeamCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "organization", + "created_by", "modified_by"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/TokenCommand.cs b/src/Jagabata/Cmdlets/TokenCommand.cs index 28a2440..ae76e1b 100644 --- a/src/Jagabata/Cmdlets/TokenCommand.cs +++ b/src/Jagabata/Cmdlets/TokenCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -32,7 +33,7 @@ public class FindTokenCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.OAuth2Application, ResourceType.User @@ -48,6 +49,8 @@ public class FindTokenCommand : FindCommandBase public ETokenType TokenType { get; set; } = ETokenType.Both; [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "description", "user", + "application", "expires", "scope"])] public override string[] OrderBy { get; set; } = ["id"]; public enum ETokenType diff --git a/src/Jagabata/Cmdlets/UnifiedJobCommand.cs b/src/Jagabata/Cmdlets/UnifiedJobCommand.cs index 7f32f90..80dccc3 100644 --- a/src/Jagabata/Cmdlets/UnifiedJobCommand.cs +++ b/src/Jagabata/Cmdlets/UnifiedJobCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Cmdlets.Utilities; using Jagabata.Resources; using System.Collections.Specialized; @@ -27,7 +28,7 @@ public class FindUnifiedJobCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.JobTemplate, ResourceType.WorkflowJobTemplate, @@ -44,6 +45,11 @@ public class FindUnifiedJobCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "unified_job_template", + "launch_type", "status", "execution_environment", "failed", "started", "finished", + "canceled_on", "elapsed", "job_explanation", "execution_node", "controller_node", + "work_unit_id", "notifications", "organization", "schedule", "created_by", + "modified_by", "credentials", "instance_group", "labels"])] public override string[] OrderBy { get; set; } = ["!id"]; private IEnumerable GetResultSet(string path, diff --git a/src/Jagabata/Cmdlets/UnifiedJobTemplateCommand.cs b/src/Jagabata/Cmdlets/UnifiedJobTemplateCommand.cs index 99f407c..39a98cb 100644 --- a/src/Jagabata/Cmdlets/UnifiedJobTemplateCommand.cs +++ b/src/Jagabata/Cmdlets/UnifiedJobTemplateCommand.cs @@ -1,3 +1,4 @@ +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Collections.Specialized; using System.Management.Automation; @@ -9,6 +10,11 @@ namespace Jagabata.Cmdlets public class FindUnifiedJobTemplateCommand : FindCommandBase { [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "last_job_run", "last_job_failed", + "next_job_run", "status", "execution_environment", "notification_templates_error", + "notification_templates_success", "notification_templates_started", "last_job", + "organization", "schedules", "labels", "created_by", "modified_by", "credentials", + "instance_groups", "next_schedule"])] public override string[] OrderBy { get; set; } = ["id"]; private IEnumerable GetResultSet(string path, diff --git a/src/Jagabata/Cmdlets/UserCommand.cs b/src/Jagabata/Cmdlets/UserCommand.cs index 0576a02..faae167 100644 --- a/src/Jagabata/Cmdlets/UserCommand.cs +++ b/src/Jagabata/Cmdlets/UserCommand.cs @@ -1,8 +1,8 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Cmdlets.Utilities; using Jagabata.Resources; using System.Management.Automation; -using System.Runtime.InteropServices; using System.Security; namespace Jagabata.Cmdlets @@ -50,7 +50,7 @@ public class FindUserCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.Organization, ResourceType.Team, @@ -66,6 +66,9 @@ public class FindUserCommand : FindCommandBase public string[]? Email { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "username", "first_name", "last_name", "email", "is_superuser", "last_login", + "enterprise_auth", "social_auth", "main_oauth2application", "activity_stream", + "roles", "profile"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -118,7 +121,7 @@ public class FindAccessListCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.InstanceGroup, ResourceType.Organization, @@ -133,6 +136,9 @@ public class FindAccessListCommand : FindCommandBase public IResource? Resource { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "username", "first_name", "last_name", "email", "is_superuser", "last_login", + "enterprise_auth", "social_auth", "main_oauth2application", "activity_stream", + "roles", "profile"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() @@ -198,7 +204,7 @@ public class NewUserCommand : NewCommandBase private string? _user; private SecureString? _password; - private bool _passwordInputedFromPrompt = false; + private bool _passwordInputedFromPrompt; private void GatherUserAndPassword() { @@ -207,7 +213,7 @@ private void GatherUserAndPassword() _user = Credential.UserName; _password = Credential.Password; } - else + else { _user = UserName; if (Password is not null) @@ -320,7 +326,6 @@ public class UpdateUserCommand : UpdateCommandBase protected override Dictionary CreateSendData() { var sendData = new Dictionary(); - string dataDescription = string.Empty; if (!string.IsNullOrEmpty(UserName)) sendData.Add("username", UserName); if (FirstName is not null) @@ -335,14 +340,7 @@ public class UpdateUserCommand : UpdateCommandBase sendData.Add("is_system_auditor", IsSystemAuditor); if (Password is not null) { - var passwordString = Marshal.PtrToStringUni(Marshal.SecureStringToGlobalAllocUnicode(Password)); - Password.Dispose(); - if (!string.IsNullOrEmpty(passwordString)) - { - sendData.Add("password", "***"); // dummy - dataDescription = Json.Stringify(sendData, pretty: true); - sendData["password"] = passwordString; - } + sendData.Add("password", Password); } return sendData; diff --git a/src/Jagabata/Cmdlets/WorkflowApprovalCommand.cs b/src/Jagabata/Cmdlets/WorkflowApprovalCommand.cs index 8db2672..e98283a 100644 --- a/src/Jagabata/Cmdlets/WorkflowApprovalCommand.cs +++ b/src/Jagabata/Cmdlets/WorkflowApprovalCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; using System.Web; @@ -30,6 +31,11 @@ public class FindWorkflowApprovalRequestCommand : FindCommandBase public JobStatus[]? Status { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "unified_job_template", + "launch_type", "status", "execution_environment", "failed", "started", "finished", + "canceled_on", "elapsed", "job_explanation", "work_unit_id", "timed_out", + "workflow_approval_template", "approved_or_denied_by", "schedule", "notifications", + "created_by", "modified_by", "instance_group", "organization", "labels"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/WorkflowJobCommand.cs b/src/Jagabata/Cmdlets/WorkflowJobCommand.cs index e1b87f9..d92504b 100644 --- a/src/Jagabata/Cmdlets/WorkflowJobCommand.cs +++ b/src/Jagabata/Cmdlets/WorkflowJobCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -28,7 +29,7 @@ public class FindWorkflowJobCommand : FindCommandBase [Parameter(Mandatory = true, ParameterSetName = "AssociatedWith", Position = 1)] public ulong Id { get; set; } - [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = true, ParameterSetName = "PipelineInput", ValueFromPipeline = true)] [ResourceTransformation(AcceptableTypes = [ ResourceType.JobTemplate, ResourceType.WorkflowApprovalTemplate @@ -47,6 +48,13 @@ public class FindWorkflowJobCommand : FindCommandBase public string[]? LaunchType { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "unified_job_template", + "launch_type", "status", "failed", "started", "finished", "canceled_on", + "elapsed", "job_explanation", "work_unit_id", "workflow_job_template", + "allow_simultaneous", "job_template", "is_sliced_job", "inventory", + "webhook_service", "webhook_credential", "webhook_guid", "notifications", + "unified_job_node", "workflow_job_template", "organization", "schedule", + "created_by", "modified_by", "instance_group", "labels"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/WorkflowJobNodeCommand.cs b/src/Jagabata/Cmdlets/WorkflowJobNodeCommand.cs index e7c254a..734b2f3 100644 --- a/src/Jagabata/Cmdlets/WorkflowJobNodeCommand.cs +++ b/src/Jagabata/Cmdlets/WorkflowJobNodeCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Management.Automation; @@ -36,6 +37,9 @@ public class FindWorkflowJobNodeCommand : FindCommandBase public WorkflowJobNodeLinkState Linked { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "extra_data", "inventory", "execution_environment", + "job", "workflow_job", "unified_job_template", "success_nodes", "failure_nodes", + "always_nodes", "all_parents_must_converge", "do_not_run", "identifier", "labels"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/WorkflowJobTemplateCommand.cs b/src/Jagabata/Cmdlets/WorkflowJobTemplateCommand.cs index cd496ea..bc84b35 100644 --- a/src/Jagabata/Cmdlets/WorkflowJobTemplateCommand.cs +++ b/src/Jagabata/Cmdlets/WorkflowJobTemplateCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Cmdlets.Utilities; using Jagabata.Resources; using System.Management.Automation; @@ -32,6 +33,15 @@ public class FindWorkflowJobTemplateCommand : FindCommandBase public ulong Organization { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "name", "description", "last_job_run", + "last_job_failed", "next_job_run", "status", "organization", "survey_enabled", + "allow_simultaneous", "ask_variables_on_launch", "inventory", + "ask_inventory_on_launch", "ask_scm_branch_on_launch", "ask_limit_on_launch", + "webhook_service", "webhook_credential", "ask_labels_on_launch", + "ask_skip_tags_on_launch", "ask_tags_on_launch", "notification_templates_error", + "notification_templates_success", "notification_templates_approvals", + "notification_templates_started", "inventory", "organization", "last_job", + "schedules", "created_by", "modified_by", "labels", "next_schedule"])] public override string[] OrderBy { get; set; } = ["id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/Cmdlets/WorkflowJobTemplateNodeCommand.cs b/src/Jagabata/Cmdlets/WorkflowJobTemplateNodeCommand.cs index c04c238..9fa08e7 100644 --- a/src/Jagabata/Cmdlets/WorkflowJobTemplateNodeCommand.cs +++ b/src/Jagabata/Cmdlets/WorkflowJobTemplateNodeCommand.cs @@ -1,4 +1,5 @@ using Jagabata.Cmdlets.ArgumentTransformation; +using Jagabata.Cmdlets.Completer; using Jagabata.Resources; using System.Diagnostics.CodeAnalysis; using System.Management.Automation; @@ -37,6 +38,9 @@ public class FindWorkflowJobTemplateNodeCommand : FindCommandBase public WorkflowJobNodeLinkState Linked { get; set; } [Parameter()] + [OrderByCompletion(Keys = ["id", "created", "modified", "extra_data", "inventory", "execution_environment", + "workflow_job_template", "unified_job_template", "success_nodes", "failure_nodes", + "always_nodes", "all_parents_must_converge", "identifier", "instance_groups", "labels"])] public override string[] OrderBy { get; set; } = ["!id"]; protected override void BeginProcessing() diff --git a/src/Jagabata/CredentialType/BoolField.cs b/src/Jagabata/CredentialType/BoolField.cs new file mode 100644 index 0000000..08c8697 --- /dev/null +++ b/src/Jagabata/CredentialType/BoolField.cs @@ -0,0 +1,57 @@ +using System.Globalization; +using System.Text; +using System.Text.Json.Serialization; + +namespace Jagabata.CredentialType; + +/// +/// Checkbox field +/// +public sealed class BoolField : FieldBase +{ + public BoolField() : base(FieldType.Boolean) + { } + public BoolField(string id, string label) : base(FieldType.Boolean, id, label) + { } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override object? Default + { + get => _default; + set => _default = value switch + { + null => null, + bool boolValue => boolValue, + _ => throw new InvalidCastException($"value should be boolean type: {value.GetType()}"), + }; + } + private bool? _default; + + public override string ToString() + { + var iv = CultureInfo.InvariantCulture; + var sb = new StringBuilder(); + sb.Append('{'); + sb.Append(iv, $" Id = {Id}"); + sb.Append(iv, $", Label = {Label}"); + sb.Append(iv, $", Type = {Type}"); + if (Required is not null) + { + sb.Append(iv, $", Required = {Required}"); + } + if (Default is not null) + { + sb.Append(iv, $", Default = {Default}"); + } + if (AskAtRuntime is not null) + { + sb.Append(iv, $", AskAtRuntime = {AskAtRuntime}"); + } + if (HelpText is not null) + { + sb.Append(iv, $", HelpText = {HelpText}"); + } + sb.Append(" }"); + return sb.ToString(); + } +} diff --git a/src/Jagabata/CredentialType/ChoiceField.cs b/src/Jagabata/CredentialType/ChoiceField.cs new file mode 100644 index 0000000..a143c72 --- /dev/null +++ b/src/Jagabata/CredentialType/ChoiceField.cs @@ -0,0 +1,59 @@ +using System.Globalization; +using System.Text; +using System.Text.Json.Serialization; + +namespace Jagabata.CredentialType; + +/// +/// Selectbox field +/// +public sealed class ChoiceField : FieldBase +{ + public ChoiceField() : base(FieldType.String) + { } + public ChoiceField(string id, string label) : base(FieldType.String, id, label) + { } + + public string[] Choices { get; set; } = []; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override object? Default + { + get => _default; + set => _default = value switch + { + null => null, + string str => str, + _ => throw new InvalidCastException($"value should be string type: {value.GetType()}"), + }; + } + private string? _default; + + public override string ToString() + { + var iv = CultureInfo.InvariantCulture; + var sb = new StringBuilder(); + sb.Append('{'); + sb.Append(iv, $" Id = {Id}"); + sb.Append(iv, $", Label = {Label}"); + sb.Append(iv, $", Type = {Type}"); + if (_default is not null) + { + sb.Append(iv, $", Default = {_default}"); + } + if (Choices is not null) + { + sb.Append(iv, $", Choices = [{string.Join(", ", Choices)}]"); + } + if (AskAtRuntime is not null) + { + sb.Append(iv, $", AskAtRuntime = {AskAtRuntime}"); + } + if (HelpText is not null) + { + sb.Append(iv, $", HelpText = {HelpText}"); + } + sb.Append(" }"); + return sb.ToString(); + } +} diff --git a/src/Jagabata/CredentialType/FieldBase.cs b/src/Jagabata/CredentialType/FieldBase.cs new file mode 100644 index 0000000..aa805cd --- /dev/null +++ b/src/Jagabata/CredentialType/FieldBase.cs @@ -0,0 +1,93 @@ +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; + +namespace Jagabata.CredentialType; + +public abstract partial class FieldBase : IEquatable +{ + public FieldBase(FieldType type) + { + Type = type; + } + public FieldBase(FieldType type, string id) + { + Type = type; + Id = id; + } + public FieldBase(FieldType type, string id, string label) + { + Type = type; + Id = id; + Label = label; + } + + [GeneratedRegex(@"^[a-zA-Z_]+[a-zA-Z0-9_]*$")] + private static partial Regex idPattern(); + + /// + /// A unique name used to reference the field value + /// + [JsonPropertyOrder(1)] + public string Id + { + get => _id; + init + { + if (!idPattern().IsMatch(value)) + { + throw new InvalidDataException("Id must be '^[a-zA-Z_]+[a-zA-Z0-9]*$'"); + } + _id = value; + } + } + private string _id = string.Empty; + + /// + /// A unique label for the field + /// + [JsonPropertyOrder(2)] + public string Label { get; set; } = string.Empty; + + /// + /// Field type: "String" or "Boolean" (default: "String") + /// + [JsonPropertyOrder(3)] + public FieldType Type { get; } = FieldType.String; + + /// + /// User-facing short text describing the field. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? HelpText { get; set; } + + [JsonIgnore] + public bool? Required { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public virtual object? Default { get; set; } + + /// + /// If true, this field is given a checkbox that can be used to prompt for input on startup. + /// + /// + /// For only managed CredentialType. + /// (Not supported for custom CredentialType) + /// + [JsonIgnore] + public bool? AskAtRuntime { get; internal set; } + + public bool Equals(FieldBase? other) + { + return other is not null && string.Equals(Id, other.Id, StringComparison.Ordinal); + } + + public override bool Equals(object? obj) + { + return obj is FieldBase field && Equals(field); + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } +} diff --git a/src/Jagabata/CredentialType/FieldFormat.cs b/src/Jagabata/CredentialType/FieldFormat.cs new file mode 100644 index 0000000..745f986 --- /dev/null +++ b/src/Jagabata/CredentialType/FieldFormat.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Jagabata.CredentialType; + +[JsonConverter(typeof(Json.EnumUpperCamelCaseStringConverter))] +public enum FieldFormat +{ + SshPrivateKey, + VaultId, + Url +} diff --git a/src/Jagabata/CredentialType/FieldList.cs b/src/Jagabata/CredentialType/FieldList.cs new file mode 100644 index 0000000..aca455f --- /dev/null +++ b/src/Jagabata/CredentialType/FieldList.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Jagabata.CredentialType; + +[JsonConverter(typeof(FieldListConverter))] +public class FieldList : List +{ + public Dictionary? Dependencies { get; internal set; } + public Dictionary[]? Metadata { get; internal set; } +} diff --git a/src/Jagabata/CredentialType/FieldListConverter.cs b/src/Jagabata/CredentialType/FieldListConverter.cs new file mode 100644 index 0000000..af503d5 --- /dev/null +++ b/src/Jagabata/CredentialType/FieldListConverter.cs @@ -0,0 +1,202 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jagabata.CredentialType; + +internal class FieldListConverter : JsonConverter +{ + private static FieldBase? ReadField(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + (string Id, string Label, string? HelpText, string[]? Choices, FieldFormat? Format, FieldType Type, + bool? Secret, bool? Multiline, string? DefaultString, bool? DefaultBool, bool? AskAtRuntime) field = + (string.Empty, string.Empty, null, null, null, FieldType.String, null, null, null, null, null); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.EndObject: + return field.Type switch + { + FieldType.Boolean => new BoolField(field.Id, field.Label) + { + Default = field.DefaultBool, + HelpText = field.HelpText, + AskAtRuntime = field.AskAtRuntime + }, + FieldType.String => field.Choices is null + ? new StringField(field.Id, field.Label) + { + Format = field.Format, + Secret = field.Secret, + Multiline = field.Multiline, + Default = field.DefaultString, + HelpText = field.HelpText, + AskAtRuntime = field.AskAtRuntime + } + : new ChoiceField(field.Id, field.Label) + { + Choices = field.Choices, + Default = field.DefaultString, + HelpText = field.HelpText, + AskAtRuntime = field.AskAtRuntime + }, + _ => throw new JsonException($"Unknown field type: {field.Type}"), + }; + case JsonTokenType.PropertyName: + string fieldPropertyName = reader.GetString() ?? throw new JsonException("PropertyName is null"); + reader.Read(); + switch (fieldPropertyName) + { + case "id": + field.Id = reader.GetString() ?? ""; + break; + case "label": + field.Label = reader.GetString() ?? ""; + break; + case "help_text": + field.HelpText = reader.GetString(); + break; + case "choices": + field.Choices = JsonSerializer.Deserialize(ref reader, options); + break; + case "format": + field.Format = JsonSerializer.Deserialize(ref reader, options); + break; + case "secret": + field.Secret = reader.GetBoolean(); + break; + case "multiline": + field.Multiline = reader.GetBoolean(); + break; + case "type": + field.Type = JsonSerializer.Deserialize(ref reader, options); + break; + case "ask_at_runtime": + field.AskAtRuntime = reader.GetBoolean(); + break; + case "default": + switch (reader.TokenType) + { + case JsonTokenType.String: + field.DefaultString = reader.GetString() ?? ""; + break; + case JsonTokenType.True: + case JsonTokenType.False: + field.DefaultBool = reader.GetBoolean(); + break; + } + break; + } + continue; + } + } + throw new JsonException(); + } + + public override FieldList? Read(ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + var container = new FieldList(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return container; + } + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException($"TokenType is not PropertyName: {reader.TokenType}"); + } + string propertyName = reader.GetString() ?? throw new JsonException("PropertyName is null"); + reader.Read(); + switch (propertyName) + { + case "fields": + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + break; + } + + var field = ReadField(ref reader, options); + if (field is not null) + { + container.Add(field); + } + } + break; + case "required": + var requiredFields = JsonSerializer.Deserialize(ref reader, options); + if (requiredFields is not null) + { + foreach (var id in requiredFields) + { + var field = container.FirstOrDefault(item => string.Equals(item.Id, id, StringComparison.Ordinal)); + if (field is not null) + { + field.Required = true; + } + } + } + break; + case "dependencies": + container.Dependencies = JsonSerializer.Deserialize>(ref reader, options); + break; + case "metadata": + container.Metadata = JsonSerializer.Deserialize[]>(ref reader, options); + break; + } + } + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, + FieldList value, + JsonSerializerOptions options) + { + var requiredFields = new List(); + + writer.WriteStartObject(); + + writer.WriteStartArray("fields"); + foreach (var field in value) + { + if (field.Required is not null && (bool)field.Required) + { + requiredFields.Add(field.Id); + } + JsonSerializer.Serialize(writer, field, field.GetType(), options); + } + writer.WriteEndArray(); + + if (value.Metadata is not null) + { + writer.WriteStartArray("metadata"); + JsonSerializer.Serialize(writer, value.Metadata, options); + writer.WriteEndArray(); + } + + if (value.Dependencies is not null) + { + writer.WriteStartArray("dependencies"); + JsonSerializer.Serialize(writer, value.Dependencies, options); + writer.WriteEndArray(); + } + + writer.WriteStartArray("required"); + foreach (var id in requiredFields) + { + writer.WriteStringValue(id); + } + writer.WriteEndArray(); + writer.WriteEndObject(); + } +} diff --git a/src/Jagabata/CredentialType/FieldType.cs b/src/Jagabata/CredentialType/FieldType.cs new file mode 100644 index 0000000..1459fe2 --- /dev/null +++ b/src/Jagabata/CredentialType/FieldType.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Jagabata.CredentialType; + +[JsonConverter(typeof(Json.EnumUpperCamelCaseStringConverter))] +public enum FieldType +{ + String, + Boolean +} + diff --git a/src/Jagabata/CredentialType/Injectors.cs b/src/Jagabata/CredentialType/Injectors.cs new file mode 100644 index 0000000..12ad89c --- /dev/null +++ b/src/Jagabata/CredentialType/Injectors.cs @@ -0,0 +1,74 @@ +using System.Collections; +using System.Text.Json.Serialization; + +namespace Jagabata.CredentialType; + +public class Injectors(StringDictionary? env = null, + ObjectDictionary? extraVars = null, + StringDictionary? file = null) +{ + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public StringDictionary? Env { get; set; } = env; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ObjectDictionary? ExtraVars { get; set; } = extraVars; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public StringDictionary? File { get; set; } = file; +} + +public class StringDictionary : Dictionary +{ + public StringDictionary() + { } + public StringDictionary(IDictionary dict) + { + foreach (var entry in dict.Cast()) + { + this[$"{entry.Key}"] = $"{entry.Value}"; + } + } +} + +public class ObjectDictionary : Dictionary +{ + public ObjectDictionary() + { } + public ObjectDictionary(IDictionary dict) + { + foreach (var entry in dict.Cast()) + { + this[$"{entry.Key}"] = entry.Value switch + { + IDictionary subDict => ConvertDict(subDict), + IList list => ConvertList(list), + _ => entry.Value + }; + } + } + private static Dictionary ConvertDict(IDictionary dict) + { + return dict.Cast().ToDictionary( + d => $"{d.Key}", + d => d.Value switch + { + IDictionary subDict => ConvertDict(subDict), + IList list => ConvertList(list), + _ => d.Value + }); + } + private static ArrayList ConvertList(IList list) + { + var result = new ArrayList(); + foreach (var item in list) + { + result.Add(item switch + { + IDictionary subDict => ConvertDict(subDict), + IList subList => ConvertList(subList), + _ => item + }); + } + return result; + } +} diff --git a/src/Jagabata/CredentialType/SecretField.cs b/src/Jagabata/CredentialType/SecretField.cs new file mode 100644 index 0000000..4c0c399 --- /dev/null +++ b/src/Jagabata/CredentialType/SecretField.cs @@ -0,0 +1,16 @@ +namespace Jagabata.CredentialType; + +/// +/// Secret Input field +/// +public class SecretField : StringField +{ + public SecretField() : base() + { + Secret = true; + } + public SecretField(string id, string label) : base(id, label) + { + Secret = true; + } +} diff --git a/src/Jagabata/CredentialType/SshPrivateKeyField.cs b/src/Jagabata/CredentialType/SshPrivateKeyField.cs new file mode 100644 index 0000000..a4535bb --- /dev/null +++ b/src/Jagabata/CredentialType/SshPrivateKeyField.cs @@ -0,0 +1,20 @@ +namespace Jagabata.CredentialType; + +/// +/// SSH Provate Key field +/// +public sealed class SshPrivateKeyField : StringField +{ + public SshPrivateKeyField() : base() + { + Secret = true; + Format = FieldFormat.SshPrivateKey; + Multiline = true; + } + public SshPrivateKeyField(string id, string label) : base(id, label) + { + Secret = true; + Format = FieldFormat.SshPrivateKey; + Multiline = true; + } +} diff --git a/src/Jagabata/CredentialType/StringField.cs b/src/Jagabata/CredentialType/StringField.cs new file mode 100644 index 0000000..a0f7991 --- /dev/null +++ b/src/Jagabata/CredentialType/StringField.cs @@ -0,0 +1,88 @@ +using System.Globalization; +using System.Text; +using System.Text.Json.Serialization; + +namespace Jagabata.CredentialType; + +/// +/// Normal Input field +/// +public class StringField : FieldBase +{ + public StringField() : base(FieldType.String) + { } + public StringField(string id, string label) : base(FieldType.String, id, label) + { } + + /// + /// Optional, can be used to enforce data format validity for SSH private key data + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public FieldFormat? Format { get; internal set; } + + /// + /// Optional, can be used to provide a default value if the field is left empty; + /// when creating a credential of this type, credential forms will use this value + /// as a prefill when making credentials of this type + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override object? Default + { + get => _default; + set => _default = value switch + { + null => null, + string str => str, + _ => throw new InvalidCastException($"value should be string type: {value.GetType()}"), + }; + } + private string? _default; + + /// + /// If true, the field value will be encrypted + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Secret { get; internal set; } + + /// + /// If true, the field should be rendered as multi-line for input entry + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Multiline { get; set; } + + public override string ToString() + { + var iv = CultureInfo.InvariantCulture; + var sb = new StringBuilder(); + sb.Append('{'); + sb.Append(iv, $" Id = {Id}"); + sb.Append(iv, $", Label = {Label}"); + sb.Append(iv, $", Type = {Type}"); + if (Secret is not null) + { + sb.Append(iv, $", Secret = {Secret}"); + } + if (_default is not null) + { + sb.Append(iv, $", Default = {_default}"); + } + if (Format is not null) + { + sb.Append(iv, $", Format = {Format}"); + } + if (Multiline is not null) + { + sb.Append(iv, $", Multiline = {Multiline}"); + } + if (AskAtRuntime is not null) + { + sb.Append(iv, $", AskAtRuntime = {AskAtRuntime}"); + } + if (HelpText is not null) + { + sb.Append(iv, $", HelpText = {HelpText}"); + } + sb.Append(" }"); + return sb.ToString(); + } +} diff --git a/src/Jagabata/CredentialType/UrlField.cs b/src/Jagabata/CredentialType/UrlField.cs new file mode 100644 index 0000000..a3cd9b8 --- /dev/null +++ b/src/Jagabata/CredentialType/UrlField.cs @@ -0,0 +1,16 @@ +namespace Jagabata.CredentialType; + +/// +/// Url Input field +/// +public sealed class UrlField : StringField +{ + public UrlField() : base() + { + Format = FieldFormat.Url; + } + public UrlField(string id, string label) : base(id, label) + { + Format = FieldFormat.Url; + } +} diff --git a/src/Jagabata/Jagabata.csproj b/src/Jagabata/Jagabata.csproj index d56c9ce..290cb16 100644 --- a/src/Jagabata/Jagabata.csproj +++ b/src/Jagabata/Jagabata.csproj @@ -6,7 +6,7 @@ enable Jagabata.psm Jagabata - 2.1.0 + 2.2.0 true diff --git a/src/Jagabata/Json.cs b/src/Jagabata/Json.cs index da22567..d89e2ee 100644 --- a/src/Jagabata/Json.cs +++ b/src/Jagabata/Json.cs @@ -173,7 +173,25 @@ public class RelatedResourceConverter : JsonConverter public override void Write(Utf8JsonWriter writer, RelatedDictionary value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value, options); + writer.WriteStartObject(); + foreach (var kv in value) + { + writer.WritePropertyName(kv.Key); + if (kv.Value is IList list) + { + writer.WriteStartArray(); + foreach (var item in list) + { + writer.WriteStringValue($"{item}"); + } + writer.WriteEndArray(); + } + else + { + writer.WriteStringValue($"{kv.Value}"); + } + } + writer.WriteEndObject(); } } /// diff --git a/src/Jagabata/Resources/CredentialType.cs b/src/Jagabata/Resources/CredentialType.cs index 3180d4d..d80e6df 100644 --- a/src/Jagabata/Resources/CredentialType.cs +++ b/src/Jagabata/Resources/CredentialType.cs @@ -1,7 +1,6 @@ using System.Collections.Specialized; -using System.Text; -using System.Text.Json; using System.Text.Json.Serialization; +using Jagabata.CredentialType; namespace Jagabata.Resources { @@ -69,8 +68,8 @@ public interface ICredentialType /// string Description { get; } CredentialTypeKind Kind { get; } - CredentialTypeInputs Inputs { get; } - Dictionary> Injectors { get; } + FieldList Inputs { get; } + Injectors Injectors { get; } } public class CredentialType(ulong id, @@ -85,8 +84,8 @@ public class CredentialType(ulong id, CredentialTypeKind kind, string nameSpace, bool managed, - CredentialTypeInputs inputs, - Dictionary> injectors) + FieldList inputs, + Injectors injectors) : ICredentialType, IResource { public const string PATH = "/api/v2/credential_types/"; @@ -134,197 +133,7 @@ public record Summary(Capability UserCapabilities); public CredentialTypeKind Kind { get; } = kind; public string Namespace { get; } = nameSpace; public bool Managed { get; } = managed; - public CredentialTypeInputs Inputs { get; } = inputs; - public Dictionary> Injectors { get; } = injectors; - } - - public record CredentialTypeInputs(CredentialInputField[] Fields, string[]? Required) - { - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append("{ "); - sb.Append("Fields = ["); - if (Fields.Length > 0) sb.Append($" {string.Join(", ", Fields.Select(field => field.Id))} "); - sb.Append(']'); - if (Required is not null) - { - sb.Append(", Required = ["); - if (Required.Length > 0) sb.Append($" {string.Join(", ", Required)} "); - sb.Append(']'); - } - sb.Append(" }"); - return sb.ToString(); - } - } - - [JsonConverter(typeof(CredentialInputFieldConverter))] - public abstract record CredentialInputField(string Id, string Label, string Type, string? HelpText); - - public record CredentialBoolInputField(string Id, - string Label, - string Type, - string? HelpText, - bool? Default) - : CredentialInputField(Id, Label, Type, HelpText) - { - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('{'); - sb.Append($" Id = {Id}"); - sb.Append($", Label = {Label}"); - sb.Append($", Type = {Type}"); - if (Default is not null) sb.Append($", Default = {Default}"); - if (HelpText is not null) sb.Append($", HelpText = {HelpText}"); - sb.Append(" }"); - return sb.ToString(); - } - } - public record CredentialStringInputField(string Id, - string Label, - string Type, - string? HelpText, - string[]? Choices, - string? Format, - bool Secret = false, - bool Multiline = false, - string? Default = null) - : CredentialInputField(Id, Label, Type, HelpText) - { - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('{'); - sb.Append($" Id = {Id}"); - sb.Append($", Label = {Label}"); - sb.Append($", Type = {Type}"); - if (Default is not null) sb.Append($", Default = {Default}"); - if (Choices is not null) sb.Append($", Choices = [{string.Join(", ", Choices)}]"); - if (Format is not null) sb.Append($", Format = {Format}"); - if (Secret) sb.Append(", Secret = True"); - if (Multiline) sb.Append(", Multiline = True"); - if (HelpText is not null) sb.Append($", HelpText = {HelpText}"); - sb.Append(" }"); - return sb.ToString(); - } - } - - internal class CredentialInputFieldConverter : JsonConverter - { - public override CredentialInputField? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - (string id, string label, string? help_text, string[]? choices, string? format, string type, bool secret, bool multiline) = - ("", "", null, null, null, "string", false, false); - string? defaultString = null; - bool? defaultBool = null; - while (reader.Read()) - { - if (reader.TokenType == JsonTokenType.EndObject) - { - switch (type) - { - case "boolean": - return new CredentialBoolInputField(id, label, type, help_text, defaultBool); - case "string": - default: - return new CredentialStringInputField( - id, label, type, help_text, choices, format, secret, multiline, defaultString); - } - } - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException($"TokenType is not PropertyName: {reader.TokenType}"); - } - string propertyName = reader.GetString() ?? throw new JsonException("PropertyName is null"); - reader.Read(); - switch (propertyName) - { - case "id": - id = reader.GetString() ?? ""; - break; - case "label": - label = reader.GetString() ?? ""; - break; - case "help_text": - help_text = reader.GetString(); - break; - case "choices": - choices = JsonSerializer.Deserialize(ref reader, options); - break; - case "format": - format = reader.GetString(); - break; - case "secret": - secret = reader.GetBoolean(); - break; - case "multiline": - multiline = reader.GetBoolean(); - break; - case "type": - type = reader.GetString() ?? "string"; - break; - case "default": - switch (reader.TokenType) - { - case JsonTokenType.String: - defaultString = reader.GetString() ?? ""; - break; - case JsonTokenType.True: - case JsonTokenType.False: - defaultBool = reader.GetBoolean(); - break; - } - break; - } - } - throw new JsonException(); - } - - public override void Write(Utf8JsonWriter writer, CredentialInputField value, JsonSerializerOptions options) - { - writer.WriteStartObject(); - writer.WriteString("id", value.Id); - writer.WriteString("label", value.Label); - writer.WriteString("type", value.Type); - if (value.HelpText is not null) - { - writer.WriteString("help_text", value.HelpText); - } - switch (value) - { - case CredentialBoolInputField boolField: - if (boolField.Default is not null) - { - writer.WriteBoolean("default", (bool)boolField.Default); - } - break; - case CredentialStringInputField strField: - if (strField.Choices is not null) - { - writer.WritePropertyName("choices"); - JsonSerializer.Serialize(writer, strField.Choices, options); - } - if (strField.Default is not null) - { - writer.WriteString("default", strField.Default); - } - if (strField.Format is not null) - { - writer.WriteString("format", strField.Format); - } - if (strField.Secret) - { - writer.WriteBoolean("secret", strField.Secret); - } - if (strField.Multiline) - { - writer.WriteBoolean("multiline", strField.Multiline); - } - break; - } - writer.WriteEndObject(); - } + public FieldList Inputs { get; } = inputs; + public Injectors Injectors { get; } = injectors; } } - diff --git a/src/Jagabata/Resources/ResourceBase.cs b/src/Jagabata/Resources/ResourceBase.cs index 902d4fe..2b64b98 100644 --- a/src/Jagabata/Resources/ResourceBase.cs +++ b/src/Jagabata/Resources/ResourceBase.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace Jagabata.Resources { public interface IResource @@ -12,7 +14,37 @@ public interface IResource ResourceType Type { get; } } - public record struct Resource(ResourceType Type, ulong Id) : IResource; + public record struct Resource(ResourceType Type, ulong Id) : IResource, IParsable + { + public static Resource Parse(string s, IFormatProvider? provider = null) + { + return TryParse(s, provider, out var result) + ? result + : throw new FormatException($"Resource format should be '{{Type}}:{{Id}}': {s}"); + } + + public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Resource result) + { + result = default; + if (s is null) + { + return false; + } + var list = s.Split([':', '#'], StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + if (list.Length > 1 + && Enum.TryParse(list[0], true, out var resourceType) + && ulong.TryParse(list[1], System.Globalization.NumberStyles.Integer, provider, out var id)) + { + result = new Resource(resourceType, id); + return true; + } + return false; + } + public override readonly string ToString() + { + return $"{Type}:{Id}"; + } + } public interface IResource : IResource { diff --git a/src/Jagabata/Resources/Survey.cs b/src/Jagabata/Resources/Survey.cs index a10455f..fccaa56 100644 --- a/src/Jagabata/Resources/Survey.cs +++ b/src/Jagabata/Resources/Survey.cs @@ -46,11 +46,11 @@ public SurveySpec(SurveySpecType type, string name, string variableName) : this( public string Name { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public SurveySpecType Type { get; internal set; } - public bool Required { get; set; } = false; + public bool Required { get; set; } public string Variable { get; set; } = string.Empty; public virtual object? Default { get; set; } public virtual object Choices { get; set; } = string.Empty; - public int Min { get; set; } = 0; + public int Min { get; set; } public int Max { get; set; } = 1024; public bool NewQuestion { get; set; } @@ -81,16 +81,22 @@ internal class SurveySpecConverter : JsonConverter { case "max": if (reader.TryGetInt32(out int max)) + { spec.Max = max; + } break; case "min": if (reader.TryGetInt32(out int min)) + { spec.Min = min; + } break; case "type": var typeName = reader.GetString(); if (Enum.TryParse(typeName, true, out var type)) + { spec.Type = type; + } break; case "choices": switch (reader.TokenType) @@ -110,9 +116,13 @@ internal class SurveySpecConverter : JsonConverter { case JsonTokenType.Number: if (reader.TryGetInt32(out var defaultInt)) + { spec.Default = defaultInt; + } else if (reader.TryGetSingle(out var defaultFloat)) + { spec.Default = defaultFloat; + } break; case JsonTokenType.String: default: @@ -164,7 +174,10 @@ public override void Write(Utf8JsonWriter writer, SurveySpec value, JsonSerializ { writer.WritePropertyName("choices"); writer.WriteStartArray(); - foreach (var item in list) writer.WriteStringValue(item.ToString()); + foreach (var item in list) + { + writer.WriteStringValue(item.ToString()); + } writer.WriteEndArray(); } else diff --git a/src/formats.ps1xml b/src/formats.ps1xml index cbe37a8..e273ea8 100644 --- a/src/formats.ps1xml +++ b/src/formats.ps1xml @@ -917,6 +917,80 @@ + + CredentialTypeInputs + + Jagabata.CredentialType.StringField + Jagabata.CredentialType.BoolField + Jagabata.CredentialType.ChoiceField + + + + + + + Right + + + + + Right + + + Right + + + Right + + + + + Right + + + + + + true + + + Id + + + Type + + + Label + + + Format + + + Required + + + Secret + + + Multiline + + + Default + + + Choices + + + AskAtRuntime + + + HelpText + + + + + + Inventory