DscTestJsonResource.cpp (7096B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "DscTestJsonResource.h" 5 #include "DscComposableObject.h" 6 #include <AppInstallerRuntime.h> 7 8 using namespace AppInstaller::Utility::literals; 9 10 namespace AppInstaller::CLI 11 { 12 namespace 13 { 14 WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(PropertyProperty, std::string, Property, "property", DscComposablePropertyFlag::Required | DscComposablePropertyFlag::CopyToOutput, "The JSON property name."_lis); 15 WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(ValueProperty, Json::Value, Value, "value", "The value for the JSON property."_lis); 16 17 using TestJsonObject = DscComposableObject<StandardExistProperty, PropertyProperty, ValueProperty>; 18 19 struct TestJsonFunctionData 20 { 21 TestJsonFunctionData() 22 { 23 InitializeFileData(); 24 } 25 26 TestJsonFunctionData(const std::optional<Json::Value>& json) : Input(json), Output(Input.CopyForOutput()) 27 { 28 InitializeFileData(); 29 } 30 31 TestJsonObject Input; 32 TestJsonObject Output; 33 std::filesystem::path FilePath; 34 Json::Value RootValue; 35 36 static std::filesystem::path GetFilePath() 37 { 38 std::filesystem::path result = Runtime::GetPathTo(Runtime::PathName::LocalState); 39 result /= "test-json-file.json"; 40 return result; 41 } 42 43 // Fills the Output object with the current state 44 void Get() 45 { 46 const std::string& propertyName = Input.Property().value(); 47 const Json::Value* propertyValue = RootValue.find(propertyName.data(), propertyName.data() + propertyName.length()); 48 49 if (propertyValue) 50 { 51 Output.Exist(true); 52 53 Output.Value(*propertyValue); 54 } 55 else 56 { 57 Output.Exist(false); 58 } 59 } 60 61 private: 62 void InitializeFileData() 63 { 64 FilePath = GetFilePath(); 65 RootValue = GetJsonFromFile(); 66 } 67 68 Json::Value GetJsonFromFile() const 69 { 70 Json::Value result; 71 Json::CharReaderBuilder builder; 72 Json::String errors; 73 74 std::ifstream stream{ FilePath, std::ios::binary }; 75 76 if (stream) 77 { 78 if (!Json::parseFromStream(builder, stream, &result, &errors)) 79 { 80 AICLI_LOG(CLI, Warning, << "Failed to read test JSON file: " << errors); 81 result = Json::Value{}; 82 } 83 } 84 else 85 { 86 AICLI_LOG(CLI, Warning, << "Couldn't open test JSON file: " << FilePath); 87 } 88 89 return result; 90 } 91 }; 92 } 93 94 DscTestJsonResource::DscTestJsonResource(std::string_view parent) : 95 DscCommandBase(parent, "test-json", DscResourceKind::Resource, 96 DscFunctions::Get | DscFunctions::Set | DscFunctions::Export | DscFunctions::Schema, 97 DscFunctionModifiers::HandlesExist | DscFunctionModifiers::ReturnsState) 98 { 99 } 100 101 std::vector<Argument> DscTestJsonResource::GetArguments() const 102 { 103 auto result = DscCommandBase::GetArguments(); 104 result.emplace_back(Execution::Args::Type::DscResourceFunctionDelete, Resource::String::DscResourceFunctionDescriptionDelete, ArgumentType::Flag); 105 return result; 106 } 107 108 Resource::LocString DscTestJsonResource::ShortDescription() const 109 { 110 return "[TEST] JSON content resource"_lis; 111 } 112 113 Resource::LocString DscTestJsonResource::LongDescription() const 114 { 115 return "[TEST] This resource is only available for tests. It provides JSON content configuration of a well known file."_lis; 116 } 117 118 void DscTestJsonResource::ExecuteInternal(Execution::Context& context) const 119 { 120 if (context.Args.Contains(Execution::Args::Type::DscResourceFunctionDelete)) 121 { 122 std::filesystem::remove_all(TestJsonFunctionData::GetFilePath()); 123 return; 124 } 125 126 DscCommandBase::ExecuteInternal(context); 127 } 128 129 std::string DscTestJsonResource::ResourceType() const 130 { 131 return "TestJSON"; 132 } 133 134 void DscTestJsonResource::ResourceFunctionGet(Execution::Context& context) const 135 { 136 if (auto json = GetJsonFromInput(context)) 137 { 138 TestJsonFunctionData data{ json }; 139 140 data.Get(); 141 142 WriteJsonOutputLine(context, data.Output.ToJson()); 143 } 144 } 145 146 void DscTestJsonResource::ResourceFunctionSet(Execution::Context& context) const 147 { 148 if (auto json = GetJsonFromInput(context)) 149 { 150 TestJsonFunctionData data{ json }; 151 152 data.Get(); 153 154 if (data.RootValue.isNull()) 155 { 156 data.RootValue = Json::Value{ Json::objectValue }; 157 } 158 159 if (data.Input.ShouldExist()) 160 { 161 data.RootValue[data.Input.Property().value()] = data.Input.Value().value_or(Json::Value{ Json::nullValue }); 162 data.Output.Exist(true); 163 data.Output.Value(data.RootValue[data.Input.Property().value()]); 164 } 165 else if (data.Output.Exist().value()) 166 { 167 data.RootValue.removeMember(data.Input.Property().value()); 168 data.Output.Exist(false); 169 } 170 171 std::ofstream stream{ data.FilePath, std::ios::binary }; 172 173 Json::StreamWriterBuilder writerBuilder; 174 writerBuilder.settings_["indentation"] = " "; 175 176 stream << Json::writeString(writerBuilder, data.RootValue); 177 178 WriteJsonOutputLine(context, data.Output.ToJson()); 179 } 180 } 181 182 void DscTestJsonResource::ResourceFunctionExport(Execution::Context& context) const 183 { 184 TestJsonFunctionData data; 185 186 if (data.RootValue.isObject()) 187 { 188 for (const auto& member : data.RootValue.getMemberNames()) 189 { 190 const Json::Value* memberValue = data.RootValue.find(member.data(), member.data() + member.length()); 191 192 if (memberValue) 193 { 194 TestJsonObject output; 195 output.Property(member); 196 output.Value(*memberValue); 197 198 WriteJsonOutputLine(context, output.ToJson()); 199 } 200 } 201 } 202 } 203 204 void DscTestJsonResource::ResourceFunctionSchema(Execution::Context& context) const 205 { 206 WriteJsonOutputLine(context, TestJsonObject::Schema(ResourceType())); 207 } 208 }