Skip to content

Commit f7ec554

Browse files
committed
WIP package resource property definition
1 parent 511d4f9 commit f7ec554

File tree

9 files changed

+312
-22
lines changed

9 files changed

+312
-22
lines changed

src/AppInstallerCLICore/AppInstallerCLICore.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@
305305
<ClInclude Include="Commands\DscCommand.h" />
306306
<ClInclude Include="Commands\DscCommandBase.h" />
307307
<ClInclude Include="Commands\DscComposableObject.h" />
308+
<ClInclude Include="Commands\DscPackageResource.h" />
308309
<ClInclude Include="Commands\DscTestFileResource.h" />
309310
<ClInclude Include="Commands\DscTestJsonResource.h" />
310311
<ClInclude Include="Commands\ErrorCommand.h" />
@@ -391,6 +392,7 @@
391392
<ClCompile Include="Commands\DscCommand.cpp" />
392393
<ClCompile Include="Commands\DscCommandBase.cpp" />
393394
<ClCompile Include="Commands\DscComposableObject.cpp" />
395+
<ClCompile Include="Commands\DscPackageResource.cpp" />
394396
<ClCompile Include="Commands\DscTestFileResource.cpp" />
395397
<ClCompile Include="Commands\DscTestJsonResource.cpp" />
396398
<ClCompile Include="Commands\ErrorCommand.cpp" />

src/AppInstallerCLICore/Commands/DscCommand.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33
#include "pch.h"
44
#include "DscCommand.h"
5+
#include "DscPackageResource.h"
56

67
#ifndef AICLI_DISABLE_TEST_HOOKS
78
#include "DscTestFileResource.h"
@@ -13,6 +14,7 @@ namespace AppInstaller::CLI
1314
std::vector<std::unique_ptr<Command>> DscCommand::GetCommands() const
1415
{
1516
return InitializeFromMoveOnly<std::vector<std::unique_ptr<Command>>>({
17+
std::make_unique<DscPackageResource>(FullName()),
1618
#ifndef AICLI_DISABLE_TEST_HOOKS
1719
std::make_unique<DscTestFileResource>(FullName()),
1820
std::make_unique<DscTestJsonResource>(FullName()),

src/AppInstallerCLICore/Commands/DscComposableObject.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <AppInstallerLogging.h>
77
#include <json/json.h>
88
#include <optional>
9+
#include <string>
10+
#include <vector>
911

1012
using namespace std::string_view_literals;
1113

@@ -33,7 +35,14 @@ namespace AppInstaller::CLI
3335
Json::Value GetBaseSchema(const std::string& title);
3436

3537
// Adds a property to the schema object.
36-
void AddPropertySchema(Json::Value& object, std::string_view name, DscComposablePropertyFlag flags, std::string_view type, std::string_view description);
38+
void AddPropertySchema(
39+
Json::Value& object,
40+
std::string_view name,
41+
DscComposablePropertyFlag flags,
42+
std::string_view type,
43+
std::string_view description,
44+
const std::vector<std::string>& enumValues,
45+
const std::optional<std::string>& defaultValue);
3746
}
3847

3948
template <typename PropertyType>
@@ -135,7 +144,7 @@ namespace AppInstaller::CLI
135144
static Json::Value Schema(const std::string& title)
136145
{
137146
Json::Value result = details::GetBaseSchema(title);
138-
(FoldHelper{}, ..., details::AddPropertySchema(result, Property::Name(), Property::Flags, GetJsonTypeValue<typename Property::Type>::SchemaTypeName(), Property::Description()));
147+
(FoldHelper{}, ..., details::AddPropertySchema(result, Property::Name(), Property::Flags, GetJsonTypeValue<typename Property::Type>::SchemaTypeName(), Property::Description(), Property::EnumValues(), Property::Default()));
139148
return result;
140149
}
141150
};
@@ -190,23 +199,31 @@ namespace AppInstaller::CLI
190199
std::optional<Type> m_value;
191200
};
192201

193-
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_) \
202+
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \
194203
struct _property_type_ : public DscComposableProperty<_property_type_, _value_type_, _flags_> \
195204
{ \
196205
static std::string_view Name() { return _json_name_; } \
197206
static std::string_view Description() { return _description_; } \
207+
static std::vector<Type> EnumValues() { return std::vector<Type> _enum_vals_; } \
208+
static std::optional<Type> Default() { return _default_; } \
198209
std::optional<Type>& _property_name_() { return m_value; } \
199210
const std::optional<Type>& _property_name_() const { return m_value; } \
200211
void _property_name_(std::optional<Type> value) { m_value = std::move(value); } \
201212

202-
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_) \
203-
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_) \
213+
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \
214+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \
204215
};
205216

206-
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(_property_type_, _value_type_, _property_name_, _json_name_, _description_) WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_)
207-
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_) WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_)
217+
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(_property_type_, _value_type_, _property_name_, _json_name_, _description_) \
218+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_, {}, {})
208219

209-
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(StandardExistProperty, bool, Exist, "_exist", DscComposablePropertyFlag::None, "Indicates whether an instance should/does exist.")
220+
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_) \
221+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, {}, {})
222+
223+
#define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(_property_type_, _value_type_, _property_name_, _json_name_, _description_, _enum_vals_, _default_) \
224+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_, _enum_vals_, _default_)
225+
226+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(StandardExistProperty, bool, Exist, "_exist", DscComposablePropertyFlag::None, "Indicates whether an instance should/does exist.", {}, {})
210227
bool ShouldExist() { return m_value.value_or(true); }
211228
};
212229

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#include "pch.h"
4+
#include "DscPackageResource.h"
5+
#include "DscComposableObject.h"
6+
#include "Resources.h"
7+
8+
using namespace AppInstaller::Utility::literals;
9+
10+
namespace AppInstaller::CLI
11+
{
12+
namespace
13+
{
14+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(IdProperty, std::string, Identifier, "id", DscComposablePropertyFlag::Required | DscComposablePropertyFlag::CopyToOutput, "The identifier of the package.");
15+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(SourceProperty, std::string, Source, "source", DscComposablePropertyFlag::CopyToOutput, "The source of the package.");
16+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(VersionProperty, std::string, Version, "version", "The version of the package.");
17+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(MatchOptionProperty, std::string, MatchOption, "matchOption", "The method for matching the identifier with a package.", ({ "equals", "equalsCaseInsensitive", "startsWithCaseInsensitive", "containsCaseInsensitive" }), "equalsCaseInsensitive");
18+
19+
using PackageResourceObject = DscComposableObject<StandardExistProperty, StandardInDesiredStateProperty, IdProperty, SourceProperty>;
20+
21+
struct PackageFunctionData
22+
{
23+
PackageFunctionData(const std::optional<Json::Value>& json) : Input(json), Output(Input.CopyForOutput())
24+
{
25+
}
26+
27+
PackageResourceObject Input;
28+
PackageResourceObject Output;
29+
30+
// Fills the Output object with the current state
31+
void Get()
32+
{
33+
if (std::filesystem::exists(Path) && std::filesystem::is_regular_file(Path))
34+
{
35+
Output.Exist(true);
36+
37+
std::ifstream stream{ Path, std::ios::binary };
38+
Output.Content(Utility::ReadEntireStream(stream));
39+
}
40+
else
41+
{
42+
Output.Exist(false);
43+
}
44+
}
45+
46+
// Determines if the current Output values match the Input values state.
47+
bool Test()
48+
{
49+
// Need to populate Output before calling
50+
THROW_HR_IF(E_UNEXPECTED, !Output.Exist().has_value());
51+
52+
if (Input.ShouldExist())
53+
{
54+
if (Output.Exist().value())
55+
{
56+
return ContentMatches();
57+
}
58+
else
59+
{
60+
return false;
61+
}
62+
}
63+
else
64+
{
65+
return !Output.Exist().value();
66+
}
67+
}
68+
69+
Json::Value DiffJson()
70+
{
71+
// Need to populate Output before calling
72+
THROW_HR_IF(E_UNEXPECTED, !Output.Exist().has_value());
73+
74+
Json::Value result{ Json::ValueType::arrayValue };
75+
76+
if (Input.ShouldExist() != Output.Exist().value())
77+
{
78+
result.append(std::string{ StandardExistProperty::Name() });
79+
}
80+
else
81+
{
82+
if (!ContentMatches())
83+
{
84+
result.append(std::string{ ContentProperty::Name() });
85+
}
86+
}
87+
88+
return result;
89+
}
90+
91+
private:
92+
bool ContentMatches()
93+
{
94+
bool hasInput = Input.Content().has_value() && !Input.Content().value().empty();
95+
bool hasOutput = Output.Content().has_value() && !Output.Content().value().empty();
96+
97+
return
98+
(hasInput && hasOutput && Input.Content().value() == Output.Content().value()) ||
99+
(!hasInput && !hasOutput);
100+
}
101+
};
102+
}
103+
104+
DscPackageResource::DscPackageResource(std::string_view parent) :
105+
DscCommandBase(parent, "package", DscResourceKind::Resource,
106+
DscFunctions::Get | DscFunctions::Set | DscFunctions::WhatIf | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema,
107+
DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::HandlesExist | DscFunctionModifiers::ReturnsStateAndDiff)
108+
{
109+
}
110+
111+
Resource::LocString DscPackageResource::ShortDescription() const
112+
{
113+
return Resource::String::DscPackageResourceShortDescription;
114+
}
115+
116+
Resource::LocString DscPackageResource::LongDescription() const
117+
{
118+
return Resource::String::DscPackageResourceLongDescription;
119+
}
120+
121+
std::string DscPackageResource::ResourceType() const
122+
{
123+
return "Package";
124+
}
125+
126+
void DscPackageResource::ResourceFunctionGet(Execution::Context& context) const
127+
{
128+
if (auto json = GetJsonFromInput(context))
129+
{
130+
anon::TestFileFunctionData data{ json };
131+
132+
data.Get();
133+
134+
WriteJsonOutputLine(context, data.Output.ToJson());
135+
}
136+
}
137+
138+
void DscPackageResource::ResourceFunctionSet(Execution::Context& context) const
139+
{
140+
if (auto json = GetJsonFromInput(context))
141+
{
142+
anon::TestFileFunctionData data{ json };
143+
144+
data.Get();
145+
146+
if (!data.Test())
147+
{
148+
bool exists = std::filesystem::exists(data.Path);
149+
if (exists)
150+
{
151+
// Don't delete a directory or other special files in this test resource
152+
THROW_WIN32_IF(ERROR_DIRECTORY_NOT_SUPPORTED, !std::filesystem::is_regular_file(data.Path));
153+
}
154+
155+
if (data.Input.ShouldExist())
156+
{
157+
std::filesystem::create_directories(data.Path.parent_path());
158+
159+
std::ofstream stream{ data.Path, std::ios::binary | std::ios::trunc };
160+
if (data.Input.Content())
161+
{
162+
stream.write(data.Input.Content().value().c_str(), data.Input.Content().value().length());
163+
}
164+
}
165+
else if (exists)
166+
{
167+
std::filesystem::remove(data.Path);
168+
}
169+
}
170+
171+
// Capture the diff before updating the output
172+
auto diff = data.DiffJson();
173+
174+
data.Output.Exist(data.Input.ShouldExist());
175+
if (data.Output.Exist().value())
176+
{
177+
data.Output.Content(data.Input.Content().value_or(""));
178+
}
179+
180+
WriteJsonOutputLine(context, data.Output.ToJson());
181+
WriteJsonOutputLine(context, diff);
182+
}
183+
}
184+
185+
void DscPackageResource::ResourceFunctionTest(Execution::Context& context) const
186+
{
187+
if (auto json = GetJsonFromInput(context))
188+
{
189+
anon::TestFileFunctionData data{ json };
190+
191+
data.Get();
192+
data.Output.InDesiredState(data.Test());
193+
194+
WriteJsonOutputLine(context, data.Output.ToJson());
195+
WriteJsonOutputLine(context, data.DiffJson());
196+
}
197+
}
198+
199+
void DscPackageResource::ResourceFunctionExport(Execution::Context& context) const
200+
{
201+
if (auto json = GetJsonFromInput(context))
202+
{
203+
anon::TestFileFunctionData data{ json };
204+
205+
if (std::filesystem::exists(data.Path))
206+
{
207+
if (std::filesystem::is_regular_file(data.Path))
208+
{
209+
data.Get();
210+
WriteJsonOutputLine(context, data.Output.ToJson());
211+
}
212+
else if (std::filesystem::is_directory(data.Path))
213+
{
214+
for (const auto& file : std::filesystem::directory_iterator{ data.Path })
215+
{
216+
if (std::filesystem::is_regular_file(file))
217+
{
218+
anon::TestFileObject output;
219+
output.Path(file.path().u8string());
220+
221+
std::ifstream stream{ file.path(), std::ios::binary};
222+
output.Content(Utility::ReadEntireStream(stream));
223+
224+
WriteJsonOutputLine(context, output.ToJson());
225+
}
226+
}
227+
}
228+
}
229+
}
230+
}
231+
232+
void DscPackageResource::ResourceFunctionSchema(Execution::Context& context) const
233+
{
234+
WriteJsonOutputLine(context, anon::TestFileObject::Schema(ResourceType()));
235+
}
236+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#pragma once
4+
#include "DscCommandBase.h"
5+
6+
namespace AppInstaller::CLI
7+
{
8+
// A resource for managing package state.
9+
struct DscPackageResource : public DscCommandBase
10+
{
11+
DscPackageResource(std::string_view parent);
12+
13+
Resource::LocString ShortDescription() const override;
14+
Resource::LocString LongDescription() const override;
15+
16+
protected:
17+
std::string ResourceType() const override;
18+
19+
void ResourceFunctionGet(Execution::Context& context) const override;
20+
void ResourceFunctionSet(Execution::Context& context) const override;
21+
void ResourceFunctionTest(Execution::Context& context) const override;
22+
void ResourceFunctionExport(Execution::Context& context) const override;
23+
void ResourceFunctionSchema(Execution::Context& context) const override;
24+
};
25+
}

0 commit comments

Comments
 (0)