Skip to content

Commit 6ed0aa8

Browse files
vvgrem@gmail.comvvgrem@gmail.com
vvgrem@gmail.com
authored and
vvgrem@gmail.com
committed
SharePoint API: improved support for fields namespace, fixes for 3.8
1 parent c1e7f5e commit 6ed0aa8

26 files changed

+120
-82
lines changed

office365/runtime/auth/providers/acs_token_provider.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def get_app_only_access_token(self, target_host, target_realm):
4646
oauth2_request = self.create_access_token_request(principal_id, self.client_secret, resource)
4747
response = requests.post(url=sts_url, headers={'Content-Type': 'application/x-www-form-urlencoded'},
4848
data=oauth2_request)
49-
return TokenResponse.from_json(response.content)
49+
return TokenResponse.from_json(response.json())
5050

5151
def _get_realm_from_target_url(self):
5252
response = requests.head(url=self.url, headers={'Authorization': 'Bearer'})

office365/runtime/auth/providers/oauth_token_provider.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def acquire_token(self, parameters):
1919
response = requests.post(url=token_url,
2020
headers={'Content-Type': 'application/x-www-form-urlencoded'},
2121
data=parameters)
22-
self.token = TokenResponse.from_json(response.content)
22+
self.token = TokenResponse.from_json(response.json())
2323
return self.token.is_valid
2424
except requests.exceptions.RequestException as e:
2525
self.error = "Error: {0}".format(e)
+11-16
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
1-
import json
2-
3-
4-
def _normalize_token_response(json_data):
5-
for key in json_data.keys():
6-
key_parts = key.split("_")
7-
if len(key_parts) == 2:
8-
new_key = "".join([key_parts[0], key_parts[1].title()])
9-
json_data[new_key] = json_data[key]
10-
del json_data[key]
11-
return json_data
12-
13-
141
class TokenResponse(object):
152

163
def __init__(self, accessToken=None, tokenType=None, **kwargs):
@@ -24,6 +11,14 @@ def is_valid(self):
2411
return self.accessToken is not None and self.tokenType == 'Bearer'
2512

2613
@staticmethod
27-
def from_json(json_str):
28-
json_object = json.loads(json_str, object_hook=_normalize_token_response)
29-
return TokenResponse(**json_object)
14+
def from_json(value):
15+
16+
def _normalize_key(name):
17+
key_parts = name.split("_")
18+
if len(key_parts) >= 2:
19+
names = [n.title() for n in key_parts[1:]]
20+
return key_parts[0] + "".join(names)
21+
return name
22+
23+
token_json = {_normalize_key(k): v for k, v in value.items()}
24+
return TokenResponse(**token_json)

office365/runtime/clientValueCollection.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,26 @@
33

44
class ClientValueCollection(ClientValue):
55

6-
def __init__(self):
6+
def __init__(self, item_type):
77
super().__init__()
88
self._data = []
9+
self._item_type = item_type
910

1011
def add(self, value):
1112
self._data.append(value)
1213

1314
def __iter__(self):
1415
for item in self._data:
1516
yield item
17+
18+
def to_json(self):
19+
return self._data
20+
21+
@property
22+
def entity_type_name(self):
23+
edm_primitive_types = {
24+
int: "Edm.Int32",
25+
str: "Edm.String",
26+
}
27+
item_type_name = edm_primitive_types.get(self._item_type, "Edm.Int32")
28+
return "Collection({0})".format(item_type_name)

office365/runtime/odata/odata_request.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from office365.runtime.clientValueCollection import ClientValueCollection
12
from office365.runtime.client_object import ClientObject
23
from office365.runtime.client_object_collection import ClientObjectCollection
34
from office365.runtime.client_query import CreateEntityQuery, UpdateEntityQuery, DeleteEntityQuery
@@ -54,7 +55,8 @@ def build_request(self):
5455
if isinstance(qry, ServiceOperationQuery):
5556
self.json_format.function_tag_name = qry.method_name
5657
if qry.static:
57-
request_url = self.context.service_root_url + '.'.join([qry.binding_type.entity_type_name, qry.method_url])
58+
request_url = self.context.service_root_url + '.'.join(
59+
[qry.binding_type.entity_type_name, qry.method_url])
5860
else:
5961
request_url = '/'.join([qry.binding_type.resource_url, qry.method_url])
6062
else:
@@ -126,7 +128,11 @@ def _get_property(self, json, data_format):
126128
yield name, value
127129

128130
def _normalize_payload(self, value):
129-
if isinstance(value, ClientObject) or isinstance(value, ClientValue):
131+
if isinstance(value, ClientValueCollection):
132+
if isinstance(self._json_format,
133+
JsonLightFormat) and self._json_format.metadata == ODataMetadataLevel.Verbose:
134+
value = {"results": value.to_json()}
135+
elif isinstance(value, ClientObject) or isinstance(value, ClientValue):
130136
json = value.to_json()
131137
for k, v in json.items():
132138
json[k] = self._normalize_payload(v)
@@ -135,7 +141,8 @@ def _normalize_payload(self, value):
135141
JsonLightFormat) and self._json_format.metadata == ODataMetadataLevel.Verbose:
136142
json[self._json_format.metadata_type_tag_name] = {'type': value.entity_type_name}
137143

138-
if isinstance(self._current_query, ServiceOperationQuery) and self._current_query.parameter_name is not None:
144+
if isinstance(self._current_query,
145+
ServiceOperationQuery) and self._current_query.parameter_name is not None:
139146
json = {self._current_query.parameter_name: json}
140147
return json
141148
elif isinstance(value, dict):

office365/sharepoint/fields/field.py

+19-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ class Field(BaseEntity):
1010

1111
def __init__(self, context, resource_path=None):
1212
super().__init__(context, resource_path)
13+
14+
@staticmethod
15+
def get_field_type(type_id):
1316
from office365.sharepoint.fields.fieldText import FieldText
1417
from office365.sharepoint.fields.fieldCalculated import FieldCalculated
1518
from office365.sharepoint.fields.fieldChoice import FieldChoice
@@ -21,7 +24,7 @@ def __init__(self, context, resource_path=None):
2124
from office365.sharepoint.fields.fieldGuid import FieldGuid
2225
from office365.sharepoint.fields.fieldCurrency import FieldCurrency
2326
from office365.sharepoint.fields.fieldMultiLineText import FieldMultiLineText
24-
self._field_types = {
27+
field_types = {
2528
FieldType.Text: FieldText,
2629
FieldType.Calculated: FieldCalculated,
2730
FieldType.Choice: FieldChoice,
@@ -34,14 +37,25 @@ def __init__(self, context, resource_path=None):
3437
FieldType.Currency: FieldCurrency,
3538
FieldType.Note: FieldMultiLineText
3639
}
40+
return field_types.get(type_id, Field)
41+
42+
@staticmethod
43+
def create_field_from_type(context, field_type):
44+
field_type = Field.get_field_type(field_type)
45+
return field_type(context)
3746

3847
def set_show_in_display_form(self, flag):
39-
"""Sets the value of the ShowInDisplayForm property for this fields."""
48+
"""Sets the value of the ShowInDisplayForm property for this fields.
49+
50+
:type flag: bool
51+
"""
4052
qry = ServiceOperationQuery(self, "setShowInDisplayForm", [flag])
4153
self.context.add_query(qry)
4254

4355
def set_show_in_edit_form(self, flag):
44-
"""Sets the value of the ShowInEditForm property for this fields."""
56+
"""Sets the value of the ShowInEditForm property for this fields.
57+
:type flag: bool
58+
"""
4559
qry = ServiceOperationQuery(self, "setShowInEditForm", [flag])
4660
self.context.add_query(qry)
4761

@@ -59,7 +73,7 @@ def delete_object(self):
5973
@property
6074
def internal_name(self):
6175
"""Gets a value that specifies the field internal name."""
62-
return self.properties['InternalName']
76+
return self.properties.get('InternalName', None)
6377

6478
def set_property(self, name, value, persist_changes=True):
6579
super(Field, self).set_property(name, value, persist_changes)
@@ -68,4 +82,4 @@ def set_property(self, name, value, persist_changes=True):
6882
self._resource_path = ResourcePathServiceOperation(
6983
"getById", [value], self._parent_collection.resource_path)
7084
if name == "FieldTypeKind":
71-
self.__class__ = self._field_types.get(value, Field)
85+
self.__class__ = self.get_field_type(value)

office365/sharepoint/fields/fieldMultiChoiceValue.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,7 @@
55
class FieldMultiChoiceValue(ClientValueCollection):
66

77
def __init__(self, choices):
8-
super().__init__()
9-
[self.add(c) for c in choices]
8+
super().__init__(str)
9+
[self.add(choice) for choice in choices]
1010

11-
def to_json(self):
12-
return {"results": self._data}
1311

14-
@property
15-
def entity_type_name(self):
16-
return "Collection(Edm.String)"

office365/sharepoint/fields/fieldMultiLookupValue.py

+1-15
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,4 @@
55
class FieldMultiLookupValue(ClientValueCollection):
66

77
def __init__(self):
8-
super().__init__()
9-
10-
@staticmethod
11-
def from_lookup(ids):
12-
val = FieldMultiLookupValue()
13-
[val.add(FieldLookupValue(lookup_id)) for lookup_id in ids]
14-
return val
15-
16-
def to_json(self):
17-
lookup_ids = [v.LookupId for v in self]
18-
return {"results": lookup_ids}
19-
20-
@property
21-
def entity_type_name(self):
22-
return "Collection(Edm.Int32)"
8+
super().__init__(FieldLookupValue)

office365/sharepoint/fields/fieldMultiUserValue.py

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ class FieldMultiUserValue(FieldMultiLookupValue):
66

77
def __init__(self):
88
super().__init__()
9+
self._item_type = FieldUserValue

office365/sharepoint/fields/field_collection.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from office365.runtime.client_query import CreateEntityQuery
33
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
44
from office365.sharepoint.fields.field import Field
5+
from office365.sharepoint.fields.field_creation_information import FieldCreationInformation
56

67

78
class FieldCollection(ClientObjectCollection):
@@ -19,7 +20,7 @@ def add(self, field_creation_information):
1920
2021
:type field_creation_information: FieldCreationInformation
2122
"""
22-
field = Field(self.context)
23+
field = Field.create_field_from_type(self.context, field_creation_information.FieldTypeKind)
2324
self.add_child(field)
2425
qry = CreateEntityQuery(self, field_creation_information, field)
2526
self.context.add_query(qry)
@@ -31,13 +32,15 @@ def get_by_id(self, _id):
3132

3233
def get_by_internal_name_or_title(self, name_title):
3334
"""Returns the first Field object with the specified internal name or title from the collection.
35+
3436
:type name_title: str
3537
"""
3638
return Field(self.context,
3739
ResourcePathServiceOperation("getByInternalNameOrTitle", [name_title], self.resource_path))
3840

3941
def get_by_title(self, title):
4042
"""Returns the first fields object in the collection based on the title of the specified fields.
43+
4144
:type title: str
4245
"""
4346
return Field(self.context,

office365/sharepoint/fields/field_creation_information.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
from office365.runtime.clientValue import ClientValue
2+
from office365.runtime.clientValueCollection import ClientValueCollection
3+
from office365.sharepoint.fields.field import Field
4+
from office365.sharepoint.fields.fieldType import FieldType
25

36

47
class FieldCreationInformation(ClientValue):
@@ -13,12 +16,14 @@ def __init__(self, title, field_type_kind, description=None):
1316
self.Title = title
1417
self.FieldTypeKind = field_type_kind
1518
self.Description = description
16-
self.Choices = None
19+
self.Choices = field_type_kind == FieldType.MultiChoice and ClientValueCollection(str) or None
1720
self.LookupListId = None
1821
self.LookupFieldName = None
1922
self.LookupWebId = None
2023
self.Required = None
2124

2225
@property
2326
def entity_type_name(self):
24-
return "SP.Field"
27+
type_name = Field.get_field_type(self.FieldTypeKind).__name__
28+
return "SP.{0}".format(type_name)
29+

office365/sharepoint/listitems/listitem.py

+16-14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from office365.runtime.clientValueCollection import ClientValueCollection
12
from office365.runtime.client_query import UpdateEntityQuery, DeleteEntityQuery
23
from office365.runtime.resource_path import ResourcePath
34
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
@@ -10,20 +11,6 @@
1011
class ListItem(SecurableObject):
1112
"""ListItem resource"""
1213

13-
def set_field_value(self, name, value):
14-
"""
15-
Sets fields value
16-
17-
:param str name: Field name
18-
:param str or FieldLookupValue or FieldUserValue value: field value
19-
"""
20-
if isinstance(value, FieldMultiLookupValue):
21-
self.set_property("{name}Id".format(name=name), value, True)
22-
elif isinstance(value, FieldLookupValue):
23-
self.set_property("{name}Id".format(name=name), value, True)
24-
else:
25-
self.set_property(name, value, True)
26-
2714
def update(self):
2815
"""Update the list item."""
2916
self.ensure_type_name(self.parentList)
@@ -110,8 +97,23 @@ def ensure_type_name(self, target_list):
11097
:param office365.sharepoint.lists.list.List target_list: List entity
11198
:return:
11299
"""
100+
113101
def _init_item_type(result):
114102
self._entity_type_name = result.properties['ListItemEntityTypeFullName']
103+
115104
if not self._entity_type_name:
116105
target_list.ensure_property("ListItemEntityTypeFullName", _init_item_type)
117106

107+
def to_json(self):
108+
payload_orig = super(ListItem, self).to_json()
109+
payload = {}
110+
for k, v in payload_orig.items():
111+
if isinstance(v, FieldMultiLookupValue):
112+
collection = ClientValueCollection(int)
113+
[collection.add(lv.LookupId) for lv in v]
114+
payload["{name}Id".format(name=k)] = collection
115+
elif isinstance(v, FieldLookupValue):
116+
payload["{name}Id".format(name=k)] = v
117+
else:
118+
payload[k] = v
119+
return payload

office365/sharepoint/tenant/administration/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from office365.runtime.client_object_collection import ClientObjectCollection
22
from office365.runtime.queries.serviceOperationQuery import ServiceOperationQuery
3-
from office365.sharepoint.tenantadministration.siteProperties import SiteProperties
3+
from office365.sharepoint.tenant.administration.siteProperties import SiteProperties
44

55

66
class SitePropertiesCollection(ClientObjectCollection):
@@ -9,7 +9,7 @@ def __init__(self, context, resource_path=None):
99
super(SitePropertiesCollection, self).__init__(context, SiteProperties, resource_path)
1010

1111
def get_by_id(self, site_id):
12-
site_props = SiteProperties(self.context, None)
12+
site_props = SiteProperties(self.context)
1313
qry = ServiceOperationQuery(self, "GetById", [site_id], None, None, site_props)
1414
self.context.add_query(qry)
1515
return site_props

office365/sharepoint/tenantadministration/tenant.py renamed to office365/sharepoint/tenant/administration/tenant.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
from office365.runtime.resource_path import ResourcePath
22
from office365.runtime.queries.serviceOperationQuery import ServiceOperationQuery
33
from office365.sharepoint.base_entity import BaseEntity
4-
from office365.sharepoint.tenantadministration.siteProperties import SiteProperties
5-
from office365.sharepoint.tenantadministration.sitePropertiesCollection import SitePropertiesCollection
6-
from office365.sharepoint.tenantadministration.sitePropertiesEnumerableFilter import SitePropertiesEnumerableFilter
7-
from office365.sharepoint.tenantadministration.spoOperation import SpoOperation
8-
from office365.sharepoint.tenantadministration.siteCreationProperties import SiteCreationProperties
9-
from office365.sharepoint.tenantadministration.secondaryAdministratorsFieldsData import SecondaryAdministratorsFieldsData
4+
from office365.sharepoint.tenant.administration.siteProperties import SiteProperties
5+
from office365.sharepoint.tenant.administration.sitePropertiesCollection import SitePropertiesCollection
6+
from office365.sharepoint.tenant.administration.sitePropertiesEnumerableFilter import SitePropertiesEnumerableFilter
7+
from office365.sharepoint.tenant.administration.spoOperation import SpoOperation
108

119

1210
class Tenant(BaseEntity):

0 commit comments

Comments
 (0)