public void GiveCorrectParametersOnGoodInputs(string[] parameters) { var stringParams = _resolver.ResolveParams(parameters).StringParameters; Assert.AreEqual("testServer", stringParams[Identifiers.Server]); Assert.AreEqual("exampleClient", stringParams[Identifiers.Client]); }
public CommandResult Execute(IEnumerable <string> inputParams) { var outputMessages = new MessageLines(); var(error, stringParams, _) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var solutionName = stringParams[ParamId.SolutionName]; if (_fileSystem.GetInvalidFileNameChars().Any(solutionName.Contains)) { AppioLogger.Warn(LoggingText.InvalidSolutionName); outputMessages.Add(string.Format(OutputText.NewSlnCommandFailure, solutionName), string.Empty); return(new CommandResult(false, outputMessages)); } var solutionFilePath = $"{solutionName}{Constants.FileExtension.Appiosln}"; _fileSystem.CreateFile(solutionFilePath, _fileSystem.LoadTemplateFile(Resources.Resources.AppioSlnTemplateFileName)); AppioLogger.Info(string.Format(LoggingText.NewSlnCommandSuccess, solutionFilePath)); outputMessages.Add(string.Format(OutputText.NewSlnCommandSuccess, solutionName), string.Empty); return(new CommandResult(true, outputMessages)); }
public void GiveCorrectParametersOnGoodInputs(string[] parameters, string[] expectedResult) { var stringParameters = _resolver.ResolveParams(parameters).StringParameters; Assert.AreEqual(expectedResult[(int)Identifiers.Server], stringParameters[Identifiers.Server]); Assert.AreEqual(expectedResult[(int)Identifiers.Client], stringParameters[Identifiers.Client]); Assert.AreEqual(expectedResult[(int)Identifiers.Type], stringParameters[Identifiers.Type]); }
public void GiveCorrectParametersOnGoodInputs(string[] parameters, string[] expectedStrings, bool expectedBool) { var(_, stringParams, boolParams) = _resolver.ResolveParams(parameters); Assert.AreEqual(expectedStrings[(int)Identifiers.Client], stringParams[Identifiers.Client]); Assert.AreEqual(expectedStrings[(int)Identifiers.Server], stringParams[Identifiers.Server]); Assert.AreEqual(expectedBool, boolParams[Identifiers.Help]); }
public CommandResult Execute(IEnumerable <string> inputParams) { var outputMessages = new MessageLines(); var validationMessages = new SlnUtility.ResultMessages(); var(error, stringParams, _) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var solutionName = stringParams[ParamId.SolutionName]; // validate solution name if (!SlnUtility.ValidateSolution(ref validationMessages, solutionName, _fileSystem)) { AppioLogger.Warn(validationMessages.LoggerMessage); outputMessages.Add(validationMessages.OutputMessage, string.Empty); return(new CommandResult(false, outputMessages)); } // deserialize *.appiosln file var solutionFullName = solutionName + Constants.FileExtension.Appiosln; Solution appioSolution = SlnUtility.DeserializeFile <Solution>(solutionFullName, _fileSystem); if (appioSolution == null) { AppioLogger.Warn(LoggingText.SlnCouldntDeserliazeSln); outputMessages.Add(string.Format(OutputText.SlnCouldntDeserliazeSln, solutionName), string.Empty); return(new CommandResult(false, outputMessages)); } // build projects that are part of solution foreach (var project in appioSolution.Projects) { var commandResult = _subcommand.Execute(new string[] { project.Name }); if (!commandResult.Success) { return(commandResult); } } // exit method with success AppioLogger.Info(SuccessLoggerMessage); outputMessages.Add(string.Format(SuccessOutputMessage, solutionName), string.Empty); return(new CommandResult(true, outputMessages)); }
protected bool ExecuteCommon(IEnumerable <string> inputParams) { var resolver = new ParameterResolver <ParamId>(Constants.CommandName.Reference + " " + Name, new [] { new StringParameterSpecification <ParamId> { Identifier = ParamId.ClientName, Short = Constants.ReferenceCommandOptions.Client, Verbose = Constants.ReferenceCommandOptions.VerboseClient }, new StringParameterSpecification <ParamId> { Identifier = ParamId.ServerName, Short = Constants.ReferenceCommandOptions.Server, Verbose = Constants.ReferenceCommandOptions.VerboseServer } }); var(error, stringParams, _) = resolver.ResolveParams(inputParams); if (error != null) { _outputMessages = new MessageLines { { error, string.Empty } }; return(false); } _serverName = stringParams[ParamId.ServerName]; _clientName = stringParams[ParamId.ClientName]; _outputMessages = new MessageLines(); // check if client appioproj file exists _clientFullName = _fileSystem.CombinePaths(_clientName, _clientName + Constants.FileExtension.Appioproject); if (!_fileSystem.FileExists(_clientFullName)) { AppioLogger.Warn(LoggingText.ReferenceClientAppioprojFileNotFound); _outputMessages.Add(string.Format(OutputText.ReferenceClientAppioprojFileNotFound, _clientFullName), string.Empty); return(false); } // exit with success return(true); }
public CommandResult Execute(IEnumerable <string> inputParams) { var(error, stringParams, _) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var projectName = stringParams[ParamId.AppName]; var outputMessages = new MessageLines(); // deserialize appioproj file var appioprojFilePath = _fileSystem.CombinePaths(projectName, projectName + Constants.FileExtension.Appioproject); var opcuaappData = Deserialize.Opcuaapp(appioprojFilePath, _fileSystem); if (opcuaappData == null) { AppioLogger.Warn(LoggingText.GenerateInformationModelFailureCouldntDeserliazeOpcuaapp); outputMessages.Add(string.Format(OutputText.GenerateInformationModelFailureCouldntDeserliazeOpcuaapp, projectName, appioprojFilePath), string.Empty); return(new CommandResult(false, outputMessages)); } if ((opcuaappData as IOpcuaClientApp)?.Type == Constants.ApplicationType.Client) { AppioLogger.Warn(LoggingText.GenerateInformationModelFailuteOpcuaappIsAClient); outputMessages.Add(string.Format(OutputText.GenerateInformationModelFailuteOpcuaappIsAClient, projectName), string.Empty); return(new CommandResult(false, outputMessages)); } var opcuaappModels = (opcuaappData as IOpcuaServerApp)?.Models; // check if models are valid if (!ValidateModels(opcuaappModels)) { AppioLogger.Warn(LoggingText.GenerateInformationModelInvalidModelsList); outputMessages.Add(string.Format(OutputText.GenerateInformationModelInvalidModelsList, projectName), string.Empty); return(new CommandResult(false, outputMessages)); } // check if there is any circular dependency between models if (SearchForCircularDependencies(opcuaappModels)) { AppioLogger.Warn(LoggingText.GenerateInformationModelCircularDependency); outputMessages.Add(string.Format(OutputText.GenerateInformationModelCircularDependency, projectName), string.Empty); return(new CommandResult(false, outputMessages)); } // sort models SortModels(opcuaappModels); // generate models foreach (var model in opcuaappModels) { var requiredModelData = GetListOfRequiredModels(opcuaappModels, model); if (!_nodesetGenerator.GenerateNodesetSourceCodeFiles(projectName, model, requiredModelData)) { outputMessages.Add(_nodesetGenerator.GetOutputMessage(), string.Empty); return(new CommandResult(false, outputMessages)); } } // add noodeset variables CreateNamespaceVariables(projectName, opcuaappModels); // exit method with positive result AppioLogger.Info(LoggingText.GenerateInformationModelSuccess); outputMessages.Add(string.Format(OutputText.GenerateInformationModelSuccess, projectName), string.Empty); return(new CommandResult(true, outputMessages)); }
public CommandResult Execute(IEnumerable <string> inputParams) { var(error, stringParams, options) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var project = stringParams[ParamId.Project]; var certificate = stringParams[ParamId.Certificate]; var key = stringParams[ParamId.Key]; var forServer = options[ParamId.ForServer]; var forClient = options[ParamId.ForClient]; string GetExtension(string str) => str.Substring(Math.Max(0, str.Length - 3)); var isCertPEM = GetExtension(certificate) != "der"; var isKeyPEM = GetExtension(key) != "der"; if (forClient && forServer) { return(FailureWrongClientServer()); } var appioprojContent = _fileSystem.ReadFile(_fileSystem.CombinePaths(project, project + Constants.FileExtension.Appioproject)); string appType; using (var reader = new StreamReader(appioprojContent, Encoding.ASCII)) { appType = (string)JObject.Parse(reader.ReadToEnd())["type"]; } if (appType != Constants.ApplicationType.ClientServer && (forClient || forServer)) { return(FailureWrongClientServer()); } var keyTarget = Constants.FileName.PrivateKeyDER; var certTarget = Constants.FileName.Certificate; if (appType == Constants.ApplicationType.ClientServer) { string prefix; if (forServer) { prefix = Constants.FileName.ServerCryptoPrefix; } else if (forClient) { prefix = Constants.FileName.ClientCryptoPrefix; } else { return(FailureMissingClientServer()); } certTarget = prefix + "_" + Constants.FileName.Certificate; keyTarget = prefix + "_" + Constants.FileName.PrivateKeyDER; } var certificatesFolder = _fileSystem.CombinePaths(project, Constants.DirectoryName.Certificates); _fileSystem.CreateDirectory(certificatesFolder); Import(certificatesFolder, isKeyPEM, Constants.ExternalExecutableArguments.OpenSSLConvertKeyFromPEM, key, keyTarget); Import(certificatesFolder, isCertPEM, Constants.ExternalExecutableArguments.OpenSSLConvertCertificateFromPEM, certificate, certTarget); AppioLogger.Info(string.Format(LoggingText.ImportCertificateSuccess, certificate, key)); return(new CommandResult(true, new MessageLines { { OutputText.ImportCertificateCommandSuccess, string.Empty } })); }
public CommandResult Execute(IEnumerable <string> inputParams) { var outputMessages = new MessageLines(); var(error, stringParams, _) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var solutionName = stringParams[ParamId.SolutionName]; var projectName = stringParams[ParamId.ProjectName]; // check if solution file is existing var solutionFullName = _fileSystem.CombinePaths(solutionName + Constants.FileExtension.Appiosln); if (string.IsNullOrEmpty(solutionName) || !_fileSystem.FileExists(solutionFullName)) { AppioLogger.Warn(LoggingText.SlnAppioslnFileNotFound); outputMessages.Add(string.Format(OutputText.SlnAppioslnNotFound, solutionFullName), string.Empty); return(new CommandResult(false, outputMessages)); } // deserialise solution file Solution appioSolution = SlnUtility.DeserializeFile <Solution>(solutionFullName, _fileSystem); if (appioSolution == null) { AppioLogger.Warn(LoggingText.SlnCouldntDeserliazeSln); outputMessages.Add(string.Format(OutputText.SlnCouldntDeserliazeSln, solutionName), string.Empty); return(new CommandResult(false, outputMessages)); } // check if the project to remove is part of the solution var appioproj = appioSolution.Projects.SingleOrDefault(x => x.Name == projectName); if (appioproj != null) { // remove opcuaapp from sln appioSolution.Projects.Remove(appioproj); // serialize and write sln var slnNewContent = JsonConvert.SerializeObject(appioSolution, Formatting.Indented); _fileSystem.WriteFile(solutionFullName, new List <string> { slnNewContent }); } else { AppioLogger.Warn(LoggingText.SlnRemoveOpcuaappIsNotInSln); outputMessages.Add(string.Format(OutputText.SlnRemoveOpcuaappIsNotInSln, projectName, solutionName), string.Empty); return(new CommandResult(false, outputMessages)); } // exit method with success AppioLogger.Info(LoggingText.SlnRemoveSuccess); outputMessages.Add(string.Format(OutputText.SlnRemoveSuccess, projectName, solutionName), string.Empty); return(new CommandResult(true, outputMessages)); }
public CommandResult Execute(IEnumerable <string> inputParams) { var outputMessages = new MessageLines(); var validationMessages = new SlnUtility.ResultMessages(); var(error, stringParams, _) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var solutionName = stringParams[ParamId.SolutionName]; var projectName = stringParams[ParamId.ProjectName]; // validate solution name if (!SlnUtility.ValidateSolution(ref validationMessages, solutionName, _fileSystem)) { AppioLogger.Warn(validationMessages.LoggerMessage); outputMessages.Add(validationMessages.OutputMessage, string.Empty); return(new CommandResult(false, outputMessages)); } // check if *.appioproj file exists var appioprojFilePath = _fileSystem.CombinePaths(projectName, projectName + Constants.FileExtension.Appioproject); if (string.IsNullOrEmpty(projectName) || !_fileSystem.FileExists(appioprojFilePath)) { AppioLogger.Warn(LoggingText.SlnAddAppioprojFileNotFound); outputMessages.Add(string.Format(OutputText.SlnAddOpcuaappNotFound, appioprojFilePath), string.Empty); return(new CommandResult(false, outputMessages)); } // deserialize *.appiosln file var solutionFullName = solutionName + Constants.FileExtension.Appiosln; Solution appioSolution = SlnUtility.DeserializeFile <Solution>(solutionFullName, _fileSystem); if (appioSolution == null) { AppioLogger.Warn(LoggingText.SlnCouldntDeserliazeSln); outputMessages.Add(string.Format(OutputText.SlnCouldntDeserliazeSln, solutionName), string.Empty); return(new CommandResult(false, outputMessages)); } // deserialize *.appioproj file OpcuaappReference appioproj = SlnUtility.DeserializeFile <OpcuaappReference>(appioprojFilePath, _fileSystem); if (appioproj == null) { AppioLogger.Warn(LoggingText.SlnAddCouldntDeserliazeOpcuaapp); outputMessages.Add(string.Format(OutputText.SlnAddCouldntDeserliazeOpcuaapp, projectName), string.Empty); return(new CommandResult(false, outputMessages)); } // check if sln does not contain opcuaapp yet if (!appioSolution.Projects.Any(x => x.Name == appioproj.Name)) { // add opcuaapp to sln appioproj.Path = appioprojFilePath; appioSolution.Projects.Add(appioproj); // serialize and write sln var slnNewContent = JsonConvert.SerializeObject(appioSolution, Formatting.Indented); _fileSystem.WriteFile(solutionFullName, new List <string> { slnNewContent }); } else { AppioLogger.Info(LoggingText.SlnAddContainsOpcuaapp); outputMessages.Add(string.Format(OutputText.SlnAddContainsOpcuaapp, solutionName, projectName), string.Empty); return(new CommandResult(false, outputMessages)); } // exit method with success AppioLogger.Info(LoggingText.SlnAddSuccess); outputMessages.Add(string.Format(OutputText.SlnAddSuccess, projectName, solutionName), string.Empty); return(new CommandResult(true, outputMessages)); }
public CommandResult Execute(IEnumerable <string> inputParams) { var(error, parameters, options) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var opcuaAppName = parameters[ParamId.AppName]; var modelPath = parameters[ParamId.ModelPath]; var typesPath = parameters[ParamId.TypesPath]; // opcuaapp name validation if (!ValidateOpcuaAppName(opcuaAppName)) { return(new CommandResult(false, _outputMessages)); } var appioprojFilePath = _fileSystem.CombinePaths(opcuaAppName, opcuaAppName + Constants.FileExtension.Appioproject); var modelData = new ModelData(); if (options[ParamId.Sample]) { var modelsDir = _fileSystem.CombinePaths(opcuaAppName, Constants.DirectoryName.Models); var nodesetContent = _fileSystem.LoadTemplateFile(Resources.Resources.SampleInformationModelFileName); var nodesetFilePath = _fileSystem.CombinePaths(modelsDir, Constants.FileName.SampleInformationModelFile); _fileSystem.CreateFile(nodesetFilePath, nodesetContent); var typesContent = _fileSystem.LoadTemplateFile(Resources.Resources.SampleInformationModelTypesFileName); var typesFilePath = _fileSystem.CombinePaths(modelsDir, Constants.FileName.SampleInformationModelTypesFile); _fileSystem.CreateFile(typesFilePath, typesContent); if (!UpdateAppioProjFile(appioprojFilePath, modelData, opcuaAppName, Constants.FileName.SampleInformationModelFile, nodesetFilePath, Constants.FileName.SampleInformationModelTypesFile)) { return(new CommandResult(false, _outputMessages)); } _outputMessages.Add(string.Format(OutputText.ImportSampleInformationModelSuccess, Constants.FileName.SampleInformationModelFile), string.Empty); AppioLogger.Info(string.Format(LoggingText.ImportInforamtionModelCommandSuccess, Constants.FileName.SampleInformationModelFile)); return(new CommandResult(true, _outputMessages)); } // nodeset validation if (!ValidateModel(modelPath)) { return(new CommandResult(false, _outputMessages)); } var modelFileName = _fileSystem.GetFileName(modelPath); // types validation var typesFileName = string.Empty; if (typesPath != string.Empty && !ValidateTypes(out typesFileName, typesPath)) { return(new CommandResult(false, _outputMessages)); } if (!UpdateAppioProjFile(appioprojFilePath, modelData, opcuaAppName, modelFileName, modelPath, typesFileName)) { return(new CommandResult(false, _outputMessages)); } // copy model file var modelsDirectory = _fileSystem.CombinePaths(opcuaAppName, Constants.DirectoryName.Models); var targetModelFilePath = _fileSystem.CombinePaths(modelsDirectory, modelFileName); _fileSystem.CopyFile(modelPath, targetModelFilePath); // copy types file if (typesPath != string.Empty) { var targetTypesFilePath = _fileSystem.CombinePaths(modelsDirectory, typesFileName); _fileSystem.CopyFile(typesPath, targetTypesFilePath); } // exit with success AppioLogger.Info(string.Format(LoggingText.ImportInforamtionModelCommandSuccess, modelPath)); _outputMessages.Add(string.Format(OutputText.ImportInformationModelCommandSuccess, modelPath), string.Empty); return(new CommandResult(true, _outputMessages)); }
public static void AssertFailWith <T>(ParameterResolver <T> resolver, string[] parameters, string errorMessage) where T : struct, IConvertible { Assert.AreEqual(errorMessage, resolver.ResolveParams(parameters).ErrorMessage); }
public CommandResult Execute(IEnumerable <string> inputParams) { var outputMessages = new MessageLines(); var messages = new Messages(); var(error, stringParams, options) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var opcuaAppName = stringParams[ParamId.OpcuaAppName]; var applicationType = stringParams[ParamId.ApplicationType]; var url = stringParams[ParamId.Url]; var port = stringParams[ParamId.Port]; // validate opcuaapp name if (_fileSystem.GetInvalidFileNameChars().Any(opcuaAppName.Contains) || _fileSystem.GetInvalidPathChars().Any(opcuaAppName.Contains)) { AppioLogger.Warn(LoggingText.InvalidOpcuaappName); outputMessages.Add(string.Format(OutputText.NewOpcuaappCommandFailureInvalidProjectName, opcuaAppName), string.Empty); return(new CommandResult(false, outputMessages)); } // validate opcuaapp type if (!ValidateApplicationType(ref messages, applicationType, url, port)) { AppioLogger.Warn(messages.loggerMessage); outputMessages.Add(messages.outputMessage, string.Empty); return(new CommandResult(false, outputMessages)); } // combine project file paths var projectFilePath = _fileSystem.CombinePaths(opcuaAppName, $"{opcuaAppName}{Constants.FileExtension.Appioproject}"); var sourceDirectory = _fileSystem.CombinePaths(opcuaAppName, Constants.DirectoryName.SourceCode); var mesonFilePath = _fileSystem.CombinePaths(opcuaAppName, Constants.FileName.SourceCode_meson_build); // create project directories _fileSystem.CreateDirectory(opcuaAppName); _fileSystem.CreateDirectory(sourceDirectory); IOpcuaapp opcuaapp = null; // deploy files for opcuaapp Client if (applicationType == Constants.ApplicationType.Client) { opcuaapp = new OpcuaClientApp(opcuaAppName); _fileSystem.CreateFile(mesonFilePath, _fileSystem.LoadTemplateFile( Resources.Resources.AppioOpcuaAppTemplateFileName_meson_ClientType_build)); DeployTemplateOpcuaClientSourceFiles(sourceDirectory); } // deploy files for opcuaapp Server else if (applicationType == Constants.ApplicationType.Server) { opcuaapp = new OpcuaServerApp(opcuaAppName, url, port); _fileSystem.CreateFile(mesonFilePath, _fileSystem.LoadTemplateFile( Resources.Resources.AppioOpcuaAppTemplateFileName_meson_ServerType_build)); CreateModelsDirectory(opcuaAppName); DeployTemplateOpcuaServerSourceFiles(sourceDirectory); } // deploy files for opcuaapp ClientServer else if (applicationType == Constants.ApplicationType.ClientServer) { opcuaapp = new OpcuaClientServerApp(opcuaAppName, url, port); _fileSystem.CreateFile(mesonFilePath, _fileSystem.LoadTemplateFile(Resources.Resources .AppioOpcuaAppTemplateFileName_meson_ClientServerType_build)); CreateModelsDirectory(opcuaAppName); DeployTemplateOpcuaClientSourceFiles(sourceDirectory); DeployTemplateOpcuaServerSourceFiles(sourceDirectory); } if (!options[ParamId.NoCert]) { if (applicationType == Constants.ApplicationType.ClientServer) { _certificateGenerator.Generate(opcuaAppName, Constants.FileName.ClientCryptoPrefix); _certificateGenerator.Generate(opcuaAppName, Constants.FileName.ServerCryptoPrefix); } else { _certificateGenerator.Generate(opcuaAppName); } } // create *.appioproj file var opcuaappAsJson = JsonConvert.SerializeObject(opcuaapp, Formatting.Indented); _fileSystem.CreateFile(projectFilePath, opcuaappAsJson); AppioLogger.Info(string.Format(LoggingText.NewOpcuaappCommandSuccess, opcuaAppName)); outputMessages.Add(string.Format(OutputText.NewOpcuaappCommandSuccess, opcuaAppName), string.Empty); return(new CommandResult(true, outputMessages)); }
public CommandResult Execute(IEnumerable <string> inputParams) { var(error, stringParams, _) = _resolver.ResolveParams(inputParams); if (error != null) { return(new CommandResult(false, new MessageLines { { error, string.Empty } })); } var appName = stringParams[ParamId.AppName]; uint keySize, days; try { keySize = uint.Parse(stringParams[ParamId.KeySize]); days = uint.Parse(stringParams[ParamId.Days]); } catch (FormatException) { AppioLogger.Warn(LoggingText.GenerateCertificateFailureNotParsable); return(new CommandResult(false, new MessageLines { { OutputText.GenerateCertificateCommandFailureNotParsable, string.Empty } })); } Stream appioprojContent; try { appioprojContent = _fileSystem.ReadFile(_fileSystem.CombinePaths(appName, appName + Constants.FileExtension.Appioproject)); } catch (Exception) { AppioLogger.Warn(string.Format(LoggingText.GenerateCertificateFailureNotFound, appName)); return(new CommandResult(false, new MessageLines { { string.Format(OutputText.GenerateCertificateCommandFailureNotFound, appName), string.Empty } })); } var reader = new StreamReader(appioprojContent, Encoding.ASCII); var appType = (string)JObject.Parse(reader.ReadToEnd())["type"]; if (appType == Constants.ApplicationType.ClientServer) { _certificateGenerator.Generate(appName, Constants.FileName.ClientCryptoPrefix, keySize, days, stringParams[ParamId.Org]); _certificateGenerator.Generate(appName, Constants.FileName.ServerCryptoPrefix, keySize, days, stringParams[ParamId.Org]); } else { _certificateGenerator.Generate(appName, string.Empty, keySize, days, stringParams[ParamId.Org]); } AppioLogger.Info(LoggingText.GenerateCertificateSuccess); return(new CommandResult(true, new MessageLines { { string.Format(OutputText.GenerateCertificateCommandSuccess, appName), string.Empty } })); }