Beispiel #1
0
        private MetadataValidationResult BuildServiceMapping(XmlDocSource xmlDocSource, RouteElement route, List <Type> seenTypes, JObject smdBase, bool includeDemoValue, JObject schema)
        {
            var result = new MetadataValidationResult();

            Type type = xmlDocSource.RouteAssembly.Assembly.GetType(route.Type.Substring(0, route.Type.IndexOf(",")));

            if (seenTypes.Contains(type))
            {
                return(result);
            }

            seenTypes.Add(type);

            var typeElement = type.GetXmlDocTypeNodeWithSMD();

            if (typeElement == null)
            {
                return(result);
            }

            var methodTarget = route.Endpoint.Trim('/');

            foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance))
            {
                var methodElement = type.GetXmlDocMemberNodeWithSMD(type.FullName + "." + method.Name);
                if (methodElement == null)
                {
                    continue;
                }

                // get smd xml, if present
                var methodSmdElement = methodElement.XPathSelectElement("smd");
                if (methodSmdElement == null)
                {
                    result.AddMetadataGenerationError(new MetadataGenerationError(MetadataType.SMD, type, "should not have gotten a method element without smd", "All services that have XML comments must have a <smd> tag.  See https://github.com/cityindex/RESTful-Webservice-Schema/wiki/Howto-write-XML-comments-for-SMD for details"));
                    continue; //advance to next service method
                }

                var smdXmlComment = SmdXmlComment.CreateFromXml(methodSmdElement);

                //Don't document methods that are marked exclude
                if (smdXmlComment.Exclude)
                {
                    continue; //advance to next service method
                }
                JObject service    = null;
                var     opContract = ReflectionUtils.GetAttribute <OperationContractAttribute>(method);

                if (opContract != null)
                {
                    var webGet     = ReflectionUtils.GetAttribute <WebGetAttribute>(method);
                    var methodName = method.Name;
                    if (!string.IsNullOrEmpty(smdXmlComment.MethodName))
                    {
                        methodName = smdXmlComment.MethodName;
                    }

                    string methodTransport   = null;
                    string methodEnvelope    = null;
                    string methodUriTemplate = null;

                    if (webGet != null)
                    {
                        service           = new JObject();
                        methodUriTemplate = ResolveUriTemplate(smdXmlComment, webGet.UriTemplate);
                        methodTransport   = "GET";
                        methodEnvelope    = "URL";
                    }
                    else
                    {
                        var webInvoke = ReflectionUtils.GetAttribute <WebInvokeAttribute>(method);
                        if (webInvoke != null)
                        {
                            service           = new JObject();
                            methodUriTemplate = ResolveUriTemplate(smdXmlComment, webInvoke.UriTemplate);

                            switch (webInvoke.Method.ToUpper())
                            {
                            case "POST":
                                methodTransport = "POST";
                                methodEnvelope  = "JSON";
                                break;

                            case "GET":
                                methodTransport = "GET";
                                methodEnvelope  = "URL";
                                break;

                            default:
                                result.AddMetadataGenerationError(new MetadataGenerationError(MetadataType.SMD, type,
                                                                                              string.Format("The {0} service has transport method of type {1} that is not supported", methodName, webInvoke.Method), "Service transports like DELETE or PUT are poorly supported by client http clients, so you advised to only use GET or POST"));
                                continue;     //advance to next service method
                            }
                        }
                    }

                    if (service != null)
                    {
                        JsonSchemaUtilities.ApplyDescription(service, methodElement);

                        service.Add("target", ResolveEndpoint(smdXmlComment, methodTarget));

                        if (!string.IsNullOrWhiteSpace(methodUriTemplate))
                        {
                            service.Add("uriTemplate", methodUriTemplate);
                        }
                        service.Add("contentType", "application/json");         // TODO: declare this in meta or get from WebGet/WebInvoke
                        service.Add("responseContentType", "application/json"); // TODO: declare this in meta or get from WebGet/WebInvoke
                        service.Add("transport", methodTransport);

                        try
                        {
                            smdBase.Add(methodName, service);
                        }
                        catch (ArgumentException e)
                        {
                            result.AddMetadataGenerationError(new MetadataGenerationError(MetadataType.SMD, type, string.Format("A service with the method name {0} already exists", methodName), "Ensure that methods names are unique across services"));
                            return(result);
                        }

                        // this is not accurate/valid SMD for GET but dojox.io.services is not, yet, a very good
                        // implementation of the SMD spec, which is funny as they were both written by the same person.
                        service.Add("envelope", methodEnvelope);

                        // determine if return type is object or primitive
                        JObject returnType = null;
                        if (Type.GetTypeCode(method.ReturnType) == TypeCode.Object)
                        {
                            if (method.ReturnType.Name != "Void")
                            {
                                string methodReturnTypeName = method.ReturnType.Name;
                                if (schema["properties"][methodReturnTypeName] == null)
                                {
                                    result.AddMetadataGenerationError(new MetadataGenerationError(MetadataType.SMD, type, "Schema missing referenced return type " + methodReturnTypeName + " for method " + method.Name, "All types used by services must be decorated with the <jschema> tag.  See https://github.com/cityindex/RESTful-Webservice-Schema/wiki/Howto-write-XML-comments-for-JSchema"));
                                }
                                returnType = new JObject(new JProperty("$ref", JsonSchemaUtilities.RootDelimiter + methodReturnTypeName));
                            }
                            else
                            {
                                returnType = null;
                            }
                        }
                        else if (Type.GetTypeCode(method.ReturnType) == TypeCode.Empty)
                        {
                            returnType = null;
                        }
                        else
                        {
                            returnType = new JObject(new JProperty("type", method.ReturnType.GetSchemaType()["type"].Value <string>()));
                        }
                        if (returnType != null)
                        {
                            service.Add("returns", returnType);
                        }

                        SetStringAttribute(methodSmdElement, service, "group");
                        SetIntAttribute(methodSmdElement, service, "cacheDuration");
                        SetStringAttribute(methodSmdElement, service, "throttleScope");

                        var paramResult = AddParameters(type, method, methodElement, service, includeDemoValue, schema);
                        if (paramResult.HasErrors)
                        {
                            result.AddMetadataGenerationErrors(paramResult.MetadataGenerationErrors);
                        }
                    }
                }
                if (!result.HasErrors)
                {
                    result.AddMetadataGenerationSuccess(new MetadataGenerationSuccess(MetadataType.SMD, type));
                }
            }

            return(result);
        }
Beispiel #2
0
        public static JObject BuildParameterSchema(XElement docElement, XElement metaElement, bool includeDemoValue, JArray metaContainer, string propertyName, Type propertyType, string parentName)
        {
            // no jschema, no process

            var underlyingType = metaElement.Attributes("underlyingType").FirstOrDefault();

            if (underlyingType != null)
            {
                propertyType = Type.GetType(underlyingType.Value, true, false);
            }


            JObject propBase = JsonSchemaUtilities.BuildPropertyBase(propertyType);

            propBase.Add("name", propertyName);

            metaContainer.Add(propBase);

            AddPropertyDescription(propBase, metaElement, docElement);

            if (includeDemoValue)
            {
                var demoValueAttribute = metaElement.Attributes("demoValue").FirstOrDefault();

                // if type is class and not System.* then the demoValue indicates embedded JSON.
                // typically this is the case when the shape of the type is problematic due to recursion
                // or circular references

                bool isComplexType = JsonSchemaUtilities.IsComplexType(propertyType);


                // we do not force demo values on complex types. if one is not present, the js will try to compose one.
                if (demoValueAttribute == null)
                {
                    //if (!isComplexType)
                    //{
                    //    throw new Exception(
                    //        string.Format("includeDemoValue is true but {0}.{1} has no demoValue attribute", parentName,
                    //                      propertyName));
                    //}
                }

                else
                {
                    JsonSchemaUtilities.ApplyTypedValue(propBase, demoValueAttribute, isComplexType);
                }
            }


            JObject attributeTarget = propBase;

            if (propBase["type"] != null && propBase["type"].Value <string>() == "array")
            {
                attributeTarget = propBase["items"].Value <JObject>();
            }

            foreach (var attribute in metaElement.Attributes())
            {
                JsonSchemaUtilities.ApplyPropertyAttribute(attributeTarget, attribute, parentName, propertyName);
            }

            //if (propBase["required"] == null)
            //{
            //    propBase.Add("optional", false);
            //}
            return(propBase);
        }