diff --git a/README.md b/README.md
index 3126d35f..0d8d0d6b 100644
--- a/README.md
+++ b/README.md
@@ -96,7 +96,7 @@ Steps to access:
Example: [connect_with_client_certificate.py](examples/sharepoint/connect_with_client_certificate.py)
-#### 4. interactive
+#### 4. Interactive
to login interactively i.e. via a local browser
@@ -108,11 +108,12 @@ Steps to access:
Example: [connect_interactive.py](examples/sharepoint/connect_interactive.py)
Usage:
- ```python
- ctx = ClientContext(site_url).with_interactive(tenant_name_or_id, client_id)
- me = ctx.web.current_user.get().execute_query()
- print(me.login_name)
- ```
+```python
+from office365.sharepoint.client_context import ClientContext
+ctx = ClientContext(site_url).with_interactive(tenant_name_or_id, client_id)
+me = ctx.web.current_user.get().execute_query()
+print(me.login_name)
+```
### Examples
diff --git a/examples/sharepoint/fields/create_lookup.py b/examples/sharepoint/fields/create_lookup.py
new file mode 100644
index 00000000..587b0ca2
--- /dev/null
+++ b/examples/sharepoint/fields/create_lookup.py
@@ -0,0 +1,16 @@
+"""
+Demonstrates how to create lookup field
+"""
+from office365.sharepoint.client_context import ClientContext
+from tests import test_team_site_url, test_client_credentials, create_unique_name
+
+field_name = create_unique_name("MultilookupField")
+client = ClientContext(test_team_site_url).with_credentials(test_client_credentials)
+lookup_list = client.web.default_document_library()
+
+lookup_field = client.web.fields.add_lookup_field(title=field_name,
+ lookup_list=lookup_list,
+ lookup_field_name='Title',
+ allow_multiple_values=True).execute_query()
+print(f"Field {lookup_field.internal_name} has been created")
+lookup_field.delete_object().execute_query()
diff --git a/examples/sharepoint/listitems/create_batch.py b/examples/sharepoint/listitems/create_batch.py
index 3648ea3e..366478cd 100644
--- a/examples/sharepoint/listitems/create_batch.py
+++ b/examples/sharepoint/listitems/create_batch.py
@@ -1,5 +1,5 @@
"""
-Demonstrates how to create multiple list items via batch mode
+Demonstrates how to create multiple list items via batch request
"""
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.lists.template_type import ListTemplateType
@@ -8,8 +8,11 @@
ctx = ClientContext(test_team_site_url).with_credentials(test_client_credentials)
list_tasks = ctx.web.add_list(create_unique_name("Tasks"), ListTemplateType.Tasks).execute_query()
-num_of_items = 200
-task_items = [list_tasks.add_item({"Title": create_unique_name("Task")}) for idx in range(0, num_of_items)]
+num_of_items = 2
+item_props = {
+ "Title": create_unique_name("Task")
+}
+task_items = [list_tasks.add_item(item_props) for idx in range(0, num_of_items)]
ctx.execute_batch()
print("{0} task items created".format(len(task_items)))
diff --git a/generator/metadata/MicrosoftGraph.xml b/generator/metadata/MicrosoftGraph.xml
index 3cd8efd9..7771f1f2 100644
--- a/generator/metadata/MicrosoftGraph.xml
+++ b/generator/metadata/MicrosoftGraph.xml
@@ -20296,6 +20296,7 @@
+
@@ -20425,6 +20426,7 @@
+
@@ -20902,6 +20904,7 @@
+
@@ -20953,6 +20956,7 @@
+
@@ -20979,6 +20983,7 @@
+
@@ -21224,6 +21229,7 @@
+
@@ -21268,6 +21274,7 @@
+
@@ -22060,6 +22067,13 @@
+
+
+
+
+
+
+
@@ -22103,13 +22117,6 @@
-
-
-
-
-
-
-
@@ -29301,6 +29308,7 @@
+
@@ -35980,12 +35988,20 @@
+
+
+
+
+
+
+
+
diff --git a/office365/directory/licenses/service_plan_info.py b/office365/directory/licenses/service_plan_info.py
index af489671..e6f689e8 100644
--- a/office365/directory/licenses/service_plan_info.py
+++ b/office365/directory/licenses/service_plan_info.py
@@ -27,3 +27,7 @@ def __init__(self, _id=None, name=None, provisioning_status=None, applies_to=Non
self.servicePlanName = name
self.provisioningStatus = provisioning_status
self.appliesTo = applies_to
+
+ def __repr__(self):
+ return self.servicePlanName
+
diff --git a/office365/directory/licenses/subscribed_sku.py b/office365/directory/licenses/subscribed_sku.py
index e28dea14..abff0f59 100644
--- a/office365/directory/licenses/subscribed_sku.py
+++ b/office365/directory/licenses/subscribed_sku.py
@@ -1,4 +1,5 @@
from office365.directory.licenses.service_plan_info import ServicePlanInfo
+from office365.directory.licenses.units_detail import LicenseUnitsDetail
from office365.entity import Entity
from office365.runtime.client_value_collection import ClientValueCollection
@@ -9,10 +10,20 @@ class SubscribedSku(Entity):
@property
def account_id(self):
"""
+ The unique ID of the account this SKU belongs to.
:rtype: str
"""
return self.properties.get("accountId", None)
+ @property
+ def applies_to(self):
+ """
+ The target class for this SKU. Only SKUs with target class User are assignable.
+ Possible values are: "User", "Company".
+ :rtype: str
+ """
+ return self.properties.get("appliesTo", None)
+
@property
def sku_id(self):
"""
@@ -21,6 +32,20 @@ def sku_id(self):
"""
return self.properties.get("skuId", None)
+ @property
+ def sku_part_number(self):
+ """
+ The SKU part number; for example: "AAD_PREMIUM" or "RMSBASIC".
+ To get a list of commercial subscriptions that an organization has acquired, see List subscribedSkus.
+ :rtype: str
+ """
+ return self.properties.get("skuPartNumber", None)
+
+ @property
+ def prepaid_units(self):
+ """Information about the number and status of prepaid licenses."""
+ return self.properties.get("prepaidUnits", LicenseUnitsDetail())
+
@property
def service_plans(self):
"""Information about the service plans that are available with the SKU. Not nullable"""
@@ -29,6 +54,7 @@ def service_plans(self):
def get_property(self, name, default_value=None):
if default_value is None:
property_mapping = {
+ "prepaidUnits": self.prepaid_units,
"servicePlans": self.service_plans
}
default_value = property_mapping.get(name, None)
diff --git a/office365/directory/licenses/units_detail.py b/office365/directory/licenses/units_detail.py
new file mode 100644
index 00000000..b2d89891
--- /dev/null
+++ b/office365/directory/licenses/units_detail.py
@@ -0,0 +1,11 @@
+from office365.runtime.client_value import ClientValue
+
+
+class LicenseUnitsDetail(ClientValue):
+ """"""
+
+ def __init__(self, enabled=None):
+ """
+ :param int enabled: The number of units that are enabled for the active subscription of the service SKU.
+ """
+ self.enabled = enabled
diff --git a/office365/directory/policies/authentication_flows.py b/office365/directory/policies/authentication_flows.py
new file mode 100644
index 00000000..23d884df
--- /dev/null
+++ b/office365/directory/policies/authentication_flows.py
@@ -0,0 +1,9 @@
+from office365.entity import Entity
+
+
+class AuthenticationFlowsPolicy(Entity):
+ """
+ Represents the policy configuration of self-service sign-up experience at a tenant level that lets external
+ users request to sign up for approval. It contains information, such as the identifier, display name, and
+ description, and indicates whether self-service sign-up is enabled for the policy.
+ """
diff --git a/office365/directory/policies/authentication_strength.py b/office365/directory/policies/authentication_strength.py
new file mode 100644
index 00000000..1010f0dd
--- /dev/null
+++ b/office365/directory/policies/authentication_strength.py
@@ -0,0 +1,10 @@
+from office365.entity import Entity
+
+
+class AuthenticationStrengthPolicy(Entity):
+ """
+ A collection of settings that define specific combinations of authentication methods and metadata.
+ The authentication strength policy, when applied to a given scenario using Azure AD Conditional Access,
+ defines which authentication methods must be used to authenticate in that scenario. An authentication strength
+ may be built-in or custom (defined by the tenant) and may or may not fulfill the requirements to grant an MFA claim.
+ """
diff --git a/office365/directory/policies/root.py b/office365/directory/policies/root.py
index cfdd2bb1..2a113ff1 100644
--- a/office365/directory/policies/root.py
+++ b/office365/directory/policies/root.py
@@ -1,4 +1,7 @@
+from office365.directory.policies.app_management import AppManagementPolicy
+from office365.directory.policies.authentication_flows import AuthenticationFlowsPolicy
from office365.directory.policies.authentication_methods import AuthenticationMethodsPolicy
+from office365.directory.policies.authentication_strength import AuthenticationStrengthPolicy
from office365.directory.policies.permission_grant import PermissionGrantPolicy
from office365.directory.policies.authorization import AuthorizationPolicy
from office365.directory.policies.conditional_access import ConditionalAccessPolicy
@@ -21,6 +24,22 @@ def authentication_methods_policy(self):
ResourcePath("authenticationMethodsPolicy",
self.resource_path)))
+ def authentication_strength_policies(self):
+ """
+ The authentication method combinations that are to be used in scenarios defined by Azure AD Conditional Access.
+ """
+ return self.properties.get('authenticationStrengthPolicies',
+ EntityCollection(self.context, AuthenticationStrengthPolicy,
+ ResourcePath("authenticationStrengthPolicies", self.resource_path)))
+
+ @property
+ def authentication_flows_policy(self):
+ """ The policy configuration of the self-service sign-up experience of external users."""
+ return self.properties.get('authenticationFlowsPolicy',
+ AuthenticationFlowsPolicy(self.context,
+ ResourcePath("authenticationFlowsPolicy",
+ self.resource_path)))
+
@property
def authorization_policy(self):
"""The policy that controls Azure AD authorization settings."""
@@ -33,8 +52,8 @@ def app_management_policies(self):
"""The policies that enforce app management restrictions for specific applications and service principals,
overriding the defaultAppManagementPolicy."""
return self.properties.get('appManagementPolicies',
- AuthorizationPolicy(self.context,
- ResourcePath("appManagementPolicies", self.resource_path)))
+ EntityCollection(self.context, AppManagementPolicy,
+ ResourcePath("appManagementPolicies", self.resource_path)))
@property
def permission_grant_policies(self):
@@ -57,6 +76,8 @@ def conditional_access_policies(self):
def get_property(self, name, default_value=None):
if default_value is None:
property_mapping = {
+ "authenticationStrengthPolicies": self.authentication_strength_policies,
+ "authenticationFlowsPolicy": self.authentication_flows_policy,
"appManagementPolicies": self.app_management_policies,
"authenticationMethodsPolicy": self.authentication_methods_policy,
"authorizationPolicy": self.authorization_policy,
diff --git a/office365/directory/rolemanagement/unified_role_assignment.py b/office365/directory/rolemanagement/unified_role_assignment.py
index c8e63654..02c4280c 100644
--- a/office365/directory/rolemanagement/unified_role_assignment.py
+++ b/office365/directory/rolemanagement/unified_role_assignment.py
@@ -1,4 +1,6 @@
+from office365.directory.rolemanagement.unified_role_definition import UnifiedRoleDefinition
from office365.entity import Entity
+from office365.runtime.paths.resource_path import ResourcePath
class UnifiedRoleAssignment(Entity):
@@ -6,3 +8,20 @@ class UnifiedRoleAssignment(Entity):
A role assignment is used to grant access to resources. It represents a role definition assigned to a principal
(for example, a user or a role-assignable group) at a particular scope.
"""
+
+ @property
+ def role_definition(self):
+ """
+ The roleDefinition the assignment is for. Supports $expand. roleDefinition.Id will be auto expanded.
+ """
+ return self.properties.get('roleDefinition',
+ UnifiedRoleDefinition(self.context,
+ ResourcePath("roleDefinition", self.resource_path)))
+
+ def get_property(self, name, default_value=None):
+ if default_value is None:
+ property_mapping = {
+ "roleDefinition": self.role_definition,
+ }
+ default_value = property_mapping.get(name, None)
+ return super(UnifiedRoleAssignment, self).get_property(name, default_value)
diff --git a/office365/directory/security/attacksimulations/automation.py b/office365/directory/security/attacksimulations/automation.py
new file mode 100644
index 00000000..c3cae47b
--- /dev/null
+++ b/office365/directory/security/attacksimulations/automation.py
@@ -0,0 +1,5 @@
+from office365.entity import Entity
+
+
+class SimulationAutomation(Entity):
+ """Represents simulation automation created to run on a tenant."""
diff --git a/office365/directory/security/attacksimulations/root.py b/office365/directory/security/attacksimulations/root.py
index 33cac716..f65becd1 100644
--- a/office365/directory/security/attacksimulations/root.py
+++ b/office365/directory/security/attacksimulations/root.py
@@ -1,6 +1,33 @@
+from office365.directory.security.attacksimulations.automation import SimulationAutomation
+from office365.directory.security.attacksimulations.simulation import Simulation
from office365.entity import Entity
+from office365.entity_collection import EntityCollection
+from office365.runtime.paths.resource_path import ResourcePath
class AttackSimulationRoot(Entity):
"""Represents an abstract type that provides the ability to launch a realistic phishing attack that organizations
can learn from."""
+
+ @property
+ def simulations(self):
+ """Represents an attack simulation training campaign in a tenant."""
+ return self.properties.get('simulations',
+ EntityCollection(self.context, Simulation,
+ ResourcePath("simulations", self.resource_path)))
+
+ @property
+ def simulation_automations(self):
+ """Represents simulation automation created to run on a tenant."""
+ return self.properties.get('simulationAutomations',
+ EntityCollection(self.context, SimulationAutomation,
+ ResourcePath("simulationAutomations", self.resource_path)))
+
+ def get_property(self, name, default_value=None):
+ if default_value is None:
+ property_mapping = {
+ "simulationAutomations": self.simulation_automations,
+ }
+ default_value = property_mapping.get(name, None)
+ return super(AttackSimulationRoot, self).get_property(name, default_value)
+
diff --git a/office365/directory/security/attacksimulations/simulation.py b/office365/directory/security/attacksimulations/simulation.py
new file mode 100644
index 00000000..cd08d34b
--- /dev/null
+++ b/office365/directory/security/attacksimulations/simulation.py
@@ -0,0 +1,17 @@
+from office365.entity import Entity
+
+
+class Simulation(Entity):
+ """
+ Represents an attack simulation training campaign in a tenant.
+
+ Attack simulation and training is a service available as part of Microsoft Defender for Office 365.
+ This service lets tenant users experience a realistic benign phishing attack and learn from it.
+ The service enables tenant administrators to simulate, assign trainings, and read derived insights into online
+ behaviors of users in the phishing simulations. The service provides attack simulation reports that help tenants
+ identify security knowledge gaps, so that they can further train their users to decrease their susceptibility
+ to attacks.
+
+ The attack simulation and training API enables tenant administrators to list launched simulation exercises
+ and trainings, and get reports on derived insights into online behaviors of users in the phishing simulations.
+ """
diff --git a/office365/directory/security/incidents/incident.py b/office365/directory/security/incidents/incident.py
new file mode 100644
index 00000000..6f81db55
--- /dev/null
+++ b/office365/directory/security/incidents/incident.py
@@ -0,0 +1,31 @@
+from office365.directory.security.alerts.alert import Alert
+from office365.entity import Entity
+from office365.entity_collection import EntityCollection
+from office365.runtime.paths.resource_path import ResourcePath
+
+
+class Incident(Entity):
+ """
+ An incident in Microsoft 365 Defender is a collection of correlated alert instances and associated metadata
+ that reflects the story of an attack in a tenant.
+
+ Microsoft 365 services and apps create alerts when they detect a suspicious or malicious event or activity.
+ Individual alerts provide valuable clues about a completed or ongoing attack. However, attacks typically employ
+ various techniques against different types of entities, such as devices, users, and mailboxes. The result is
+ multiple alerts for multiple entities in your tenant. Because piecing the individual alerts together to gain
+ insight into an attack can be challenging and time-consuming, Microsoft 365 Defender automatically aggregates the
+ alerts and their associated information into an incident.
+ """
+
+ @property
+ def assigned_to(self):
+ """Owner of the incident, or null if no owner is assigned. Free editable text.
+ :rtype: str or None
+ """
+ return self.properties.get("assignedTo", None)
+
+ @property
+ def alerts(self):
+ """The list of related alerts. Supports $expand."""
+ return self.properties.get('alerts',
+ EntityCollection(self.context, Alert, ResourcePath("alerts", self.resource_path)))
diff --git a/office365/directory/security/security.py b/office365/directory/security/security.py
index 296693af..c6086900 100644
--- a/office365/directory/security/security.py
+++ b/office365/directory/security/security.py
@@ -1,6 +1,7 @@
from office365.directory.security.alerts.alert import Alert
from office365.directory.security.attacksimulations.root import AttackSimulationRoot
from office365.directory.security.cases_root import CasesRoot
+from office365.directory.security.incidents.incident import Incident
from office365.directory.security.triggers.root import TriggersRoot
from office365.entity import Entity
from office365.entity_collection import EntityCollection
@@ -37,6 +38,14 @@ def attack_simulation(self):
AttackSimulationRoot(self.context,
ResourcePath("attackSimulation", self.resource_path)))
+ @property
+ def incidents(self):
+ """A collection of correlated alert instances and associated metadata that reflects the story of
+ an attack in a tenant"""
+ return self.properties.get('incidents',
+ EntityCollection(self.context, Incident,
+ ResourcePath("incidents", self.resource_path)))
+
@property
def triggers(self):
return self.properties.get('triggers',
diff --git a/office365/directory/tenant_information.py b/office365/directory/tenant_information.py
index 5b03c5a4..36f80567 100644
--- a/office365/directory/tenant_information.py
+++ b/office365/directory/tenant_information.py
@@ -3,3 +3,15 @@
class TenantInformation(ClientValue):
"""Information about your Azure AD tenant that is publicly displayed to users in other Azure AD tenants."""
+
+ def __str__(self, default_domain_name=None, display_name=None, federation_brand_name=None, tenant_id=None):
+ """
+ :param str default_domain_name: Primary domain name of an Azure AD tenant.
+ :param str display_name: Display name of an Azure AD tenant.
+ :param str federation_brand_name: Name shown to users that sign in to an Azure AD tenant.
+ :param str tenant_id: Unique identifier of an Azure AD tenant.
+ """
+ self.defaultDomainName = default_domain_name
+ self.displayName = display_name
+ self.federationBrandName = federation_brand_name
+ self.tenantId = tenant_id
diff --git a/office365/graph_client.py b/office365/graph_client.py
index bbfd8853..273d5ace 100644
--- a/office365/graph_client.py
+++ b/office365/graph_client.py
@@ -336,6 +336,14 @@ def permission_grants(self):
"""
return EntityCollection(self, ResourceSpecificPermissionGrant, ResourcePath("permissionGrants"))
+ @property
+ def print(self):
+ """
+ Used to manage printers and print jobs within Universal Print.
+ """
+ from office365.intune.printing.print import Print
+ return Print(self, ResourcePath("print"))
+
@property
def search(self):
"""
diff --git a/office365/intune/printing/connectors/__init__.py b/office365/intune/printing/connectors/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/office365/intune/printing/operations/__init__.py b/office365/intune/printing/operations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/office365/intune/printing/print.py b/office365/intune/printing/print.py
new file mode 100644
index 00000000..baed835d
--- /dev/null
+++ b/office365/intune/printing/print.py
@@ -0,0 +1,6 @@
+from office365.entity import Entity
+
+
+class Print(Entity):
+ """When accompanied by a Universal Print subscription, the Print feature enables management of printers and
+ discovery of printServiceEndpoints that can be used to manage printers and print jobs within Universal Print."""
diff --git a/office365/intune/printing/services/__init__.py b/office365/intune/printing/services/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/office365/intune/printing/services/endpoint.py b/office365/intune/printing/services/endpoint.py
new file mode 100644
index 00000000..1f7797f1
--- /dev/null
+++ b/office365/intune/printing/services/endpoint.py
@@ -0,0 +1,5 @@
+from office365.entity import Entity
+
+
+class PrintServiceEndpoint(Entity):
+ """Represents URI and identifying information for a print service instance."""
diff --git a/office365/intune/printing/services/service.py b/office365/intune/printing/services/service.py
new file mode 100644
index 00000000..1daea988
--- /dev/null
+++ b/office365/intune/printing/services/service.py
@@ -0,0 +1,17 @@
+from office365.entity import Entity
+from office365.entity_collection import EntityCollection
+from office365.intune.printing.services.endpoint import PrintServiceEndpoint
+from office365.runtime.paths.resource_path import ResourcePath
+
+
+class PrintService(Entity):
+ """Represents an Azure AD tenant-specific description of a print service instance.
+ Services exist for each component of the printing infrastructure (discovery, notifications, registration, and IPP)
+ and have one or more endpoints."""
+
+ def endpoints(self):
+ """Endpoints that can be used to access the service."""
+ return self.properties.get('endpoints',
+ EntityCollection(self.context, PrintServiceEndpoint,
+ ResourcePath("endpoints", self.resource_path)))
+
diff --git a/office365/intune/printing/shares/__init__.py b/office365/intune/printing/shares/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/office365/intune/printing/taskdefinitions/__init__.py b/office365/intune/printing/taskdefinitions/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/office365/intune/servicecommunications/issues/issue.py b/office365/intune/servicecommunications/issues/issue.py
index aace8456..51a2b102 100644
--- a/office365/intune/servicecommunications/issues/issue.py
+++ b/office365/intune/servicecommunications/issues/issue.py
@@ -1,4 +1,6 @@
from office365.intune.servicecommunications.announcement_base import ServiceAnnouncementBase
+from office365.runtime.client_result import ClientResult
+from office365.runtime.queries.function import FunctionQuery
class ServiceHealthIssue(ServiceAnnouncementBase):
@@ -11,4 +13,17 @@ class ServiceHealthIssue(ServiceAnnouncementBase):
- Service advisory: "Users may experience delays in emails reception".
"""
+ def incident_report(self):
+ """
+ Provide the Post-Incident Review (PIR) document of a specified service issue for tenant.
+
+ An issue only with status of PostIncidentReviewPublished indicates that the PIR document exists for the issue.
+ The operation returns an error if the specified issue doesn't exist for the tenant or if PIR document
+ does not exist for the issue.
+ """
+ return_type = ClientResult(self.context, bytes())
+ qry = FunctionQuery(self, "incidentReport", None, return_type)
+ self.context.add_query(qry)
+ return return_type
+
diff --git a/office365/sharepoint/contenttypes/fieldlinks/collection.py b/office365/sharepoint/contenttypes/fieldlinks/collection.py
index 69d23a1f..c9cd2a4d 100644
--- a/office365/sharepoint/contenttypes/fieldlinks/collection.py
+++ b/office365/sharepoint/contenttypes/fieldlinks/collection.py
@@ -20,23 +20,22 @@ def add(self, field):
:param str or office365.sharepoint.fields.field.Field field: Specifies the internal name of the field or type
"""
- def _create_query(field_internal_name):
+ def _create_and_add_query(field_internal_name):
"""
:type field_internal_name: str
"""
return_type.set_property("FieldInternalName", field_internal_name)
- return CreateEntityQuery(self, return_type, return_type)
+ qry = CreateEntityQuery(self, return_type, return_type)
+ self.context.add_query(qry)
return_type = FieldLink(self.context)
self.add_child(return_type)
if isinstance(field, Field):
def _field_loaded():
- next_qry = _create_query(field.internal_name)
- self.context.add_query(next_qry)
+ _create_and_add_query(field.internal_name)
field.ensure_property("InternalName", _field_loaded)
else:
- qry = _create_query(field)
- self.context.add_query(qry)
+ _create_and_add_query(field)
return return_type
def get_by_id(self, _id):
diff --git a/office365/sharepoint/fields/collection.py b/office365/sharepoint/fields/collection.py
index 3b5b2e39..072ddd44 100644
--- a/office365/sharepoint/fields/collection.py
+++ b/office365/sharepoint/fields/collection.py
@@ -38,35 +38,53 @@ def add_url_field(self, title, description=None):
description=description,
field_type_kind=FieldType.URL))
- def add_lookup_field(self, title, lookup_list_id, lookup_field_name, allow_multiple_values=False):
+ def add_lookup_field(self, title, lookup_list, lookup_field_name, allow_multiple_values=False):
"""
Creates a Lookup field
- :param bool allow_multiple_values:
- :param str lookup_field_name:
- :param str lookup_list_id:
- :param str title:
+ :param bool allow_multiple_values: Flag determines whether to create multi lookup field or not
+ :param str lookup_field_name: Specifies the name of the field in the other data source when creating
+ a lookup field
+ :param str or office365.sharepoint.lists.list.List lookup_list: Lookup List object or identifier
+ :param str title: Specifies the display name of the field.
"""
- if allow_multiple_values:
- field_schema = '''
-
-
- '''.format(title=title, lookup_field_name=lookup_field_name, lookup_list_id=lookup_list_id)
- return self.create_field_as_xml(field_schema)
+ return_type = Field(self.context)
+
+ def _add_lookup_field(lookup_list_id):
+ """
+ :type lookup_list_id: str
+ """
+ if allow_multiple_values:
+ field_schema = '''
+
+
+ '''.format(title=title, lookup_field_name=lookup_field_name, lookup_list_id=lookup_list_id)
+ self.create_field_as_xml(field_schema, return_type=return_type)
+ else:
+ self.add_field(FieldCreationInformation(title=title,
+ lookup_list_id=lookup_list_id,
+ lookup_field_name=lookup_field_name,
+ field_type_kind=FieldType.Lookup),
+ return_type=return_type)
+
+ from office365.sharepoint.lists.list import List
+ if isinstance(lookup_list, List):
+
+ def _lookup_list_loaded():
+ _add_lookup_field(lookup_list.id)
+ lookup_list.ensure_property("Id", _lookup_list_loaded)
else:
- return self.add_field(FieldCreationInformation(title=title,
- lookup_list_id=lookup_list_id,
- lookup_field_name=lookup_field_name,
- field_type_kind=FieldType.Lookup))
+ _add_lookup_field(lookup_list)
+ return return_type
def add_choice_field(self, title, values, multiple_values=False):
"""
- Created Choice field
+ Creates a Choice field
:param bool multiple_values:
:param list[str] values:
- :param str title:
+ :param str title: Specifies the display name of the field.
"""
fld_type = FieldType.MultiChoice if multiple_values else FieldType.Choice
create_field_info = FieldCreationInformation(title, fld_type)
@@ -77,7 +95,7 @@ def add_user_field(self, title):
"""
Creates a User field
- :param str title: specifies the display name of the field
+ :param str title: Specifies the display name of the field
"""
return self.add_field(FieldCreationInformation(title, FieldType.User))
@@ -106,16 +124,11 @@ def add_dependent_lookup_field(self, display_name, primary_lookup_field, lookup_
"primaryLookupField": primary_lookup_field,
"lookupField": lookup_field
}
- qry = ServiceOperationQuery(self, "AddDependentLookupField", None, parameters, None, return_type)
+ qry = ServiceOperationQuery(self, "AddDependentLookupField", None, parameters,
+ None, return_type)
self.context.add_query(qry)
return return_type
- def add_taxonomy_field(self, title, description=None):
- """
- Adds a taxonomy field
- """
- pass
-
def add(self, field_create_information):
"""Adds a fields to the fields collection.
@@ -127,15 +140,18 @@ def add(self, field_create_information):
self.context.add_query(qry)
return return_type
- def add_field(self, parameters):
+ def add_field(self, parameters, return_type=None):
"""Adds a fields to the fields collection.
:type parameters: office365.sharepoint.fields.creation_information.FieldCreationInformation
+ :param Field or None return_type: Return type
"""
- return_type = Field(self.context)
+ if return_type is None:
+ return_type = Field(self.context)
self.add_child(return_type)
payload = {"parameters": parameters}
- qry = ServiceOperationQuery(self, "AddField", None, payload, None, return_type)
+ qry = ServiceOperationQuery(self, "AddField", None, payload, None,
+ return_type)
self.context.add_query(qry)
return return_type
@@ -151,7 +167,9 @@ def create_taxonomy_field(self, name, term_set, allow_multiple_values=False):
if isinstance(term_set, TermSet):
def _term_set_loaded():
- TaxonomyField.create(self, name, term_set.id, None, allow_multiple_values, return_type=return_type)
+ TaxonomyField.create(self, name, term_set.id, None, allow_multiple_values,
+ return_type=return_type)
+
term_set.ensure_property("id", _term_set_loaded)
return return_type
else:
@@ -159,6 +177,7 @@ def _term_set_loaded():
def _term_store_loaded(term_store):
TaxonomyField.create(self, name, term_set, term_store.id, allow_multiple_values,
return_type=return_type)
+
self.context.load(self.context.taxonomy.term_store, after_loaded=_term_store_loaded)
return return_type
@@ -167,7 +186,7 @@ def create_field_as_xml(self, schema_xml, return_type=None):
Creates a field based on the values defined in the parameters input parameter.
:param str schema_xml: Specifies the schema that defines the field
- :type return_type: Field
+ :param Field or None return_type: Return type
"""
if return_type is None:
return_type = Field(self.context)
diff --git a/office365/sharepoint/fields/creation_information.py b/office365/sharepoint/fields/creation_information.py
index 2125dbe9..bbd69d83 100644
--- a/office365/sharepoint/fields/creation_information.py
+++ b/office365/sharepoint/fields/creation_information.py
@@ -15,10 +15,11 @@ def __init__(self, title, field_type_kind, description=None,
:param str lookup_web_id: Specifies the identifier of the site (2) that contains the list that is the
source for the field (2) value.
:param bool required: Specifies whether the field (2) requires a value.
- :param str lookup_field_name:
- :param str lookup_list_id: A CSOM GUID that specifies the target list for the lookup field (2).
- :param str title:
- :param int field_type_kind: Specifies the type of the field (2).
+ :param str lookup_field_name: Specifies the name of the field in the other data source when creating
+ a lookup field.
+ :param str lookup_list_id: A CSOM GUID that specifies the target list for the lookup field.
+ :param str title: Specifies the display name of the field.
+ :param int field_type_kind: Specifies the type of the field.
:type description: str or None
:param str formula:
:param list[str] or None choices:
diff --git a/tests/directory/test_security.py b/tests/directory/test_security.py
new file mode 100644
index 00000000..1e207c79
--- /dev/null
+++ b/tests/directory/test_security.py
@@ -0,0 +1,15 @@
+from unittest import TestCase
+
+from office365.graph_client import GraphClient
+from tests.graph_case import acquire_token_by_client_credentials
+
+
+class TestSecurity(TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.client = GraphClient(acquire_token_by_client_credentials)
+
+ def test1_list_incidents(self):
+ col = self.client.security.incidents.top(10).get().execute_query()
+ self.assertIsNotNone(col.resource_path)
diff --git a/tests/onedrive/test_sharepoint.py b/tests/onedrive/test_sharepoint.py
index 4c553595..7b40da73 100644
--- a/tests/onedrive/test_sharepoint.py
+++ b/tests/onedrive/test_sharepoint.py
@@ -8,4 +8,8 @@ def test1_get_sharepoint_settings(self):
result = self.client.admin.sharepoint.settings.get().execute_query()
self.assertIsNotNone(result.resource_path)
+ def test2_list_issues(self):
+ result = self.client.admin.service_announcement.issues.get().execute_query()
+ self.assertIsNotNone(result.resource_path)
+
diff --git a/tests/sharepoint/test_field_value.py b/tests/sharepoint/test_field_value.py
index 903a8668..c9be9ab4 100644
--- a/tests/sharepoint/test_field_value.py
+++ b/tests/sharepoint/test_field_value.py
@@ -60,7 +60,7 @@ def test2_set_field_text_value(self):
def test3_create_multi_lookup_field(self):
lookup_field = self.target_list.fields.add_lookup_field(title=self.multi_lookup_field_name,
- lookup_list_id=self.target_list.properties['Id'],
+ lookup_list=self.target_list.properties['Id'],
lookup_field_name='Title',
allow_multiple_values=True).execute_query()
self.assertEqual(lookup_field.type_as_string, 'LookupMulti')
@@ -150,7 +150,7 @@ def test_16_set_user_field_value(self):
def test_17_create_list_lookup_field(self):
lookup_field = self.target_list.fields.add_lookup_field(title=self.lookup_field_name,
- lookup_list_id=self.lookup_list.properties['Id'],
+ lookup_list=self.lookup_list.properties['Id'],
lookup_field_name='Title').execute_query()
self.assertEqual(lookup_field.type_as_string, 'Lookup')