// Called after loading. This will check internal fields and fill in consistency data. internal void OnLoadComplete(ErrorContainer errors) { // Do integrity checks. if (_header == null) { errors.FormatNotSupported("Missing header file"); throw new DocumentException(); } if (_properties == null) { errors.FormatNotSupported("Missing properties file"); throw new DocumentException(); } // Will visit all controls and add errors var uniqueVisitor = new UniqueControlNameVistor(errors); foreach (var control in _screens.Values.Concat(_components.Values)) { uniqueVisitor.Visit(control); } // Integrity checks. foreach (var kv in _connections.NullOk()) { var connection = kv.Value; if (kv.Key != connection.id) { errors.FormatNotSupported($"Document consistency error. Connection id mismatch"); throw new DocumentException(); } } }
// Given an msapp (original source of truth), stress test the conversions public static bool StressTest(string pathToMsApp) { try { using (var temp1 = new TempFile()) { string outFile = temp1.FullPath; var log = TextWriter.Null; // MsApp --> Model CanvasDocument msapp; ErrorContainer errors = new ErrorContainer(); try { msapp = MsAppSerializer.Load(pathToMsApp, errors); // read errors.ThrowOnErrors(); } catch (NotSupportedException) { errors.FormatNotSupported($"Too old: {pathToMsApp}"); return(false); } // Model --> MsApp errors = msapp.SaveToMsApp(outFile); errors.ThrowOnErrors(); var ok = MsAppTest.Compare(pathToMsApp, outFile, log); if (!ok) { return(false); } // Model --> Source using (var tempDir = new TempDir()) { string outSrcDir = tempDir.Dir; errors = msapp.SaveToSources(outSrcDir); errors.ThrowOnErrors(); // Source --> Model (var msapp2, var errors2) = CanvasDocument.LoadFromSources(outSrcDir); errors2.ThrowOnErrors(); errors2 = msapp2.SaveToMsApp(outFile); // Write out .pa files. errors2.ThrowOnErrors(); var ok2 = MsAppTest.Compare(pathToMsApp, outFile, log); return(ok2); } } } catch (Exception e) { Console.WriteLine(e.ToString()); return(false); } }
// Called after loading. This will check internal fields and fill in consistency data. internal void OnLoadComplete(ErrorContainer errors) { // Do integrity checks. if (_header == null) { errors.FormatNotSupported("Missing header file"); throw new DocumentException(); } if (_properties == null) { errors.FormatNotSupported("Missing properties file"); throw new DocumentException(); } // Will visit all controls and add errors var uniqueVisitor = new UniqueControlNameVistor(errors); foreach (var control in _screens.Values.Concat(_components.Values)) { uniqueVisitor.Visit(control); } // Integrity checks. // Make sure every connection has a corresponding data source. foreach (var kv in _connections.NullOk()) { var connection = kv.Value; if (kv.Key != connection.id) { errors.FormatNotSupported($"Document consistency error. Connection id mismatch"); throw new DocumentException(); } foreach (var dataSourceName in connection.dataSources) { var ds = _dataSources.SelectMany(x => x.Value).Where(x => x.Name == dataSourceName).FirstOrDefault(); if (ds == null) { errors.ValidationError($"Connection '{dataSourceName}' does not have a corresponding data source."); throw new DocumentException(); } } } }
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 .pa.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, "*.pa.yaml", searchSubdirectories: false)) { AddControl(app, file._relativeName, false, file.GetContents(), errors); } foreach (var file in EnumerateComponentDirs(directory, "*.pa.yaml")) { AddControl(app, file._relativeName, true, file.GetContents(), errors); } foreach (var file in directory.EnumerateFiles(TestDir, "*.pa.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); } }
// Given an msapp (original source of truth), stress test the conversions public static bool StressTest(string pathToMsApp) { try { using (var temp1 = new TempFile()) { string outFile = temp1.FullPath; var log = TextWriter.Null; // MsApp --> Model CanvasDocument msapp; ErrorContainer errors = new ErrorContainer(); try { using (var stream = new FileStream(pathToMsApp, FileMode.Open)) { msapp = MsAppSerializer.Load(stream, errors); } errors.ThrowOnErrors(); } catch (NotSupportedException) { errors.FormatNotSupported($"Too old: {pathToMsApp}"); return(false); } // Model --> MsApp errors = msapp.SaveToMsApp(outFile); errors.ThrowOnErrors(); var ok = MsAppTest.Compare(pathToMsApp, outFile, log); if (!ok) { return(false); } // Model --> Source using (var tempDir = new TempDir()) { string outSrcDir = tempDir.Dir; errors = msapp.SaveToSources(outSrcDir, verifyOriginalPath: pathToMsApp); errors.ThrowOnErrors(); return(true); } } } catch (Exception e) { Console.WriteLine(e.ToString()); return(false); } }
// 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(); // 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; } } 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); }
// Given an msapp (original source of truth), stress test the conversions public static bool StressTest(string pathToMsApp) { try { using (var temp1 = new TempFile()) { string outFile = temp1.FullPath; var log = TextWriter.Null; // MsApp --> Model CanvasDocument msapp; ErrorContainer errors = new ErrorContainer(); try { using (var stream = new FileStream(pathToMsApp, FileMode.Open)) { msapp = MsAppSerializer.Load(stream, errors); } errors.Write(log); errors.ThrowOnErrors(); // We can still get warnings here. Commonly: // - PA2001, checksum mismatch // - PA2999, colliding asset names } catch (NotSupportedException) { errors.FormatNotSupported($"Too old: {pathToMsApp}"); return(false); } // Model --> MsApp errors = msapp.SaveToMsApp(outFile); errors.ThrowOnErrors(); var ok = MsAppTest.Compare(pathToMsApp, outFile, log); if (!ok) { return(false); } // Model --> Source using (var tempDir = new TempDir()) { string outSrcDir = tempDir.Dir; errors = msapp.SaveToSources(outSrcDir, verifyOriginalPath: pathToMsApp); errors.ThrowOnErrors(); } } // end using if (!MsAppTest.TestClone(pathToMsApp)) { return(false); } if (!MsAppTest.DiffStressTest(pathToMsApp)) { return(false); } } catch (Exception e) { Console.WriteLine(e.ToString()); return(false); } return(true); }
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 == "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); }
// Full fidelity read-write public static CanvasDocument LoadFromSource(string directory2, ErrorContainer errors) { if (File.Exists(directory2)) { if (directory2.EndsWith(".msapp", StringComparison.OrdinalIgnoreCase)) { errors.BadParameter($"Must point to a source directory, not an msapp file ({directory2}"); } } Utilities.VerifyDirectoryExists(errors, directory2); if (errors.HasErrors) { return(null); } 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(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; } } // The resource entries for sample data is sharded into individual json files. // Add each of these entries back into Resrouces.json var resources = new List <ResourceJson>(); app._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[0] }; foreach (var file in dir.EnumerateFiles(AssetsDir, "*", false)) { var fileEntry = file.ToFileEntry(); if (fileEntry.Name.GetExtension() == ".json") { // If its a json file then this must be one of the sharded files from Resources.json resources.Add(file.ToObject <ResourceJson>()); } } // Add the resources from sharded files to _resourcesJson.Resources if (resources.Count > 0) { app._resourcesJson.Resources = resources.ToArray(); } // We have processed all the json files in Assets directory, now interate through all tge files to add the asset files. foreach (var file in dir.EnumerateFiles(AssetsDir)) { // Skip adding the json files which were created to contain the information for duplicate asset files. // The name of the such json files is of the format - <assetFileName>.<assetFileExtension>.json (eg. close_1.jpg.json) var fileName = file._relativeName; var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); // Check if the original extension was .json and the remaining file name has still got an extension, // Then this is an additional file that was created to contain information for duplicate assets. if (Path.HasExtension(fileNameWithoutExtension) && Path.GetExtension(fileName) == ".json") { var localAssetInfoJson = file.ToObject <LocalAssetInfoJson>(); app._localAssetInfoJson.Add(localAssetInfoJson.NewFileName, localAssetInfoJson); } // Add non json files to _assetFiles else if (Path.GetExtension(fileName) != ".json") { app.AddAssetFile(file.ToFileEntry()); } } app.GetLogoFile(); // Add the entries for local assets back to resrouces.json TranformResourceJson.AddLocalAssetEntriesToResourceJson(app); 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' 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); }
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. ControlTreeState editorState = file.ToObject <ControlTreeState>(); if (editorState.ControlStates == null) { ApplyV24BackCompat(editorState, file); } foreach (var control in editorState.ControlStates) { control.Value.TopParentName = editorState.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 EnumerateComponentDirs(directory, "*.fx.yaml")) { AddControl(app, file._relativeName, true, file.GetContents(), errors); } foreach (var file in EnumerateComponentDirs(directory, "*.json")) { var componentTemplate = file.ToObject <CombinedTemplateState>(); app._templateStore.AddTemplate(componentTemplate.ComponentManifest.Name, componentTemplate); } foreach (var file in directory.EnumerateFiles(CodeDir, "*.fx.yaml", searchSubdirectories: false)) { AddControl(app, file._relativeName, false, file.GetContents(), errors); } // When loading TestSuites sharded files, add them within the top parent AppTest control (i.e. Test_7F478737223C4B69) // Make sure to load the the Test_7F478737223C4B69.fx.yaml file first to add the top parent control. var shardedTestSuites = new List <DirectoryReader.Entry>(); foreach (var file in directory.EnumerateFiles(TestDir, "*.fx.yaml")) { if (file.Kind == FileKind.AppTestParentControl) { AddControl(app, file._relativeName, false, file.GetContents(), errors); } else { shardedTestSuites.Add(file); } } shardedTestSuites.ForEach(x => AddControl(app, x._relativeName, false, x.GetContents(), errors)); }