From 577debb433af3984454ae1f29ffa0c005073cb5d Mon Sep 17 00:00:00 2001 From: Jorge Gallegos Date: Fri, 27 Jun 2025 12:10:15 -0600 Subject: [PATCH 1/2] According to docs, these fields are all mandatory If you specify table_reference, you should specify all 3 fields as per docs https://cloud.google.com/bigquery/docs/reference/rest/v2/TableReference --- plugins/modules/gcp_bigquery_table.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/modules/gcp_bigquery_table.py b/plugins/modules/gcp_bigquery_table.py index 0eaebb1d..ac529cf1 100644 --- a/plugins/modules/gcp_bigquery_table.py +++ b/plugins/modules/gcp_bigquery_table.py @@ -56,17 +56,17 @@ options: dataset_id: description: - The ID of the dataset containing this table. - required: false + required: true type: str project_id: description: - The ID of the project containing this table. - required: false + required: true type: str table_id: description: - The ID of the the table. - required: false + required: true type: str clustering: description: @@ -1016,7 +1016,11 @@ def main(): module = GcpModule( argument_spec=dict( state=dict(default='present', choices=['present', 'absent'], type='str'), - table_reference=dict(type='dict', options=dict(dataset_id=dict(type='str'), project_id=dict(type='str'), table_id=dict(type='str'))), + table_reference=dict(type='dict', options=dict( + dataset_id=dict(type='str', required=True), + project_id=dict(type='str', required=True), + table_id=dict(type='str', required=True) + )), clustering=dict(type='list', elements='str'), description=dict(type='str'), friendly_name=dict(type='str'), From 63d70250126aabeb511437c0607235a927e69776 Mon Sep 17 00:00:00 2001 From: Jorge Gallegos Date: Fri, 27 Jun 2025 12:12:06 -0600 Subject: [PATCH 2/2] Let fields be sent raw in table schema definition As per https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema the `fields` attribute can recursively define more fields if the `type` attribute is RECORD. There is no way to define recursive argument spec in ansible modules, but if sent as `raw`: > The raw type, performs no type validation or type casting, and maintains the type of the passed value. (from https://docs.ansible.com/ansible/latest/dev_guide/developing_program_flow_modules.html#argument-spec) Which works for what we're trying to accomplish here. Also added integration test for this change --- plugins/modules/gcp_bigquery_table.py | 8 +- .../targets/gcp_bigquery_table/tasks/main.yml | 5 + .../gcp_bigquery_table/tasks/nested.yml | 92 +++++++++++++++++++ 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 tests/integration/targets/gcp_bigquery_table/tasks/nested.yml diff --git a/plugins/modules/gcp_bigquery_table.py b/plugins/modules/gcp_bigquery_table.py index ac529cf1..c5db0afe 100644 --- a/plugins/modules/gcp_bigquery_table.py +++ b/plugins/modules/gcp_bigquery_table.py @@ -181,7 +181,7 @@ options: fields: description: - Describes the nested schema fields if the type property is set to RECORD. - elements: str + elements: raw required: false type: list mode: @@ -296,7 +296,7 @@ options: description: - Describes the nested schema fields if the type property is set to RECORD . - elements: str + elements: raw required: false type: list mode: @@ -1045,7 +1045,7 @@ def main(): elements='dict', options=dict( description=dict(type='str'), - fields=dict(type='list', elements='str'), + fields=dict(type='list', elements='raw'), mode=dict(type='str'), name=dict(type='str'), type=dict(type='str'), @@ -1072,7 +1072,7 @@ def main(): elements='dict', options=dict( description=dict(type='str'), - fields=dict(type='list', elements='str'), + fields=dict(type='list', elements='raw'), mode=dict(type='str'), name=dict(type='str'), type=dict(type='str'), diff --git a/tests/integration/targets/gcp_bigquery_table/tasks/main.yml b/tests/integration/targets/gcp_bigquery_table/tasks/main.yml index fe47378c..cbdc3200 100644 --- a/tests/integration/targets/gcp_bigquery_table/tasks/main.yml +++ b/tests/integration/targets/gcp_bigquery_table/tasks/main.yml @@ -1,3 +1,8 @@ --- - name: Generated tests ansible.builtin.include_tasks: autogen.yml + +- name: Run nested test cases + ansible.builtin.include_tasks: nested.yml + vars: + dataset_name: "{{ resource_name | replace('-', '_') }}_nested" diff --git a/tests/integration/targets/gcp_bigquery_table/tasks/nested.yml b/tests/integration/targets/gcp_bigquery_table/tasks/nested.yml new file mode 100644 index 00000000..000f4293 --- /dev/null +++ b/tests/integration/targets/gcp_bigquery_table/tasks/nested.yml @@ -0,0 +1,92 @@ +--- +- name: Run testcases + block: + - name: create dataset + google.cloud.gcp_bigquery_dataset: + name: "{{ dataset_name }}" + dataset_reference: + dataset_id: "{{ dataset_name }}" + project: "{{ gcp_project }}" + auth_kind: "{{ gcp_cred_kind }}" + service_account_file: "{{ gcp_cred_file }}" + state: present + register: _dataset + + - name: Create table with defined fields + google.cloud.gcp_bigquery_table: + name: "{{ resource_name }}-fields" + dataset: "{{ dataset_name }}" + state: present + project: "{{ gcp_project }}" + auth_kind: "{{ gcp_cred_kind }}" + service_account_file: "{{ gcp_cred_file }}" + table_reference: + dataset_id: "{{ dataset_name }}" + project_id: "{{ gcp_project }}" + table_id: "{{ resource_name }}-fields" + schema: + fields: + - name: id + description: An Integer field + type: INTEGER + - name: name + description: A String field + type: STRING + + - name: Create table with nested fields + google.cloud.gcp_bigquery_table: + name: "{{ resource_name }}-nested" + dataset: "{{ dataset_name }}" + state: present + project: "{{ gcp_project }}" + auth_kind: "{{ gcp_cred_kind }}" + service_account_file: "{{ gcp_cred_file }}" + table_reference: + dataset_id: "{{ dataset_name }}" + project_id: "{{ gcp_project }}" + table_id: "{{ resource_name }}-nested" + schema: + fields: + - name: id + description: An Integer field + type: INTEGER + - name: def + description: A Record field + type: RECORD + fields: + - name: id + description: A nested Integer field + type: INTEGER + - name: subdef + description: A nested Record field + type: RECORD + fields: + - name: id + description: A nested-nested Integer field + type: INTEGER + - name: desc + description: A nested-nested String field + type: STRING + + always: + - name: Remove tables + google.cloud.gcp_bigquery_table: + name: "{{ resource_name }}-{{ item }}" + dataset: "{{ dataset_name }}" + project: "{{ gcp_project }}" + auth_kind: "{{ gcp_cred_kind }}" + service_account_file: "{{ gcp_cred_file }}" + state: absent + loop: + - fields + - nested + + - name: Remove dataset + google.cloud.gcp_bigquery_dataset: + name: "{{ dataset_name }}" + dataset_reference: + dataset_id: "{{ dataset_name }}" + project: "{{ gcp_project }}" + auth_kind: "{{ gcp_cred_kind }}" + service_account_file: "{{ gcp_cred_file }}" + state: absent