Repository: laughingman7743/BigQuery-DatasetManager

Issue:
Manage labels field
https://github.com/GoogleCloudPlatform/google-cloud-python/pull/4245/files#diff-bafa3d472438f0a46853cc9879265cadR399

Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=h1) Report
> Merging [#7](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=desc) into [master](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/commit/f0a7640c34dcb591b5a0d39148384e257c9a20ac?src=pr&el=desc) will **increase** coverage by `0.12%`.
> The diff coverage is `45.45%`.

[![Impacted file tree graph](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7/graphs/tree.svg?width=650&src=pr&token=5Vtd53LMy2&height=150)](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master    #7      +/-   ##
=======================================
+ Coverage   30.87%   31%   +0.12%     
=======================================
  Files           6     6              
  Lines         353   358       +5     
=======================================
+ Hits          109   111       +2     
- Misses        244   247       +3
```


| [Impacted Files](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [bqdm/action.py](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=tree#diff-YnFkbS9hY3Rpb24ucHk=) | `30.46% <ø> (ø)` | :arrow_up: |
| [bqdm/util.py](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=tree#diff-YnFkbS91dGlsLnB5) | `53.84% <ø> (-1.33%)` | :arrow_down: |
| [bqdm/cli.py](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=tree#diff-YnFkbS9jbGkucHk=) | `0% <0%> (ø)` | :arrow_up: |
| [bqdm/model.py](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=tree#diff-YnFkbS9tb2RlbC5weQ==) | `67.07% <50%> (-2.79%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=footer). Last update [f0a7640...565060b](https://codecov.io/gh/laughingman7743/BigQuery-DatasetManager/pull/7?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/README.rst b/README.rst
index ef9cbc1..1b2503f 100644
--- a/README.rst
+++ b/README.rst
@@ -44,6 +44,8 @@ The resource representation of the dataset is described in `YAML format`_.
     description: null
     default_table_expiration_ms: null
     location: US
+    labels:
+        foo: bar
     access_entries:
     -   role: OWNER
         entity_type: specialGroup
@@ -75,6 +77,8 @@ See `the official documentation of BigQuery Datasets`_ for details of key names.
 +----------------+-------------+-----------+---------+----------------------------------------------------------+
 | location                                 | str     | Location in which the dataset is hosted.                 |
 +----------------+-------------+-----------+---------+----------------------------------------------------------+
+| labels                                   | map     | Labels for the dataset.                                  |
++----------------+-------------+-----------+---------+----------------------------------------------------------+
 | access_entries                           | seq     | Represent grant of an access role to an entity.          |
 +----------------+-------------+-----------+---------+----------------------------------------------------------+
 | access_entries | role                    | str     | Role granted to the entity. One of                       |
@@ -277,7 +281,4 @@ Run test multiple Python versions
 TODO
 ----
 
-#. Support labels field (Currently ``google-cloud-bigquery`` `does not support labels field of Datasets`_)
 #. Manage table resources
-
-.. _`does not support labels field of Datasets`: https://github.com/GoogleCloudPlatform/google-cloud-python/issues/2931
diff --git a/bqdm/action.py b/bqdm/action.py
index 2ec16c2..c14529a 100644
--- a/bqdm/action.py
+++ b/bqdm/action.py
@@ -1,10 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import absolute_import
 import codecs
 import logging
 import os
 import sys
 
 import click
+from future.utils import iteritems
 from google.cloud import bigquery
 
 from bqdm.model import BigQueryDataset
@@ -116,10 +118,18 @@ class DatasetAction(object):
             click.secho('  Changing... {0}'.format(converted.path), fg='yellow')
             for d in diff:
                 click.echo('    {0}'.format(d))
+            old_labels = old_dataset.labels
+            if old_labels:
+                labels = converted.labels.copy()
+                for k, v in iteritems(old_labels):
+                    if k not in labels.keys():
+                        labels[k] = None
+                converted.labels = labels
             self.client.update_dataset(converted, [
                 'friendly_name',
                 'description',
                 'default_table_expiration_ms',
+                'labels',
                 'access_entries'
             ])
             click.echo()
diff --git a/bqdm/cli.py b/bqdm/cli.py
index 5405243..ae91d99 100755
--- a/bqdm/cli.py
+++ b/bqdm/cli.py
@@ -13,7 +13,7 @@ import bqdm.message as msg
 from bqdm import CONTEXT_SETTINGS
 from bqdm.action import DatasetAction
 from bqdm.model import BigQueryDataset, BigQueryAccessEntry
-from bqdm.util import list_local_datasets, ordered_dict_constructor, str_representer
+from bqdm.util import list_local_datasets, str_representer
 
 
 _logger = logging.getLogger(__name__)
@@ -25,7 +25,6 @@ yaml.add_representer(str, str_representer)
 yaml.add_representer(unicode, str_representer)
 yaml.add_representer(BigQueryDataset, BigQueryDataset.represent)
 yaml.add_representer(BigQueryAccessEntry, BigQueryAccessEntry.represent)
-yaml.add_constructor('tag:yaml.org,2002:map', ordered_dict_constructor)
 
 
 @click.group(context_settings=CONTEXT_SETTINGS)
diff --git a/bqdm/model.py b/bqdm/model.py
index c8cd26d..8da3cd3 100644
--- a/bqdm/model.py
+++ b/bqdm/model.py
@@ -9,13 +9,14 @@ from google.cloud.bigquery.dataset import AccessEntry, Dataset
 class BigQueryDataset(object):
 
     def __init__(self, dataset_id, friendly_name, description,
-                 default_table_expiration_ms, location, access_entries):
+                 default_table_expiration_ms, location, labels, access_entries):
         self.dataset_id = dataset_id
         self.friendly_name = friendly_name
         self.description = description
         self.default_table_expiration_ms = default_table_expiration_ms
         self.location = location
-        self.access_entries = access_entries
+        self.labels = labels if labels else None
+        self.access_entries = access_entries if access_entries else None
 
     @staticmethod
     def from_dict(value):
@@ -27,6 +28,7 @@ class BigQueryDataset(object):
                                value.get('description', None),
                                value.get('default_table_expiration_ms', None),
                                value.get('location', None),
+                               value.get('labels', None),
                                access_entries)
 
     @staticmethod
@@ -39,6 +41,7 @@ class BigQueryDataset(object):
                                dataset.description,
                                dataset.default_table_expiration_ms,
                                dataset.location,
+                               dataset.labels,
                                access_entries)
 
     @staticmethod
@@ -52,6 +55,7 @@ class BigQueryDataset(object):
         dataset.description = value.description
         dataset.default_table_expiration_ms = value.default_table_expiration_ms
         dataset.location = value.location
+        dataset.labels = value.labels if value.labels is not None else dict()
         dataset.access_entries = access_entries
         return dataset
 
@@ -65,6 +69,7 @@ class BigQueryDataset(object):
                 ('description', value.description),
                 ('default_table_expiration_ms', value.default_table_expiration_ms),
                 ('location', value.location),
+                ('labels', value.labels),
                 ('access_entries', value.access_entries),
             )
         )
@@ -75,6 +80,8 @@ class BigQueryDataset(object):
                 self.description,
                 self.default_table_expiration_ms,
                 self.location,
+                frozenset(sorted(self.labels.items())) if self.labels is not None
+                else self.labels,
                 frozenset(self.access_entries) if self.access_entries is not None
                 else self.access_entries,)
 
diff --git a/bqdm/util.py b/bqdm/util.py
index b63a9a0..1a28f1e 100644
--- a/bqdm/util.py
+++ b/bqdm/util.py
@@ -4,7 +4,6 @@ import codecs
 import difflib
 import glob
 import os
-from collections import OrderedDict
 
 import click
 import yaml
@@ -15,10 +14,6 @@ def str_representer(_, data):
     return yaml.ScalarNode('tag:yaml.org,2002:str', data)
 
 
-def ordered_dict_constructor(loader, data):
-    return OrderedDict(loader.construct_pairs(data))
-
-
 def dump_dataset(data):
     return yaml.dump(data, default_flow_style=False, indent=4,
                      allow_unicode=True, canonical=False)


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
index 95bd52a..f535f26 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
+from __future__ import absolute_import
 import yaml
 
-from bqdm.util import ordered_dict_constructor, str_representer
+from bqdm.util import str_representer
 from bqdm.model import BigQueryDataset, BigQueryAccessEntry
 
 
 yaml.add_representer(str, str_representer)
 yaml.add_representer(BigQueryDataset, BigQueryDataset.represent)
 yaml.add_representer(BigQueryAccessEntry, BigQueryAccessEntry.represent)
-yaml.add_constructor('tag:yaml.org,2002:map', ordered_dict_constructor)
diff --git a/tests/test_action.py b/tests/test_action.py
index 16b2cd9..7382fcf 100644
--- a/tests/test_action.py
+++ b/tests/test_action.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import absolute_import
 import unittest
 
 from bqdm.action import DatasetAction
@@ -14,6 +15,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset2 = BigQueryDataset(
@@ -22,6 +24,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
 
@@ -50,6 +53,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset1_2 = BigQueryDataset(
@@ -58,6 +62,7 @@ class TestAction(unittest.TestCase):
             'bar',
             None,
             'UK',
+            None,
             None
         )
         dataset2_1 = BigQueryDataset(
@@ -66,6 +71,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset2_2 = BigQueryDataset(
@@ -74,6 +80,59 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
+            [
+                BigQueryAccessEntry(
+                    None,
+                    'view',
+                    {
+                        'datasetId': 'test',
+                        'projectId': 'test-project',
+                        'tableId': 'test_table'
+                    }
+                )
+            ]
+        )
+        dataset3_1 = BigQueryDataset(
+            'test3',
+            'test_friendly_name',
+            'test_description',
+            24 * 30 * 60 * 1000,
+            'US',
+            {
+                'foo': 'bar'
+            },
+            None
+        )
+        dataset3_2 = BigQueryDataset(
+            'test3',
+            'test_friendly_name',
+            'test_description',
+            24 * 30 * 60 * 1000,
+            'US',
+            None,
+            [
+                BigQueryAccessEntry(
+                    None,
+                    'view',
+                    {
+                        'datasetId': 'test',
+                        'projectId': 'test-project',
+                        'tableId': 'test_table'
+                    }
+                )
+            ]
+        )
+        dataset3_3 = BigQueryDataset(
+            'test3',
+            'test_friendly_name',
+            'test_description',
+            24 * 30 * 60 * 1000,
+            'US',
+            {
+                'aaa': 'bbb',
+                'ccc': 'ddd'
+            },
             [
                 BigQueryAccessEntry(
                     None,
@@ -117,6 +176,36 @@ class TestAction(unittest.TestCase):
         self.assertEqual(actual_count5, 0)
         self.assertEqual(actual_results5, tuple())
 
+        source6 = [dataset3_1, dataset3_2, dataset3_3]
+        target6 = [dataset3_1, dataset3_2, dataset3_3]
+        actual_count6, actual_results6 = DatasetAction.get_change_datasets(source6, target6)
+        self.assertEqual(actual_count6, 0)
+        self.assertEqual(actual_results6, tuple())
+
+        source7 = [dataset3_1]
+        target7 = [dataset3_2]
+        actual_count7, actual_results7 = DatasetAction.get_change_datasets(source7, target7)
+        self.assertEqual(actual_count7, 1)
+        self.assertEqual(actual_results7, tuple([dataset3_2]))
+
+        source8 = [dataset3_1]
+        target8 = [dataset3_3]
+        actual_count8, actual_results8 = DatasetAction.get_change_datasets(source8, target8)
+        self.assertEqual(actual_count8, 1)
+        self.assertEqual(actual_results8, tuple([dataset3_3]))
+
+        source9 = [dataset3_1]
+        target9 = [dataset3_3]
+        actual_count9, actual_results9 = DatasetAction.get_change_datasets(source9, target9)
+        self.assertEqual(actual_count9, 1)
+        self.assertEqual(actual_results9, tuple([dataset3_3]))
+
+        source10 = [dataset3_2]
+        target10 = [dataset3_3]
+        actual_count10, actual_results10 = DatasetAction.get_change_datasets(source10, target10)
+        self.assertEqual(actual_count10, 1)
+        self.assertEqual(actual_results10, tuple([dataset3_3]))
+
     def test_get_destroy_datasets(self):
         dataset1_1 = BigQueryDataset(
             'test1',
@@ -124,6 +213,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset1_2 = BigQueryDataset(
@@ -132,6 +222,7 @@ class TestAction(unittest.TestCase):
             'bar',
             None,
             'UK',
+            None,
             None
         )
         dataset2_1 = BigQueryDataset(
@@ -140,6 +231,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset2_2 = BigQueryDataset(
@@ -148,6 +240,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [
                 BigQueryAccessEntry(
                     None,
@@ -198,6 +291,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset1_2 = BigQueryDataset(
@@ -206,6 +300,7 @@ class TestAction(unittest.TestCase):
             'bar',
             None,
             'UK',
+            None,
             None
         )
         dataset2_1 = BigQueryDataset(
@@ -214,6 +309,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset2_2 = BigQueryDataset(
@@ -222,6 +318,7 @@ class TestAction(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [
                 BigQueryAccessEntry(
                     None,
diff --git a/tests/test_model.py b/tests/test_model.py
index 16dbe16..e58673d 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import absolute_import
 import unittest
 
 from bqdm.model import BigQueryDataset, BigQueryAccessEntry
@@ -13,6 +14,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset2 = BigQueryDataset(
@@ -21,6 +23,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         dataset3 = BigQueryDataset(
@@ -29,6 +32,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'EU',
+            None,
             None
         )
         dataset4 = BigQueryDataset(
@@ -37,8 +41,10 @@ class TestModel(unittest.TestCase):
             'test_description',
             None,
             'EU',
+            None,
             None
         )
+
         self.assertEqual(dataset1, dataset2)
         self.assertNotEqual(dataset1, dataset3)
         self.assertNotEqual(dataset1, dataset4)
@@ -71,6 +77,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry1]
         )
         dataset_with_access_entry2 = BigQueryDataset(
@@ -79,6 +86,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry2]
         )
         dataset_with_access_entry3 = BigQueryDataset(
@@ -87,6 +95,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry3]
         )
         dataset_with_access_entry4 = BigQueryDataset(
@@ -95,6 +104,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry1]
         )
         dataset_with_access_entry5 = BigQueryDataset(
@@ -103,6 +113,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry1, access_entry3]
         )
         dataset_with_access_entry6 = BigQueryDataset(
@@ -111,6 +122,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry3, access_entry1]
         )
         dataset_with_access_entry7 = BigQueryDataset(
@@ -119,8 +131,10 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry1, access_entry2]
         )
+
         self.assertEqual(dataset_with_access_entry1, dataset_with_access_entry2)
         self.assertNotEqual(dataset_with_access_entry1, dataset_with_access_entry3)
         self.assertNotEqual(dataset_with_access_entry1, dataset_with_access_entry4)
@@ -136,14 +150,17 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
+
         dataset_from_dict1 = BigQueryDataset.from_dict({
             'dataset_id': 'test',
             'friendly_name': 'test_friendly_name',
             'description': 'test_description',
             'default_table_expiration_ms': 24 * 30 * 60 * 1000,
             'location': 'US',
+            'labels': None,
             'access_entris': None
         })
         dataset_from_dict2 = BigQueryDataset.from_dict({
@@ -152,6 +169,7 @@ class TestModel(unittest.TestCase):
             'description': 'test_description',
             'default_table_expiration_ms': None,
             'location': 'US',
+            'labels': None,
             'access_entries': None
         })
         dataset_from_dict3 = BigQueryDataset.from_dict({
@@ -160,8 +178,10 @@ class TestModel(unittest.TestCase):
             'description': 'test_description',
             'default_table_expiration_ms': None,
             'location': 'US',
+            'labels': None,
             'access_entries': None
         })
+
         self.assertEqual(dataset, dataset_from_dict1)
         self.assertNotEqual(dataset, dataset_from_dict2)
         self.assertNotEqual(dataset, dataset_from_dict3)
@@ -188,6 +208,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry1]
         )
         dataset_with_access_entry2 = BigQueryDataset(
@@ -196,6 +217,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry2]
         )
         dataset_with_access_entry3 = BigQueryDataset(
@@ -204,6 +226,7 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry1, access_entry2]
         )
         dataset_with_access_entry4 = BigQueryDataset(
@@ -212,14 +235,17 @@ class TestModel(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry1, access_entry1]
         )
+
         dataset_with_access_entry_from_dict1 = BigQueryDataset.from_dict({
             'dataset_id': 'test',
             'friendly_name': 'test_friendly_name',
             'description': 'test_description',
             'default_table_expiration_ms': 24 * 30 * 60 * 1000,
             'location': 'US',
+            'labels': None,
             'access_entries': [
                 {
                     'role': 'OWNER',
@@ -234,6 +260,7 @@ class TestModel(unittest.TestCase):
             'description': 'test_description',
             'default_table_expiration_ms': 24 * 30 * 60 * 1000,
             'location': 'US',
+            'labels': None,
             'access_entries': [
                 {
                     'role': None,
@@ -252,6 +279,7 @@ class TestModel(unittest.TestCase):
             'description': 'test_description',
             'default_table_expiration_ms': 24 * 30 * 60 * 1000,
             'location': 'US',
+            'labels': None,
             'access_entries': [
                 {
                     'role': 'OWNER',
@@ -275,6 +303,7 @@ class TestModel(unittest.TestCase):
             'description': 'test_description',
             'default_table_expiration_ms': 24 * 30 * 60 * 1000,
             'location': 'US',
+            'labels': None,
             'access_entries': [
                 {
                     'role': 'OWNER',
@@ -288,6 +317,7 @@ class TestModel(unittest.TestCase):
                 }
             ]
         })
+
         self.assertEqual(dataset_with_access_entry1, dataset_with_access_entry_from_dict1)
         self.assertNotEqual(dataset_with_access_entry1, dataset_with_access_entry_from_dict2)
         self.assertNotEqual(dataset_with_access_entry1, dataset_with_access_entry_from_dict3)
@@ -300,3 +330,60 @@ class TestModel(unittest.TestCase):
         self.assertEqual(dataset_with_access_entry4, dataset_with_access_entry_from_dict4)
         self.assertEqual(dataset_with_access_entry1, dataset_with_access_entry_from_dict4)
         self.assertEqual(dataset_with_access_entry4, dataset_with_access_entry_from_dict1)
+
+    def test_dataset_from_dict_with_label(self):
+        label1 = {
+            'foo': 'bar'
+        }
+        label2 = {
+            'aaa': 'bbb',
+            'ccc': 'ddd'
+        }
+
+        dataset_with_label1 = BigQueryDataset(
+            'test',
+            'test_friendly_name',
+            'test_description',
+            24 * 30 * 60 * 1000,
+            'US',
+            label1,
+            None
+        )
+        dataset_with_label2 = BigQueryDataset(
+            'test',
+            'test_friendly_name',
+            'test_description',
+            24 * 30 * 60 * 1000,
+            'US',
+            label2,
+            None
+        )
+
+        dataset_with_label_from_dict1 = BigQueryDataset.from_dict({
+            'dataset_id': 'test',
+            'friendly_name': 'test_friendly_name',
+            'description': 'test_description',
+            'default_table_expiration_ms': 24 * 30 * 60 * 1000,
+            'location': 'US',
+            'labels': {
+                'foo': 'bar'
+            },
+            'access_entries': None
+        })
+        dataset_with_label_from_dict2 = BigQueryDataset.from_dict({
+            'dataset_id': 'test',
+            'friendly_name': 'test_friendly_name',
+            'description': 'test_description',
+            'default_table_expiration_ms': 24 * 30 * 60 * 1000,
+            'location': 'US',
+            'labels': {
+                'aaa': 'bbb',
+                'ccc': 'ddd'
+            },
+            'access_entries': None
+        })
+
+        self.assertEqual(dataset_with_label1, dataset_with_label_from_dict1)
+        self.assertNotEqual(dataset_with_label1, dataset_with_label_from_dict2)
+        self.assertNotEqual(dataset_with_label2, dataset_with_label_from_dict1)
+        self.assertEqual(dataset_with_label2, dataset_with_label_from_dict2)
diff --git a/tests/test_util.py b/tests/test_util.py
index a122afc..b1d29b5 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import absolute_import
 import unittest
 
 from bqdm.model import BigQueryDataset, BigQueryAccessEntry
@@ -14,6 +15,7 @@ class TestUtil(unittest.TestCase):
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             None
         )
         actual_dump_data1 = dump_dataset(dataset1)
@@ -22,6 +24,7 @@ friendly_name: test_friendly_name
 description: test_description
 default_table_expiration_ms: 43200000
 location: US
+labels: null
 access_entries: null
 """
         self.assertEqual(actual_dump_data1, expected_dump_data1)
@@ -37,6 +40,7 @@ access_entries: null
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry2]
         )
         actual_dump_data2 = dump_dataset(dataset2)
@@ -45,6 +49,7 @@ friendly_name: test_friendly_name
 description: test_description
 default_table_expiration_ms: 43200000
 location: US
+labels: null
 access_entries:
 -   role: OWNER
     entity_type: specialGroup
@@ -67,6 +72,7 @@ access_entries:
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry3]
         )
         actual_dump_data3 = dump_dataset(dataset3)
@@ -75,6 +81,7 @@ friendly_name: test_friendly_name
 description: test_description
 default_table_expiration_ms: 43200000
 location: US
+labels: null
 access_entries:
 -   role: null
     entity_type: view
@@ -91,6 +98,7 @@ access_entries:
             'test_description',
             24 * 30 * 60 * 1000,
             'US',
+            None,
             [access_entry2, access_entry3]
         )
         actual_dump_data4 = dump_dataset(dataset4)
@@ -99,6 +107,7 @@ friendly_name: test_friendly_name
 description: test_description
 default_table_expiration_ms: 43200000
 location: US
+labels: null
 access_entries:
 -   role: OWNER
     entity_type: specialGroup
@@ -111,3 +120,53 @@ access_entries:
         tableId: test_table
 """
         self.assertEqual(actual_dump_data4, expected_dump_data4)
+
+        label5 = {
+            'foo': 'bar'
+        }
+        dataset5 = BigQueryDataset(
+            'test5',
+            'test_friendly_name',
+            'test_description',
+            24 * 30 * 60 * 1000,
+            'US',
+            label5,
+            None
+        )
+        actual_dump_data5 = dump_dataset(dataset5)
+        expected_dump_data5 = """dataset_id: test5
+friendly_name: test_friendly_name
+description: test_description
+default_table_expiration_ms: 43200000
+location: US
+labels:
+    foo: bar
+access_entries: null
+"""
+        self.assertEqual(actual_dump_data5, expected_dump_data5)
+
+        label6 = {
+            'aaa': 'bbb',
+            'ccc': 'ddd'
+        }
+        dataset6 = BigQueryDataset(
+            'test6',
+            'test_friendly_name',
+            'test_description',
+            24 * 30 * 60 * 1000,
+            'US',
+            label6,
+            None
+        )
+        actual_dump_data6 = dump_dataset(dataset6)
+        expected_dump_data6 = """dataset_id: test6
+friendly_name: test_friendly_name
+description: test_description
+default_table_expiration_ms: 43200000
+location: US
+labels:
+    aaa: bbb
+    ccc: ddd
+access_entries: null
+"""
+        self.assertEqual(actual_dump_data6, expected_dump_data6)