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')