public static void EditorStateError(this ErrorContainer errors, SourceLocation loc, string message)
 {
     errors.AddError(ErrorCode.IllegalEditorState, loc, $"Illegal editorstate file. {message}");
 }
        private static void ComputeAndWriteChecksum(CanvasDocument app, ChecksumMaker checksum, ZipArchive z, ErrorContainer errors, bool isValidation)
        {
            var hash = checksum.GetChecksum();

            if (app._checksum != null && hash.wholeChecksum != app._checksum.ClientStampedChecksum)
            {
                // These warnings are Debug only. Throwing a bunch of warning messages at the customer could lead to them ignoring real errors.
#if DEBUG
                if (app._checksum.ClientPerFileChecksums != null)
                {
                    foreach (var file in app._checksum.ClientPerFileChecksums)
                    {
                        if (!hash.perFileChecksum.TryGetValue(file.Key, out var fileChecksum))
                        {
                            errors.ChecksumMismatch("Missing file " + file.Key);
                        }
                        if (fileChecksum != file.Value)
                        {
                            errors.ChecksumMismatch($"File {file.Key} checksum does not match on extract");
                        }
                    }
                    foreach (var file in hash.perFileChecksum)
                    {
                        if (!app._checksum.ClientPerFileChecksums.ContainsKey(file.Key))
                        {
                            errors.ChecksumMismatch("Extra file " + file.Key);
                        }
                    }
                }
#endif

                // These are the non-debug warnings, if it's unpack this was a serious error, on -pack it's most likely not
                if (isValidation)
                {
                    errors.PostUnpackValidationFailed();
                    throw new DocumentException();
                }

                errors.ChecksumMismatch("Checksum indicates that sources have been edited since they were unpacked. If this was intentional, ignore this warning.");
            }

            var checksumJson = new ChecksumJson
            {
                ClientStampedChecksum  = hash.wholeChecksum,
                ClientPerFileChecksums = hash.perFileChecksum,
                ServerStampedChecksum  = app._checksum?.ServerStampedChecksum,
                ServerPerFileChecksums = app._checksum?.ServerPerFileChecksums,
            };

            var entry = ToFile(FileKind.Checksum, checksumJson);
            var e     = z.CreateEntry(entry.Name.ToMsAppPath());
            using (var dest = e.Open())
            {
                dest.Write(entry.RawBytes, 0, entry.RawBytes.Length);
            }
        }
 private static ControlInfoJson.Item GetDataComponentDefinition(IEnumerable <ControlInfoJson> topParents, string templateGuid, ErrorContainer errors)
 {
     foreach (var topParent in topParents)
     {
         if (topParent.TopParent.Template.Name == templateGuid)
         {
             return(topParent.TopParent);
         }
     }
     errors.GenericError("Could not find DataComponent Definition for template " + templateGuid);
     throw new DocumentException();
 }
        private static void RepopulateTemplateCustomProperties(FunctionNode func, CombinedTemplateState templateState, ErrorContainer errors)
        {
            var funcName   = func.Identifier;
            var customProp = templateState.CustomProperties.FirstOrDefault(prop => prop.Name == funcName);

            if (customProp == default)
            {
                errors.ParseError(func.SourceSpan.GetValueOrDefault(), "Functions are not yet supported without corresponding custom properties in ControlTemplates.json");
                throw new DocumentException();
            }

            var scopeArgs = customProp.PropertyScopeKey.PropertyScopeRulesKey.ToDictionary(scope => scope.Name);
            var argTypes  = func.Args.ToDictionary(arg => arg.Identifier, arg => arg.Kind.TypeName);

            int i = 1;

            foreach (var arg in func.Metadata)
            {
                if (arg.Identifier == PAConstants.ThisPropertyIdentifier)
                {
                    continue;
                }

                var defaultRule  = arg.Default.Expression;
                var propertyName = funcName + "_" + arg.Identifier;

                if (!scopeArgs.TryGetValue(propertyName, out var propScopeRule))
                {
                    errors.ParseError(func.SourceSpan.GetValueOrDefault(), "Functions are not yet supported without corresponding custom properties in ControlTemplates.json");
                    throw new DocumentException();
                }
                if (!argTypes.TryGetValue(arg.Identifier, out var propType) || !Enum.TryParse <PropertyDataType>(propType, out var propTypeEnum))
                {
                    errors.ParseError(func.SourceSpan.GetValueOrDefault(), "Function metadata blocks must correspond to a function parameter with a valid type");
                    throw new DocumentException();
                }

                propScopeRule.ScopeVariableInfo.DefaultRule           = defaultRule;
                propScopeRule.ScopeVariableInfo.ParameterIndex        = i;
                propScopeRule.ScopeVariableInfo.ParentPropertyName    = funcName;
                propScopeRule.ScopeVariableInfo.ScopePropertyDataType = (int)propTypeEnum;

                ++i;
            }
        }
        private static ControlInfoJson.RuleEntry GetPropertyEntry(ControlState state, ErrorContainer errors, string propName, string expression)
        {
            var property = new ControlInfoJson.RuleEntry();

            property.Property        = propName;
            property.InvariantScript = expression;

            PropertyState propState = null;

            if (state.Properties != null && state.Properties.ToDictionary(prop => prop.PropertyName).TryGetValue(propName, out propState))
            {
                property.ExtensionData    = propState.ExtensionData;
                property.NameMap          = propState.NameMap;
                property.RuleProviderType = propState.RuleProviderType;
            }
            else
            {
                if (state.IsComponentDefinition ?? false)
                {
                    errors.UnsupportedOperationError("This tool currently does not support adding new custom properties to components. Please use Power Apps Studio to edit component definitions");
                    throw new DocumentException();
                }

                property.RuleProviderType = "Unknown";
            }

            return(property);
        }
Exemple #6
0
        private static void WriteDataSources(DirectoryWriter dir, CanvasDocument app, ErrorContainer errors)
        {
            // Data Sources  - write out each individual source.
            HashSet <string> filenames = new HashSet <string>();

            foreach (var kvp in app.GetDataSources())
            {
                // Filename doesn't actually matter, but careful to avoid collisions and overwriting.
                // Also be determinstic.
                string filename = kvp.Key + ".json";

                if (!filenames.Add(filename.ToLower()))
                {
                    int index       = 1;
                    var altFileName = kvp.Key + "_" + index + ".json";
                    while (!filenames.Add(altFileName.ToLower()))
                    {
                        ++index;
                    }

                    errors.GenericWarning("Data source name collision: " + filename + ", writing as " + altFileName + " to avoid.");
                    filename = altFileName;
                }
                var dataSourceStateToWrite         = kvp.Value.JsonClone().OrderBy(ds => ds.Name, StringComparer.Ordinal);
                DataSourceDefinition dataSourceDef = null;

                // Split out the changeable parts of the data source.
                foreach (var ds in dataSourceStateToWrite.Where(ds => ds.Type != "ViewInfo"))
                {
                    // CDS DataSource
                    if (ds.TableDefinition != null)
                    {
                        dataSourceDef = new DataSourceDefinition();
                        dataSourceDef.TableDefinition = Utilities.JsonParse <DataSourceTableDefinition>(ds.TableDefinition);
                        dataSourceDef.DatasetName     = ds.DatasetName;
                        dataSourceDef.EntityName      = ds.RelatedEntityName ?? ds.Name;
                        ds.DatasetName     = null;
                        ds.TableDefinition = null;
                    }
                    // CDP DataSource
                    else if (ds.DataEntityMetadataJson != null)
                    {
                        if (ds.ApiId == "/providers/microsoft.powerapps/apis/shared_commondataservice")
                        {
                            // This is the old CDS connector, we can't support it since it's optionset format is incompatable with the newer one
                            errors.ValidationError($"Connection {ds.Name} is using the old CDS connector which is incompatable with this tool");
                            throw new DocumentException();
                        }
                        dataSourceDef = new DataSourceDefinition();
                        dataSourceDef.DataEntityMetadataJson = ds.DataEntityMetadataJson;
                        dataSourceDef.EntityName             = ds.Name;
                        dataSourceDef.TableName   = ds.TableName;
                        ds.TableName              = null;
                        ds.DataEntityMetadataJson = null;
                    }
                    else if (ds.Type == "OptionSetInfo")
                    {
                        // This looks like a left over from previous versions of studio, account for it by
                        // tracking optionsets with empty dataset names
                        ds.DatasetName = ds.DatasetName == null ? string.Empty : null;
                    }
                    else if (ds.WadlMetadata != null)
                    {
                        // For some reason some connectors have both, investigate if one could be discarded by the server?
                        if (ds.WadlMetadata.WadlXml != null)
                        {
                            dir.WriteAllXML(WadlPackageDir, new FilePath(filename.Replace(".json", ".xml")), ds.WadlMetadata.WadlXml);
                        }
                        if (ds.WadlMetadata.SwaggerJson != null)
                        {
                            dir.WriteAllJson(SwaggerPackageDir, new FilePath(filename), JsonSerializer.Deserialize <SwaggerDefinition>(ds.WadlMetadata.SwaggerJson, Utilities._jsonOpts));
                        }
                        ds.WadlMetadata = null;
                    }
                }

                if (dataSourceDef != null)
                {
                    TrimViewNames(dataSourceStateToWrite, dataSourceDef.DatasetName);
                }

                if (dataSourceDef?.DatasetName != null && app._dataSourceReferences.TryGetValue(dataSourceDef.DatasetName, out var referenceJson))
                {
                    // copy over the localconnectionreference
                    if (referenceJson.dataSources.TryGetValue(dataSourceDef.EntityName, out var dsRef))
                    {
                        dataSourceDef.LocalReferenceDSJson = dsRef;
                    }
                    dataSourceDef.InstanceUrl   = referenceJson.instanceUrl;
                    dataSourceDef.ExtensionData = referenceJson.ExtensionData;
                }

                if (dataSourceDef != null)
                {
                    dir.WriteAllJson(DataSourcePackageDir, new FilePath(filename), dataSourceDef);
                }

                dir.WriteAllJson(DataSourcesDir, new FilePath(filename), dataSourceStateToWrite);
            }
        }
Exemple #7
0
        // Full fidelity read-write

        public static CanvasDocument LoadFromSource(string directory2, ErrorContainer errors)
        {
            if (File.Exists(directory2))
            {
                if (directory2.EndsWith(".msapp", StringComparison.OrdinalIgnoreCase))
                {
                    throw new ArgumentException($"Must point to a source directory, not an msapp file ({directory2}");
                }
            }

            if (!Directory.Exists(directory2))
            {
                throw new InvalidOperationException($"No directory {directory2}");
            }
            var    dir = new DirectoryReader(directory2);
            var    app = new CanvasDocument();
            string appInsightsInstumentationKey = null;

            // Do the manifest check (and version check) first.
            // MAnifest lives in top-level directory.
            foreach (var file in dir.EnumerateFiles("", "*.json"))
            {
                switch (file.Kind)
                {
                case FileKind.CanvasManifest:
                    var manifest = file.ToObject <CanvasManifestJson>();

                    if (manifest.FormatVersion != CurrentSourceVersion)
                    {
                        errors.FormatNotSupported($"This tool only supports {CurrentSourceVersion}, the manifest version is {manifest.FormatVersion}");
                        throw new DocumentException();
                    }

                    app._properties  = manifest.Properties;
                    app._header      = manifest.Header;
                    app._publishInfo = manifest.PublishInfo;
                    app._screenOrder = manifest.ScreenOrder;
                    break;

                case FileKind.Templates:
                    foreach (var kvp in file.ToObject <Dictionary <string, CombinedTemplateState> >())
                    {
                        app._templateStore.AddTemplate(kvp.Key, kvp.Value);
                    }
                    break;

                case FileKind.ComponentReferences:
                    var refs = file.ToObject <ComponentDependencyInfo[]>();
                    app._libraryReferences = refs;
                    break;

                case FileKind.AppInsightsKey:
                    var appInsights = file.ToObject <AppInsightsKeyJson>();
                    appInsightsInstumentationKey = appInsights.InstrumentationKey;
                    break;
                }
            }
            if (appInsightsInstumentationKey != null)
            {
                app._properties.InstrumentationKey = appInsightsInstumentationKey;
            }
            if (app._header == null)
            {
                // Manifest not found.
                errors.FormatNotSupported($"Can't find CanvasManifest.json file - is sources an old version?");
                throw new DocumentException();
            }

            // Load template files, recreate References/templates.json
            LoadTemplateFiles(errors, app, Path.Combine(directory2, PackagesDir), out var templateDefaults);

            foreach (var file in dir.EnumerateFiles(AssetsDir))
            {
                if (file._relativeName == "Resources.json")
                {
                    app._resourcesJson = file.ToObject <ResourcesJson>();
                    continue;
                }
                app.AddAssetFile(file.ToFileEntry());
            }

            foreach (var file in dir.EnumerateFiles(EntropyDir))
            {
                switch (file.Kind)
                {
                case FileKind.Entropy:
                    app._entropy = file.ToObject <Entropy>();
                    break;

                case FileKind.AppCheckerResult:
                    app._appCheckerResultJson = file.ToObject <AppCheckerResultJson>();
                    break;

                case FileKind.Checksum:
                    app._checksum = file.ToObject <ChecksumJson>();
                    app._checksum.ClientBuildDetails = _buildVerJson;
                    break;

                default:
                    errors.GenericWarning($"Unexpected file in Entropy, discarding");
                    break;
                }
            }


            foreach (var file in dir.EnumerateFiles(OtherDir))
            {
                // Special files like Header / Properties
                switch (file.Kind)
                {
                case FileKind.Unknown:
                    // Track any unrecognized files so we can save back.
                    app.AddFile(file.ToFileEntry());
                    break;

                default:
                    // Shouldn't find anything else not unknown in here, but just ignore them for now
                    errors.GenericWarning($"Unexpected file in Other, discarding");
                    break;
                }
            } // each loose file in '\other'


            app.GetLogoFile();

            LoadDataSources(app, dir, errors);
            LoadSourceFiles(app, dir, templateDefaults, errors);

            foreach (var file in dir.EnumerateFiles(ConnectionDir))
            {
                // Special files like Header / Properties
                switch (file.Kind)
                {
                case FileKind.Connections:
                    app._connections = file.ToObject <IDictionary <string, ConnectionJson> >();
                    break;
                }
            }


            // Defaults.
            // - DynamicTypes.Json, Resources.Json , Templates.Json - could all be empty
            // - Themes.json- default to


            app.OnLoadComplete(errors);

            return(app);
        }
 public static void UnsupportedError(this ErrorContainer errors, string message)
 {
     errors.AddError(ErrorCode.UnsupportedError, default(SourceLocation), $"Not Supported: {message}");
 }
 public static void ChecksumMismatch(this ErrorContainer errors, string message)
 {
     errors.AddError(ErrorCode.ChecksumMismatch, default(SourceLocation), $"Checksum mismatch. {message}");
 }
 public static void ValidationError(this ErrorContainer errors, SourceLocation span, string message)
 {
     errors.AddError(ErrorCode.Validation, span, $"Validation error: {message}");
 }
 public static void MsAppFormatError(this ErrorContainer errors, string message)
 {
     errors.AddError(ErrorCode.CantReadMsApp, default(SourceLocation), $"MsApp is corrupted: {message}");
 }
 public static void ParseError(this ErrorContainer errors, SourceLocation span, string message)
 {
     errors.AddError(ErrorCode.ParseError, span, $"Parse error: {message}");
 }
 public static void DuplicateSymbolError(this ErrorContainer errors, SourceLocation loc, string message, SourceLocation loc2)
 {
     errors.AddError(ErrorCode.DuplicateSymbol, loc, $"Symbol '{message}' is already defined. Previously at {loc2}");
 }
 public static void GenericMsAppError(this ErrorContainer errors, string message)
 {
     errors.AddError(ErrorCode.MsAppError, default(SourceLocation), $"MsApp is corrupted. {message}");
 }
Exemple #15
0
        private static void AddControl(CanvasDocument app, string filePath, bool isComponent, string fileContents, ErrorContainer errors)
        {
            var filename = Path.GetFileName(filePath);

            try
            {
                var parser    = new Parser.Parser(filePath, fileContents, errors);
                var controlIR = parser.ParseControl();
                if (controlIR == null)
                {
                    return; // error condition
                }

                var collection = (isComponent) ? app._components : app._screens;
                collection.Add(controlIR.Name.Identifier, controlIR);
            }
            catch (DocumentException)
            {
                // On DocumentException, continue looking for errors in other files.
            }
        }
 public static void PostUnpackValidationFailed(this ErrorContainer errors)
 {
     errors.AddError(ErrorCode.UnpackValidationFailed, default(SourceLocation), "Roundtrip validation on unpack failed. This is always a bug, please file an issue at https://github.com/microsoft/PowerApps-Language-Tooling");
 }
Exemple #17
0
        // Write out to a directory (this shards it)
        public static void SaveAsSource(CanvasDocument app, string directory2, ErrorContainer errors)
        {
            var dir = new DirectoryWriter(directory2);

            dir.DeleteAllSubdirs();

            // Shard templates, parse for default values
            var templateDefaults = new Dictionary <string, ControlTemplate>();

            foreach (var template in app._templates.UsedTemplates)
            {
                var filename = $"{template.Name}_{template.Version}.xml";
                dir.WriteAllXML(PackagesDir, new FilePath(filename), template.Template);
                if (!ControlTemplateParser.TryParseTemplate(app._templateStore, template.Template, app._properties.DocumentAppType, templateDefaults, out _, out _))
                {
                    throw new NotSupportedException($"Unable to parse template file {template.Name}");
                }
            }

            // Also add Screen and App templates (not xml, constructed in code on the server)
            GlobalTemplates.AddCodeOnlyTemplates(app._templateStore, templateDefaults, app._properties.DocumentAppType);

            var importedComponents = app.GetImportedComponents();

            foreach (var control in app._screens)
            {
                string controlName = control.Key;
                var    isTest      = controlName == AppTestControlName;
                var    subDir      = isTest ? TestDir : CodeDir;

                WriteTopParent(dir, app, control.Key, control.Value, subDir);
            }

            foreach (var control in app._components)
            {
                string controlName = control.Key;
                app._templateStore.TryGetTemplate(controlName, out var templateState);

                bool isImported = importedComponents.Contains(templateState.TemplateOriginalName);
                var  subDir     = (isImported) ? ComponentPackageDir : ComponentCodeDir;
                WriteTopParent(dir, app, control.Key, control.Value, subDir);
            }

            // Write out control templates at top level, skipping component templates which are written alongside components
            var nonComponentControlTemplates = app._templateStore.Contents.Where(kvp => !(kvp.Value.IsComponentTemplate ?? false)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            dir.WriteAllJson("", new FilePath("ControlTemplates.json"), nonComponentControlTemplates);

            if (app._checksum != null)
            {
                app._checksum.ClientBuildDetails = _buildVerJson;
                dir.WriteAllJson(EntropyDir, FileKind.Checksum, app._checksum);
            }

            if (app._appCheckerResultJson != null)
            {
                dir.WriteAllJson(EntropyDir, FileKind.AppCheckerResult, app._appCheckerResultJson);
            }

            foreach (var file in app._assetFiles.Values)
            {
                dir.WriteAllBytes(AssetsDir, file.Name, file.RawBytes);
            }

            if (app._logoFile != null)
            {
                dir.WriteAllBytes(AssetsDir, app._logoFile.Name, app._logoFile.RawBytes);
            }

            if (app._themes != null)
            {
                dir.WriteAllJson(CodeDir, new FilePath("Themes.json"), app._themes);
            }

            if (app._resourcesJson != null)
            {
                dir.WriteAllJson(AssetsDir, new FilePath("Resources.json"), app._resourcesJson);
            }

            WriteDataSources(dir, app, errors);

            // Loose files.
            foreach (FileEntry file in app._unknownFiles.Values)
            {
                // Standardize the .json files so they're determinsitc and comparable
                if (file.Name.HasExtension(".json"))
                {
                    ReadOnlyMemory <byte> span = file.RawBytes;
                    var je      = JsonDocument.Parse(span).RootElement;
                    var jsonStr = JsonNormalizer.Normalize(je);
                    dir.WriteAllText(OtherDir, file.Name, jsonStr);
                }
                else
                {
                    dir.WriteAllBytes(OtherDir, file.Name, file.RawBytes);
                }
            }

            var manifest = new CanvasManifestJson
            {
                FormatVersion = CurrentSourceVersion,
                Properties    = app._properties,
                Header        = app._header,
                PublishInfo   = app._publishInfo,
                ScreenOrder   = app._screenOrder
            };

            dir.WriteAllJson("", FileKind.CanvasManifest, manifest);

            if (app._connections != null)
            {
                dir.WriteAllJson(ConnectionDir, FileKind.Connections, app._connections);
            }

            if (app._libraryReferences != null)
            {
                dir.WriteAllJson("", FileKind.ComponentReferences, app._libraryReferences);
            }
            if (app._appInsights != null)
            {
                dir.WriteAllJson("", FileKind.AppInsightsKey, app._appInsights);
            }

            dir.WriteAllJson(EntropyDir, FileKind.Entropy, app._entropy);
        }
 public static void YamlWontRoundTrip(this ErrorContainer errors, SourceLocation loc, string message)
 {
     errors.AddError(ErrorCode.YamlWontRoundtrip, loc, message);
 }
Exemple #19
0
        private static void LoadDataSources(CanvasDocument app, DirectoryReader directory, ErrorContainer errors)
        {
            var tableDefs = new Dictionary <string, DataSourceDefinition>();

            app._dataSourceReferences = new Dictionary <string, LocalDatabaseReferenceJson>();

            foreach (var file in directory.EnumerateFiles(DataSourcePackageDir, "*.json"))
            {
                var tableDef = file.ToObject <DataSourceDefinition>();
                tableDefs.Add(tableDef.EntityName, tableDef);
                if (tableDef.DatasetName == null)
                {
                    continue;
                }

                if (!app._dataSourceReferences.TryGetValue(tableDef.DatasetName, out var localDatabaseReferenceJson))
                {
                    localDatabaseReferenceJson = new LocalDatabaseReferenceJson()
                    {
                        dataSources   = new Dictionary <string, LocalDatabaseReferenceDataSource>(),
                        ExtensionData = tableDef.ExtensionData,
                        instanceUrl   = tableDef.InstanceUrl
                    };
                    app._dataSourceReferences.Add(tableDef.DatasetName, localDatabaseReferenceJson);
                }
                if (localDatabaseReferenceJson.instanceUrl != tableDef.InstanceUrl)
                {
                    // Generate an error, dataset defs have diverged in a way that shouldn't be possible
                    // Each dataset has one instanceurl
                    errors.ValidationError($"For file {file._relativeName}, the dataset {tableDef.DatasetName} has multiple instanceurls");
                    throw new DocumentException();
                }

                localDatabaseReferenceJson.dataSources.Add(tableDef.EntityName, tableDef.LocalReferenceDSJson);
            }

            // key is filename, value is stringified xml
            var xmlDefs = new Dictionary <string, string>();

            foreach (var file in directory.EnumerateFiles(WadlPackageDir, "*.xml"))
            {
                xmlDefs.Add(Path.GetFileNameWithoutExtension(file._relativeName), file.GetContents());
            }

            // key is filename, value is stringified json
            var swaggerDefs = new Dictionary <string, string>();

            foreach (var file in directory.EnumerateFiles(SwaggerPackageDir, "*.json"))
            {
                swaggerDefs.Add(Path.GetFileNameWithoutExtension(file._relativeName), file.GetContents());
            }

            foreach (var file in directory.EnumerateFiles(DataSourcesDir, "*"))
            {
                var dataSources = file.ToObject <List <DataSourceEntry> >();
                foreach (var ds in dataSources)
                {
                    if (tableDefs.TryGetValue(ds.RelatedEntityName ?? ds.Name, out var definition))
                    {
                        switch (ds.Type)
                        {
                        case "NativeCDSDataSourceInfo":
                            ds.DatasetName     = definition.DatasetName;
                            ds.TableDefinition = JsonSerializer.Serialize(definition.TableDefinition, Utilities._jsonOpts);
                            break;

                        case "ConnectedDataSourceInfo":
                            ds.DataEntityMetadataJson = definition.DataEntityMetadataJson;
                            ds.TableName = definition.TableName;
                            break;

                        case "OptionSetInfo":
                            ds.DatasetName = ds.DatasetName != string.Empty? definition.DatasetName : null;
                            break;

                        case "ViewInfo":
                            if (definition != null)
                            {
                                RestoreViewName(ds, definition.DatasetName);
                            }
                            break;

                        case "ServiceInfo":
                        default:
                            break;
                        }
                    }
                    else if (ds.Type == "ServiceInfo")
                    {
                        var foundXML  = xmlDefs.TryGetValue(Path.GetFileNameWithoutExtension(file._relativeName), out string xmlDef);
                        var foundJson = swaggerDefs.TryGetValue(Path.GetFileNameWithoutExtension(file._relativeName), out string swaggerDef);

                        if (foundXML || foundJson)
                        {
                            ds.WadlMetadata = new WadlDefinition()
                            {
                                WadlXml = xmlDef, SwaggerJson = swaggerDef
                            };
                        }
                    }

                    app.AddDataSourceForLoad(ds);
                }
            }
        }
 public static void FormatNotSupported(this ErrorContainer errors, string message)
 {
     errors.AddError(ErrorCode.FormatNotSupported, default(SourceLocation), $"Format is not supported. {message}");
 }
        // Returns pair of item and index (with respect to parent order)
        private static (ControlInfoJson.Item item, int index) CombineIRAndState(BlockNode blockNode, ErrorContainer errors, string parent, bool isInResponsiveLayout, EditorStateStore stateStore, TemplateStore templateStore, UniqueIdRestorer uniqueIdRestorer, Entropy entropy)
        {
            var controlName  = blockNode.Name.Identifier;
            var templateIR   = blockNode.Name.Kind;
            var templateName = templateIR.TypeName;
            var variantName  = templateIR.OptionalVariant;

            // Bottom up, merge children first
            var children = new List <(ControlInfoJson.Item item, int index)>();

            foreach (var childBlock in blockNode.Children)
            {
                children.Add(CombineIRAndState(childBlock, errors, controlName, DynamicProperties.AddsChildDynamicProperties(templateName, variantName), stateStore, templateStore, uniqueIdRestorer, entropy));
            }

            var orderedChildren = children.OrderBy(childPair => childPair.index).Select(pair => pair.item).ToArray();

            ControlInfoJson.Template template;
            if (!templateStore.TryGetTemplate(templateName, out var templateState))
            {
                template = ControlInfoJson.Template.CreateDefaultTemplate(templateName, null);
            }
            else
            {
                template = templateState.ToControlInfoTemplate();
            }

            RecombineCustomTemplates(entropy, template, controlName);

            var uniqueId = uniqueIdRestorer.GetControlId(controlName);

            ControlInfoJson.Item resultControlInfo;
            if (stateStore.TryGetControlState(controlName, out var state))
            {
                var properties        = new List <ControlInfoJson.RuleEntry>();
                var dynamicProperties = new List <ControlInfoJson.DynamicPropertyJson>();
                foreach (var propIR in blockNode.Properties)
                {
                    // Dynamic properties could be null for the galleryTemplateTemplate
                    if (isInResponsiveLayout && state.DynamicProperties != null && DynamicProperties.IsResponsiveLayoutProperty(propIR.Identifier))
                    {
                        dynamicProperties.Add(CombineDynamicPropertyIRAndState(propIR, state));
                    }
                    else
                    {
                        properties.Add(CombinePropertyIRAndState(propIR, errors, state));
                    }
                }

                if (isInResponsiveLayout && state.DynamicProperties != null)
                {
                    // Add dummy dynamic output props in the state at the end
                    foreach (var dynPropState in state.DynamicProperties.Where(propState => propState.Property == null))
                    {
                        dynamicProperties.Add(new ControlInfoJson.DynamicPropertyJson()
                        {
                            PropertyName = dynPropState.PropertyName
                        });
                    }

                    // Reorder to preserve roundtripping
                    dynamicProperties = dynamicProperties.OrderBy(prop => state.DynamicProperties.Select(propState => propState.PropertyName).ToList().IndexOf(prop.PropertyName)).ToList();
                }

                if (blockNode.Functions.Any())
                {
                    foreach (var func in blockNode.Functions)
                    {
                        var funcName          = func.Identifier;
                        var thisPropertyBlock = func.Metadata.FirstOrDefault(metadata => metadata.Identifier == PAConstants.ThisPropertyIdentifier);
                        if (thisPropertyBlock == default)
                        {
                            errors.ParseError(func.SourceSpan.GetValueOrDefault(), "Function definition missing ThisProperty block");
                            throw new DocumentException();
                        }
                        properties.Add(GetPropertyEntry(state, errors, funcName, thisPropertyBlock.Default.Expression));

                        foreach (var arg in func.Metadata)
                        {
                            if (arg.Identifier == PAConstants.ThisPropertyIdentifier)
                            {
                                continue;
                            }

                            properties.Add(GetPropertyEntry(state, errors, funcName + "_" + arg.Identifier, arg.Default.Expression));
                        }

                        RepopulateTemplateCustomProperties(func, templateState, errors);
                    }
                }
                else if (template.CustomProperties?.Any(prop => prop.IsFunctionProperty) ?? false)
                {
                    // For component uses, recreate the dummy props for function parameters
                    foreach (var hiddenScopeRule in template.CustomProperties.Where(prop => prop.IsFunctionProperty).SelectMany(prop => prop.PropertyScopeKey.PropertyScopeRulesKey))
                    {
                        properties.Add(GetPropertyEntry(state, errors, hiddenScopeRule.Name, hiddenScopeRule.ScopeVariableInfo.DefaultRule));
                    }
                }

                // Preserve ordering from serialized IR
                // Required for roundtrip checks
                properties        = properties.OrderBy(prop => state.Properties?.Select(propState => propState.PropertyName).ToList().IndexOf(prop.Property) ?? -1).ToList();
                resultControlInfo = new ControlInfoJson.Item()
                {
                    Parent               = parent,
                    Name                 = controlName,
                    ControlUniqueId      = uniqueId.ToString(),
                    VariantName          = variantName ?? string.Empty,
                    Rules                = properties.ToArray(),
                    DynamicProperties    = (isInResponsiveLayout && dynamicProperties.Any()) ? dynamicProperties.ToArray() : null,
                    HasDynamicProperties = state.HasDynamicProperties,
                    StyleName            = state.StyleName,
                    ExtensionData        = state.ExtensionData,
                    IsGroupControl       = state.IsGroupControl,
                    GroupedControlsKey   = state.GroupedControlsKey
                };

                if (state.IsComponentDefinition ?? false)
                {
                    templateState.ComponentDefinitionInfo = new ComponentDefinitionInfoJson(resultControlInfo, template.LastModifiedTimestamp, orderedChildren);
                    template = templateState.ToControlInfoTemplate();
                    template.IsComponentDefinition   = true;
                    template.ComponentDefinitionInfo = null;
                }
                else
                {
                    template.IsComponentDefinition = state.IsComponentDefinition;
                }
            }
            else
            {
                state                             = null;
                resultControlInfo                 = ControlInfoJson.Item.CreateDefaultControl();
                resultControlInfo.Name            = controlName;
                resultControlInfo.ControlUniqueId = uniqueId.ToString();
                resultControlInfo.Parent          = parent;
                resultControlInfo.VariantName     = variantName ?? string.Empty;
                resultControlInfo.StyleName       = $"default{templateName.FirstCharToUpper()}Style";
                var properties        = new List <ControlInfoJson.RuleEntry>();
                var dynamicProperties = new List <ControlInfoJson.DynamicPropertyJson>();
                foreach (var propIR in blockNode.Properties)
                {
                    if (isInResponsiveLayout && DynamicProperties.IsResponsiveLayoutProperty(propIR.Identifier))
                    {
                        dynamicProperties.Add(CombineDynamicPropertyIRAndState(propIR));
                    }
                    else
                    {
                        properties.Add(CombinePropertyIRAndState(propIR, errors));
                    }
                }
                resultControlInfo.Rules = properties.ToArray();
                bool hasDynamicProperties = isInResponsiveLayout && dynamicProperties.Any();
                resultControlInfo.DynamicProperties    = hasDynamicProperties ? dynamicProperties.ToArray() : null;
                resultControlInfo.HasDynamicProperties = hasDynamicProperties;
            }
            resultControlInfo.Template = template;
            resultControlInfo.Children = orderedChildren;

            return(resultControlInfo, state?.ParentIndex ?? -1);
        }
 public static void GenericError(this ErrorContainer errors, string message)
 {
     errors.AddError(ErrorCode.Generic, default(SourceLocation), message);
 }
        private static ControlInfoJson.RuleEntry CombinePropertyIRAndState(PropertyNode node, ErrorContainer errors, ControlState state = null)
        {
            var propName   = node.Identifier;
            var expression = node.Expression.Expression;

            if (state == null)
            {
                var property = new ControlInfoJson.RuleEntry();
                property.Property         = propName;
                property.InvariantScript  = expression;
                property.RuleProviderType = "Unknown";
                return(property);
            }
            else
            {
                var property = GetPropertyEntry(state, errors, propName, expression);
                return(property);
            }
        }
Exemple #24
0
        public static CanvasDocument Create(string appName, string packagesPath, IList <string> paFiles, ErrorContainer errors)
        {
            var app = new CanvasDocument();

            app._properties = DocumentPropertiesJson.CreateDefault(appName);
            app._header     = HeaderJson.CreateDefault();

            LoadTemplateFiles(errors, app, packagesPath, out var loadedTemplates);
            app._entropy  = new Entropy();
            app._checksum = new ChecksumJson()
            {
                ClientStampedChecksum = "Foo", ClientBuildDetails = _buildVerJson
            };

            AddDefaultTheme(app);

            CreateControls(app, paFiles, loadedTemplates, errors);

            return(app);
        }
        public static CanvasDocument Load(Stream streamToMsapp, ErrorContainer errors)
        {
            if (streamToMsapp == null)
            {
                throw new ArgumentNullException(nameof(streamToMsapp));
            }

            // Read raw files.
            // Apply transforms.
            var app = new CanvasDocument();

            app._checksum         = new ChecksumJson(); // default empty. Will get overwritten if the file is present.
            app._templateStore    = new EditorState.TemplateStore();
            app._editorStateStore = new EditorState.EditorStateStore();

            ComponentsMetadataJson     componentsMetadata = null;
            DataComponentTemplatesJson dctemplate         = null;
            DataComponentSourcesJson   dcsources          = null;

            ChecksumMaker checksumMaker = new ChecksumMaker();
            // key = screen, value = index
            var screenOrder = new Dictionary <string, double>();

            ZipArchive zipOpen;

            try
            {
                zipOpen = new ZipArchive(streamToMsapp, ZipArchiveMode.Read);
            }
            catch (Exception e)
            {
                // Catch cases where stream is corrupted, can't be read, or unavailable.
                errors.MsAppFormatError(e.Message);
                return(null);
            }

            using (var z = zipOpen)
            {
                foreach (var entry in z.Entries)
                {
                    checksumMaker.AddFile(entry.FullName, entry.ToBytes());

                    var fullName = entry.FullName;
                    var kind     = FileEntry.TriageKind(FilePath.FromMsAppPath(fullName));

                    switch (kind)
                    {
                    default:
                        // Track any unrecognized files so we can save back.
                        app.AddFile(FileEntry.FromZip(entry));
                        break;

                    case FileKind.Resources:
                        app._resourcesJson = ToObject <ResourcesJson>(entry);
                        foreach (var resource in app._resourcesJson.Resources)
                        {
                            if (resource.ResourceKind == ResourceKind.LocalFile)
                            {
                                app._entropy.LocalResourceRootPaths.Add(resource.Name, resource.RootPath);
                                resource.RootPath = null;
                            }
                        }

                        break;

                    case FileKind.Asset:
                        app.AddAssetFile(FileEntry.FromZip(entry, name: fullName.Substring("Assets\\".Length)));
                        break;

                    case FileKind.Checksum:
                        app._checksum = ToObject <ChecksumJson>(entry);
                        break;

                    case FileKind.OldEntityJSon:
                        errors.FormatNotSupported($"This is using an older v1 msapp format that is not supported.");
                        throw new DocumentException();

                    case FileKind.DataComponentTemplates:
                        dctemplate = ToObject <DataComponentTemplatesJson>(entry);
                        break;

                    case FileKind.ComponentsMetadata:
                        componentsMetadata = ToObject <ComponentsMetadataJson>(entry);
                        break;

                    case FileKind.DataComponentSources:
                        dcsources = ToObject <DataComponentSourcesJson>(entry);
                        break;

                    case FileKind.Properties:
                        app._properties = ToObject <DocumentPropertiesJson>(entry);
                        break;

                    case FileKind.Themes:
                        app._themes = ToObject <ThemesJson>(entry);
                        break;

                    case FileKind.Header:
                        app._header = ToObject <HeaderJson>(entry);
                        app._entropy.SetHeaderLastSaved(app._header.LastSavedDateTimeUTC);
                        app._header.LastSavedDateTimeUTC = null;
                        break;

                    case FileKind.PublishInfo:
                        app._publishInfo = ToObject <PublishInfoJson>(entry);
                        break;

                    case FileKind.AppCheckerResult:
                        app._appCheckerResultJson = ToObject <AppCheckerResultJson>(entry);
                        break;

                    case FileKind.ComponentSrc:
                    case FileKind.ControlSrc:
                    case FileKind.TestSrc:
                    {
                        var control = ToObject <ControlInfoJson>(entry);
                        var sf      = SourceFile.New(control);
                        // Add to screen order, only screens have meaningful indices, components may have collisions
                        if (!ExcludeControlFromScreenOrdering(sf))
                        {
                            screenOrder.Add(control.TopParent.Name, control.TopParent.Index);
                        }
                        var flattenedControlTree = sf.Flatten();

                        foreach (var ctrl in flattenedControlTree)
                        {
                            // Add PublishOrderIndex to Entropy so it doesn't affect the editorstate diff.
                            app._entropy.PublishOrderIndices.Add(ctrl.Name, ctrl.PublishOrderIndex);

                            // For component instances, also track their index in Entropy
                            if (ctrl.Index == 0.0 || ctrl.Template.Id == "http://microsoft.com/appmagic/screen")
                            {
                                continue;
                            }
                            app._entropy.ComponentIndexes.Add(ctrl.Name, ctrl.Index);
                        }

                        IRStateHelpers.SplitIRAndState(sf, app._editorStateStore, app._templateStore, app._entropy, out var controlIR);
                        if (kind == FileKind.ComponentSrc)
                        {
                            app._components.Add(sf.ControlName, controlIR);
                        }
                        else
                        {
                            app._screens.Add(sf.ControlName, controlIR);
                        }
                    }
                    break;


                    case FileKind.DataSources:
                    {
                        var dataSources = ToObject <DataSourcesJson>(entry);
                        Utilities.EnsureNoExtraData(dataSources.ExtensionData);

                        int iOrder = 0;
                        foreach (var ds in dataSources.DataSources)
                        {
                            app.AddDataSourceForLoad(ds, iOrder);
                            iOrder++;
                        }
                    }
                    break;

                    case FileKind.Templates:
                    {
                        app._templates = ToObject <TemplatesJson>(entry);
                        int iOrder = 0;
                        foreach (var template in app._templates.UsedTemplates)
                        {
                            app._entropy.Add(template, iOrder);
                            iOrder++;
                        }
                        iOrder = 0;
                        foreach (var template in app._templates.ComponentTemplates ?? Enumerable.Empty <TemplateMetadataJson>())
                        {
                            app._entropy.AddComponent(template, iOrder);
                            iOrder++;
                        }
                    }
                    break;
                    }
                } // foreach zip entry

                foreach (var componentTemplate in app._templates.ComponentTemplates ?? Enumerable.Empty <TemplateMetadataJson>())
                {
                    if (!app._templateStore.TryGetTemplate(componentTemplate.Name, out var template))
                    {
                        continue;
                    }
                    template.TemplateOriginalName            = componentTemplate.OriginalName;
                    template.IsComponentLocked               = componentTemplate.IsComponentLocked;
                    template.ComponentChangedSinceFileImport = componentTemplate.ComponentChangedSinceFileImport;
                    template.ComponentAllowCustomization     = componentTemplate.ComponentAllowCustomization;
                    template.ComponentExtraMetadata          = componentTemplate.ExtensionData;

                    if (template.Version != componentTemplate.Version)
                    {
                        app._entropy.SetTemplateVersion(template.Name, componentTemplate.Version);
                    }
                }

                app._screenOrder = screenOrder.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key).ToList();

                // Checksums?
                var currentChecksum = checksumMaker.GetChecksum();

                // This is debug only. The server checksum is out of date with the client checksum
                // The main checksum validation that matters is the repack after unpack
#if DEBUG
                if (app._checksum.ServerStampedChecksum != null && app._checksum.ServerStampedChecksum != currentChecksum.wholeChecksum)
                {
                    // The server checksum doesn't match the actual contents.
                    // likely has been tampered.
                    errors.ChecksumMismatch("Checksum doesn't match on extract.");
                    if (app._checksum.ServerPerFileChecksums != null)
                    {
                        foreach (var file in app._checksum.ServerPerFileChecksums)
                        {
                            if (!currentChecksum.perFileChecksum.TryGetValue(file.Key, out var fileChecksum))
                            {
                                errors.ChecksumMismatch("Missing file " + file.Key);
                            }
                            if (fileChecksum != file.Value)
                            {
                                errors.ChecksumMismatch($"File {file.Key} checksum does not match on extract");
                            }
                        }
                        foreach (var file in currentChecksum.perFileChecksum)
                        {
                            if (!app._checksum.ServerPerFileChecksums.ContainsKey(file.Key))
                            {
                                errors.ChecksumMismatch("Extra file " + file.Key);
                            }
                        }
                    }
                }
#endif

                app._checksum.ClientStampedChecksum  = currentChecksum.wholeChecksum;
                app._checksum.ClientPerFileChecksums = currentChecksum.perFileChecksum;
                // Normalize logo filename.
                app.TranformLogoOnLoad();

                if (app._properties.LibraryDependencies != null)
                {
                    var refs = Utilities.JsonParse <ComponentDependencyInfo[]>(app._properties.LibraryDependencies);
                    app._libraryReferences = refs;
                    app._properties.LibraryDependencies = null;
                }

                if (app._properties.LocalConnectionReferences != null)
                {
                    var cxs = Utilities.JsonParse <IDictionary <String, ConnectionJson> >(app._properties.LocalConnectionReferences);
                    app._connections = cxs;
                    app._properties.LocalConnectionReferences = null;
                }

                if (!string.IsNullOrEmpty(app._properties.LocalDatabaseReferences))
                {
                    var dsrs = Utilities.JsonParse <IDictionary <String, LocalDatabaseReferenceJson> >(app._properties.LocalDatabaseReferences);
                    app._dataSourceReferences = dsrs;
                    app._properties.LocalDatabaseReferences     = null;
                    app._entropy.LocalDatabaseReferencesAsEmpty = false;
                }
                else
                {
                    app._entropy.LocalDatabaseReferencesAsEmpty = true;
                }


                if (componentsMetadata?.Components != null)
                {
                    int order = 0;
                    foreach (var x in componentsMetadata.Components)
                    {
                        var manifest = ComponentManifest.Create(x);
                        if (!app._templateStore.TryGetTemplate(x.TemplateName, out var templateState))
                        {
                            errors.FormatNotSupported("Component Metadata contains template not present in the app");
                            throw new DocumentException();
                        }

                        templateState.ComponentManifest = manifest;
                        app._entropy.Add(x, order);
                        order++;
                    }
                }

                // Only for data-compoents.
                if (dctemplate?.ComponentTemplates != null)
                {
                    int order = 0;
                    foreach (var x in dctemplate.ComponentTemplates)
                    {
                        if (x.ComponentType == null)
                        {
                            errors.FormatNotSupported($"Data component {x.Name} is using an outdated format");
                            throw new DocumentException();
                        }

                        if (!app._templateStore.TryGetTemplate(x.Name, out var templateState))
                        {
                            errors.FormatNotSupported("Component Metadata contains template not present in the app");
                            throw new DocumentException();
                        }

                        ComponentManifest manifest = templateState.ComponentManifest; // Should already exist
                        app._entropy.SetTemplateVersion(x.Name, x.Version);
                        app._entropy.Add(x, order);
                        manifest.Apply(x);
                        order++;
                    }
                }

                if (dcsources?.DataSources != null)
                {
                    // Component Data sources only appear if the data component is actually
                    // used as a data source in this app.
                    foreach (var x in dcsources.DataSources)
                    {
                        if (x.Type != DataComponentSourcesJson.NativeCDSDataSourceInfo)
                        {
                            throw new NotImplementedException(x.Type);
                        }

                        var ds = new DataSourceEntry
                        {
                            Name = x.Name,
                            DataComponentDetails = x, // pass in all details for full-fidelity
                            Type = DataSourceModel.DataComponentType
                        };

                        app.AddDataSourceForLoad(ds);
                    }
                }
            }

            app.ApplyAfterMsAppLoadTransforms(errors);
            app.OnLoadComplete(errors);

            return(app);
        }
Exemple #26
0
        private static void LoadSourceFiles(CanvasDocument app, DirectoryReader directory, Dictionary <string, ControlTemplate> templateDefaults, ErrorContainer errors)
        {
            foreach (var file in directory.EnumerateFiles(EditorStateDir, "*.json"))
            {
                if (!file._relativeName.EndsWith(".editorstate.json"))
                {
                    errors.FormatNotSupported($"Unexpected file present in {EditorStateDir}");
                    throw new DocumentException();
                }

                // Json peer to a .pa file.
                var controlExtraData = file.ToObject <Dictionary <string, ControlState> >();
                var topParentName    = file._relativeName.Replace(".editorstate.json", "");
                foreach (var control in controlExtraData)
                {
                    control.Value.TopParentName = topParentName;
                    if (!app._editorStateStore.TryAddControl(control.Value))
                    {
                        // Can't have duplicate control names.
                        // This might happen due to a bad merge.
                        errors.EditorStateError(file.SourceSpan, $"Control '{control.Value.Name}' is already defined.");
                    }
                }
            }

            // For now, the Themes file lives in CodeDir as a json file
            // We'd like to make this .fx.yaml as well eventually
            foreach (var file in directory.EnumerateFiles(CodeDir, "*.json", searchSubdirectories: false))
            {
                if (Path.GetFileName(file._relativeName) == "Themes.json")
                {
                    app._themes = file.ToObject <ThemesJson>();
                }
            }


            foreach (var file in directory.EnumerateFiles(CodeDir, "*.fx.yaml", searchSubdirectories: false))
            {
                AddControl(app, file._relativeName, false, file.GetContents(), errors);
            }

            foreach (var file in EnumerateComponentDirs(directory, "*.fx.yaml"))
            {
                AddControl(app, file._relativeName, true, file.GetContents(), errors);
            }

            foreach (var file in directory.EnumerateFiles(TestDir, "*.fx.yaml"))
            {
                AddControl(app, file._relativeName, false, file.GetContents(), errors);
            }

            foreach (var file in EnumerateComponentDirs(directory, "*.json"))
            {
                var componentTemplate = file.ToObject <CombinedTemplateState>();
                app._templateStore.AddTemplate(componentTemplate.ComponentManifest.Name, componentTemplate);
            }
        }
        // Get everything that should be stored as a file in the .msapp.
        private static IEnumerable <FileEntry> GetMsAppFiles(this CanvasDocument app, ErrorContainer errors)
        {
            // Loose files
            foreach (var file in app._unknownFiles.Values)
            {
                yield return(file);
            }

            yield return(ToFile(FileKind.Themes, app._themes));

            var header = app._header.JsonClone();

            header.LastSavedDateTimeUTC = app._entropy.GetHeaderLastSaved();
            yield return(ToFile(FileKind.Header, header));

            var props = app._properties.JsonClone();

            if (app._connections != null)
            {
                var json = Utilities.JsonSerialize(app._connections);
                props.LocalConnectionReferences = json;
            }
            if (app._dataSourceReferences != null)
            {
                var json = Utilities.JsonSerialize(app._dataSourceReferences);

                // Some formats serialize empty as "", some serialize as "{}"
                if (app._dataSourceReferences.Count == 0)
                {
                    if (app._entropy.LocalDatabaseReferencesAsEmpty)
                    {
                        json = "";
                    }
                    else
                    {
                        json = "{}";
                    }
                }

                props.LocalDatabaseReferences = json;
            }

            if (app._libraryReferences != null)
            {
                var json = Utilities.JsonSerialize(app._libraryReferences);
                props.LibraryDependencies = json;
            }

            yield return(ToFile(FileKind.Properties, props));

            var(publishInfo, logoFile) = app.TransformLogoOnSave();
            yield return(logoFile);

            if (publishInfo != null)
            {
                yield return(ToFile(FileKind.PublishInfo, publishInfo));
            }

            // "DataComponent" data sources are not part of DataSource.json, and instead in their own file
            var dataSources = new DataSourcesJson
            {
                DataSources = app.GetDataSources()
                              .SelectMany(x => x.Value)
                              .Where(x => !x.IsDataComponent)
                              .OrderBy(x => app._entropy.GetOrder(x))
                              .ToArray()
            };

            yield return(ToFile(FileKind.DataSources, dataSources));

            var sourceFiles = new List <SourceFile>();



            var idRestorer           = new UniqueIdRestorer(app._entropy);
            var maxPublishOrderIndex = app._entropy.PublishOrderIndices.Any() ? app._entropy.PublishOrderIndices.Values.Max() : 0;

            // Rehydrate sources before yielding any to be written, processing component defs first
            foreach (var controlData in app._screens.Concat(app._components)
                     .OrderBy(source =>
                              (app._editorStateStore.TryGetControlState(source.Value.Name.Identifier, out var control) &&
                               (control.IsComponentDefinition ?? false)) ? -1 : 1))
            {
                var sourceFile = IRStateHelpers.CombineIRAndState(controlData.Value, errors, app._editorStateStore, app._templateStore, idRestorer, app._entropy);
                // Offset the publishOrderIndex based on Entropy.json
                foreach (var ctrl in sourceFile.Flatten())
                {
                    if (app._entropy.PublishOrderIndices.TryGetValue(ctrl.Name, out var index))
                    {
                        ctrl.PublishOrderIndex = index;
                    }
                    else
                    {
                        ctrl.PublishOrderIndex = ++maxPublishOrderIndex;
                    }
                }
                sourceFiles.Add(sourceFile);
            }

            CheckUniqueIds(errors, sourceFiles);

            // Repair order when screens are unchanged
            if (sourceFiles.Where(file => !ExcludeControlFromScreenOrdering(file)).Count() == app._screenOrder.Count &&
                sourceFiles.Where(file => !ExcludeControlFromScreenOrdering(file)).All(file => app._screenOrder.Contains(file.ControlName)))
            {
                double i = 0.0;
                foreach (var screen in app._screenOrder)
                {
                    sourceFiles.First(file => file.ControlName == screen).Value.TopParent.Index = i;
                    i += 1;
                }
            }
            else
            {
                // Make up an order, it doesn't really matter.
                double i = 0.0;
                foreach (var sourceFile in sourceFiles)
                {
                    if (ExcludeControlFromScreenOrdering(sourceFile))
                    {
                        continue;
                    }
                    sourceFile.Value.TopParent.Index = i;
                    i += 1;
                }
            }

            RepairComponentInstanceIndex(app._entropy?.ComponentIndexes ?? new Dictionary <string, double>(), sourceFiles);


            // This ordering is essential, we need to match the order in which Studio writes the files to replicate certain order-dependent behavior.
            foreach (var sourceFile in sourceFiles.OrderBy(file => file.GetMsAppFilename()))
            {
                yield return(sourceFile.ToMsAppFile());
            }

            var componentTemplates = new List <TemplateMetadataJson>();

            foreach (var template in app._templateStore.Contents.Where(template => template.Value.IsComponentTemplate ?? false))
            {
                if (((template.Value.CustomProperties?.Any() ?? false) || template.Value.ComponentAllowCustomization.HasValue) &&
                    !(template.Value.ComponentManifest?.IsDataComponent ?? false))
                {
                    componentTemplates.Add(template.Value.ToTemplateMetadata(app._entropy));
                }
            }

            app._templates = new TemplatesJson()
            {
                ComponentTemplates = componentTemplates.Any() ? componentTemplates.OrderBy(x => app._entropy.GetComponentOrder(x)).ToArray() : null,
                UsedTemplates      = app._templates.UsedTemplates.OrderBy(x => app._entropy.GetOrder(x)).ToArray()
            };

            yield return(ToFile(FileKind.Templates, app._templates));

            var componentsMetadata = new List <ComponentsMetadataJson.Entry>();
            var dctemplate         = new List <TemplateMetadataJson>();


            foreach (var componentTemplate in app._templateStore.Contents.Values.Where(state => state.ComponentManifest != null))
            {
                var manifest = componentTemplate.ComponentManifest;
                componentsMetadata.Add(new ComponentsMetadataJson.Entry
                {
                    Name          = manifest.Name,
                    TemplateName  = manifest.TemplateGuid,
                    ExtensionData = manifest.ExtensionData
                });

                if (manifest.IsDataComponent)
                {
                    var controlId = GetDataComponentDefinition(sourceFiles.Select(source => source.Value), manifest.TemplateGuid, errors).ControlUniqueId;

                    var template = new TemplateMetadataJson
                    {
                        Name              = manifest.TemplateGuid,
                        ComponentType     = ComponentType.DataComponent,
                        Version           = app._entropy.GetTemplateVersion(manifest.TemplateGuid),
                        IsComponentLocked = false,
                        ComponentChangedSinceFileImport = true,
                        ComponentAllowCustomization     = true,
                        CustomProperties           = componentTemplate.CustomProperties,
                        DataComponentDefinitionKey = manifest.DataComponentDefinitionKey
                    };

                    // Rehydrate fields.
                    template.DataComponentDefinitionKey.ControlUniqueId = controlId;

                    dctemplate.Add(template);
                }
            }

            if (componentsMetadata.Count > 0)
            {
                // If the components file is present, then write out all files.
                yield return(ToFile(FileKind.ComponentsMetadata, new ComponentsMetadataJson
                {
                    Components = componentsMetadata
                                 .OrderBy(x => app._entropy.GetOrder(x))
                                 .ToArray()
                }));
            }

            if (dctemplate.Count > 0)
            {
                yield return(ToFile(FileKind.DataComponentTemplates, new DataComponentTemplatesJson
                {
                    ComponentTemplates = dctemplate
                                         .OrderBy(x => app._entropy.GetOrder(x))
                                         .ToArray()
                }));
            }


            // Rehydrate the DataComponent DataSource file.
            {
                IEnumerable <DataComponentSourcesJson.Entry> ds =
                    from item in app.GetDataSources().SelectMany(x => x.Value).Where(x => x.IsDataComponent)
                    select item.DataComponentDetails;

                var dsArray = ds.ToArray();

                // backcompat-nit: if we have any DC, then always emit the DC Sources file, even if empty.
                // if (dcmetadataList.Count > 0)
                if (dctemplate.Count > 0 || dsArray.Length > 0)
                {
                    yield return(ToFile(FileKind.DataComponentSources, new DataComponentSourcesJson
                    {
                        DataSources = dsArray
                    }));
                }
            }

            if (app._appCheckerResultJson != null)
            {
                yield return(ToFile(FileKind.AppCheckerResult, app._appCheckerResultJson));
            }

            if (app._resourcesJson != null)
            {
                var resources = app._resourcesJson.JsonClone();
                foreach (var resource in resources.Resources)
                {
                    if (resource.ResourceKind == ResourceKind.LocalFile)
                    {
                        var rootPath = string.Empty;
                        if (app._entropy?.LocalResourceRootPaths.TryGetValue(resource.Name, out rootPath) ?? false)
                        {
                            resource.RootPath = rootPath;
                        }
                        else
                        {
                            resource.RootPath = string.Empty;
                        }
                    }
                }
                yield return(ToFile(FileKind.Resources, resources));
            }

            foreach (var assetFile in app._assetFiles)
            {
                yield return(new FileEntry {
                    Name = FilePath.RootedAt("Assets", assetFile.Value.Name), RawBytes = assetFile.Value.RawBytes
                });
            }
        }
Exemple #28
0
        private static void CreateControls(CanvasDocument app, IList <string> paFiles, Dictionary <string, ControlTemplate> templateDefaults, ErrorContainer errors)
        {
            foreach (var file in paFiles)
            {
                var fileEntry = new DirectoryReader.Entry(file);

                AddControl(app, file, false, fileEntry.GetContents(), errors);
            }
        }
Exemple #29
0
 public UniqueControlNameVistor(ErrorContainer errors)
 {
     _errors = errors;
 }
 public static void UnsupportedOperationError(this ErrorContainer errors, string message)
 {
     errors.AddError(ErrorCode.UnsupportedUseStudio, default(SourceLocation), $"Unsupported operation error. {message}");
 }