diff --git a/google/cloud/bigquery/v2/minimal/internal/job_response.cc b/google/cloud/bigquery/v2/minimal/internal/job_response.cc index 1a531a60c8139..3eb2633f5bd8f 100644 --- a/google/cloud/bigquery/v2/minimal/internal/job_response.cc +++ b/google/cloud/bigquery/v2/minimal/internal/job_response.cc @@ -37,7 +37,7 @@ bool valid_list_format_job(nlohmann::json const& j) { } bool valid_jobs_list(nlohmann::json const& j) { - return (j.contains("kind") && j.contains("etag") && j.contains("jobs")); + return (j.contains("kind") && j.contains("etag")); } StatusOr parse_json(std::string const& payload) { @@ -95,9 +95,16 @@ StatusOr ListJobsResponse::BuildFromHttpResponse( ListJobsResponse result; result.http_response = http_response; - result.kind = json->value("kind", ""); result.etag = json->value("etag", ""); + + if (!(*json).contains("jobs")) { + // For accounts with no jobs, "jobs" json key will not be returned. + // Only "kind" and "etag" keys are returned. Since the server returns + // a 200 in this case, we need to do the same. + return result; + } + result.next_page_token = json->value("nextPageToken", ""); for (auto const& kv : json->at("jobs").items()) { diff --git a/google/cloud/bigquery/v2/minimal/internal/job_response_test.cc b/google/cloud/bigquery/v2/minimal/internal/job_response_test.cc index d44e3b8939e90..4e56ee42c4365 100644 --- a/google/cloud/bigquery/v2/minimal/internal/job_response_test.cc +++ b/google/cloud/bigquery/v2/minimal/internal/job_response_test.cc @@ -126,6 +126,20 @@ TEST(GetJobResponseTest, InvalidJob) { HasSubstr("Not a valid Json Job object"))); } +TEST(ListJobsResponseTest, NoJobs) { + BigQueryHttpResponse http_response; + http_response.payload = + R"({"kind": "kind-1", + "etag": "tag-1"})"; + auto const list_jobs_response = + ListJobsResponse::BuildFromHttpResponse(http_response); + + ASSERT_STATUS_OK(list_jobs_response); + EXPECT_FALSE(list_jobs_response->http_response.payload.empty()); + EXPECT_EQ(list_jobs_response->kind, "kind-1"); + EXPECT_EQ(list_jobs_response->etag, "tag-1"); +} + TEST(ListJobsResponseTest, SuccessMultiplePages) { BigQueryHttpResponse http_response; http_response.payload = @@ -225,16 +239,6 @@ TEST(ListJobsResponseTest, InvalidJson) { HasSubstr("Error parsing Json from response payload"))); } -TEST(ListJobsResponseTest, InvalidJobList) { - BigQueryHttpResponse http_response; - http_response.payload = - R"({"kind": "jkind", - "etag": "jtag"})"; - auto const response = ListJobsResponse::BuildFromHttpResponse(http_response); - EXPECT_THAT(response, StatusIs(StatusCode::kInternal, - HasSubstr("Not a valid Json JobList object"))); -} - TEST(ListJobsResponseTest, InvalidListFormatJob) { BigQueryHttpResponse http_response; http_response.payload = diff --git a/google/cloud/bigquery/v2/minimal/internal/table_response.cc b/google/cloud/bigquery/v2/minimal/internal/table_response.cc index cd26a9f7bfca1..d274a8f73b16a 100644 --- a/google/cloud/bigquery/v2/minimal/internal/table_response.cc +++ b/google/cloud/bigquery/v2/minimal/internal/table_response.cc @@ -36,7 +36,7 @@ bool valid_list_format_table(nlohmann::json const& j) { } bool valid_tables_list(nlohmann::json const& j) { - return (j.contains("kind") && j.contains("etag") && j.contains("tables")); + return (j.contains("kind") && j.contains("etag")); } StatusOr parse_json(std::string const& payload) { @@ -88,6 +88,11 @@ StatusOr ListTablesResponse::BuildFromHttpResponse( result.next_page_token = json->value("nextPageToken", ""); result.total_items = json->value("totalItems", 0); + if (result.total_items == 0) { + // If the dataset is empty, server does not return the "tables" key. + return result; + } + for (auto const& kv : json->at("tables").items()) { auto const& json_list_format_table_obj = kv.value(); if (!valid_list_format_table(json_list_format_table_obj)) { diff --git a/google/cloud/bigquery/v2/minimal/internal/table_response_test.cc b/google/cloud/bigquery/v2/minimal/internal/table_response_test.cc index f5fa565848eae..c4bc19ac826c4 100644 --- a/google/cloud/bigquery/v2/minimal/internal/table_response_test.cc +++ b/google/cloud/bigquery/v2/minimal/internal/table_response_test.cc @@ -64,6 +64,20 @@ TEST(GetTableResponseTest, InvalidTable) { HasSubstr("Not a valid Json Table object"))); } +TEST(ListTablesResponseTest, EmptyDatasetNoTables) { + BigQueryHttpResponse http_response; + http_response.payload = + R"({"kind":"kind-1", "etag":"tag-1", "totalItems":0})"; + auto const list_tables_response = + ListTablesResponse::BuildFromHttpResponse(http_response); + ASSERT_STATUS_OK(list_tables_response); + EXPECT_FALSE(list_tables_response->http_response.payload.empty()); + EXPECT_EQ(list_tables_response->kind, "kind-1"); + EXPECT_EQ(list_tables_response->etag, "tag-1"); + EXPECT_EQ(list_tables_response->total_items, 0); + EXPECT_THAT(list_tables_response->next_page_token, ""); +} + TEST(ListTablesResponseTest, SuccessMultiplePages) { BigQueryHttpResponse http_response; auto tables_json_txt = @@ -133,18 +147,6 @@ TEST(ListTablesResponseTest, InvalidJson) { HasSubstr("Error parsing Json from response payload"))); } -TEST(ListTablesResponseTest, InvalidTableList) { - BigQueryHttpResponse http_response; - http_response.payload = - R"({"kind": "dkind", - "etag": "dtag"})"; - auto const response = - ListTablesResponse::BuildFromHttpResponse(http_response); - EXPECT_THAT(response, - StatusIs(StatusCode::kInternal, - HasSubstr("Not a valid Json TableList object"))); -} - TEST(ListTablesResponseTest, InvalidListFormatTable) { BigQueryHttpResponse http_response; http_response.payload =