void parseMethods(JProperty jpMethods, Service svc, string connectionString, IDictionary<string, ParameterType> parameterTypes, IDictionary<string, Method> methods, Func<string, string> tokenLookup)
        {
            if (jpMethods.Value.Type != JTokenType.Object)
            {
                svc.Errors.Add("The `methods` property is expected to be of type object");
                return;
            }

            var joMethods = (JObject)jpMethods.Value;

            // Parse each method:
            foreach (var jpMethod in joMethods.Properties())
            {
                // Is the method set to null?
                if (jpMethod.Value.Type == JTokenType.Null)
                {
                    // Remove it:
                    methods.Remove(jpMethod.Name);
                    continue;
                }
                if (jpMethod.Value.Type != JTokenType.Object)
                {
                    svc.Errors.Add("The method property `{0}` is expected to be of type object".F(jpMethod.Name));
                    continue;
                }

                var joMethod = ((JObject)jpMethod.Value);

                // Create a clone of the inherited descriptor or a new descriptor:
                Method method;
                if (methods.TryGetValue(jpMethod.Name, out method))
                    method = method.Clone();
                else
                {
                    method = new Method()
                    {
                        Name = jpMethod.Name,
                        ConnectionString = connectionString,
                        Errors = new List<string>(5)
                    };
                }
                methods[jpMethod.Name] = method;
                method.Service = svc;

                Debug.Assert(method.Errors != null);

                // Parse the definition:

                method.Description = getString(joMethod.Property("description")).Interpolate(tokenLookup);
                method.DeprecatedMessage = getString(joMethod.Property("deprecated")).Interpolate(tokenLookup);

                // Parse connection:
                var jpConnection = joMethod.Property("connection");
                if (jpConnection != null)
                {
                    method.ConnectionString = parseConnection(jpConnection, method.Errors, (s) => s.Interpolate(tokenLookup));
                }

                // Parse the parameters:
                var jpParameters = joMethod.Property("parameters");
                if (jpParameters != null)
                {
                    parseParameters(jpParameters, method, parameterTypes, tokenLookup);
                }

                // Parse query:
                var jpQuery = joMethod.Property("query");
                if (jpQuery != null)
                {
                    parseQuery(jpQuery, method, tokenLookup);
                }

                if (method.Query == null)
                {
                    method.Errors.Add("No query specified");
                }

                // Parse result mapping:
                var jpMapping = joMethod.Property("result");
                if (jpMapping != null)
                {
                    var joMapping = (JObject)jpMapping.Value;
                    method.Mapping = parseMapping(joMapping, method.Errors);
                }
            } // foreach (var method)
        }
 IHttpResponseAction errorsMethod(SHA1Hashed<ServicesOffering> main, Service service, Method method)
 {
     return new JsonRootResponse(
         links: new RestfulLink[]
         {
         },
         meta: new
         {
             configHash = main.HashHexString,
             serviceName = service.Name,
             methodName = method.Name,
             methodErrors = method.Errors
         }
     );
 }
 IHttpResponseAction errorsService(SHA1Hashed<ServicesOffering> main, Service service)
 {
     return new JsonRootResponse(
         links: new RestfulLink[]
         {
         },
         meta: new
         {
             configHash = main.HashHexString,
             serviceName = service.Name,
             serviceErrors = service.Errors,
             methodsErrors = service.Methods.Where(m => m.Value.Errors.Any()).ToDictionary(
                 m => m.Key,
                 m => new { errors = m.Value.Errors },
                 StringComparer.OrdinalIgnoreCase
             )
         }
     );
 }
        ServiceCollection ParseConfigData(JObject joConfig)
        {
            // Create the ServiceCollection that will be returned:
            var coll = new ServiceCollection()
            {
                Errors = new List<string>(5),
                Services = new Dictionary<string, Service>(StringComparer.OrdinalIgnoreCase)
            };

            // Parse the root token dictionary first:
            var rootTokens = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            var jpTokens = joConfig.Property("$");
            if (jpTokens != null)
            {
                // Extract the key/value pairs onto a copy of the token dictionary:
                foreach (var prop in ((JObject)jpTokens.Value).Properties())
                {
                    // Newtonsoft.Json already guarantees that property names must be unique; it will throw a JsonReaderException in that case.
#if false
                    if (rootTokens.ContainsKey(prop.Name))
                    {
                        coll.Errors.Add("A token named '{0}' already exists in the '$' collection; cannot add a duplicate".F(prop.Name));
                        continue;
                    }
#endif
                    rootTokens[prop.Name] = getString(prop);
                }
            }

            // Parse root parameter types:
            var rootParameterTypes = new Dictionary<string, ParameterType>(StringComparer.OrdinalIgnoreCase);
            var jpParameterTypes = joConfig.Property("parameterTypes");
            if (jpParameterTypes != null)
            {
                var errors = new List<string>(5);
                parseParameterTypes(jpParameterTypes, errors, rootParameterTypes, (s) => s);
                if (errors.Count > 0)
                {
                    // Add errors encountered and keep going:
                    coll.Errors.AddRange(errors);
                }
            }

            // 'services' section is not optional:
            JToken jtServices;
            if (!joConfig.TryGetValue("services", out jtServices))
            {
                coll.Errors.Add("A 'services' section is required");
                return coll;
            }
            var joServices = (JObject)jtServices;

            // Parse each service descriptor:
            foreach (var jpService in joServices.Properties())
            {
                if (jpService.Name == "$") continue;
                var joService = (JObject)jpService.Value;

                // This property is a service:
                var svcErrors = new List<string>(5);

                Service baseService = null;
                IDictionary<string, string> tokens;
                string connectionString;
                IDictionary<string, ParameterType> parameterTypes;
                IDictionary<string, Method> methods;

                // Go through the properties of the named service object:
                var jpBase = joService.Property("base");
                if (jpBase != null)
                {
                    // NOTE(jsd): Forward references are not allowed. Base service must be defined before the current service in document order.
                    string baseName = getString(jpBase);
                    if (!coll.Services.TryGetValue(baseName, out baseService))
                    {
                        coll.Errors.Add("Unknown base service name '{0}' for service '{1}'; services must declared in document order".F(baseName, jpService.Name));
                        continue;
                    }

                    // Create copies of what's inherited from the base service to mutate:
                    connectionString = baseService.ConnectionString;
                    tokens = new Dictionary<string, string>(baseService.Tokens);
                    parameterTypes = new Dictionary<string, ParameterType>(baseService.ParameterTypes, StringComparer.OrdinalIgnoreCase);
                    methods = new Dictionary<string, Method>(baseService.Methods, StringComparer.OrdinalIgnoreCase);
                }
                else
                {
                    // Nothing inherited:
                    connectionString = null;
                    tokens = new Dictionary<string, string>(rootTokens, StringComparer.OrdinalIgnoreCase);
                    parameterTypes = new Dictionary<string, ParameterType>(rootParameterTypes, StringComparer.OrdinalIgnoreCase);
                    methods = new Dictionary<string, Method>(StringComparer.OrdinalIgnoreCase);
                }

                // Parse tokens:
                jpTokens = joService.Property("$");
                if (jpTokens != null)
                {
                    // Assigning a `null`?
                    if (jpTokens.Value.Type == JTokenType.Null)
                    {
                        // Clear out all inherited tokens:
                        tokens.Clear();
                    }
                    else
                    {
                        // Extract the key/value pairs onto our token dictionary:
                        foreach (var prop in ((JObject)jpTokens.Value).Properties())
                            // NOTE(jsd): No interpolation over tokens themselves.
                            tokens[prop.Name] = getString(prop);
                    }
                }

                // A lookup-or-null function used with `Interpolate`:
                Func<string, string> tokenLookup = (key) =>
                {
                    string value;
                    // TODO: add to a Warnings collection!
                    if (!tokens.TryGetValue(key, out value))
                        return null;
                    return value;
                };

                // Parse connection:
                var jpConnection = joService.Property("connection");
                if (jpConnection != null)
                {
                    connectionString = parseConnection(jpConnection, svcErrors, (s) => s.Interpolate(tokenLookup));
                }

                // Parse the parameter types:
                jpParameterTypes = joService.Property("parameterTypes");
                if (jpParameterTypes != null)
                {
                    // Assigning a `null`?
                    if (jpParameterTypes.Value.Type == JTokenType.Null)
                    {
                        // Clear out all inherited parameter types:
                        parameterTypes.Clear();
                    }
                    else
                    {
                        parseParameterTypes(jpParameterTypes, svcErrors, parameterTypes, (s) => s.Interpolate(tokenLookup));
                    }
                }

                // Create the service descriptor:
                Service svc = new Service()
                {
                    Name = jpService.Name,
                    BaseService = baseService,
                    ConnectionString = connectionString,
                    ParameterTypes = parameterTypes,
                    Methods = methods,
                    Tokens = tokens,
                    Errors = svcErrors
                };

                // Parse the methods:
                var jpMethods = joService.Property("methods");
                if (jpMethods != null)
                {
                    parseMethods(jpMethods, svc, connectionString, parameterTypes, methods, tokenLookup);
                }

                // Add the parsed service descriptor:
                coll.Services.Add(jpService.Name, svc);
            }

            // 'aliases' section is optional:
            var jpAliases = joConfig.Property("aliases");
            if (jpAliases != null)
            {
                // Parse the named aliases:
                var joAliases = (JObject)jpAliases.Value;
                foreach (var jpAlias in joAliases.Properties())
                {
                    // Add the existing Service reference to the new name:
                    string svcName = getString(jpAlias);

                    // Must find the existing service by its name first:
                    Service svcref;
                    if (!coll.Services.TryGetValue(svcName, out svcref))
                    {
                        coll.Errors.Add("Unknown service name '{0}' for alias '{1}'".F(svcName, jpAlias.Name));
                        continue;
                    }

                    // Can't add an alias name that already exists:
                    if (coll.Services.ContainsKey(jpAlias.Name))
                    {
                        coll.Errors.Add("Cannot add alias name '{0}' because that name is already in use".F(jpAlias.Name));
                        continue;
                    }

                    coll.Services.Add(jpAlias.Name, svcref);
                }
            }

            return coll;
        }
 IHttpResponseAction debugService(SHA1Hashed<ServicesOffering> main, Service service)
 {
     return new JsonRootResponse(
         links: new RestfulLink[]
         {
             RestfulLink.Create("self", "/debug/{0}".F(service.Name), "self"),
             RestfulLink.Create("parent", "/debug", "parent"),
             RestfulLink.Create("meta", "/meta/{0}".F(service.Name)),
             RestfulLink.Create("errors", "/errors/{0}".F(service.Name))
         },
         meta: new
         {
             configHash = main.HashHexString,
             service = new ServiceDebug(service)
         }
     );
 }
 IHttpResponseAction debugMethod(SHA1Hashed<ServicesOffering> main, Service service, Method method)
 {
     return new JsonRootResponse(
         links: new RestfulLink[]
         {
             RestfulLink.Create("self", "/debug/{0}/{1}".F(service.Name, method.Name), "self"),
             RestfulLink.Create("parent", "/debug/{0}".F(service.Name), "parent"),
             RestfulLink.Create("meta", "/meta/{0}/{1}".F(service.Name, method.Name)),
             RestfulLink.Create("errors", "/errors/{0}/{1}".F(service.Name, method.Name)),
             RestfulLink.Create("data", "/data/{0}/{1}".F(service.Name, method.Name))
         },
         meta: new
         {
             configHash = main.HashHexString,
             method = new MethodDebug(method)
         }
     );
 }
Example #7
0
 internal ServiceDebug(Service desc)
 {
     this.desc = desc;
 }
Example #8
0
 internal ServiceMetadata(Service desc)
 {
     this.desc = desc;
 }