Пример #1
0
            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                var wrapper = (Controllers.ServerMethodsController.ApiSingleValueOutputWrapper)value;

                if (wrapper.Value == null)
                {
                    writer.WriteNull();
                    return;
                }
                // TODO: Can we use traversal during SerializeCSharpToJavaScript to track symbols and their required Output Converters
                var serialisedValue = GlobalTypescriptTypeLookup.SerializeCSharpToJavaScript(wrapper.Name, wrapper.Value);

                writer.WriteRawValue(serialisedValue);
            }
        // generates and caches the ServerMethod .js and .tsd for a specific ServerMethod Plugin Registration
        private void GenerateAndCacheJsInterface()
        {
            try
            {
                var definitionsJS  = JavaScriptDefinitions = new Dictionary <string, List <Definition> >();
                var definitionsTSD = TypescriptDefinitions = new Dictionary <string, List <Definition> >();

                JavaScriptDefinitionsHash = TypescriptDefinitionsHash = null;

                ConverterLookup = new List <string>();

                var namespaceLookup = new List <string>();

                // add default namespace
                definitionsJS.Add("ServerMethods", new List <Definition>());
                definitionsTSD.Add("ServerMethods", new List <Definition>());

                foreach (var method in this.Registration.Methods)
                {
                    var namespaceKey    = "ServerMethods";
                    var namespaceKeyTSD = "ServerMethods";
                    var jsNamespaceVar  = Strings.@null; // null for main ServerMethod namespace

                    if (!string.IsNullOrEmpty(method.Namespace))
                    {
                        namespaceKey    = method.Namespace;
                        namespaceKeyTSD = method.Namespace;
                    }

                    var isCustomNamespace = !namespaceKey.Equals("ServerMethods", StringComparison.Ordinal);

                    if (isCustomNamespace)
                    {
                        if (!namespaceLookup.Contains(namespaceKey))
                        {
                            namespaceLookup.Add(namespaceKey);
                        }
                        jsNamespaceVar = $"_ns[{namespaceLookup.IndexOf(namespaceKey)}]";
                    }

                    if (!definitionsJS.ContainsKey(namespaceKey))
                    {
                        definitionsJS.Add(namespaceKey, new List <Definition>());
                    }

                    if (!definitionsTSD.ContainsKey(namespaceKeyTSD))
                    {
                        definitionsTSD.Add(namespaceKeyTSD, new List <Definition>());
                    }

                    var methodParameters = method.AssemblyMethodInfo.GetParameters();

                    var inputConvertersLookup   = new Dictionary <string, ConverterDefinition>();
                    var outputConvertersLookup  = new Dictionary <string, ConverterDefinition>();
                    var resultsConvertersLookup = new Dictionary <string, ConverterDefinition>();

                    foreach (var inputParam in methodParameters)
                    {
                        GlobalConverterLookup.AnalyseForRequiredOutputConverters(inputParam.Name, inputParam.ParameterType, null, ref inputConvertersLookup);
                    }

                    foreach (var outputParam in methodParameters.Where(p => p.IsOut || p.ParameterType.IsByRef))
                    {
                        GlobalConverterLookup.AnalyseForRequiredOutputConverters(outputParam.Name, outputParam.ParameterType, null, ref outputConvertersLookup);
                    }

                    GlobalConverterLookup.AnalyseForRequiredOutputConverters("$result$", method.AssemblyMethodInfo.ReturnType, null, ref resultsConvertersLookup);

                    string inputConverter  = null;
                    string outputConverter = null;
                    string resultConverter = null;

                    var allRequiredConverters = inputConvertersLookup.Select(c => c.Value.ToJson()).Distinct()
                                                .Concat(outputConvertersLookup.Select(c => c.Value.ToJson()).Distinct())
                                                .Concat(resultsConvertersLookup.Select(c => c.Value.ToJson()).Distinct())
                                                .ToList();

                    if (allRequiredConverters.Count > 0)
                    {
                        foreach (var converterJson in allRequiredConverters)
                        {
                            if (ConverterLookup.IndexOf(converterJson) == -1)
                            {
                                ConverterLookup.Add(converterJson);
                            }
                        }
                    }

                    inputConverter = string.Join(",", (from kv in inputConvertersLookup
                                                       select $"\"{kv.Key}\": $c[{kv.Value.ToJson()}]")); // ignore ConverterOptions for now as we don't actually have any use for it at the moment

                    outputConverter = string.Join(",", (from kv in outputConvertersLookup
                                                        select $"\"{kv.Key}\": $c[{kv.Value.ToJson()}]")); // ignore ConverterOptions for now as we don't actually have any use for it at the moment


                    resultConverter = string.Join(",", (from kv in resultsConvertersLookup
                                                        select $"\"{kv.Key}\": $c[{kv.Value.ToJson()}]")); // ignore ConverterOptions for now as we don't actually have any use for it at the moment

                    inputConverter  = string.IsNullOrWhiteSpace(inputConverter) ? null : inputConverter;
                    outputConverter = string.IsNullOrWhiteSpace(outputConverter) ? null : outputConverter;
                    resultConverter = string.IsNullOrWhiteSpace(resultConverter) ? null : resultConverter;

                    var hasConverters = inputConverter != null || outputConverter != null || resultConverter != null;

                    // js
                    var methodLineJS = ServerMethodManager.TEMPLATE_ServerMethodFunctionTemplate
                                       .Replace("<<FUNC_NAME>>", method.Name)
                                       .Replace("<<NAMESPACE>>", jsNamespaceVar);
                    ;

                    if (!hasConverters)
                    {
                        methodLineJS = methodLineJS.Replace("<<CONV_SEP>>", "");
                        methodLineJS = methodLineJS.Replace("<<CONVERTERS>>", "");
                    }

                    if (methodParameters.Count() > 0)
                    {
                        methodLineJS = methodLineJS.Replace("<<ARG_SEP>>", ", ");
                        methodLineJS = methodLineJS.Replace("<<HAS_PARMS>>", "o");
                    }
                    else
                    {
                        methodLineJS = methodLineJS.Replace("<<ARG_SEP>>", "");
                        methodLineJS = methodLineJS.Replace("<<HAS_PARMS>>", "");
                    }

                    definitionsJS[namespaceKey].Add(new Definition()
                    {
                        MethodName = method.Name, Line = methodLineJS, InputConverter = inputConverter, OutputConverter = outputConverter, ResultsConverter = resultConverter
                    });

                    // TSD
                    string methodLineTSD = null;

                    if (isCustomNamespace)
                    {
                        methodLineTSD = SERVER_TSD_METHOD_NONSTATIC_TEMPLATE.Replace("<<FUNC_NAME>>", method.Name);
                    }
                    else
                    {
                        methodLineTSD = SERVER_TSD_METHOD_TEMPLATE.Replace("<<FUNC_NAME>>", method.Name);
                    }

                    var inputParmListLookup = from p in methodParameters
                                              select new
                    {
                        Name = p.Name,
                        p.IsOut,
                        p.ParameterType.IsByRef,
                        p.ParameterType.IsArray,
                        p.ParameterType.IsValueType,
                        HasDefault         = p.IsOptional,
                        IsNullable         = Nullable.GetUnderlyingType(p.ParameterType) != null,
                        TypescriptDataType = GlobalTypescriptTypeLookup.GetTypescriptTypeFromCSharp(p.ParameterType)
                    };

                    var inputParmsFormatted = from p in inputParmListLookup
                                              select $"{p.Name}{((p.HasDefault) ? "?" : "")}: {p.TypescriptDataType}";
                    // TODO: Revise. Not clear if IsNullable should also be output with a '?'. In TypeScript this means optional and not 'nullable'. So in C# even if a parameter is nullable it is still required to specified it. ? should be reserved for OPTIONAL parameters
                    //select $"{p.Name}{((p.HasDefault || p.IsNullable ) ? "?" : "")}: {p.TypescriptDataType}";


                    string typeNameBase        = $"{(isCustomNamespace ? namespaceKeyTSD + "_" : "")}{ method.Name }";
                    string typeNameInputParms  = $"{typeNameBase}_In";
                    string typeNameOutputParms = $"{typeNameBase}_Out";
                    string typeNameResult      = $"{typeNameBase}_Res";

                    string typeDefInputParms  = null;
                    string typeDefOutputParms = null;
                    string typeDefResult      = null;


                    // if there are INPUT parameters
                    if (inputParmsFormatted.Count() > 0)
                    {
                        methodLineTSD = methodLineTSD.Replace("<<PARM_LIST>>", "parameters?: __." + typeNameInputParms);

                        typeDefInputParms  = $"type {typeNameInputParms} = {"{" + string.Join(", ", inputParmsFormatted) + "}"};";
                        typeNameInputParms = "__." + typeNameInputParms;
                    }
                    else
                    {
                        methodLineTSD      = methodLineTSD.Replace("<<PARM_LIST>>", "");
                        typeNameInputParms = "void";
                    }

                    // if there are OUTPUT parameters
                    if (inputParmListLookup.Count(p => p.IsOut) > 0)
                    {
                        typeDefOutputParms = $"type {typeNameOutputParms} = {{ " + string.Join(", ", (from p in inputParmListLookup
                                                                                                      where p.IsOut
                                                                                                      select $"{p.Name}: {p.TypescriptDataType}")) + " };";
                        typeNameOutputParms = "__." + typeNameOutputParms;
                    }
                    else
                    {
                        typeNameOutputParms = "void";
                    }


                    if (method.AssemblyMethodInfo.ReturnType == typeof(void)) // no result
                    {
                        //IServerMethodVoid<OuputParameters, InputParameters>
                        methodLineTSD = methodLineTSD.Replace("<<RET_TYPE>>", $"IServerMethodVoid<{typeNameOutputParms}, {typeNameInputParms}>");
                    }
                    else
                    {
                        //IServerMethod<OuputParameters, ResultType, InputParameters>

                        var retType = GlobalTypescriptTypeLookup.GetTypescriptTypeFromCSharp(method.AssemblyMethodInfo.ReturnType);

                        // if a built-in js/TS type
                        if (new string[] { "number", "string", "date", "boolean", "any", "number[]", "string[]", "date[]", "boolean[]", "any[]" }.Contains(retType.ToLower()))
                        {
                            typeDefResult = null;
                            methodLineTSD = methodLineTSD.Replace("<<RET_TYPE>>", $"IServerMethod<{typeNameOutputParms}, {retType}, {typeNameInputParms}>");
                        }
                        else
                        {
                            typeDefResult = $"type {typeNameResult} = {retType};";
                            methodLineTSD = methodLineTSD.Replace("<<RET_TYPE>>", $"IServerMethod<{typeNameOutputParms}, __.{typeNameResult}, {typeNameInputParms}>");
                        }
                    }

                    definitionsTSD[namespaceKeyTSD].Add(new Definition()
                    {
                        MethodName = method.Name, Line = methodLineTSD, TypesLines = new List <string>()
                        {
                            typeDefInputParms, typeDefOutputParms, typeDefResult
                        }
                    });
                } // foreach (var method in this.Methods)


                var jsLines  = string.Join("\n", definitionsJS.Select(kv => $"{kv.Key}§{string.Join('\n', kv.Value.Select(d => d.Line).ToArray())}").ToArray());
                var tsdLines = string.Join("\n", definitionsTSD.Select(kv => $"{kv.Key}§{string.Join('\n', kv.Value.Select(d => d.Line).ToArray())}").ToArray());

                using (var sha = System.Security.Cryptography.SHA256.Create())
                {
                    var jsHash  = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(jsLines));
                    var tsdHash = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(tsdLines));

                    // TODO: Compare with current Hashes. If different then let all relevant Apps know

                    this.JavaScriptDefinitionsHash = jsHash;
                    this.TypescriptDefinitionsHash = tsdHash;
                }
            }
            catch (Exception ex)
            {
                SessionLog.Exception(ex);
            }
        }