diff --git a/tableauserverclient/models/datasource_item.py b/tableauserverclient/models/datasource_item.py
index b5568a778..dbaa0ff91 100644
--- a/tableauserverclient/models/datasource_item.py
+++ b/tableauserverclient/models/datasource_item.py
@@ -32,7 +32,7 @@ def __repr__(self):
             self.project_id,
         )
 
-    def __init__(self, project_id: str, name: Optional[str] = None) -> None:
+    def __init__(self, project_id: Optional[str] = None, name: Optional[str] = None) -> None:
         self._ask_data_enablement = None
         self._certified = None
         self._certification_note = None
@@ -135,12 +135,11 @@ def id(self) -> Optional[str]:
         return self._id
 
     @property
-    def project_id(self) -> str:
+    def project_id(self) -> Optional[str]:
         return self._project_id
 
     @project_id.setter
-    @property_not_nullable
-    def project_id(self, value: str):
+    def project_id(self, value: Optional[str]):
         self._project_id = value
 
     @property
diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py
index 295a4941f..5e2784b55 100644
--- a/tableauserverclient/server/endpoint/workbooks_endpoint.py
+++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py
@@ -309,6 +309,7 @@ def publish(
         as_job: bool = False,
         hidden_views: Optional[Sequence[str]] = None,
         skip_connection_check: bool = False,
+        parameters=None,
     ):
         if connection_credentials is not None:
             import warnings
@@ -412,7 +413,7 @@ def publish(
 
         # Send the publishing request to server
         try:
-            server_response = self.post_request(url, xml_request, content_type)
+            server_response = self.post_request(url, xml_request, content_type, parameters)
         except InternalServerError as err:
             if err.code == 504 and not as_job:
                 err.content = "Timeout error while publishing. Please use asynchronous publishing to avoid timeouts."
diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py
index b19c3cc56..050874c91 100644
--- a/tableauserverclient/server/request_factory.py
+++ b/tableauserverclient/server/request_factory.py
@@ -9,6 +9,8 @@
 if TYPE_CHECKING:
     from tableauserverclient.server import Server
 
+# this file could be largely replaced if we were willing to import the huge file from generateDS
+
 
 def _add_multipart(parts: Dict) -> Tuple[Any, str]:
     mime_multipart_parts = list()
@@ -146,10 +148,11 @@ def update_req(self, database_item):
 
 
 class DatasourceRequest(object):
-    def _generate_xml(self, datasource_item, connection_credentials=None, connections=None):
+    def _generate_xml(self, datasource_item: DatasourceItem, connection_credentials=None, connections=None):
         xml_request = ET.Element("tsRequest")
         datasource_element = ET.SubElement(xml_request, "datasource")
-        datasource_element.attrib["name"] = datasource_item.name
+        if datasource_item.name:
+            datasource_element.attrib["name"] = datasource_item.name
         if datasource_item.description:
             datasource_element.attrib["description"] = str(datasource_item.description)
         if datasource_item.use_remote_query_agent is not None:
@@ -157,10 +160,16 @@ def _generate_xml(self, datasource_item, connection_credentials=None, connection
 
         if datasource_item.ask_data_enablement:
             ask_data_element = ET.SubElement(datasource_element, "askData")
-            ask_data_element.attrib["enablement"] = datasource_item.ask_data_enablement
+            ask_data_element.attrib["enablement"] = datasource_item.ask_data_enablement.__str__()
 
-        project_element = ET.SubElement(datasource_element, "project")
-        project_element.attrib["id"] = datasource_item.project_id
+        if datasource_item.certified:
+            datasource_element.attrib["isCertified"] = datasource_item.certified.__str__()
+        if datasource_item.certification_note:
+            datasource_element.attrib["certificationNote"] = datasource_item.certification_note
+
+        if datasource_item.project_id:
+            project_element = ET.SubElement(datasource_element, "project")
+            project_element.attrib["id"] = datasource_item.project_id
 
         if connection_credentials is not None and connections is not None:
             raise RuntimeError("You cannot set both `connections` and `connection_credentials`")
diff --git a/tableauserverclient/server/request_options.py b/tableauserverclient/server/request_options.py
index baedd74de..299b9db2f 100644
--- a/tableauserverclient/server/request_options.py
+++ b/tableauserverclient/server/request_options.py
@@ -38,6 +38,7 @@ class Operator:
     class Field:
         Args = "args"
         CompletedAt = "completedAt"
+        ContentUrl = "contentUrl"
         CreatedAt = "createdAt"
         DomainName = "domainName"
         DomainNickname = "domainNickname"
@@ -147,7 +148,7 @@ def get_query_params(self):
         return params
 
 
-class ExcelRequestOptions(RequestOptionsBase):
+class ExcelRequestOptions(_FilterOptionsBase):
     def __init__(self, maxage: int = -1) -> None:
         super().__init__()
         self.max_age = maxage
diff --git a/test/test_datasource_model.py b/test/test_datasource_model.py
index 2360574ec..655284194 100644
--- a/test/test_datasource_model.py
+++ b/test/test_datasource_model.py
@@ -3,11 +3,9 @@
 
 
 class DatasourceModelTests(unittest.TestCase):
-    def test_invalid_project_id(self):
-        self.assertRaises(ValueError, TSC.DatasourceItem, None)
-        datasource = TSC.DatasourceItem("10")
-        with self.assertRaises(ValueError):
-            datasource.project_id = None
+    def test_nullable_project_id(self):
+        datasource = TSC.DatasourceItem(name="10")
+        self.assertEqual(datasource.project_id, None)
 
     def test_require_boolean_flag_bridge_fail(self):
         datasource = TSC.DatasourceItem("10")
diff --git a/test/test_view.py b/test/test_view.py
index f5d3db47b..1459150bb 100644
--- a/test/test_view.py
+++ b/test/test_view.py
@@ -299,3 +299,19 @@ def test_populate_excel(self) -> None:
 
             excel_file = b"".join(single_view.excel)
             self.assertEqual(response, excel_file)
+
+    def test_filter_excel(self) -> None:
+        self.server.version = "3.8"
+        self.baseurl = self.server.views.baseurl
+        with open(POPULATE_EXCEL, "rb") as f:
+            response = f.read()
+        with requests_mock.mock() as m:
+            m.get(self.baseurl + "/d79634e1-6063-4ec9-95ff-50acbf609ff5/crosstab/excel?maxAge=1", content=response)
+            single_view = TSC.ViewItem()
+            single_view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5"
+            request_option = TSC.ExcelRequestOptions(maxage=1)
+            request_option.vf("stuff", "1")
+            self.server.views.populate_excel(single_view, request_option)
+
+            excel_file = b"".join(single_view.excel)
+            self.assertEqual(response, excel_file)