Example #1
0
        private Dictionary <string, ParameterData> GetParameterMetadata(string parameterPath, JsonPathFinder parameter, Dictionary <string, RuntimeTypeData> definitionsCache, Dictionary <string, Dictionary <string, ParameterData> > parameterCache, JsonPathFinder rootDoc)
        {
            if (parameterCache.ContainsKey(parameterPath))
            {
                // We've already transformed this parameter into one or more parameters and found the correct module name
                return(parameterCache[parameterPath]);
            }

            Dictionary <string, ParameterData> ret = new Dictionary <string, ParameterData>();

            parameterCache[parameterPath] = ret;
            // 1. Check if it's a ref
            JsonPathFinder refNode = parameter.Find(new JsonQueryBuilder().Property("$ref")).SingleOrDefault();

            if (refNode != null)
            {
                // A direct ref should reference a parameter object
                string refParameterPath = refNode.GetValue <string>();
                return(GetParameterMetadata(refParameterPath.ToLowerInvariant(), rootDoc.Find(new JsonQueryBuilder(refParameterPath)).Single(), definitionsCache, parameterCache, rootDoc));
            }
            else
            {
                // 2. Get the name and jsonName
                string         jsonName       = String.Empty;
                string         clientName     = String.Empty;
                JsonPathFinder jsonNameNode   = parameter.Find(new JsonQueryBuilder().Property("name")).SingleOrDefault();
                JsonPathFinder clientNameNode = parameter.Find(new JsonQueryBuilder().Property("x-ms-client-name")).SingleOrDefault();
                if (jsonNameNode != null)
                {
                    jsonName = jsonNameNode.GetValue <string>().ToLowerInvariant();
                }

                if (clientNameNode != null)
                {
                    clientName = clientNameNode.GetValue <string>().ToLowerInvariant();
                }

                // 2. Check if it's a flatten
                JsonPathFinder flattenNode   = parameter.Find(new JsonQueryBuilder().Property("x-ms-client-flatten")).SingleOrDefault();
                JsonPathFinder schemaRefNode = parameter.Find(new JsonQueryBuilder().RecursiveDescent().Property("$ref")).SingleOrDefault();
                if (flattenNode != null)
                {
                    // Get the ref node under the schema node - using recursive descent from the parameter node should work
                    // This should be a definitions object
                    string          definitionPath = schemaRefNode.GetValue <string>();
                    RuntimeTypeData definitionData = definitionsCache[definitionPath];
                    // Don't think there will be a flatten of an object that's just an array?
                    foreach (string propertyName in definitionData.Properties.Keys)
                    {
                        ParameterData data = definitionData.Properties[propertyName];
                        if (!String.IsNullOrEmpty(data.Name))
                        {
                            ret[data.Name] = data;
                        }
                        else
                        {
                            ret[data.JsonName] = data;
                        }
                    }
                }
                else
                {
                    // 3. Just a regular parameter - check if it defines a definition as the type
                    ParameterData data = new ParameterData();
                    data.Name     = clientName;
                    data.JsonName = jsonName;
                    if (schemaRefNode != null)
                    {
                        string definitionPath = schemaRefNode.GetValue <string>().ToLowerInvariant();
                        data.Type = definitionsCache[definitionPath];
                    }
                    else
                    {
                        // Type with no properties, and the Type itself will be filled in later
                        data.Type = new RuntimeTypeData();
                    }

                    if (!String.IsNullOrEmpty(clientName))
                    {
                        ret[clientName] = data;
                    }
                    else
                    {
                        ret[jsonName] = data;
                    }
                }
            }

            return(ret);
        }
Example #2
0
        private RuntimeTypeData GetRuntimeTypeDataFromDefinition(string definitionName, JsonPathFinder definitionNode, Dictionary <string, RuntimeTypeData> cache, JsonPathFinder rootDoc)
        {
            // 0. Check if the type already exists
            if (cache.ContainsKey(definitionName))
            {
                return(cache[definitionName]);
            }

            RuntimeTypeData data = new RuntimeTypeData();

            cache[definitionName] = data;
            // 1. Get properties node
            JsonPathFinder propertiesNode = definitionNode.Find(new JsonQueryBuilder().Property("properties")).SingleOrDefault();

            if (propertiesNode != null)
            {
                // 2. Read flat properties
                foreach (JsonPathFinder propertyContainerNode in propertiesNode.Children())
                {
                    JsonPathFinder propertyNode = propertyContainerNode.OpenContainer();
                    // First things first - check if the property is renamed
                    JsonPathFinder clientNameNode = propertyNode.Find(new JsonQueryBuilder().Property("x-ms-client-name")).SingleOrDefault();
                    string         clientName     = String.Empty;
                    if (clientNameNode != null)
                    {
                        clientName = clientNameNode.GetValue <string>().ToLowerInvariant();
                    }

                    string jsonName    = propertyNode.Key.ToLowerInvariant();
                    string rawJsonName = propertyNode.Key;
                    // 5 cases:
                    //  1: Property contains a $ref node AND property contains an x-ms-client-flatten node
                    //  2: Property contains only a $ref node
                    //  3: Property is of type object and contains additionalProperties (not sure how to handle this yet)
                    //  4: Property is of type array (decode the individual items type)
                    //  5: Property is a simple property
                    JsonPathFinder refNode     = propertyNode.Find(new JsonQueryBuilder().Property("$ref")).SingleOrDefault();
                    JsonPathFinder flattenNode = propertyNode.Find(new JsonQueryBuilder().Property("x-ms-client-flatten")).SingleOrDefault();
                    JsonPathFinder typeNode    = propertyNode.Find(new JsonQueryBuilder().Property("type")).SingleOrDefault();
                    JsonPathFinder additionalPropertiesNode = propertyNode.Find(new JsonQueryBuilder().Property("additionalProperties")).SingleOrDefault();
                    JsonPathFinder arrayItemsRefNode        = propertyNode.Find(new JsonQueryBuilder().Property("items").Property("$ref")).SingleOrDefault();
                    JsonPathFinder propertiesSubNode        = propertyNode.Find(new JsonQueryBuilder().Property("properties")).SingleOrDefault();
                    if (refNode != null)
                    {
                        JsonPathFinder  subDefinition      = rootDoc.Find(new JsonQueryBuilder(refNode.GetValue <string>())).Single();
                        RuntimeTypeData referencedTypeData = GetRuntimeTypeDataFromDefinition(subDefinition.Path, subDefinition, cache, rootDoc);
                        if (flattenNode != null && flattenNode.GetValue <bool>())
                        {
                            // Push the properties of the referenced type to this type
                            // If it's flattened, the jsonName should actually be flatJsonName
                            foreach (string propertyName in referencedTypeData.Properties.Keys)
                            {
                                data.Properties[propertyName]             = referencedTypeData.Properties[propertyName].Clone();
                                data.Properties[propertyName].SubNodeName = "properties";
                            }
                        }
                        else
                        {
                            // The referenced type is simply the type of this property
                            data.Properties[jsonName] = new ParameterData()
                            {
                                Name        = clientName,
                                JsonName    = jsonName,
                                RawJsonName = rawJsonName,
                                Type        = referencedTypeData
                            };
                        }
                    }
                    else if (typeNode != null && typeNode.GetValue <string>().ToLowerInvariant().Equals("object") && additionalPropertiesNode != null)
                    {
                        // No converter required, this will probably be a Dictionary<string, something>
                        data.Properties[jsonName] = new ParameterData()
                        {
                            Name        = clientName,
                            JsonName    = jsonName,
                            RawJsonName = rawJsonName
                        };

                        // TODO: Support complex object as the additionalProperties type. Don't have a good example if this yet...
                    }
                    else if (typeNode != null && typeNode.GetValue <string>().ToLowerInvariant().Equals("array"))
                    {
                        // No converter required
                        data.Properties[jsonName] = new ParameterData()
                        {
                            Name        = clientName,
                            JsonName    = jsonName,
                            RawJsonName = rawJsonName
                        };

                        // When this is an array, we should add the type of the items in the array so that a converter is registered later
                        if (arrayItemsRefNode != null)
                        {
                            JsonPathFinder subDefinition = rootDoc.Find(new JsonQueryBuilder(arrayItemsRefNode.GetValue <string>())).Single();
                            data.Properties[jsonName].Type = new RuntimeTypeData();
                            data.Properties[jsonName].Type.CollectionTypes.Add(GetRuntimeTypeDataFromDefinition(subDefinition.Path, subDefinition, cache, rootDoc));
                        }
                    }
                    else if (propertiesSubNode != null)
                    {
                        // Not sure what to do with definition name yet
                        // The actual Type of the anonymous type will be filled in by postprocessing
                        // TODO: The properties of the subtype might be prefixed with "properties."
                        data.Properties[jsonName] = new ParameterData()
                        {
                            Name        = clientName,
                            JsonName    = jsonName,
                            RawJsonName = rawJsonName,
                            Type        = GetRuntimeTypeDataFromDefinition(Guid.NewGuid().ToString(), propertyNode, cache, rootDoc)
                        };
                    }
                    else
                    {
                        // The last case should be the case of a simple property - assign the name and jsonName and let the type be inferred later
                        data.Properties[jsonName] = new ParameterData()
                        {
                            Name        = clientName,
                            JsonName    = jsonName,
                            RawJsonName = rawJsonName,
                            Type        = new RuntimeTypeData()
                        };
                    }
                }
            }

            // 3. Read allOf node
            JsonPathFinder allOfNode = definitionNode.Find(new JsonQueryBuilder().Property("allOf")).SingleOrDefault();

            if (allOfNode != null)
            {
                // Get all $ref's
                foreach (JsonPathFinder refNode in allOfNode.Find(new JsonQueryBuilder().RecursiveDescent().Property("$ref")))
                {
                    JsonPathFinder  subDefinition      = rootDoc.Find(new JsonQueryBuilder(refNode.GetValue <string>())).Single();
                    RuntimeTypeData referencedTypeData = GetRuntimeTypeDataFromDefinition(subDefinition.Path, subDefinition, cache, rootDoc);
                    // Push the properties of the referenced type to this type
                    foreach (string propertyName in referencedTypeData.Properties.Keys)
                    {
                        data.Properties[propertyName] = referencedTypeData.Properties[propertyName];
                    }
                }
            }

            return(data);
        }
Example #3
0
        private OperationData ParseOperationForMetadata(JsonPathFinder path, Dictionary <string, RuntimeTypeData> definitionsCache, Dictionary <string, Dictionary <string, ParameterData> > parameterCache, JsonPathFinder rootDoc)
        {
            JsonPathFinder parametersNode  = path.Find(new JsonQueryBuilder().Property("parameters")).SingleOrDefault();
            JsonPathFinder operationIdNode = path.Find(new JsonQueryBuilder().Property("operationId")).SingleOrDefault();

            if (operationIdNode == null)
            {
                return(null);
            }

            string operationId = operationIdNode.GetValue <string>();

            if (!this.Operations.ContainsKey(operationId.ToLowerInvariant()))
            {
                Log(String.Format(CultureInfo.InvariantCulture, "Skipping operation '{0}' because no matching command was found.", operationId));
                return(null);
            }

            OperationData operationData = this.Operations[operationId.ToLowerInvariant()];

            Log(String.Format(CultureInfo.InvariantCulture, "Loading metadata for operation '{0}' and command '{1}'", operationId, operationData.Command));

            if (parametersNode != null)
            {
                foreach (JsonPathFinder parameterNode in parametersNode.Children())
                {
                    Dictionary <string, ParameterData> parameters = GetParameterMetadata(parameterNode.Path, parameterNode, definitionsCache, parameterCache, rootDoc);
                    foreach (string parameterName in parameters.Keys)
                    {
                        if (operationData.Parameters.ContainsKey(parameterName.ToLowerInvariant()))
                        {
                            Log(String.Format(CultureInfo.InvariantCulture, "Found parameter with name '{0}' and JSON name '{1}'", parameterName, parameters[parameterName].JsonName));
                            operationData.Parameters[parameterName.ToLowerInvariant()].MergeWith(parameters[parameterName]);
                        }
                    }
                }
            }

            // Find the first response in the [200-299] range, as that's all PSSwagger supports right now
            JsonPathFinder responsesNode          = path.Find(new JsonQueryBuilder().Property("responses")).SingleOrDefault();
            string         responseDefinitionPath = null;

            if (responsesNode != null && operationData.ResponseType != null)
            {
                foreach (JsonPathFinder response in responsesNode.Children())
                {
                    int statusCode;
                    if (Int32.TryParse(response.Key, out statusCode) && statusCode >= 200 && statusCode <= 299)
                    {
                        // For now we only support ref'd schema - need to fix that
                        JsonPathFinder schemaRefNode = response.Find(new JsonQueryBuilder().RecursiveDescent().Property("$ref")).SingleOrDefault();
                        if (schemaRefNode != null)
                        {
                            responseDefinitionPath = schemaRefNode.GetValue <string>();
                            string definitionPathCaseInsensitive = responseDefinitionPath.ToLowerInvariant();
                            if (definitionsCache.ContainsKey(definitionPathCaseInsensitive))
                            {
                                operationData.ResponseType.SpecificationData.Add(definitionsCache[definitionPathCaseInsensitive]);
                                break;
                            }
                        }
                    }
                }
            }

            // Check for x-ms-pageable and set the page type if a response type exists
            JsonPathFinder pageableExtension = path.Find(new JsonQueryBuilder().Property("x-ms-pageable")).SingleOrDefault();

            if (pageableExtension != null && operationData.ResponseType != null)
            {
                string         nextLinkJsonName = "nextLink";
                string         itemsJsonName    = "value";
                JsonPathFinder nextLinkName     = pageableExtension.Find(new JsonQueryBuilder().RecursiveDescent().Property("nextLinkName")).SingleOrDefault();
                JsonPathFinder itemName         = pageableExtension.Find(new JsonQueryBuilder().RecursiveDescent().Property("itemName")).SingleOrDefault();
                if (nextLinkName != null)
                {
                    nextLinkJsonName = nextLinkName.GetValue <string>();
                }

                if (itemName != null)
                {
                    itemsJsonName = itemName.GetValue <string>();
                }

                // Assuming the module is already loaded, find the correct .NET type for the Page
                foreach (Type candidateType in AppDomain.CurrentDomain.GetAssemblies().SelectMany(asm => asm.GetTypes()))
                {
                    PropertyInfo itemsProperty        = candidateType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(pi => pi.Name.Equals("Items")).FirstOrDefault();
                    PropertyInfo nextPageLinkProperty = candidateType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(pi => pi.Name.Equals("NextPageLink")).FirstOrDefault();
                    bool         candidate            = true;
                    if (itemsProperty != null)
                    {
                        JsonPropertyAttribute jsonPropertyAttribute = (JsonPropertyAttribute)itemsProperty.GetCustomAttribute(typeof(JsonPropertyAttribute));
                        if (jsonPropertyAttribute == null || !jsonPropertyAttribute.PropertyName.Equals(itemsJsonName, StringComparison.OrdinalIgnoreCase))
                        {
                            candidate = false;
                        }
                    }
                    else
                    {
                        candidate = false;
                    }

                    if (candidate && nextPageLinkProperty != null)
                    {
                        JsonPropertyAttribute jsonPropertyAttribute = (JsonPropertyAttribute)nextPageLinkProperty.GetCustomAttribute(typeof(JsonPropertyAttribute));
                        if (jsonPropertyAttribute == null || !jsonPropertyAttribute.PropertyName.Equals(nextLinkJsonName, StringComparison.OrdinalIgnoreCase))
                        {
                            candidate = false;
                        }
                    }
                    else
                    {
                        candidate = false;
                    }

                    if (candidate)
                    {
                        operationData.ResponseType.PageType = candidateType;
                    }
                }

                // Find the correct specification data. If x-ms-pageable is used, the referenced output type is probably an array of items
                // And PSSwagger will output the individual item type rather than the array type
                // Set the output type of the individual item type
                if (!String.IsNullOrEmpty(responseDefinitionPath))
                {
                    JsonPathFinder responseDefinitionNode = rootDoc.Find(new JsonQueryBuilder(responseDefinitionPath)).SingleOrDefault();
                    if (responseDefinitionNode != null)
                    {
                        JsonPathFinder itemType = responseDefinitionNode.Find(new JsonQueryBuilder().RecursiveDescent().Property("$ref")).FirstOrDefault();
                        if (itemType != null)
                        {
                            operationData.ResponseType.SpecificationData.Add(definitionsCache[itemType.GetValue <string>().ToLowerInvariant()]);
                        }
                    }
                }
            }

            return(operationData);
        }