Exemplo n.º 1
0
        protected static async Task <IList <T> > ParseResultsAsync <T>(string response, Func <JsonElement, JsonElement?> rootResultsSelector) where T : TransientObject, new()
        {
            var personProps = new List <T>();
            var json        = JsonSerializer.Deserialize <JsonElement>(response);
            var results     = rootResultsSelector(json);

            if (results == null)
            {
                throw new ClientException(ErrorType.Unsupported, PnPCoreResources.Exception_Json_Unexpected);
            }

            var dataRows = results.Value;

            if (dataRows.GetArrayLength() == 0)
            {
                return(personProps);
            }

            var entityInfo = EntityManager.Instance.GetStaticClassInfo(typeof(T));

            foreach (var row in dataRows.EnumerateArray())
            {
                var prop = new T();
                await JsonMappingHelper.FromJson(prop, entityInfo, new ApiResponse(new ApiCall(String.Empty, ApiType.SPORest), row, Guid.Empty)).ConfigureAwait(false);

                personProps.Add(prop);
            }

            return(personProps);
        }
Exemplo n.º 2
0
        private static void AddExpandableSelectRest(StringBuilder sb, EntityFieldInfo field, EntityFieldExpandInfo expandFields, string path)
        {
            EntityInfo collectionEntityInfo = null;

            if (expandFields == null)
            {
                collectionEntityInfo = EntityManager.Instance.GetStaticClassInfo(field.ExpandFieldInfo.Type);
                expandFields         = field.ExpandFieldInfo;
            }
            else
            {
                if (expandFields.Type != null)
                {
                    collectionEntityInfo = EntityManager.Instance.GetStaticClassInfo(expandFields.Type);
                }
            }

            if (collectionEntityInfo != null)
            {
                foreach (var expandableField in expandFields.Fields.OrderBy(p => p.Expandable))
                {
                    var expandableFieldInfo = collectionEntityInfo.Fields.First(p => p.Name == expandableField.Name);
                    if (!expandableFieldInfo.SharePointExpandable)
                    {
                        sb.Append($"{JsonMappingHelper.GetRestField(field)}{path}/{JsonMappingHelper.GetRestField(expandableFieldInfo)},");
                    }
                    else
                    {
                        path = path + "/" + JsonMappingHelper.GetRestField(expandableFieldInfo);
                        AddExpandableSelectRest(sb, field, expandableField, path);
                        path = "";
                    }
                }
            }
        }
Exemplo n.º 3
0
        public async Task <IFollowingInfo> GetFollowingInfoAsync()
        {
            var apiCall = new ApiCall($"{Uri}/my", ApiType.SPORest)
            {
                Interactive = true,
            };

            var response = await RawRequestAsync(apiCall, HttpMethod.Get).ConfigureAwait(false);

            var document = JsonSerializer.Deserialize <JsonElement>(response.Json);

            var entityInfo = EntityManager.Instance.GetStaticClassInfo(typeof(FollowingInfo));
            var socialInfo = new FollowingInfo();
            await JsonMappingHelper.FromJson(socialInfo, entityInfo, new ApiResponse(apiCall, document, Guid.Empty)).ConfigureAwait(false);

            var socialActorNode = document.Get(nameof(socialInfo.SocialActor));

            if (socialActorNode != null)
            {
                var socialActor = new SocialActor();
                socialInfo.SocialActor = socialActor;
                entityInfo             = EntityManager.Instance.GetStaticClassInfo(typeof(SocialActor));
                await JsonMappingHelper.FromJson(socialActor, entityInfo, new ApiResponse(apiCall, socialActorNode.Value, Guid.Empty)).ConfigureAwait(false);
            }

            return(socialInfo);
        }
Exemplo n.º 4
0
        private static void ProcessChangeElement(object pnpObject, IDataModelParent parent, PnPContext context, JsonElement element, Guid batchRequestId)
        {
            var metadataBasedObject = pnpObject as IMetadataExtensible;
            var entity = EntityManager.GetClassInfo <IChange>(pnpObject.GetType(), null);

            SetBatchRequestId(pnpObject as TransientObject, batchRequestId);

            // Enumerate the received properties and try to map them to the model
            foreach (var property in element.EnumerateObject())
            {
                // Find the model field linked to this field
                EntityFieldInfo entityField = entity.Fields.FirstOrDefault(p => p.SharePointName == property.Name);

                // Entity field should be populated for the actual fields we've requested
                if (entityField != null)
                {
                    if (entityField.PropertyInfo.PropertyType == typeof(IChangeToken)) // Special case
                    {
                        var changeToken = GetConcreteInstance(property.Value);
                        ProcessChangeElement(changeToken, parent, context, property.Value, batchRequestId);
                        entityField.PropertyInfo?.SetValue(pnpObject, changeToken);
                    }
                    else if (entityField.PropertyInfo.PropertyType == typeof(IContentType))
                    {
                        // Since this is an SP.ContentTypeId and NOT an SP.ContentType, it's not a perfect entity match; we'll do it manually
                        var concreteInstance = (IContentType)EntityManager.GetEntityConcreteInstance(entityField.PropertyInfo.PropertyType, parent, context);
                        var contentTypeId    = property.Value.GetProperty("StringValue").GetString();
                        concreteInstance.SetSystemProperty(ct => ct.Id, contentTypeId);
                        concreteInstance.SetSystemProperty(ct => ct.StringId, contentTypeId);
                        (concreteInstance as IMetadataExtensible).Metadata.Add(PnPConstants.MetaDataType, "SP.ContentTypeId");
                        (concreteInstance as IMetadataExtensible).Metadata.Add(PnPConstants.MetaDataId, contentTypeId);
                        (concreteInstance as IMetadataExtensible).Metadata.Add(PnPConstants.MetaDataRestId, contentTypeId);
                        concreteInstance.Requested = true;

                        entityField.PropertyInfo?.SetValue(pnpObject, concreteInstance);
                    }
                    else // Simple property mapping
                    {
                        // Set the object property value taken from the JSON payload
                        entityField.PropertyInfo?.SetValue(pnpObject, JsonMappingHelper.GetJsonFieldValue(null, entityField.Name,
                                                                                                          property.Value, entityField.DataType, entityField.SharePointUseCustomMapping, null));
                    }
                }
                else
                {
                    // Let's keep track of the object metadata, useful when creating new requests
                    if (property.Name == "__metadata")
                    {
                        JsonMappingHelper.TrackSharePointMetaData(metadataBasedObject, property);
                    }
                }
            }
        }
Exemplo n.º 5
0
        protected async Task <IPersonProperties> ParsePersonPropertiesResultAsync(ApiCallResponse response)
        {
            var results = new List <IPersonProperties>();
            var json    = JsonSerializer.Deserialize <JsonElement>(response.Json);

            var entityInfo = EntityManager.Instance.GetStaticClassInfo(typeof(PersonProperties));

            var prop = new PersonProperties();
            await JsonMappingHelper.FromJson(prop, entityInfo, new ApiResponse(new ApiCall(String.Empty, ApiType.SPORest), json, Guid.Empty)).ConfigureAwait(false);

            return(prop);
        }
Exemplo n.º 6
0
        private void Swap(IDictionary <string, object> a, IDictionary <string, object> b)
        {
            a.Clear();
            foreach (var pair in b)
            {
                a[pair.Key] = pair.Value;

                // Also commit the changes in the TransientDictionary if needed
                if (a[pair.Key] is TransientDictionary)
                {
                    (a[pair.Key] as TransientDictionary).Commit();
                }
                // Also commit the changes in the ComplexTypeModel classes
                else if (a[pair.Key] != null && JsonMappingHelper.IsTypeWithoutGet(a[pair.Key].GetType()))
                {
                    (a[pair.Key] as TransientObject).Commit();
                }
            }

            changes.Clear();
        }
Exemplo n.º 7
0
        /// <summary>
        /// Translates model type into a set of classes that are used to drive CRUD operations
        /// </summary>
        /// <param name="type">The reference model type</param>
        /// <returns>Entity model class describing this model instance</returns>
        internal EntityInfo GetStaticClassInfo(Type type)
        {
            type = GetEntityConcreteType(type);

            // Check if we can deliver this entity from cache
            if (entityCache.TryGetValue(type, out EntityInfo entityInfoFromCache))
            {
                return(entityInfoFromCache);
            }
            else
            {
                // Load and process type attributes
                var sharePointTypeAttributes = type.GetCustomAttributes <SharePointTypeAttribute>(false);
                var graphTypeAttributes      = type.GetCustomAttributes <GraphTypeAttribute>(false);

                if (sharePointTypeAttributes.Any() || graphTypeAttributes.Any())
                {
                    EntityInfo classInfo = new EntityInfo
                    {
                        UseOverflowField = type.ImplementsInterface(typeof(IExpandoDataModel))
                    };

                    if (sharePointTypeAttributes.Any())
                    {
                        foreach (var sharePointTypeAttribute in sharePointTypeAttributes)
                        {
                            var sharePointTargetToAdd = new EntitySharePointTypeInfo
                            {
                                Type             = sharePointTypeAttribute.Type,
                                Target           = sharePointTypeAttribute.Target ?? type,
                                Uri              = sharePointTypeAttribute.Uri,
                                Get              = !string.IsNullOrEmpty(sharePointTypeAttribute.Get) ? sharePointTypeAttribute.Get : sharePointTypeAttribute.Uri,
                                LinqGet          = !string.IsNullOrEmpty(sharePointTypeAttribute.LinqGet) ? sharePointTypeAttribute.LinqGet : sharePointTypeAttribute.Uri,
                                OverflowProperty = sharePointTypeAttribute.OverflowProperty,
                                Update           = !string.IsNullOrEmpty(sharePointTypeAttribute.Update) ? sharePointTypeAttribute.Update : sharePointTypeAttribute.Uri,
                                Delete           = !string.IsNullOrEmpty(sharePointTypeAttribute.Delete) ? sharePointTypeAttribute.Delete : sharePointTypeAttribute.Uri,
                            };

                            classInfo.SharePointTargets.Add(sharePointTargetToAdd);
                        }
                    }

                    if (graphTypeAttributes.Any())
                    {
                        foreach (var graphTypeAttribute in graphTypeAttributes)
                        {
                            var graphTargetToAdd = new EntityGraphTypeInfo
                            {
                                Target           = graphTypeAttribute.Target ?? type,
                                Id               = !string.IsNullOrEmpty(graphTypeAttribute.Id) ? graphTypeAttribute.Id : "id",
                                Get              = !string.IsNullOrEmpty(graphTypeAttribute.Get) ? graphTypeAttribute.Get : graphTypeAttribute.Uri,
                                LinqGet          = !string.IsNullOrEmpty(graphTypeAttribute.LinqGet) ? graphTypeAttribute.LinqGet : graphTypeAttribute.Uri,
                                OverflowProperty = graphTypeAttribute.OverflowProperty,
                                Update           = !string.IsNullOrEmpty(graphTypeAttribute.Update) ? graphTypeAttribute.Update : graphTypeAttribute.Uri,
                                Delete           = !string.IsNullOrEmpty(graphTypeAttribute.Delete) ? graphTypeAttribute.Delete : graphTypeAttribute.Uri,
                                Beta             = graphTypeAttribute.Beta,
                            };

                            classInfo.GraphTargets.Add(graphTargetToAdd);
                        }
                    }

                    string keyPropertyName = null;
                    foreach (var property in type.GetProperties())
                    {
                        EntityFieldInfo classField = null;

                        var  propertyAttributes = property.GetCustomAttributes();
                        bool skipField          = false;
                        foreach (var attribute in propertyAttributes)
                        {
                            switch (attribute)
                            {
                            // Field metadata
                            case SharePointPropertyAttribute sharePointPropertyAttribute:
                            {
                                classField = EnsureClassField(type, property, classInfo);
                                classField.SharePointName             = !string.IsNullOrEmpty(sharePointPropertyAttribute.FieldName) ? sharePointPropertyAttribute.FieldName : property.Name;
                                classField.ExpandableByDefault        = sharePointPropertyAttribute.ExpandByDefault;
                                classField.SharePointUseCustomMapping = sharePointPropertyAttribute.UseCustomMapping;
                                classField.SharePointJsonPath         = sharePointPropertyAttribute.JsonPath;
                                break;
                            }

                            case GraphPropertyAttribute graphPropertyAttribute:
                            {
                                classField                       = EnsureClassField(type, property, classInfo);
                                classField.GraphName             = !string.IsNullOrEmpty(graphPropertyAttribute.FieldName) ? graphPropertyAttribute.FieldName : ToCamelCase(property.Name);
                                classField.GraphExpandable       = graphPropertyAttribute.Expandable;
                                classField.ExpandableByDefault   = graphPropertyAttribute.ExpandByDefault;
                                classField.GraphUseCustomMapping = graphPropertyAttribute.UseCustomMapping;
                                classField.GraphJsonPath         = graphPropertyAttribute.JsonPath;
                                classField.GraphGet              = graphPropertyAttribute.Get;
                                classField.GraphBeta             = graphPropertyAttribute.Beta;
                                break;
                            }

                            case KeyPropertyAttribute keyPropertyAttribute:
                            {
                                keyPropertyName = keyPropertyAttribute.KeyPropertyName;
                                skipField       = true;
                                break;
                            }

                            case SystemPropertyAttribute systemPropertyAttribute:
                            {
                                skipField = true;
                                break;
                            }
                            }
                        }

                        if (!skipField)
                        {
                            if (classField == null)
                            {
                                classField = EnsureClassField(type, property, classInfo);
                                // Property was not decorated with attributes
                                if (!classInfo.SharePointTargets.Any())
                                {
                                    // This is a Graph only property
                                    classField.GraphName = ToCamelCase(property.Name);
                                }
                                else
                                {
                                    // This is SharePoint/Graph property, we're not setting the GraphName here because in "mixed" objects the Graph properties must be explicitely marked with the GraphProperty attribute
                                    classField.SharePointName = property.Name;
                                }
                            }

                            // Automatically determine "expand" value for SharePoint properties
                            if (!string.IsNullOrEmpty(classField.SharePointName))
                            {
                                if (JsonMappingHelper.IsModelCollection(classField.PropertyInfo.PropertyType) ||
                                    JsonMappingHelper.IsModelType(classField.PropertyInfo.PropertyType))
                                {
                                    classField.SharePointExpandable = true;
                                }
                            }
                        }
                    }

                    // Find the property set as key field and mark it as such
                    if (!string.IsNullOrEmpty(keyPropertyName))
                    {
                        // Store the actual key property name
                        classInfo.ActualKeyFieldName = keyPropertyName;

                        // Process the SharePoint and Graph ID fields
                        var keyProperty = classInfo.Fields.FirstOrDefault(p => p.Name == keyPropertyName);
                        if (keyProperty != null)
                        {
                            if (classInfo.SharePointTargets.Any())
                            {
                                keyProperty.IsSharePointKey = true;
                            }

                            keyProperty.IsGraphKey = true;
                            // If a property is defined as graph key then ensure the GraphName is correctly set
                            if (string.IsNullOrEmpty(keyProperty.GraphName) && classInfo.GraphTargets.Any())
                            {
                                keyProperty.GraphName = ToCamelCase(keyProperty.Name);
                            }
                        }
                    }

                    // Update the field used for overflow, this field was added (as it's part of the ExpandoBaseDataModel base data class),
                    // But since there's no field to set properties on the fieldname property comes from the class mapping attribute
                    if (classInfo.UseOverflowField)
                    {
                        var overflowField = classInfo.Fields.FirstOrDefault(p => p.Name == ExpandoBaseDataModel <IExpandoDataModel> .OverflowFieldName);

                        if (!classInfo.SharePointTargets.Any())
                        {
                            // This is a Graph only property
                            overflowField.GraphName = classInfo.GraphTargets.First().OverflowProperty;
                        }
                        else
                        {
                            // This is SharePoint/Graph property
                            overflowField.SharePointName = classInfo.SharePointTargets.First().OverflowProperty;
                            overflowField.GraphName      = classInfo.GraphTargets.FirstOrDefault()?.OverflowProperty;
                        }
                    }

                    // Add to our cache to speed up future retrievals
                    entityCache.TryAdd(type, classInfo);
                    return(classInfo);
                }
                else
                {
                    throw new ClientException(ErrorType.ModelMetadataIncorrect,
                                              PnPCoreResources.Exception_ModelMetadataIncorrect_MissingClassMapping);
                }
            }
        }
Exemplo n.º 8
0
        private static async Task <ApiCallRequest> BuildGetAPICallRestAsync <TModel>(BaseDataModel <TModel> model, EntityInfo entity, ODataQuery <TModel> oDataQuery, ApiCall apiOverride, bool useLinqGet, bool loadPages)
        {
            string getApi = useLinqGet ? entity.SharePointLinqGet : entity.SharePointGet;

            IEnumerable <EntityFieldInfo> fields = entity.Fields.Where(p => p.Load);

            Dictionary <string, string> urlParameters = new Dictionary <string, string>();

            StringBuilder sb = new StringBuilder();

            // Only add select statement whenever there was a filter specified
            if (entity.SharePointFieldsLoadedViaExpression)
            {
                // $select
                foreach (var field in fields)
                {
                    // If there was a selection on which fields to include in an expand (via the QueryProperties() option) then add those fields
                    if (field.SharePointExpandable && field.ExpandFieldInfo != null)
                    {
                        AddExpandableSelectRest(sb, field, null, "");
                    }
                    else
                    {
                        sb.Append($"{JsonMappingHelper.GetRestField(field)},");
                    }
                }

                urlParameters.Add("$select", sb.ToString().TrimEnd(new char[] { ',' }));
                sb.Clear();
            }

            // $expand
            foreach (var field in fields.Where(p => p.SharePointExpandable))
            {
                if (entity.SharePointFieldsLoadedViaExpression)
                {
                    sb.Append($"{JsonMappingHelper.GetRestField(field)},");

                    // If there was a selection on which fields to include in an expand (via the Include() option) and the included field was expandable itself then add it
                    if (field.ExpandFieldInfo != null)
                    {
                        string path = "";
                        AddExpandableExpandRest(sb, field, null, path);
                    }
                }
                else
                {
                    if (field.ExpandableByDefault)
                    {
                        sb.Append($"{JsonMappingHelper.GetRestField(field)},");
                    }
                }
            }
            urlParameters.Add("$expand", sb.ToString().TrimEnd(new char[] { ',' }));

            oDataQuery.AddODataToUrlParameters(urlParameters, ODataTargetPlatform.SPORest);

            // REST apis do not apply a default top
            // In order to not receive all items in one request, we apply a default top
            // We don't change the original ODataQuery to avoid side effects
            if (useLinqGet && !urlParameters.ContainsKey(ODataQuery <TModel> .TopKey))
            {
                urlParameters.Add(ODataQuery <TModel> .TopKey, model.PnPContext.GlobalOptions.HttpSharePointRestDefaultPageSize.ToString());
            }

            sb.Clear();

            // Build the API call
            string baseApiCall = "";

            if (apiOverride.Equals(default(ApiCall)))
            {
                baseApiCall = $"{model.PnPContext.Uri.AbsoluteUri.TrimEnd(new char[] { '/' })}/{getApi}";
            }
            else
            {
                baseApiCall = $"{model.PnPContext.Uri.AbsoluteUri.TrimEnd(new char[] { '/' })}/{apiOverride.Request}";
            }

            // Parse tokens in the base api call
            baseApiCall = await ApiHelper.ParseApiCallAsync(model, baseApiCall).ConfigureAwait(false);

            sb.Append(baseApiCall);

            // Build the querystring parameters
            NameValueCollection queryString = HttpUtility.ParseQueryString(string.Empty);

            foreach (var urlParameter in urlParameters.Where(i => !string.IsNullOrEmpty(i.Value)))
            {
                // Add key and value, which will be automatically URL-encoded, if needed
                queryString.Add(urlParameter.Key, urlParameter.Value);
            }

            // Build the whole URL
            if (queryString.AllKeys.Length > 0)
            {
                // In .NET Framework to ToString() of a NameValueCollection will use HttpUtility.UrlEncodeUnicode under
                // the covers resulting in issues. So we decode and encode again as a workaround. This code produces the
                // same result when used under .NET5/Core versus .NET Framework
                sb.Append($"?{queryString.ToEncodedString()}");
            }

            // Create ApiCall instance and call the override option if needed
            var call = new ApiCallRequest(new ApiCall(sb.ToString(), ApiType.SPORest, loadPages: loadPages));

            if (model.GetApiCallOverrideHandler != null)
            {
                call = await model.GetApiCallOverrideHandler.Invoke(call).ConfigureAwait(false);
            }

            return(call);
        }