public ExecutionBase BeginChildStage(string name)
        {
            if (!this.IsOpen)
            {
                return(null);
            }

            var re = new ExecutionBase(name);

            this._childStages.Add(re);

            return(re);
        }
Exemple #2
0
        public static void GenerateJsFileV2(string source, Endpoint endpoint, JsFile jsFile, Dictionary <string, ChangeDescriptor> fullChangeSet = null, bool rulesChanged = false)
        {
            var generateMetric = new Performance.ExecutionBase("GenerateJsFileV2");
            var noChanges      = false;

            try
            {
                // TODO: Figure out out casing on this property
                string jsNamespace = null;//endpoint.JsNamespace;
                if (string.IsNullOrWhiteSpace(jsNamespace))
                {
                    jsNamespace = endpoint.MetadataConnection.InitialCatalog;
                }

                var jsSafeNamespace = MakeNameJsSafe(jsNamespace);

                var routineContainerTemplate       = WorkSpawner.TEMPLATE_RoutineContainer;
                var routineTemplate                = WorkSpawner.TEMPLATE_Routine;
                var typescriptDefinitionsContainer = WorkSpawner.TEMPLATE_TypescriptDefinitions;

                endpoint.ApplyRules(jsFile);

                var includedRoutines = (from row in endpoint.CachedRoutines
                                        where !row.IsDeleted && (row.RuleInstructions[jsFile]?.Included ?? false == true)
                                        orderby row.FullName
                                        select row).ToList();

                List <KeyValuePair <string, ChangeDescriptor> > changesInFile = null;


                if (fullChangeSet != null)
                {
                    changesInFile = fullChangeSet.Where(change => includedRoutines.Count(inc => inc.FullName.Equals(change.Key, StringComparison.OrdinalIgnoreCase)) > 0).ToList();

                    // TODO: Consider if this is a good idea?
                    //       If we can reasonably say that there are no changes to routines that this JsFile cares about, why regenerate this file and why give it a new Version
                    if (changesInFile.Count == 0)
                    {
                        noChanges = true;
                        return;
                    }
                }

                var jsSchemaLookupForJsFunctions = new Dictionary <string, List <string> /*Routine defs*/>();
                var tsSchemaLookup = new Dictionary <string, List <string> /*Routine defs*/>();

                var typeScriptParameterAndResultTypesSB = new StringBuilder();

                var serverMethodPlugins = PluginLoader.Instance.PluginAssemblies
                                          .SelectMany(pa => pa.Plugins)
                                          .Where(p => p.Type == PluginType.ServerMethod && endpoint.Application.IsPluginIncluded(p.Guid.ToString()));

                var uniqueSchemas = new List <string>();

                var mainLoopMetric = generateMetric.BeginChildStage("Main loop");

                includedRoutines.ForEach(r =>
                {
                    try
                    {
                        if (r.TypescriptMethodStub == null)
                        {
                            r.PrecalculateJsGenerationValues(endpoint);
                        }

                        var jsSchemaName   = JsFileGenerator.MakeNameJsSafe(r.Schema);
                        var jsFunctionName = JsFileGenerator.MakeNameJsSafe(r.Routine);

                        if (!jsSchemaLookupForJsFunctions.ContainsKey(jsSchemaName))
                        {
                            jsSchemaLookupForJsFunctions.Add(jsSchemaName, new List <string>());
                        }

                        if (!tsSchemaLookup.ContainsKey(jsSchemaName))
                        {
                            tsSchemaLookup.Add(jsSchemaName, new List <string>());
                        }

                        if (!uniqueSchemas.Contains(r.Schema))
                        {
                            uniqueSchemas.Add(r.Schema);
                        }

                        var schemaIx = uniqueSchemas.IndexOf(r.Schema);

                        // .js
                        {
                            var jsFunctionDefLine = routineTemplate.Replace("<<FUNC_NAME>>", jsFunctionName).Replace("<<SCHEMA_IX>>", schemaIx.ToString()).Replace("<<ROUTINE>>", r.Routine);

                            if (r.Type.Equals(string.Intern("PROCEDURE"), StringComparison.OrdinalIgnoreCase))
                            {
                                jsFunctionDefLine = jsFunctionDefLine.Replace("<<CLASS>>", "S");
                            }
                            else
                            {
                                jsFunctionDefLine = jsFunctionDefLine.Replace("<<CLASS>>", "U");
                            }

                            jsSchemaLookupForJsFunctions[jsSchemaName].Add(jsFunctionDefLine);
                        }

                        // .tsd
                        {
                            typeScriptParameterAndResultTypesSB.AppendLine(r.TypescriptParameterTypeDefinition);
                            typeScriptParameterAndResultTypesSB.AppendLine(r.TypescriptOutputParameterTypeDefinition);
                            typeScriptParameterAndResultTypesSB.AppendLine(r.TypescriptResultSetDefinitions);
                            tsSchemaLookup[jsSchemaName].Add(r.TypescriptMethodStub);
                        }
                    }
                    catch (Exception ex)
                    {
                        SessionLog.Exception(ex);
                        // TODO: quit whole process
                    }
                });

                mainLoopMetric.End();

                var finalSBMetric = generateMetric.BeginChildStage("Final SB");

                var schemaAndRoutineDefs   = string.Join("\r\n", jsSchemaLookupForJsFunctions.Select(s => "\tx." + s.Key + " = {\r\n\t\t" + string.Join(",\r\n\t\t", s.Value.ToArray()) + "\r\n\t}\r\n").ToArray());
                var tsSchemaAndRoutineDefs = string.Join("\r\n", tsSchemaLookup.Select(s => "\t\tclass " + s.Key + " {\r\n" + string.Join(";\r\n", s.Value.ToArray()) + "\r\n\t\t}\r\n").ToArray());

                var finalSB = new StringBuilder(routineContainerTemplate);

                jsFile.IncrementVersion();

                // record changes against new version

                if (changesInFile != null && changesInFile.Count > 0)
                {
                    JsFileChangesTracker.Instance.AddUpdate(endpoint, jsFile, changesInFile.Select(kv => kv.Value).ToList());
                }

                if (rulesChanged)
                {
                    JsFileChangesTracker.Instance.AddUpdate(endpoint, jsFile, new List <ChangeDescriptor> {
                        ChangeDescriptor.Create("System", "One or more rules changed.")
                    });
                }

                finalSB.Replace("<<DATE>>", DateTime.Now.ToString("dd MMM yyyy, HH:mm"))
                .Replace("<<FILE_VERSION>>", jsFile.Version.ToString())
                .Replace("<<SERVER_NAME>>", Environment.MachineName)
                .Replace("<<ENDPOINT>>", endpoint.Pedigree)
                .Replace("<<UNIQUE_SCHEMAS>>", string.Join(',', uniqueSchemas.Select(k => $"'{k}'")))
                .Replace("<<Catalog>>", jsSafeNamespace)
                .Replace("<<ROUTINES>>", schemaAndRoutineDefs)
                ;

                var finalTypeScriptSB = new StringBuilder();

                finalTypeScriptSB = finalTypeScriptSB.Append(typescriptDefinitionsContainer);

                // Custom/User types
                if (endpoint.CustomTypeLookupWithTypeScriptDef.Count > 0)
                {
                    var customTSD = from kv in endpoint.CustomTypeLookupWithTypeScriptDef select $"\t\ttype {kv.Key} = {kv.Value};";
                    typeScriptParameterAndResultTypesSB.Insert(0, string.Join("\r\n", customTSD));
                }

                var resultAndParameterTypes = typeScriptParameterAndResultTypesSB.ToString();

                finalTypeScriptSB.Replace("<<DATE>>", DateTime.Now.ToString("dd MMM yyyy, HH:mm"))
                .Replace("<<FILE_VERSION>>", jsFile.Version.ToString())
                .Replace("<<SERVER_NAME>>", Environment.MachineName)
                .Replace("<<Catalog>>", jsSafeNamespace)
                .Replace("<<ResultAndParameterTypes>>", resultAndParameterTypes)
                .Replace("<<MethodsStubs>>", tsSchemaAndRoutineDefs)
                ;

                finalSBMetric.End();

                var toStringMetric = generateMetric.BeginChildStage("ToString");
                var typescriptDefinitionsOutput = finalTypeScriptSB.ToString();
                var finalOutput = finalSB.ToString();
                toStringMetric.End();

                var filePath          = endpoint.OutputFilePath(jsFile);
                var minfiedFilePath   = endpoint.MinifiedOutputFilePath(jsFile);
                var tsTypingsFilePath = endpoint.OutputTypeScriptTypingsFilePath(jsFile);

                var minifyMetric = generateMetric.BeginChildStage("Minify");

                var minifiedSource = Uglify.Js(finalOutput /*, { }*/).Code;

                minifyMetric.End();

                if (!Directory.Exists(endpoint.OutputDir))
                {
                    Directory.CreateDirectory(endpoint.OutputDir);
                }

                var fileOutputMetric = generateMetric.BeginChildStage("Write");

                var jsFinalBytes         = System.Text.Encoding.UTF8.GetBytes(finalOutput);
                var jsFinalMinifiedBytes = System.Text.Encoding.UTF8.GetBytes(minifiedSource);

                jsFile.ETag         = Controllers.PublicController.ComputeETag(jsFinalBytes);
                jsFile.ETagMinified = Controllers.PublicController.ComputeETag(jsFinalMinifiedBytes);

                File.WriteAllText(filePath, finalOutput);
                File.WriteAllText(minfiedFilePath, minifiedSource);
                File.WriteAllText(tsTypingsFilePath, typescriptDefinitionsOutput);

                fileOutputMetric.End();
            }
            finally
            {
                generateMetric.End();

                SessionLog.InfoToFileOnly($"{endpoint.Pedigree.PadRight(25, ' ')} - {generateMetric.DurationInMS.ToString().PadLeft(4)} ms {jsFile.Filename.PadRight(20)} (source={source};rulesChanged={rulesChanged};changes={!noChanges}); {generateMetric.ChildDurationsSingleLine()}");
            }
        }