mirror of https://github.com/Squidex/squidex.git
75 changed files with 4231 additions and 0 deletions
@ -0,0 +1,5 @@ |
|||||
|
<configuration> |
||||
|
<packageSources> |
||||
|
<add key="Qooroo" value="https://www.myget.org/F/qooroo/api/v2" /> |
||||
|
</packageSources> |
||||
|
</configuration> |
||||
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
// Controls if the editor shows reference information for the modes that support it |
||||
|
"editor.referenceInfos": false, |
||||
|
|
||||
|
// When opening a file, `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents. |
||||
|
"editor.detectIndentation": false, |
||||
|
|
||||
|
// Typescript version from local package to be consistent |
||||
|
"typescript.tsdk": "node_modules/typescript/lib", |
||||
|
|
||||
|
// Configure glob patterns for excluding files and folders. |
||||
|
"files.exclude": { |
||||
|
"_test-output": true, |
||||
|
"**/node_modules": true, |
||||
|
"**/artifacts": true, |
||||
|
"**/build": true, |
||||
|
"**/out": true, |
||||
|
"**/obj": true, |
||||
|
"**/bin": true, |
||||
|
"**/*.lock.json": true, |
||||
|
"**/*.bat": true, |
||||
|
"**/*.sln": true, |
||||
|
"**/*.sln.DotSettings": true, |
||||
|
"**/*.user": true, |
||||
|
"**/*.xproj": true, |
||||
|
"**/*.gitattributes": true, |
||||
|
".vs:": true |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
|
||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
|
# Visual Studio 14 |
||||
|
VisualStudioVersion = 14.0.25420.1 |
||||
|
MinimumVisualStudioVersion = 10.0.40219.1 |
||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{24A3171D-2905-49C9-8A49-A473799014E8}" |
||||
|
EndProject |
||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4B5539DB-F68E-4DBA-B22A-72B9FE7FE2D8}" |
||||
|
ProjectSection(SolutionItems) = preProject |
||||
|
global.json = global.json |
||||
|
EndProjectSection |
||||
|
EndProject |
||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot", "src\PinkParrot\PinkParrot.xproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}" |
||||
|
EndProject |
||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pinkparrot_infrastructure", "pinkparrot_infrastructure", "{8CF53B92-5EB1-461D-98F8-70DA9B603FBF}" |
||||
|
EndProject |
||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pinkparrot_core", "pinkparrot_core", "{4C6B06C2-6D77-4E0E-AE32-D7050236433A}" |
||||
|
EndProject |
||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Core.Tests", "src\pinkparrot_core\PinkParrot.Core.Tests\PinkParrot.Core.Tests.xproj", "{4A27B9DE-F553-4A82-B866-A29EF8A5A0AF}" |
||||
|
EndProject |
||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Core", "src\pinkparrot_core\PinkParrot.Core\PinkParrot.Core.xproj", "{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}" |
||||
|
EndProject |
||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Infrastructure.Tests", "src\pinkparrot_infrastructure\PinkParrot.Infrastructure.Tests\PinkParrot.Infrastructure.Tests.xproj", "{840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}" |
||||
|
EndProject |
||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Infrastructure", "src\pinkparrot_infrastructure\PinkParrot.Infrastructure\PinkParrot.Infrastructure.xproj", "{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}" |
||||
|
EndProject |
||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pinkparrot_events", "pinkparrot_events", "{6AE39761-FD74-45CD-99CF-73D3D2E5D064}" |
||||
|
EndProject |
||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Events", "src\pinkparrot_events\PinkParrot.Events\PinkParrot.Events.xproj", "{25F66C64-058A-4D44-BC0C-F12A054F9A91}" |
||||
|
EndProject |
||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pinkparrot_write", "pinkparrot_write", "{4AED438F-684F-4FAE-B016-21CF2EAEA79F}" |
||||
|
EndProject |
||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Write", "src\pinkparrot_write\PinkParrot.Write\PinkParrot.Write.xproj", "{A85201C6-6AF8-4B63-8365-08F741050438}" |
||||
|
EndProject |
||||
|
Global |
||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
|
Debug|Any CPU = Debug|Any CPU |
||||
|
Release|Any CPU = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
|
{61F6BBCE-A080-4400-B194-70E2F5D2096E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{61F6BBCE-A080-4400-B194-70E2F5D2096E}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{4A27B9DE-F553-4A82-B866-A29EF8A5A0AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{4A27B9DE-F553-4A82-B866-A29EF8A5A0AF}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{4A27B9DE-F553-4A82-B866-A29EF8A5A0AF}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{4A27B9DE-F553-4A82-B866-A29EF8A5A0AF}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{A85201C6-6AF8-4B63-8365-08F741050438}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{A85201C6-6AF8-4B63-8365-08F741050438}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{A85201C6-6AF8-4B63-8365-08F741050438}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{A85201C6-6AF8-4B63-8365-08F741050438}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(SolutionProperties) = preSolution |
||||
|
HideSolutionNode = FALSE |
||||
|
EndGlobalSection |
||||
|
GlobalSection(NestedProjects) = preSolution |
||||
|
{61F6BBCE-A080-4400-B194-70E2F5D2096E} = {24A3171D-2905-49C9-8A49-A473799014E8} |
||||
|
{4A27B9DE-F553-4A82-B866-A29EF8A5A0AF} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A} |
||||
|
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A} |
||||
|
{840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} |
||||
|
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} |
||||
|
{25F66C64-058A-4D44-BC0C-F12A054F9A91} = {6AE39761-FD74-45CD-99CF-73D3D2E5D064} |
||||
|
{A85201C6-6AF8-4B63-8365-08F741050438} = {4AED438F-684F-4FAE-B016-21CF2EAEA79F} |
||||
|
EndGlobalSection |
||||
|
EndGlobal |
||||
@ -0,0 +1,84 @@ |
|||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002Aapp_002A_002A_002Ejs/@EntryIndexedValue">False</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002Aapp_002A_002A_002Ejs/@EntryIndexRemoved">True</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002Aapp_002F_002A_002A_002F_002A_002Ejs/@EntryIndexedValue">False</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002A_002Fapp_002F_002A_002A_002F_002A_002Ejs/@EntryIndexedValue">False</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002A_002Fapp_002F_002A_002A_002F_002A_002Ejs/@EntryIndexRemoved">True</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecshtml/@EntryIndexedValue">True</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ed_002Ets/@EntryIndexedValue">True</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejs/@EntryIndexedValue">True</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002Aapp_002F_002A_002A_002F_002A_002Ejs/@EntryIndexRemoved">True</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ets/@EntryIndexedValue">False</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ets/@EntryIndexRemoved">True</s:Boolean> |
||||
|
|
||||
|
|
||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=A2569FB7_002D99E7_002D48B4_002DB97F_002DE48B6F57E492_002Fd_003Awwwroot_002Fd_003Anode_005Fmodules/@EntryIndexedValue">ExplicitlyExcluded</s:String> |
||||
|
|
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertMethodToExpressionBody/@EntryIndexedValue">DO_NOT_SHOW</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertPropertyToExpressionBody/@EntryIndexedValue">DO_NOT_SHOW</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToAutoProperty/@EntryIndexedValue">DO_NOT_SHOW</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToAutoPropertyWhenPossible/@EntryIndexedValue">DO_NOT_SHOW</s:String> |
||||
|
|
||||
|
|
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=Html_002ETagNotResolved/@EntryIndexedValue">DO_NOT_SHOW</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InconsistentNaming/@EntryIndexedValue">DO_NOT_SHOW</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MissingHasOwnPropertyInForeach/@EntryIndexedValue">DO_NOT_SHOW</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">SUGGESTION</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FElsewhere/@EntryIndexedValue">SUGGESTION</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">SUGGESTION</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=TsResolvedFromInaccessibleModule/@EntryIndexedValue">DO_NOT_SHOW</s:String> |
||||
|
<s:String x:Key="/Default/CodeInspection/TypeScriptInspections/Level/@EntryValue">TypeScript16</s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Namespaces/@EntryIndexedValue"><?xml version="1.0" encoding="utf-16"?><Profile name="Namespaces"><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSUpdateFileHeader>True</CSUpdateFileHeader></Profile></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Typescript/@EntryIndexedValue"><?xml version="1.0" encoding="utf-16"?><Profile name="Typescript"><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs></Profile></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue"></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/JavaScriptCodeFormatting/QUOTE_STYLE/@EntryValue">SingleQuoted</s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">==========================================================================
 |
||||
|
$FILENAME$
 |
||||
|
PinkParrot Headless CMS
 |
||||
|
==========================================================================
 |
||||
|
Copyright (c) PinkParrot Group
 |
||||
|
All rights reserved.
 |
||||
|
==========================================================================</s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCLASS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCONSTRUCTOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FGLOBAL_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLABEL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLOCAL_005FCONSTRUCTOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLOCAL_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FOBJECT_005FPROPERTY_005FOF_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FPARAMETER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FCLASS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM_005FMEMBER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FINTERFACE/@EntryIndexedValue"><Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FEXPORTED/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FLOCAL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FPARAMETER/@EntryIndexedValue"><Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FHTML_005FCONTROL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FNAME/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FPREFIX/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String> |
||||
|
<s:Boolean x:Key="/Default/CodeStyle/TypeScriptCodeStyle/ExplicitPublicModifier/@EntryValue">True</s:Boolean> |
||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsWrapperSettingsUpgrader/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
||||
@ -0,0 +1,6 @@ |
|||||
|
{ |
||||
|
"projects": [ "src" ], |
||||
|
"sdk": { |
||||
|
"version": "1.0.0-preview2-003121" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> |
||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>61f6bbce-a080-4400-b194-70e2f5d2096e</ProjectGuid> |
||||
|
<RootNamespace>PinkParrot</RootNamespace> |
||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> |
||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> |
||||
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<DnxInvisibleContent Include="bower.json" /> |
||||
|
<DnxInvisibleContent Include=".bowerrc" /> |
||||
|
</ItemGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
</Project> |
||||
@ -0,0 +1,28 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Program.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.IO; |
||||
|
using Microsoft.AspNetCore.Hosting; |
||||
|
|
||||
|
namespace PinkParrot |
||||
|
{ |
||||
|
public class Program |
||||
|
{ |
||||
|
public static void Main(string[] args) |
||||
|
{ |
||||
|
var host = new WebHostBuilder() |
||||
|
.UseKestrel() |
||||
|
.UseContentRoot(Directory.GetCurrentDirectory()) |
||||
|
.UseIISIntegration() |
||||
|
.UseStartup<Startup>() |
||||
|
.Build(); |
||||
|
|
||||
|
host.Run(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,187 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="utf-8" /> |
||||
|
<title>Welcome to ASP.NET Core</title> |
||||
|
<style> |
||||
|
html { |
||||
|
background: #f1f1f1; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
background: #fff; |
||||
|
color: #505050; |
||||
|
font: 14px 'Segoe UI', tahoma, arial, helvetica, sans-serif; |
||||
|
margin: 1%; |
||||
|
min-height: 95.5%; |
||||
|
border: 1px solid silver; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
#header { |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
#header h1 { |
||||
|
font-size: 44px; |
||||
|
font-weight: normal; |
||||
|
margin: 0; |
||||
|
padding: 10px 30px 10px 30px; |
||||
|
} |
||||
|
|
||||
|
#header span { |
||||
|
margin: 0; |
||||
|
padding: 0 30px; |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
#header p { |
||||
|
font-size: 20px; |
||||
|
color: #fff; |
||||
|
background: #007acc; |
||||
|
padding: 0 30px; |
||||
|
line-height: 50px; |
||||
|
margin-top: 25px; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#header p a { |
||||
|
color: #fff; |
||||
|
text-decoration: underline; |
||||
|
font-weight: bold; |
||||
|
padding-right: 35px; |
||||
|
background: no-repeat right bottom url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAWCAMAAAAcqPc3AAAANlBMVEUAAAAAeswfitI9mthXp91us+KCvuaTx+mjz+2x1u+83PLH4vTR5/ba7Pjj8Pns9fv1+v3////wy3dWAAAAAXRSTlMAQObYZgAAAHxJREFUeNp9kVcSwCAIRMHUYoH7XzaxOxJ9P8oyQ1uIqNPwh3s2aLmIM2YtqrLcQIeQEylhuCeUOlhgve5yoBCfWmlnlgkN4H8ykbpaE7gR03AbUHiwoOxUH9Xp+ubd41p1HF3mBPrfC87BHeTdaB3ceeKL9HGpcvX9zu6+DdMWT9KQPvYAAAAASUVORK5CYII=); |
||||
|
} |
||||
|
|
||||
|
#main { |
||||
|
padding: 5px 30px; |
||||
|
clear: both; |
||||
|
} |
||||
|
|
||||
|
.section { |
||||
|
width: 21.7%; |
||||
|
float: left; |
||||
|
margin: 0 0 0 4%; |
||||
|
} |
||||
|
|
||||
|
.section h2 { |
||||
|
font-size: 13px; |
||||
|
text-transform: uppercase; |
||||
|
margin: 0; |
||||
|
border-bottom: 1px solid silver; |
||||
|
padding-bottom: 12px; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.section.first { |
||||
|
margin-left: 0; |
||||
|
} |
||||
|
|
||||
|
.section.first h2 { |
||||
|
font-size: 24px; |
||||
|
text-transform: none; |
||||
|
margin-bottom: 25px; |
||||
|
border: none; |
||||
|
} |
||||
|
|
||||
|
.section.first li { |
||||
|
border-top: 1px solid silver; |
||||
|
padding: 8px 0; |
||||
|
} |
||||
|
|
||||
|
.section.last { |
||||
|
margin-right: 0; |
||||
|
} |
||||
|
|
||||
|
ul { |
||||
|
list-style: none; |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
line-height: 20px; |
||||
|
} |
||||
|
|
||||
|
li { |
||||
|
padding: 4px 0; |
||||
|
} |
||||
|
|
||||
|
a { |
||||
|
color: #267cb2; |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
|
||||
|
a:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
#footer { |
||||
|
clear: both; |
||||
|
padding-top: 50px; |
||||
|
} |
||||
|
|
||||
|
#footer p { |
||||
|
position: absolute; |
||||
|
bottom: 10px; |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
|
||||
|
<div id="header"> |
||||
|
<h1>Welcome to ASP.NET Core</h1> |
||||
|
<span> |
||||
|
We've made some big updates in this release, so it’s <b>important</b> that you spend |
||||
|
a few minutes to learn what’s new. |
||||
|
</span> |
||||
|
<p>You've created a new ASP.NET Core project. <a href="http://go.microsoft.com/fwlink/?LinkId=518016">Learn what's new</a></p> |
||||
|
</div> |
||||
|
|
||||
|
<div id="main"> |
||||
|
<div class="section first"> |
||||
|
<h2>This application consists of:</h2> |
||||
|
<ul> |
||||
|
<li>Sample pages using ASP.NET Core MVC</li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518004">Bower</a> for managing client-side libraries</li> |
||||
|
<li>Theming using <a href="http://go.microsoft.com/fwlink/?LinkID=398939">Bootstrap</a></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="section"> |
||||
|
<h2>How to</h2> |
||||
|
<ul> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398600">Add a Controller and View</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699562">Add an appsetting in config and access it in app.</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699315">Manage User Secrets using Secret Manager.</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699316">Use logging to log a message.</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699317">Add packages using NuGet.</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699318">Add client packages using Bower.</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699319">Target development, staging or production environment.</a></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="section"> |
||||
|
<h2>Overview</h2> |
||||
|
<ul> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518008">Conceptual overview of what is ASP.NET Core</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699320">Fundamentals of ASP.NET Core such as Startup and middleware.</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=398602">Working with Data</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=398603">Security</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699321">Client side development</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699322">Develop on different platforms</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699323">Read more on the documentation site</a></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="section last"> |
||||
|
<h2>Run & Deploy</h2> |
||||
|
<ul> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517851">Run your app</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517853">Run tools such as EF migrations and more</a></li> |
||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398609">Publish to Microsoft Azure Web Apps</a></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
|
||||
|
<div id="footer"> |
||||
|
<p>We would love to hear your <a href="http://go.microsoft.com/fwlink/?LinkId=518015">feedback</a></p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,27 @@ |
|||||
|
{ |
||||
|
"iisSettings": { |
||||
|
"windowsAuthentication": false, |
||||
|
"anonymousAuthentication": true, |
||||
|
"iisExpress": { |
||||
|
"applicationUrl": "http://localhost:65351/", |
||||
|
"sslPort": 0 |
||||
|
} |
||||
|
}, |
||||
|
"profiles": { |
||||
|
"IIS Express": { |
||||
|
"commandName": "IISExpress", |
||||
|
"launchBrowser": true, |
||||
|
"environmentVariables": { |
||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||
|
} |
||||
|
}, |
||||
|
"PinkParrot": { |
||||
|
"commandName": "Project", |
||||
|
"launchBrowser": true, |
||||
|
"launchUrl": "http://localhost:5000", |
||||
|
"environmentVariables": { |
||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Startup.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using Microsoft.AspNetCore.Hosting; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace PinkParrot |
||||
|
{ |
||||
|
public class Startup |
||||
|
{ |
||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
|
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
|
||||
|
public void ConfigureServices(IServiceCollection services) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) |
||||
|
{ |
||||
|
loggerFactory.AddConsole(); |
||||
|
|
||||
|
if (env.IsDevelopment()) |
||||
|
{ |
||||
|
app.UseDeveloperExceptionPage(); |
||||
|
} |
||||
|
|
||||
|
app.Run(async (context) => |
||||
|
{ |
||||
|
await context.Response.WriteAsync("Hello World!"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
{ |
||||
|
"dependencies": { |
||||
|
"Microsoft.NETCore.App": { |
||||
|
"version": "1.0.0", |
||||
|
"type": "platform" |
||||
|
}, |
||||
|
"Microsoft.AspNetCore.Diagnostics": "1.0.0", |
||||
|
|
||||
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", |
||||
|
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0", |
||||
|
"Microsoft.Extensions.Logging.Console": "1.0.0" |
||||
|
}, |
||||
|
|
||||
|
"tools": { |
||||
|
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final" |
||||
|
}, |
||||
|
|
||||
|
"frameworks": { |
||||
|
"netcoreapp1.0": { |
||||
|
"imports": [ |
||||
|
"dotnet5.6", |
||||
|
"portable-net45+win8" |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
"buildOptions": { |
||||
|
"emitEntryPoint": true, |
||||
|
"preserveCompilationContext": true |
||||
|
}, |
||||
|
|
||||
|
"runtimeOptions": { |
||||
|
"configProperties": { |
||||
|
"System.GC.Server": true |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
"publishOptions": { |
||||
|
"include": [ |
||||
|
"wwwroot", |
||||
|
"web.config" |
||||
|
] |
||||
|
}, |
||||
|
|
||||
|
"scripts": { |
||||
|
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<configuration> |
||||
|
|
||||
|
<!-- |
||||
|
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380 |
||||
|
--> |
||||
|
|
||||
|
<system.webServer> |
||||
|
<handlers> |
||||
|
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/> |
||||
|
</handlers> |
||||
|
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/> |
||||
|
</system.webServer> |
||||
|
</configuration> |
||||
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion> |
||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>4a27b9de-f553-4a82-b866-a29ef8a5a0af</ProjectGuid> |
||||
|
<RootNamespace>PinkParrot.Core.Tests</RootNamespace> |
||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> |
||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
</Project> |
||||
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"version": "1.0.0-*", |
||||
|
"testRunner": "xunit", |
||||
|
"dependencies": { |
||||
|
"dotnet-test-xunit": "2.2.0-preview2-build1029", |
||||
|
"PinkParrot.Core": "1.0.0-*", |
||||
|
"xunit": "2.2.0-beta2-build3300" |
||||
|
}, |
||||
|
"frameworks": { |
||||
|
"netcoreapp1.0": { |
||||
|
"dependencies": { |
||||
|
"Microsoft.NETCore.App": { |
||||
|
"type": "platform", |
||||
|
"version": "1.0.0" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"buildOptions": { |
||||
|
"copyToOutput": { |
||||
|
"include": [ |
||||
|
"xunit.runner.json" |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
"tooling": { |
||||
|
"defaultNamespace": "PinkParrot.Core.Tests" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
{ |
||||
|
"diagnosticMessages": false, |
||||
|
"methodDisplay": "classAndMethod", |
||||
|
"parallelizeTestCollections": true |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion> |
||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>47f3c27e-698b-4edf-a7e8-d7f4232afbb0</ProjectGuid> |
||||
|
<RootNamespace>PinkParrot.Core</RootNamespace> |
||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> |
||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
</Project> |
||||
@ -0,0 +1,200 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using System.Threading.Tasks; |
||||
|
using PinkParrot.Infrastructure; |
||||
|
using PinkParrot.Infrastructure.Tasks; |
||||
|
|
||||
|
// ReSharper disable ConvertIfStatementToReturnStatement
|
||||
|
|
||||
|
namespace PinkParrot.Core.Schema |
||||
|
{ |
||||
|
public abstract class ModelField |
||||
|
{ |
||||
|
private readonly Guid id; |
||||
|
private string name; |
||||
|
private string hint; |
||||
|
private string displayName; |
||||
|
private bool isRequired; |
||||
|
private bool isDisabled; |
||||
|
private bool isHidden; |
||||
|
|
||||
|
public Guid Id |
||||
|
{ |
||||
|
get { return id; } |
||||
|
} |
||||
|
|
||||
|
public string Name |
||||
|
{ |
||||
|
get { return name; } |
||||
|
} |
||||
|
|
||||
|
public string Hint |
||||
|
{ |
||||
|
get { return hint; } |
||||
|
} |
||||
|
|
||||
|
public string DisplayName |
||||
|
{ |
||||
|
get { return displayName; } |
||||
|
} |
||||
|
|
||||
|
public bool IsRequired |
||||
|
{ |
||||
|
get { return isRequired; } |
||||
|
} |
||||
|
|
||||
|
public bool IsHidden |
||||
|
{ |
||||
|
get { return isHidden; } |
||||
|
} |
||||
|
|
||||
|
public bool IsDisabled |
||||
|
{ |
||||
|
get { return isDisabled; } |
||||
|
} |
||||
|
|
||||
|
protected ModelField(Guid id, string name) |
||||
|
{ |
||||
|
Guard.NotEmpty(id, nameof(id)); |
||||
|
Guard.ValidSlug(name, nameof(name)); |
||||
|
|
||||
|
this.id = id; |
||||
|
|
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public ModelField Configure(PropertiesBag settings, ICollection<string> errors) |
||||
|
{ |
||||
|
var clone = Clone(); |
||||
|
|
||||
|
if (settings.Contains("Name")) |
||||
|
{ |
||||
|
clone.name = settings["Name"].ToString(); |
||||
|
|
||||
|
if (!clone.name.IsSlug()) |
||||
|
{ |
||||
|
errors.Add("Field name must be a slug"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (settings.Contains("Hint")) |
||||
|
{ |
||||
|
clone.hint = settings["Hint"].ToString()?.Trim() ?? string.Empty; |
||||
|
} |
||||
|
|
||||
|
if (settings.Contains("DisplayName")) |
||||
|
{ |
||||
|
clone.displayName = settings["DisplayName"].ToString()?.Trim() ?? string.Empty; |
||||
|
} |
||||
|
|
||||
|
if (settings.Contains("IsRequired")) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
clone.isRequired = settings["IsRequired"].ToBoolean(CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
catch (InvalidCastException) |
||||
|
{ |
||||
|
errors.Add("IsRequired is not a valid boolean"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
clone.ConfigureCore(settings, errors); |
||||
|
|
||||
|
return clone; |
||||
|
} |
||||
|
|
||||
|
protected virtual void ConfigureCore(PropertiesBag settings, ICollection<string> errors) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Task ValidateAsync(PropertyValue property, ICollection<string> errors) |
||||
|
{ |
||||
|
Guard.NotNull(property, nameof(property)); |
||||
|
|
||||
|
if (isRequired && property.RawValue == null) |
||||
|
{ |
||||
|
errors.Add("<Field> is required"); |
||||
|
} |
||||
|
|
||||
|
if (property.RawValue == null) |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
return ValidateCoreAsync(property, errors); |
||||
|
} |
||||
|
|
||||
|
protected virtual Task ValidateCoreAsync(PropertyValue property, ICollection<string> errors) |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public ModelField Hide() |
||||
|
{ |
||||
|
if (isHidden) |
||||
|
{ |
||||
|
throw new DomainValidationException($"The field '{name} is already hidden."); |
||||
|
} |
||||
|
|
||||
|
var clone = Clone(); |
||||
|
|
||||
|
clone.isHidden = true; |
||||
|
|
||||
|
return Clone(); |
||||
|
} |
||||
|
|
||||
|
public ModelField Show() |
||||
|
{ |
||||
|
if (!isHidden) |
||||
|
{ |
||||
|
throw new DomainValidationException($"The field '{name} is already visible."); |
||||
|
} |
||||
|
|
||||
|
var clone = Clone(); |
||||
|
|
||||
|
clone.isHidden = false; |
||||
|
|
||||
|
return Clone(); |
||||
|
} |
||||
|
|
||||
|
public ModelField Disable() |
||||
|
{ |
||||
|
if (isDisabled) |
||||
|
{ |
||||
|
throw new DomainValidationException($"The field '{name} is already disabled."); |
||||
|
} |
||||
|
|
||||
|
var clone = Clone(); |
||||
|
|
||||
|
clone.isDisabled = true; |
||||
|
|
||||
|
return clone; |
||||
|
} |
||||
|
|
||||
|
public ModelField Enable() |
||||
|
{ |
||||
|
if (!isDisabled) |
||||
|
{ |
||||
|
throw new DomainValidationException($"The field '{name} is already enabled."); |
||||
|
} |
||||
|
|
||||
|
var clone = Clone(); |
||||
|
|
||||
|
clone.isDisabled = false; |
||||
|
|
||||
|
return clone; |
||||
|
} |
||||
|
|
||||
|
protected abstract ModelField Clone(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelFieldFactory.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Core.Schema |
||||
|
{ |
||||
|
public class ModelFieldFactory |
||||
|
{ |
||||
|
public virtual ModelField CreateField(Guid id, string type, string fieldName) |
||||
|
{ |
||||
|
return new NumberField(id, fieldName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,177 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelSchema.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using PinkParrot.Infrastructure; |
||||
|
|
||||
|
namespace PinkParrot.Core.Schema |
||||
|
{ |
||||
|
public sealed class ModelSchema |
||||
|
{ |
||||
|
private readonly ModelSchemaMetadata metadata; |
||||
|
private readonly ImmutableDictionary<Guid, ModelField> fields; |
||||
|
private readonly Dictionary<string, ModelField> fieldsByName; |
||||
|
|
||||
|
public ModelSchema(ModelSchemaMetadata metadata, ImmutableDictionary<Guid, ModelField> fields) |
||||
|
{ |
||||
|
Guard.NotNull(fields, nameof(fields)); |
||||
|
Guard.NotNull(metadata, nameof(metadata)); |
||||
|
|
||||
|
this.fields = fields; |
||||
|
|
||||
|
this.metadata = metadata; |
||||
|
|
||||
|
fieldsByName = fields.Values.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); |
||||
|
} |
||||
|
|
||||
|
public static ModelSchema Create(string name) |
||||
|
{ |
||||
|
if (!name.IsSlug()) |
||||
|
{ |
||||
|
throw new DomainValidationException("Cannot create the schema.", $"'{name}' is not a valid slug."); |
||||
|
} |
||||
|
|
||||
|
return new ModelSchema(new ModelSchemaMetadata(name), ImmutableDictionary<Guid, ModelField>.Empty); |
||||
|
} |
||||
|
|
||||
|
public IReadOnlyDictionary<Guid, ModelField> Fields |
||||
|
{ |
||||
|
get { return fields; } |
||||
|
} |
||||
|
|
||||
|
public ModelSchemaMetadata Metadata |
||||
|
{ |
||||
|
get { return metadata; } |
||||
|
} |
||||
|
|
||||
|
public ModelSchema Update(ModelSchemaMetadata newMetadata) |
||||
|
{ |
||||
|
Guard.NotNull(newMetadata, nameof(newMetadata)); |
||||
|
|
||||
|
return new ModelSchema(newMetadata, fields); |
||||
|
} |
||||
|
|
||||
|
public ModelSchema AddField(Guid id, string type, string fieldName, ModelFieldFactory factory) |
||||
|
{ |
||||
|
var field = factory.CreateField(id, type, fieldName); |
||||
|
|
||||
|
return SetField(field); |
||||
|
} |
||||
|
|
||||
|
public ModelSchema SetField(Guid fieldId, PropertiesBag settings) |
||||
|
{ |
||||
|
Guard.NotNull(settings, nameof(settings)); |
||||
|
|
||||
|
return UpdateField(fieldId, field => |
||||
|
{ |
||||
|
var errors = new List<string>(); |
||||
|
|
||||
|
var newField = field.Configure(settings, errors); |
||||
|
|
||||
|
if (errors.Any()) |
||||
|
{ |
||||
|
throw new DomainValidationException($"Cannot update field with id '{fieldId}', becase the settings are invalid.", errors); |
||||
|
} |
||||
|
|
||||
|
return newField; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public ModelSchema DisableField(Guid fieldId) |
||||
|
{ |
||||
|
return UpdateField(fieldId, field => field.Disable()); |
||||
|
} |
||||
|
|
||||
|
public ModelSchema EnableField(Guid fieldId) |
||||
|
{ |
||||
|
return UpdateField(fieldId, field => field.Enable()); |
||||
|
} |
||||
|
|
||||
|
public ModelSchema HideField(Guid fieldId) |
||||
|
{ |
||||
|
return UpdateField(fieldId, field => field.Show()); |
||||
|
} |
||||
|
|
||||
|
public ModelSchema ShowField(Guid fieldId) |
||||
|
{ |
||||
|
return UpdateField(fieldId, field => field.Show()); |
||||
|
} |
||||
|
|
||||
|
public ModelSchema SetField(ModelField field) |
||||
|
{ |
||||
|
Guard.NotNull(field, nameof(field)); |
||||
|
|
||||
|
if (fields.Values.Any(f => f.Name == field.Name && f.Id != field.Id)) |
||||
|
{ |
||||
|
throw new DomainValidationException($"A field with name '{field.Name}' already exists."); |
||||
|
} |
||||
|
|
||||
|
return new ModelSchema(metadata, fields.SetItem(field.Id, field)); |
||||
|
} |
||||
|
|
||||
|
public ModelSchema DeleteField(Guid fieldId) |
||||
|
{ |
||||
|
Guard.NotEmpty(fieldId, nameof(fieldId)); |
||||
|
|
||||
|
if (!fields.ContainsKey(fieldId)) |
||||
|
{ |
||||
|
throw new DomainValidationException($"A field with id {fieldId} does not exist."); |
||||
|
} |
||||
|
|
||||
|
return new ModelSchema(metadata, fields.Remove(fieldId)); |
||||
|
} |
||||
|
|
||||
|
private ModelSchema UpdateField(Guid fieldId, Func<ModelField, ModelField> updater) |
||||
|
{ |
||||
|
ModelField field; |
||||
|
|
||||
|
if (!fields.TryGetValue(fieldId, out field)) |
||||
|
{ |
||||
|
throw new DomainValidationException($"Cannot update field with id '{fieldId}'.", "Field does not exist."); |
||||
|
} |
||||
|
|
||||
|
var newField = updater(field); |
||||
|
|
||||
|
return SetField(newField); |
||||
|
} |
||||
|
|
||||
|
public async Task ValidateAsync(PropertiesBag data) |
||||
|
{ |
||||
|
Guard.NotNull(data, nameof(data)); |
||||
|
|
||||
|
var errors = new List<string>(); |
||||
|
|
||||
|
foreach (var kvp in data.Properties) |
||||
|
{ |
||||
|
ModelField field; |
||||
|
|
||||
|
if (fieldsByName.TryGetValue(kvp.Key, out field)) |
||||
|
{ |
||||
|
var newErrors = new List<string>(); |
||||
|
|
||||
|
await field.ValidateAsync(kvp.Value, newErrors); |
||||
|
|
||||
|
errors.AddRange(newErrors.Select(e => e.Replace("<Field>", "'" + field.Name + "'"))); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
errors.Add($"'{kvp.Key}' is not a known field"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (errors.Any()) |
||||
|
{ |
||||
|
throw new DomainValidationException("The data is not valid.", errors); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,81 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelSchemaMetadata.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using PinkParrot.Infrastructure; |
||||
|
|
||||
|
namespace PinkParrot.Core.Schema |
||||
|
{ |
||||
|
public sealed class ModelSchemaMetadata |
||||
|
{ |
||||
|
private string name; |
||||
|
private string displayName; |
||||
|
private string hint; |
||||
|
private string itemTitle; |
||||
|
|
||||
|
public string Name |
||||
|
{ |
||||
|
get { return name; } |
||||
|
} |
||||
|
|
||||
|
public string DisplayName |
||||
|
{ |
||||
|
get { return displayName; } |
||||
|
} |
||||
|
|
||||
|
public string Hint |
||||
|
{ |
||||
|
get { return hint; } |
||||
|
} |
||||
|
|
||||
|
public string ItemTitle |
||||
|
{ |
||||
|
get { return itemTitle; } |
||||
|
} |
||||
|
|
||||
|
public ModelSchemaMetadata(string name) |
||||
|
{ |
||||
|
Guard.ValidSlug(name, nameof(name)); |
||||
|
|
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public ModelSchemaMetadata Configure(string newName, PropertiesBag properties) |
||||
|
{ |
||||
|
Guard.NotNull(properties, nameof(properties)); |
||||
|
|
||||
|
var clone = (ModelSchemaMetadata) MemberwiseClone(); |
||||
|
|
||||
|
if (newName != null) |
||||
|
{ |
||||
|
if (!newName.IsSlug()) |
||||
|
{ |
||||
|
throw new DomainValidationException("Cannot update the schema.", $"'{newName}' is not a valid slug."); |
||||
|
} |
||||
|
|
||||
|
clone.name = newName; |
||||
|
} |
||||
|
|
||||
|
if (properties.Contains("Hint")) |
||||
|
{ |
||||
|
clone.hint = properties["Hint"].ToString()?.Trim(); |
||||
|
} |
||||
|
|
||||
|
if (properties.Contains("DisplayName")) |
||||
|
{ |
||||
|
clone.displayName = properties["DisplayName"].ToString()?.Trim(); |
||||
|
} |
||||
|
|
||||
|
if (properties.Contains("ItemTitle")) |
||||
|
{ |
||||
|
clone.itemTitle = properties["ItemTitle"].ToString()?.Trim(); |
||||
|
} |
||||
|
|
||||
|
return clone; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// ==========================================================================
|
||||
|
// NumberField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using System.Threading.Tasks; |
||||
|
using PinkParrot.Infrastructure; |
||||
|
using PinkParrot.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace PinkParrot.Core.Schema |
||||
|
{ |
||||
|
public sealed class NumberField : ModelField |
||||
|
{ |
||||
|
private double? maxValue; |
||||
|
private double? minValue; |
||||
|
|
||||
|
public double? MaxValue |
||||
|
{ |
||||
|
get { return maxValue; } |
||||
|
} |
||||
|
|
||||
|
public double? MinValue |
||||
|
{ |
||||
|
get { return minValue; } |
||||
|
} |
||||
|
|
||||
|
public NumberField(Guid id, string name) |
||||
|
: base(id, name) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void ConfigureCore(PropertiesBag settings, ICollection<string> errors) |
||||
|
{ |
||||
|
maxValue = ParseNumber("MaxValue", settings, errors); |
||||
|
minValue = ParseNumber("MinValue", settings, errors); |
||||
|
|
||||
|
if (maxValue.HasValue && minValue.HasValue && minValue.Value > maxValue.Value) |
||||
|
{ |
||||
|
errors.Add("MinValue cannot be larger than max value"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static double? ParseNumber(string key, PropertiesBag settings, ICollection<string> errors) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (settings.Contains(key)) |
||||
|
{ |
||||
|
return settings[key].ToNullableDouble(CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
} |
||||
|
catch (InvalidCastException) |
||||
|
{ |
||||
|
errors.Add($"'{key}' is not a valid number"); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected override Task ValidateCoreAsync(PropertyValue property, ICollection<string> errors) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var value = property.ToDouble(CultureInfo.InvariantCulture); |
||||
|
|
||||
|
if (MinValue.HasValue && value < MinValue.Value) |
||||
|
{ |
||||
|
errors.Add($"<Field> must be greater than {MinValue}"); |
||||
|
} |
||||
|
|
||||
|
if (MaxValue.HasValue && value > MaxValue.Value) |
||||
|
{ |
||||
|
errors.Add($"<Field> must be less than {MaxValue}"); |
||||
|
} |
||||
|
} |
||||
|
catch (InvalidCastException) |
||||
|
{ |
||||
|
errors.Add("<Field> is not a valid number"); |
||||
|
} |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
protected override ModelField Clone() |
||||
|
{ |
||||
|
return (ModelField)MemberwiseClone(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
{ |
||||
|
"version": "1.0.0-*", |
||||
|
|
||||
|
"dependencies": { |
||||
|
"EventStore.ClientAPI.DotNetCore": "1.0.0", |
||||
|
"NETStandard.Library": "1.6.0", |
||||
|
"NodaTime": "2.0.0-alpha20160729", |
||||
|
"PinkParrot.Infrastructure": "1.0.0-*", |
||||
|
"protobuf-net": "2.1.0" |
||||
|
}, |
||||
|
|
||||
|
"frameworks": { |
||||
|
"netcoreapp1.0": { |
||||
|
"dependencies": { |
||||
|
"Microsoft.NETCore.App": { |
||||
|
"type": "platform", |
||||
|
"version": "1.0.0" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
"tooling": { |
||||
|
"defaultNamespace": "PinkParrot.Core" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,234 @@ |
|||||
|
## Ignore Visual Studio temporary files, build results, and |
||||
|
## files generated by popular Visual Studio add-ons. |
||||
|
|
||||
|
# User-specific files |
||||
|
*.suo |
||||
|
*.user |
||||
|
*.userosscache |
||||
|
*.sln.docstates |
||||
|
|
||||
|
# User-specific files (MonoDevelop/Xamarin Studio) |
||||
|
*.userprefs |
||||
|
|
||||
|
# Build results |
||||
|
[Dd]ebug/ |
||||
|
[Dd]ebugPublic/ |
||||
|
[Rr]elease/ |
||||
|
[Rr]eleases/ |
||||
|
x64/ |
||||
|
x86/ |
||||
|
build/ |
||||
|
bld/ |
||||
|
[Bb]in/ |
||||
|
[Oo]bj/ |
||||
|
|
||||
|
# Visual Studio 2015 cache/options directory |
||||
|
.vs/ |
||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot |
||||
|
#wwwroot/ |
||||
|
|
||||
|
# MSTest test Results |
||||
|
[Tt]est[Rr]esult*/ |
||||
|
[Bb]uild[Ll]og.* |
||||
|
|
||||
|
# NUNIT |
||||
|
*.VisualState.xml |
||||
|
TestResult.xml |
||||
|
|
||||
|
# Build Results of an ATL Project |
||||
|
[Dd]ebugPS/ |
||||
|
[Rr]eleasePS/ |
||||
|
dlldata.c |
||||
|
|
||||
|
# DNX |
||||
|
project.lock.json |
||||
|
artifacts/ |
||||
|
|
||||
|
*_i.c |
||||
|
*_p.c |
||||
|
*_i.h |
||||
|
*.ilk |
||||
|
*.meta |
||||
|
*.obj |
||||
|
*.pch |
||||
|
*.pdb |
||||
|
*.pgc |
||||
|
*.pgd |
||||
|
*.rsp |
||||
|
*.sbr |
||||
|
*.tlb |
||||
|
*.tli |
||||
|
*.tlh |
||||
|
*.tmp |
||||
|
*.tmp_proj |
||||
|
*.log |
||||
|
*.vspscc |
||||
|
*.vssscc |
||||
|
.builds |
||||
|
*.pidb |
||||
|
*.svclog |
||||
|
*.scc |
||||
|
|
||||
|
# Chutzpah Test files |
||||
|
_Chutzpah* |
||||
|
|
||||
|
# Visual C++ cache files |
||||
|
ipch/ |
||||
|
*.aps |
||||
|
*.ncb |
||||
|
*.opendb |
||||
|
*.opensdf |
||||
|
*.sdf |
||||
|
*.cachefile |
||||
|
|
||||
|
# Visual Studio profiler |
||||
|
*.psess |
||||
|
*.vsp |
||||
|
*.vspx |
||||
|
*.sap |
||||
|
|
||||
|
# TFS 2012 Local Workspace |
||||
|
$tf/ |
||||
|
|
||||
|
# Guidance Automation Toolkit |
||||
|
*.gpState |
||||
|
|
||||
|
# ReSharper is a .NET coding add-in |
||||
|
_ReSharper*/ |
||||
|
*.[Rr]e[Ss]harper |
||||
|
*.DotSettings.user |
||||
|
|
||||
|
# JustCode is a .NET coding add-in |
||||
|
.JustCode |
||||
|
|
||||
|
# TeamCity is a build add-in |
||||
|
_TeamCity* |
||||
|
|
||||
|
# DotCover is a Code Coverage Tool |
||||
|
*.dotCover |
||||
|
|
||||
|
# NCrunch |
||||
|
_NCrunch_* |
||||
|
.*crunch*.local.xml |
||||
|
nCrunchTemp_* |
||||
|
|
||||
|
# MightyMoose |
||||
|
*.mm.* |
||||
|
AutoTest.Net/ |
||||
|
|
||||
|
# Web workbench (sass) |
||||
|
.sass-cache/ |
||||
|
|
||||
|
# Installshield output folder |
||||
|
[Ee]xpress/ |
||||
|
|
||||
|
# DocProject is a documentation generator add-in |
||||
|
DocProject/buildhelp/ |
||||
|
DocProject/Help/*.HxT |
||||
|
DocProject/Help/*.HxC |
||||
|
DocProject/Help/*.hhc |
||||
|
DocProject/Help/*.hhk |
||||
|
DocProject/Help/*.hhp |
||||
|
DocProject/Help/Html2 |
||||
|
DocProject/Help/html |
||||
|
|
||||
|
# Click-Once directory |
||||
|
publish/ |
||||
|
|
||||
|
# Publish Web Output |
||||
|
*.[Pp]ublish.xml |
||||
|
*.azurePubxml |
||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings |
||||
|
# but database connection strings (with potential passwords) will be unencrypted |
||||
|
*.pubxml |
||||
|
*.publishproj |
||||
|
|
||||
|
# NuGet Packages |
||||
|
*.nupkg |
||||
|
# The packages folder can be ignored because of Package Restore |
||||
|
**/packages/* |
||||
|
# except build/, which is used as an MSBuild target. |
||||
|
!**/packages/build/ |
||||
|
# Uncomment if necessary however generally it will be regenerated when needed |
||||
|
#!**/packages/repositories.config |
||||
|
|
||||
|
# Microsoft Azure Build Output |
||||
|
csx/ |
||||
|
*.build.csdef |
||||
|
|
||||
|
# Microsoft Azure Emulator |
||||
|
ecf/ |
||||
|
rcf/ |
||||
|
|
||||
|
# Microsoft Azure ApplicationInsights config file |
||||
|
ApplicationInsights.config |
||||
|
|
||||
|
# Windows Store app package directory |
||||
|
AppPackages/ |
||||
|
BundleArtifacts/ |
||||
|
|
||||
|
# Visual Studio cache files |
||||
|
# files ending in .cache can be ignored |
||||
|
*.[Cc]ache |
||||
|
# but keep track of directories ending in .cache |
||||
|
!*.[Cc]ache/ |
||||
|
|
||||
|
# Others |
||||
|
ClientBin/ |
||||
|
~$* |
||||
|
*~ |
||||
|
*.dbmdl |
||||
|
*.dbproj.schemaview |
||||
|
*.pfx |
||||
|
*.publishsettings |
||||
|
node_modules/ |
||||
|
orleans.codegen.cs |
||||
|
|
||||
|
# RIA/Silverlight projects |
||||
|
Generated_Code/ |
||||
|
|
||||
|
# Backup & report files from converting an old project file |
||||
|
# to a newer Visual Studio version. Backup files are not needed, |
||||
|
# because we have git ;-) |
||||
|
_UpgradeReport_Files/ |
||||
|
Backup*/ |
||||
|
UpgradeLog*.XML |
||||
|
UpgradeLog*.htm |
||||
|
|
||||
|
# SQL Server files |
||||
|
*.mdf |
||||
|
*.ldf |
||||
|
|
||||
|
# Business Intelligence projects |
||||
|
*.rdl.data |
||||
|
*.bim.layout |
||||
|
*.bim_*.settings |
||||
|
|
||||
|
# Microsoft Fakes |
||||
|
FakesAssemblies/ |
||||
|
|
||||
|
# GhostDoc plugin setting file |
||||
|
*.GhostDoc.xml |
||||
|
|
||||
|
# Node.js Tools for Visual Studio |
||||
|
.ntvs_analysis.dat |
||||
|
|
||||
|
# Visual Studio 6 build log |
||||
|
*.plg |
||||
|
|
||||
|
# Visual Studio 6 workspace options file |
||||
|
*.opt |
||||
|
|
||||
|
# Visual Studio LightSwitch build output |
||||
|
**/*.HTMLClient/GeneratedArtifacts |
||||
|
**/*.DesktopClient/GeneratedArtifacts |
||||
|
**/*.DesktopClient/ModelManifest.xml |
||||
|
**/*.Server/GeneratedArtifacts |
||||
|
**/*.Server/ModelManifest.xml |
||||
|
_Pvt_Extensions |
||||
|
|
||||
|
# Paket dependency manager |
||||
|
.paket/paket.exe |
||||
|
|
||||
|
# FAKE - F# Make |
||||
|
.fake/ |
||||
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion> |
||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>25f66c64-058a-4d44-bc0c-f12a054f9a91</ProjectGuid> |
||||
|
<RootNamespace>PinkParrot.Events</RootNamespace> |
||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> |
||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
</Project> |
||||
@ -0,0 +1,22 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelFieldAdded.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelFieldAdded : IEvent |
||||
|
{ |
||||
|
public Guid FieldId { get; set; } |
||||
|
|
||||
|
public string FieldType; |
||||
|
|
||||
|
public string FieldName { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelFieldDeleted.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelFieldDeleted : IEvent |
||||
|
{ |
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelFieldDisabled.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelFieldDisabled : IEvent |
||||
|
{ |
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelFieldEnabled.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelFieldEnabled : IEvent |
||||
|
{ |
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelFieldHidden.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelFieldHidden : IEvent |
||||
|
{ |
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelFieldShown.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelFieldShown : IEvent |
||||
|
{ |
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelFieldUpdated.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelFieldUpdated : IEvent |
||||
|
{ |
||||
|
public Guid FieldId { get; set; } |
||||
|
|
||||
|
public PropertiesBag Settings { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelSchemaCreated.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelSchemaCreated : IEvent |
||||
|
{ |
||||
|
public string Name; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelSchemaDeleted.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelSchemaDeleted : IEvent |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelSchemaUpdated.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using PinkParrot.Infrastructure; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
|
||||
|
namespace PinkParrot.Events.Schema |
||||
|
{ |
||||
|
public class ModelSchemaUpdated : IEvent |
||||
|
{ |
||||
|
public string NewName; |
||||
|
|
||||
|
public PropertiesBag Settings { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
{ |
||||
|
"version": "1.0.0-*", |
||||
|
|
||||
|
"dependencies": { |
||||
|
"NETStandard.Library": "1.6.0", |
||||
|
"NodaTime": "2.0.0-alpha20160729", |
||||
|
"PinkParrot.Infrastructure": "1.0.0-*" |
||||
|
}, |
||||
|
|
||||
|
"frameworks": { |
||||
|
"netcoreapp1.0": { |
||||
|
"dependencies": { |
||||
|
"Microsoft.NETCore.App": { |
||||
|
"type": "platform", |
||||
|
"version": "1.0.0" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
"tooling": { |
||||
|
"defaultNamespace": "PinkParrot.Events" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,169 @@ |
|||||
|
// ==========================================================================
|
||||
|
// CollectionExtensionsTest.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public class CollectionExtensionTest |
||||
|
{ |
||||
|
private readonly Dictionary<int, int> valueDictionary = new Dictionary<int, int>(); |
||||
|
private readonly Dictionary<int, List<int>> listDictionary = new Dictionary<int, List<int>>(); |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrDefault_should_return_value_if_key_exists() |
||||
|
{ |
||||
|
valueDictionary[12] = 34; |
||||
|
|
||||
|
Assert.Equal(34, valueDictionary.GetOrDefault(12)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrDefault_should_return_default_and_not_add_it_if_key_not_exists() |
||||
|
{ |
||||
|
Assert.Equal(0, valueDictionary.GetOrDefault(12)); |
||||
|
Assert.False(valueDictionary.ContainsKey(12)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrAddDefault_should_return_value_if_key_exists() |
||||
|
{ |
||||
|
valueDictionary[12] = 34; |
||||
|
|
||||
|
Assert.Equal(34, valueDictionary.GetOrAddDefault(12)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrAddDefault_should_return_default_and_add_it_if_key_not_exists() |
||||
|
{ |
||||
|
Assert.Equal(0, valueDictionary.GetOrAddDefault(12)); |
||||
|
Assert.Equal(0, valueDictionary[12]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrCreate_should_return_value_if_key_exists() |
||||
|
{ |
||||
|
valueDictionary[12] = 34; |
||||
|
|
||||
|
Assert.Equal(34, valueDictionary.GetOrCreate(12, x => 34)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrCreate_should_return_default_but_not_add_it_if_key_not_exists() |
||||
|
{ |
||||
|
Assert.Equal(24, valueDictionary.GetOrCreate(12, x => 24)); |
||||
|
Assert.False(valueDictionary.ContainsKey(12)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrAdd_should_return_value_if_key_exists() |
||||
|
{ |
||||
|
valueDictionary[12] = 34; |
||||
|
|
||||
|
Assert.Equal(34, valueDictionary.GetOrAdd(12, x => 34)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrAdd_should_return_default_and_add_it_if_key_not_exists() |
||||
|
{ |
||||
|
Assert.Equal(24, valueDictionary.GetOrAdd(12, x => 24)); |
||||
|
Assert.Equal(24, valueDictionary[12]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrNew_should_return_value_if_key_exists() |
||||
|
{ |
||||
|
var list = new List<int>(); |
||||
|
listDictionary[12] = list; |
||||
|
|
||||
|
Assert.Equal(list, listDictionary.GetOrNew(12)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrNew_should_return_default_but_not_add_it_if_key_not_exists() |
||||
|
{ |
||||
|
var list = new List<int>(); |
||||
|
|
||||
|
Assert.Equal(list, listDictionary.GetOrNew(12)); |
||||
|
Assert.False(listDictionary.ContainsKey(12)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrAddNew_should_return_value_if_key_exists() |
||||
|
{ |
||||
|
var list = new List<int>(); |
||||
|
listDictionary[12] = list; |
||||
|
|
||||
|
Assert.Equal(list, listDictionary.GetOrAddNew(12)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetOrAddNew_should_return_default_but_not_add_it_if_key_not_exists() |
||||
|
{ |
||||
|
var list = new List<int>(); |
||||
|
|
||||
|
Assert.Equal(list, listDictionary.GetOrAddNew(12)); |
||||
|
Assert.Equal(list, listDictionary[12]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void SequentialHashCode_should_return_same_hash_codes_for_list_with_same_order() |
||||
|
{ |
||||
|
var collection1 = new[] { 3, 5, 6 }; |
||||
|
var collection2 = new[] { 3, 5, 6 }; |
||||
|
|
||||
|
Assert.Equal(collection2.SequentialHashCode(), collection1.SequentialHashCode()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void SequentialHashCode_should_return_different_hash_codes_for_list_with_different_items() |
||||
|
{ |
||||
|
var collection1 = new[] { 3, 5, 6 }; |
||||
|
var collection2 = new[] { 3, 4, 1 }; |
||||
|
|
||||
|
Assert.NotEqual(collection2.SequentialHashCode(), collection1.SequentialHashCode()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void SequentialHashCode_should_return_different_hash_codes_for_list_with_different_order() |
||||
|
{ |
||||
|
var collection1 = new[] { 3, 5, 6 }; |
||||
|
var collection2 = new[] { 6, 5, 3 }; |
||||
|
|
||||
|
Assert.NotEqual(collection2.SequentialHashCode(), collection1.SequentialHashCode()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void OrderedHashCode_should_return_same_hash_codes_for_list_with_same_order() |
||||
|
{ |
||||
|
var collection1 = new[] { 3, 5, 6 }; |
||||
|
var collection2 = new[] { 3, 5, 6 }; |
||||
|
|
||||
|
Assert.Equal(collection2.OrderedHashCode(), collection1.OrderedHashCode()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void OrderedHashCode_should_return_different_hash_codes_for_list_with_different_items() |
||||
|
{ |
||||
|
var collection1 = new[] { 3, 5, 6 }; |
||||
|
var collection2 = new[] { 3, 4, 1 }; |
||||
|
|
||||
|
Assert.NotEqual(collection2.OrderedHashCode(), collection1.OrderedHashCode()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void OrderedHashCode_should_return_same_hash_codes_for_list_with_different_order() |
||||
|
{ |
||||
|
var collection1 = new[] { 3, 5, 6 }; |
||||
|
var collection2 = new[] { 6, 5, 3 }; |
||||
|
|
||||
|
Assert.Equal(collection2.OrderedHashCode(), collection1.OrderedHashCode()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
// ==========================================================================
|
||||
|
// EnumExtensionsTest.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public sealed class EnumExtensionsTest |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_return_true_if_enum_is_valid() |
||||
|
{ |
||||
|
Assert.True(DateTimeKind.Local.IsEnumValue()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_return_false_if_enum_is_not_valid() |
||||
|
{ |
||||
|
Assert.False(((DateTimeKind)13).IsEnumValue()); |
||||
|
Assert.False(123.IsEnumValue()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,128 @@ |
|||||
|
// ==========================================================================
|
||||
|
// GuardTests.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public class GuardTest |
||||
|
{ |
||||
|
[Theory] |
||||
|
[InlineData("")] |
||||
|
[InlineData(" ")] |
||||
|
public void Should_throw_when_target_is_null_for_empty_string(string invalidString) |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => Guard.NotNullOrEmpty(invalidString, "parameter")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_do_nothing_if_target_string_is_valid() |
||||
|
{ |
||||
|
Guard.NotNullOrEmpty("value", "parameter"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_do_nothing_if_target_is_not_null() |
||||
|
{ |
||||
|
Guard.NotNull("value", "parameter"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_do_nothing_if_enum_is_valid() |
||||
|
{ |
||||
|
Guard.Enum(DateTimeKind.Local, "Parameter"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_enum_is_not_valid() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => Guard.Enum((DateTimeKind)13, "Parameter")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_do_nothing_when_guid_is_not_empty() |
||||
|
{ |
||||
|
Guard.NotEmpty(Guid.NewGuid(), "parameter"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_guid_is_empty() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => Guard.NotEmpty(Guid.Empty, "parameter")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_target_is_null() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentNullException>(() => Guard.NotNull(null, "parameter")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_target_is_null_for_null_string() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentNullException>(() => Guard.NotNullOrEmpty(null, "parameter")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_do_nothing_when_target_has_correct_type() |
||||
|
{ |
||||
|
Guard.HasType<int>(123, "parameter"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_target_has_wrong_type() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => Guard.HasType<int>("value", "parameter")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_checking_for_null_and_target_is_null() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentNullException>(() => Guard.HasType<int>(null, "parameter")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_do_nothing_when_target_is_not_default_value() |
||||
|
{ |
||||
|
Guard.NotDefault(Guid.NewGuid(), "parameter"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_exception_when_value_has_default() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => Guard.NotDefault(Guid.Empty, "parameter")); |
||||
|
Assert.Throws<ArgumentException>(() => Guard.NotDefault(0, "parameter")); |
||||
|
Assert.Throws<ArgumentException>(() => Guard.NotDefault((string)null, "parameter")); |
||||
|
Assert.Throws<ArgumentException>(() => Guard.NotDefault(false, "parameter")); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData("")] |
||||
|
[InlineData(" ")] |
||||
|
[InlineData(" Not a Slug ")] |
||||
|
[InlineData(" not--a--slug ")] |
||||
|
[InlineData(" not-a-slug ")] |
||||
|
[InlineData("-not-a-slug-")] |
||||
|
[InlineData("not$-a-slug")] |
||||
|
public void Should_throw_exception_for_invalid_slug(string slug) |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => Guard.ValidSlug(slug, "slug")); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData("slug")] |
||||
|
[InlineData("slug23")] |
||||
|
[InlineData("other-slug")] |
||||
|
[InlineData("just-another-slug")] |
||||
|
public void Should_do_nothing_for_valid_slug(string slug) |
||||
|
{ |
||||
|
Guard.ValidSlug(slug, "parameter"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> |
||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>840c02b1-48f8-4c8a-8862-8a3fdefde8d5</ProjectGuid> |
||||
|
<RootNamespace>PinkParrot.Infrastructure</RootNamespace> |
||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> |
||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> |
||||
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
</Project> |
||||
@ -0,0 +1,281 @@ |
|||||
|
// ==========================================================================
|
||||
|
// PropertiesBagTests.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using System.Linq; |
||||
|
using NodaTime; |
||||
|
using Xunit; |
||||
|
|
||||
|
// ReSharper disable PossibleInvalidOperationException
|
||||
|
// ReSharper disable UnusedParameter.Local
|
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public class PropertiesBagTest |
||||
|
{ |
||||
|
private readonly CultureInfo c = CultureInfo.InvariantCulture; |
||||
|
private readonly PropertiesBag bag = new PropertiesBag(); |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_return_false_when_renaming_unknown_property() |
||||
|
{ |
||||
|
Assert.False(bag.Rename("OldKey", "NewKey")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_renaming_to_existing_property() |
||||
|
{ |
||||
|
bag.Set("NewKey", 1); |
||||
|
|
||||
|
Assert.Throws<ArgumentException>(() => bag.Rename("OldKey", "NewKey")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_renaming_to_same_key() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => bag.Rename("SameKey", "SameKey")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_provide_property_with_new_name_after_rename() |
||||
|
{ |
||||
|
bag.Set("OldKey", 123); |
||||
|
|
||||
|
Assert.True(bag.Rename("OldKey", "NewKey")); |
||||
|
Assert.True(bag.Contains("NewKey")); |
||||
|
|
||||
|
Assert.Equal(1, bag.Count); |
||||
|
Assert.Equal(123, bag["NewKey"].ToInt32(c)); |
||||
|
|
||||
|
Assert.False(bag.Contains("OldKey")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_calculate_count_correctly() |
||||
|
{ |
||||
|
bag.Set("Key1", 1); |
||||
|
bag.Set("Key2", 1); |
||||
|
|
||||
|
Assert.Equal(2, bag.Count); |
||||
|
Assert.Equal(new[] { "Key1", "Key2" }, bag.PropertyNames.ToArray()); |
||||
|
Assert.Equal(new[] { "Key1", "Key2" }, bag.Properties.Keys.ToArray()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_return_correct_value_when_contains_check() |
||||
|
{ |
||||
|
Assert.False(bag.Contains("Key")); |
||||
|
|
||||
|
bag.Set("Key", 1); |
||||
|
|
||||
|
Assert.True(bag.Contains("Key")); |
||||
|
Assert.True(bag.Contains("KEY")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_returne_false_when_property_to_rename_does_not_exist() |
||||
|
{ |
||||
|
Assert.False(bag.Remove("NOTFOUND")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_ignore_casing_when_returning() |
||||
|
{ |
||||
|
bag.Set("Key", 1); |
||||
|
|
||||
|
Assert.True(bag.Remove("KEY")); |
||||
|
Assert.False(bag.Contains("KEY")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_setting_value_with_invalid_type() |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => bag.Set("Key", (byte)1)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_string_to_numbers() |
||||
|
{ |
||||
|
bag.Set("Key", 123); |
||||
|
|
||||
|
AssertNumber(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_int_to_numbers() |
||||
|
{ |
||||
|
bag.Set("Key", 123); |
||||
|
|
||||
|
AssertNumber(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_long_to_numbers() |
||||
|
{ |
||||
|
bag.Set("Key", 123L); |
||||
|
|
||||
|
AssertNumber(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_casting_from_large_long() |
||||
|
{ |
||||
|
bag.Set("Key", long.MaxValue); |
||||
|
|
||||
|
Assert.Throws<InvalidCastException>(() => bag["Key"].ToInt32(c)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_float_to_number() |
||||
|
{ |
||||
|
bag.Set("Key", 123f); |
||||
|
|
||||
|
AssertNumber(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_double_to_number() |
||||
|
{ |
||||
|
bag.Set("Key", 123d); |
||||
|
|
||||
|
AssertNumber(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_casting_from_large_doule() |
||||
|
{ |
||||
|
bag.Set("Key", double.MaxValue); |
||||
|
|
||||
|
Assert.Equal(float.PositiveInfinity, bag["Key"].ToSingle(c)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_from_instant_value() |
||||
|
{ |
||||
|
var time = SystemClock.Instance.GetCurrentInstant(); |
||||
|
|
||||
|
bag.Set("Key", time); |
||||
|
|
||||
|
AssertInstant(time); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_from_instant_string() |
||||
|
{ |
||||
|
var time = SystemClock.Instance.GetCurrentInstant(); |
||||
|
|
||||
|
bag.Set("Key", time.ToString()); |
||||
|
|
||||
|
AssertInstant(time); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_from_guid_value() |
||||
|
{ |
||||
|
var id = new Guid(); |
||||
|
|
||||
|
bag.Set("Key", id); |
||||
|
|
||||
|
AssertGuid(id); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_from_guid_string() |
||||
|
{ |
||||
|
var id = new Guid(); |
||||
|
|
||||
|
bag.Set("Key", id.ToString()); |
||||
|
|
||||
|
AssertGuid(id); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_from_boolean_value() |
||||
|
{ |
||||
|
bag.Set("Key", true); |
||||
|
|
||||
|
AssertBoolean(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_from_boolean_string() |
||||
|
{ |
||||
|
bag.Set("Key", "true"); |
||||
|
|
||||
|
AssertBoolean(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_boolean_from_number() |
||||
|
{ |
||||
|
bag.Set("Key", 1); |
||||
|
|
||||
|
AssertBoolean(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_when_converting_instant_to_number() |
||||
|
{ |
||||
|
bag.Set("Key", SystemClock.Instance.GetCurrentInstant()); |
||||
|
|
||||
|
Assert.Throws<InvalidCastException>(() => bag["Key"].ToGuid(CultureInfo.InvariantCulture)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_return_default_when_property_value_is_null() |
||||
|
{ |
||||
|
bag.Set("Key", null); |
||||
|
|
||||
|
Assert.Equal(null, bag["Key"].ToString()); |
||||
|
|
||||
|
Assert.Equal(0f, bag["Key"].ToSingle(CultureInfo.CurrentCulture)); |
||||
|
Assert.Equal(0d, bag["Key"].ToDouble(CultureInfo.CurrentCulture)); |
||||
|
Assert.Equal(0L, bag["Key"].ToInt64(CultureInfo.CurrentCulture)); |
||||
|
Assert.Equal(0, bag["Key"].ToInt32(CultureInfo.CurrentCulture)); |
||||
|
|
||||
|
Assert.Equal(false, bag["Key"].ToBoolean(CultureInfo.CurrentCulture)); |
||||
|
|
||||
|
Assert.Equal(new Guid(), bag["Key"].ToGuid(CultureInfo.CurrentCulture)); |
||||
|
|
||||
|
Assert.Equal(new Instant(), bag["Key"].ToInstant(CultureInfo.CurrentCulture)); |
||||
|
} |
||||
|
|
||||
|
private void AssertBoolean() |
||||
|
{ |
||||
|
Assert.True(bag["Key"].ToBoolean(c)); |
||||
|
Assert.True(bag["Key"].ToNullableBoolean(c)); |
||||
|
} |
||||
|
|
||||
|
private void AssertInstant(Instant expected) |
||||
|
{ |
||||
|
Assert.Equal(expected.ToUnixTimeSeconds(), bag["Key"].ToInstant(c).ToUnixTimeSeconds()); |
||||
|
Assert.Equal(expected.ToUnixTimeSeconds(), bag["Key"].ToNullableInstant(c).Value.ToUnixTimeSeconds()); |
||||
|
} |
||||
|
|
||||
|
private void AssertGuid(Guid expected) |
||||
|
{ |
||||
|
Assert.Equal(expected, bag["Key"].ToGuid(c)); |
||||
|
Assert.Equal(expected, bag["Key"].ToNullableGuid(c)); |
||||
|
} |
||||
|
|
||||
|
private void AssertNumber() |
||||
|
{ |
||||
|
Assert.Equal(123, bag["Key"].ToInt32(c)); |
||||
|
Assert.Equal(123, bag["Key"].ToNullableInt32(c)); |
||||
|
Assert.Equal(123L, bag["Key"].ToInt64(c)); |
||||
|
Assert.Equal(123L, bag["Key"].ToNullableInt64(c)); |
||||
|
Assert.Equal(123f, bag["Key"].ToSingle(c)); |
||||
|
Assert.Equal(123f, bag["Key"].ToNullableSingle(c)); |
||||
|
Assert.Equal(123d, bag["Key"].ToDouble(c)); |
||||
|
Assert.Equal(123d, bag["Key"].ToNullableDouble(c)); |
||||
|
|
||||
|
Assert.True(bag["Key"].ToBoolean(c)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"version": "1.0.0-*", |
||||
|
"testRunner": "xunit", |
||||
|
"dependencies": { |
||||
|
"dotnet-test-xunit": "2.2.0-preview2-build1029", |
||||
|
"PinkParrot.Infrastructure": "1.0.0-*", |
||||
|
"xunit": "2.2.0-beta2-build3300" |
||||
|
}, |
||||
|
"frameworks": { |
||||
|
"netcoreapp1.0": { |
||||
|
"dependencies": { |
||||
|
"Microsoft.NETCore.App": { |
||||
|
"type": "platform", |
||||
|
"version": "1.0.0" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"buildOptions": { |
||||
|
"copyToOutput": { |
||||
|
"include": [ |
||||
|
"xunit.runner.json" |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
"tooling": { |
||||
|
"defaultNamespace": "PinkParrot.Core.Tests" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
{ |
||||
|
"diagnosticMessages": false, |
||||
|
"methodDisplay": "classAndMethod", |
||||
|
"parallelizeTestCollections": true |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// CommonHeaders.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
namespace PinkParrot.Infrastructure.CQRS |
||||
|
{ |
||||
|
public sealed class CommonHeaders |
||||
|
{ |
||||
|
public const string AggregateId = "AggregateId"; |
||||
|
public const string CommitId = "CommitId"; |
||||
|
public const string Timestamp = "Timestamp"; |
||||
|
public const string EventId = "EventId"; |
||||
|
public const string EventType = "EventType"; |
||||
|
public const string EventNumber = "EventNumber"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
// ==========================================================================
|
||||
|
// DomainObject.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure.CQRS |
||||
|
{ |
||||
|
public abstract class DomainObject : IAggregate, IEquatable<IAggregate> |
||||
|
{ |
||||
|
private readonly List<Envelope<IEvent>> uncomittedEvents = new List<Envelope<IEvent>>(); |
||||
|
private readonly Guid id; |
||||
|
private int version; |
||||
|
|
||||
|
public int Version |
||||
|
{ |
||||
|
get { return version; } |
||||
|
} |
||||
|
|
||||
|
public Guid Id |
||||
|
{ |
||||
|
get { return id; } |
||||
|
} |
||||
|
|
||||
|
protected DomainObject(Guid id, int version) |
||||
|
{ |
||||
|
Guard.NotEmpty(id, nameof(id)); |
||||
|
Guard.GreaterEquals(version, 0, nameof(version)); |
||||
|
|
||||
|
this.id = id; |
||||
|
|
||||
|
this.version = version; |
||||
|
} |
||||
|
|
||||
|
protected void RaiseEvent<TEvent>(Envelope<TEvent> envelope, bool disableApply = false) where TEvent : class, IEvent |
||||
|
{ |
||||
|
Guard.NotNull(envelope, nameof(envelope)); |
||||
|
|
||||
|
uncomittedEvents.Add(envelope.To<IEvent>()); |
||||
|
|
||||
|
if (!disableApply) |
||||
|
{ |
||||
|
ApplyEvent(envelope.Payload); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void RaiseEvent(IEvent @event, bool disableApply = false) |
||||
|
{ |
||||
|
Guard.NotNull(@event, nameof(@event)); |
||||
|
|
||||
|
uncomittedEvents.Add(EnvelopeFactory.ForEvent(@event, id)); |
||||
|
|
||||
|
if (!disableApply) |
||||
|
{ |
||||
|
ApplyEvent(@event); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void Apply(object @event) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private void ApplyEvent(dynamic @event) |
||||
|
{ |
||||
|
Apply(@event); |
||||
|
version++; |
||||
|
} |
||||
|
|
||||
|
void IAggregate.ApplyEvent(IEvent @event) |
||||
|
{ |
||||
|
Apply(@event as dynamic); |
||||
|
version++; |
||||
|
} |
||||
|
|
||||
|
void IAggregate.ClearUncommittedEvents() |
||||
|
{ |
||||
|
uncomittedEvents.Clear(); |
||||
|
} |
||||
|
|
||||
|
ICollection<Envelope<IEvent>> IAggregate.GetUncomittedEvents() |
||||
|
{ |
||||
|
return uncomittedEvents; |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return id.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
return Equals(obj as IAggregate); |
||||
|
} |
||||
|
|
||||
|
public bool Equals(IAggregate other) |
||||
|
{ |
||||
|
return other != null && other.Id.Equals(id); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Envelope.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
namespace PinkParrot.Infrastructure.CQRS |
||||
|
{ |
||||
|
public class Envelope<TPayload> where TPayload : class |
||||
|
{ |
||||
|
private readonly EnvelopeHeaders headers; |
||||
|
private readonly TPayload payload; |
||||
|
|
||||
|
public EnvelopeHeaders Headers |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return headers; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public TPayload Payload |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return payload; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Envelope(TPayload payload) |
||||
|
{ |
||||
|
Guard.NotNull(payload, nameof(payload)); |
||||
|
|
||||
|
this.payload = payload; |
||||
|
|
||||
|
headers = new EnvelopeHeaders(); |
||||
|
} |
||||
|
|
||||
|
public Envelope(TPayload payload, EnvelopeHeaders headers) |
||||
|
{ |
||||
|
Guard.NotNull(payload, nameof(payload)); |
||||
|
Guard.NotNull(headers, nameof(headers)); |
||||
|
|
||||
|
this.payload = payload; |
||||
|
this.headers = headers; |
||||
|
} |
||||
|
|
||||
|
public Envelope<TOther> To<TOther>() where TOther : class |
||||
|
{ |
||||
|
return new Envelope<TOther>(payload as TOther, headers.Clone()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
// ==========================================================================
|
||||
|
// EnvelopeExtensions.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using NodaTime; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure.CQRS |
||||
|
{ |
||||
|
public static class EnvelopeExtensions |
||||
|
{ |
||||
|
public static int EventNumber<T>(this Envelope<T> envelope) where T : class |
||||
|
{ |
||||
|
return envelope.Headers[CommonHeaders.EventNumber].ToInt32(CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
|
||||
|
public static Envelope<T> SetEventNumber<T>(this Envelope<T> envelope, int value) where T : class |
||||
|
{ |
||||
|
envelope.Headers.Set(CommonHeaders.EventNumber, value); |
||||
|
|
||||
|
return envelope; |
||||
|
} |
||||
|
|
||||
|
public static Guid CommitId<T>(this Envelope<T> envelope) where T : class |
||||
|
{ |
||||
|
return envelope.Headers[CommonHeaders.CommitId].ToGuid(CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
|
||||
|
public static Envelope<T> SetCommitId<T>(this Envelope<T> envelope, Guid value) where T : class |
||||
|
{ |
||||
|
envelope.Headers.Set(CommonHeaders.CommitId, value); |
||||
|
|
||||
|
return envelope; |
||||
|
} |
||||
|
|
||||
|
public static Guid AggregateId<T>(this Envelope<T> envelope) where T : class |
||||
|
{ |
||||
|
return envelope.Headers[CommonHeaders.AggregateId].ToGuid(CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
|
||||
|
public static Envelope<T> SetAggregateId<T>(this Envelope<T> envelope, Guid value) where T : class |
||||
|
{ |
||||
|
envelope.Headers.Set(CommonHeaders.AggregateId, value); |
||||
|
|
||||
|
return envelope; |
||||
|
} |
||||
|
|
||||
|
public static Guid EventId<T>(this Envelope<T> envelope) where T : class |
||||
|
{ |
||||
|
return envelope.Headers[CommonHeaders.EventId].ToGuid(CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
|
||||
|
public static Envelope<T> SetEventId<T>(this Envelope<T> envelope, Guid value) where T : class |
||||
|
{ |
||||
|
envelope.Headers.Set(CommonHeaders.EventId, value); |
||||
|
|
||||
|
return envelope; |
||||
|
} |
||||
|
|
||||
|
public static Instant Timestamp<T>(this Envelope<T> envelope) where T : class |
||||
|
{ |
||||
|
return envelope.Headers[CommonHeaders.Timestamp].ToInstant(CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
|
||||
|
public static Envelope<T> SetTimestamp<T>(this Envelope<T> envelope, Instant value) where T : class |
||||
|
{ |
||||
|
envelope.Headers.Set(CommonHeaders.Timestamp, value); |
||||
|
|
||||
|
return envelope; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// ==========================================================================
|
||||
|
// EnvelopeFactory.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using NodaTime; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure.CQRS |
||||
|
{ |
||||
|
public static class EnvelopeFactory |
||||
|
{ |
||||
|
public static Envelope<IEvent> ForEvent(IEvent @event, Guid aggregateId) |
||||
|
{ |
||||
|
return new Envelope<IEvent>(@event) |
||||
|
.SetAggregateId(aggregateId) |
||||
|
.SetEventId(aggregateId) |
||||
|
.SetTimestamp(SystemClock.Instance.GetCurrentInstant()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// ==========================================================================
|
||||
|
// EnvelopeHeaders.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
namespace PinkParrot.Infrastructure.CQRS |
||||
|
{ |
||||
|
public sealed class EnvelopeHeaders : PropertiesBag |
||||
|
{ |
||||
|
public EnvelopeHeaders Clone() |
||||
|
{ |
||||
|
var clone = new EnvelopeHeaders(); |
||||
|
|
||||
|
foreach (var property in Properties) |
||||
|
{ |
||||
|
clone.Set(property.Key, property.Value.RawValue); |
||||
|
} |
||||
|
|
||||
|
return clone; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
// ==========================================================================
|
||||
|
// IAggregate.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure.CQRS |
||||
|
{ |
||||
|
public interface IAggregate |
||||
|
{ |
||||
|
Guid Id { get; } |
||||
|
|
||||
|
int Version { get; } |
||||
|
|
||||
|
void ApplyEvent(IEvent @event); |
||||
|
|
||||
|
void ClearUncommittedEvents(); |
||||
|
|
||||
|
ICollection<Envelope<IEvent>> GetUncomittedEvents(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,13 @@ |
|||||
|
// ==========================================================================
|
||||
|
// IEvent.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
namespace PinkParrot.Infrastructure.CQRS |
||||
|
{ |
||||
|
public interface IEvent |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,110 @@ |
|||||
|
// ==========================================================================
|
||||
|
// CollectionExtensions.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
// ReSharper disable InvertIf
|
||||
|
// ReSharper disable LoopCanBeConvertedToQuery
|
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public static class CollectionExtensions |
||||
|
{ |
||||
|
public static int SequentialHashCode<T>(this IEnumerable<T> collection) |
||||
|
{ |
||||
|
return collection.SequentialHashCode(EqualityComparer<T>.Default); |
||||
|
} |
||||
|
|
||||
|
public static int SequentialHashCode<T>(this IEnumerable<T> collection, IEqualityComparer<T> comparer) |
||||
|
{ |
||||
|
var hashCode = 17; |
||||
|
|
||||
|
foreach (var item in collection) |
||||
|
{ |
||||
|
if (item != null) |
||||
|
{ |
||||
|
hashCode = hashCode * 23 + item.GetHashCode(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return hashCode; |
||||
|
} |
||||
|
|
||||
|
public static int OrderedHashCode<T>(this IEnumerable<T> collection) |
||||
|
{ |
||||
|
return collection.OrderedHashCode(EqualityComparer<T>.Default); |
||||
|
} |
||||
|
|
||||
|
public static int OrderedHashCode<T>(this IEnumerable<T> collection, IEqualityComparer<T> comparer) |
||||
|
{ |
||||
|
var hashCodes = collection.Where(x => x != null).Select(x => x.GetHashCode()).OrderBy(x => x).ToArray(); |
||||
|
|
||||
|
var hashCode = 17; |
||||
|
|
||||
|
foreach (var code in hashCodes) |
||||
|
{ |
||||
|
hashCode = hashCode * 23 + code; |
||||
|
} |
||||
|
|
||||
|
return hashCode; |
||||
|
} |
||||
|
|
||||
|
public static bool EqualsDictionary<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, IDictionary<TKey, TValue> other) |
||||
|
{ |
||||
|
return Equals(dictionary, other) || (other != null && dictionary.Count == other.Count && !dictionary.Except(other).Any()); |
||||
|
} |
||||
|
|
||||
|
public static TValue GetOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key) |
||||
|
{ |
||||
|
return dictionary.GetOrCreate(key, _ => default(TValue)); |
||||
|
} |
||||
|
|
||||
|
public static TValue GetOrAddDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) |
||||
|
{ |
||||
|
return dictionary.GetOrAdd(key, _ => default(TValue)); |
||||
|
} |
||||
|
|
||||
|
public static TValue GetOrNew<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key) where TValue : class, new() |
||||
|
{ |
||||
|
return dictionary.GetOrCreate(key, _ => new TValue()); |
||||
|
} |
||||
|
|
||||
|
public static TValue GetOrAddNew<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) where TValue : class, new() |
||||
|
{ |
||||
|
return dictionary.GetOrAdd(key, _ => new TValue()); |
||||
|
} |
||||
|
|
||||
|
public static TValue GetOrCreate<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> creator) |
||||
|
{ |
||||
|
TValue result; |
||||
|
|
||||
|
if (!dictionary.TryGetValue(key, out result)) |
||||
|
{ |
||||
|
result = creator(key); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> creator) |
||||
|
{ |
||||
|
TValue result; |
||||
|
|
||||
|
if (!dictionary.TryGetValue(key, out result)) |
||||
|
{ |
||||
|
result = creator(key); |
||||
|
|
||||
|
dictionary.Add(key, result); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
// ==========================================================================
|
||||
|
// DomainValidationException.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public class DomainValidationException : Exception |
||||
|
{ |
||||
|
private readonly IReadOnlyList<string> errors; |
||||
|
|
||||
|
public IReadOnlyList<string> Errors |
||||
|
{ |
||||
|
get { return errors; } |
||||
|
} |
||||
|
|
||||
|
public DomainValidationException(string message, params string[] errors) |
||||
|
: base(message) |
||||
|
{ |
||||
|
this.errors = errors != null ? errors.ToList() : new List<string>(); |
||||
|
} |
||||
|
|
||||
|
public DomainValidationException(string message, IReadOnlyList<string> errors) |
||||
|
: base(message) |
||||
|
{ |
||||
|
this.errors = errors ?? new List<string>(); |
||||
|
} |
||||
|
|
||||
|
public DomainValidationException(string message, Exception inner, params string[] errors) |
||||
|
: base(message, inner) |
||||
|
{ |
||||
|
this.errors = errors != null ? errors.ToList() : new List<string>(); |
||||
|
} |
||||
|
|
||||
|
public DomainValidationException(string message, Exception inner, IReadOnlyList<string> errors) |
||||
|
: base(message, inner) |
||||
|
{ |
||||
|
this.errors = errors ?? new List<string>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
// ==========================================================================
|
||||
|
// EnumExtensions.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public static class EnumExtensions |
||||
|
{ |
||||
|
public static bool IsEnumValue<TEnum>(this TEnum value) where TEnum : struct |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
return Enum.IsDefined(typeof(TEnum), value); |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Extensions.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text.RegularExpressions; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public static class Extensions |
||||
|
{ |
||||
|
private static readonly Regex SlugRegex = new Regex("^[a-z0-9]+(\\-[a-z0-9]+)*$", RegexOptions.Compiled); |
||||
|
|
||||
|
public static bool IsSlug(this string value) |
||||
|
{ |
||||
|
return value != null && SlugRegex.IsMatch(value); |
||||
|
} |
||||
|
|
||||
|
public static bool IsBetween<TValue>(this TValue value, TValue low, TValue high) where TValue : IComparable |
||||
|
{ |
||||
|
return Comparer<TValue>.Default.Compare(low, value) <= 0 && Comparer<TValue>.Default.Compare(high, value) >= 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,240 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Guard.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.Globalization; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
// ReSharper disable InvertIf
|
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public static class Guard |
||||
|
{ |
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ValidNumber(float target, string parameterName) |
||||
|
{ |
||||
|
if (float.IsNaN(target) || float.IsPositiveInfinity(target) || float.IsNegativeInfinity(target)) |
||||
|
{ |
||||
|
throw new ArgumentException("Value must be a valid number.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ValidNumber(double target, string parameterName) |
||||
|
{ |
||||
|
if (double.IsNaN(target) || double.IsPositiveInfinity(target) || double.IsNegativeInfinity(target)) |
||||
|
{ |
||||
|
throw new ArgumentException("Value must be a valid number.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ValidSlug(string target, string parameterName) |
||||
|
{ |
||||
|
NotNullOrEmpty(target, parameterName); |
||||
|
|
||||
|
if (!target.IsSlug()) |
||||
|
{ |
||||
|
throw new ArgumentException("Target is not a valid slug.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void HasType<T>(object target, string parameterName) |
||||
|
{ |
||||
|
NotNull(target, "parameterName"); |
||||
|
|
||||
|
if (target.GetType() != typeof(T)) |
||||
|
{ |
||||
|
throw new ArgumentException("The parameter must be of type " + typeof(T), parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Between<TValue>(TValue target, TValue lower, TValue upper, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (!target.IsBetween(lower, upper)) |
||||
|
{ |
||||
|
var message = string.Format(CultureInfo.CurrentCulture, "Value must be between {0} and {1}", lower, upper); |
||||
|
|
||||
|
throw new ArgumentException(message, parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Enum<TEnum>(TEnum target, string parameterName) where TEnum : struct |
||||
|
{ |
||||
|
if (!target.IsEnumValue()) |
||||
|
{ |
||||
|
var message = string.Format(CultureInfo.CurrentCulture, "Value must be a valid enum type {0}", typeof(TEnum)); |
||||
|
|
||||
|
throw new ArgumentException(message, parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void GreaterThan<TValue>(TValue target, TValue lower, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (target.CompareTo(lower) <= 0) |
||||
|
{ |
||||
|
var message = string.Format(CultureInfo.CurrentCulture, "Value must be greater than {0}", lower); |
||||
|
|
||||
|
throw new ArgumentException(message, parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void GreaterEquals<TValue>(TValue target, TValue lower, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (target.CompareTo(lower) < 0) |
||||
|
{ |
||||
|
var message = string.Format(CultureInfo.CurrentCulture, "Value must be greater than {0}", lower); |
||||
|
|
||||
|
throw new ArgumentException(message, parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void LessThan<TValue>(TValue target, TValue upper, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (target.CompareTo(upper) >= 0) |
||||
|
{ |
||||
|
var message = string.Format(CultureInfo.CurrentCulture, "Value must be less than {0}", upper); |
||||
|
|
||||
|
throw new ArgumentException(message, parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void LessEquals<TValue>(TValue target, TValue upper, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (target.CompareTo(upper) > 0) |
||||
|
{ |
||||
|
var message = string.Format(CultureInfo.CurrentCulture, "Value must be less than {0}", upper); |
||||
|
|
||||
|
throw new ArgumentException(message, parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotEmpty<TType>(ICollection<TType> enumerable, string parameterName) |
||||
|
{ |
||||
|
if (enumerable == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(enumerable)); |
||||
|
} |
||||
|
|
||||
|
if (enumerable.Count == 0) |
||||
|
{ |
||||
|
throw new ArgumentException("Collection does not contain an item", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotEmpty(Guid target, string parameterName) |
||||
|
{ |
||||
|
if (target == Guid.Empty) |
||||
|
{ |
||||
|
throw new ArgumentException("Value cannot be empty.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotNull(object target, string parameterName) |
||||
|
{ |
||||
|
if (target == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotDefault<T>(T target, string parameterName) |
||||
|
{ |
||||
|
if (Equals(target, default(T))) |
||||
|
{ |
||||
|
throw new ArgumentException("Value cannot be an the default value", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotNullOrEmpty(string target, string parameterName, bool allowWhitespacesAtStartOrEnd = true) |
||||
|
{ |
||||
|
if (target == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(parameterName); |
||||
|
} |
||||
|
|
||||
|
if (string.IsNullOrWhiteSpace(target)) |
||||
|
{ |
||||
|
throw new ArgumentException("String parameter cannot be null or empty and cannot contain only blanks.", parameterName); |
||||
|
} |
||||
|
|
||||
|
if (!allowWhitespacesAtStartOrEnd && target.Trim() != target) |
||||
|
{ |
||||
|
throw new ArgumentException("String cannot start or end with whitespaces", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ValidFileName(string target, string parameterName) |
||||
|
{ |
||||
|
NotNullOrEmpty(target, parameterName); |
||||
|
|
||||
|
if (target.Intersect(Path.GetInvalidFileNameChars()).Any()) |
||||
|
{ |
||||
|
throw new ArgumentException("Value contains an invalid character.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void IsType<T>(object target, string parameterName) |
||||
|
{ |
||||
|
if (target != null && target.GetType() != typeof(T)) |
||||
|
{ |
||||
|
var message = string.Format(CultureInfo.CurrentCulture, "Value must be of type {0}", typeof(T)); |
||||
|
|
||||
|
throw new ArgumentException(message, parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void IsType(object target, Type expectedType, string parameterName) |
||||
|
{ |
||||
|
if (target != null && expectedType != null && target.GetType() != expectedType) |
||||
|
{ |
||||
|
var message = string.Format(CultureInfo.CurrentCulture, "Value must be of type {0}", expectedType); |
||||
|
|
||||
|
throw new ArgumentException(message, parameterName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> |
||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>bd1c30a8-8ffa-4a92-a9bd-b67b1cddd84c</ProjectGuid> |
||||
|
<RootNamespace>PinkParrot.Infrastructure</RootNamespace> |
||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> |
||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> |
||||
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
</Project> |
||||
@ -0,0 +1,94 @@ |
|||||
|
// ==========================================================================
|
||||
|
// PropertiesBag.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public class PropertiesBag |
||||
|
{ |
||||
|
private readonly Dictionary<string, PropertyValue> internalDictionary = new Dictionary<string, PropertyValue>(StringComparer.OrdinalIgnoreCase); |
||||
|
|
||||
|
public int Count |
||||
|
{ |
||||
|
get { return internalDictionary.Count; } |
||||
|
} |
||||
|
|
||||
|
public IReadOnlyDictionary<string, PropertyValue> Properties |
||||
|
{ |
||||
|
get { return internalDictionary; } |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<string> PropertyNames |
||||
|
{ |
||||
|
get { return internalDictionary.Keys; } |
||||
|
} |
||||
|
|
||||
|
public PropertyValue this[string propertyName] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); |
||||
|
|
||||
|
return internalDictionary[propertyName]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool Contains(string propertyName) |
||||
|
{ |
||||
|
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); |
||||
|
|
||||
|
return internalDictionary.ContainsKey(propertyName); |
||||
|
} |
||||
|
|
||||
|
public PropertiesBag Set(string propertyName, object value) |
||||
|
{ |
||||
|
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); |
||||
|
|
||||
|
internalDictionary[propertyName] = new PropertyValue(value); |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public bool Remove(string propertyName) |
||||
|
{ |
||||
|
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); |
||||
|
|
||||
|
return internalDictionary.Remove(propertyName); |
||||
|
} |
||||
|
|
||||
|
public bool Rename(string oldPropertyName, string newPropertyName) |
||||
|
{ |
||||
|
Guard.NotNullOrEmpty(oldPropertyName, nameof(oldPropertyName)); |
||||
|
Guard.NotNullOrEmpty(newPropertyName, nameof(newPropertyName)); |
||||
|
|
||||
|
if (internalDictionary.ContainsKey(newPropertyName)) |
||||
|
{ |
||||
|
throw new ArgumentException($"An property with the key '{newPropertyName}' already exists.", newPropertyName); |
||||
|
} |
||||
|
|
||||
|
if (string.Equals(oldPropertyName, newPropertyName, StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
throw new ArgumentException($"The property names '{newPropertyName}' are equal.", newPropertyName); |
||||
|
} |
||||
|
|
||||
|
PropertyValue property; |
||||
|
|
||||
|
if (!internalDictionary.TryGetValue(oldPropertyName, out property)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
internalDictionary[newPropertyName] = property; |
||||
|
internalDictionary.Remove(oldPropertyName); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,211 @@ |
|||||
|
// ==========================================================================
|
||||
|
// PropertyValue.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using NodaTime; |
||||
|
using NodaTime.Text; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure |
||||
|
{ |
||||
|
public sealed class PropertyValue |
||||
|
{ |
||||
|
private readonly object rawValue; |
||||
|
|
||||
|
private static readonly HashSet<Type> AllowedTypes = new HashSet<Type> |
||||
|
{ |
||||
|
typeof(string), |
||||
|
typeof(bool), |
||||
|
typeof(bool?), |
||||
|
typeof(float), |
||||
|
typeof(float?), |
||||
|
typeof(double), |
||||
|
typeof(double?), |
||||
|
typeof(int), |
||||
|
typeof(int?), |
||||
|
typeof(long), |
||||
|
typeof(long?), |
||||
|
typeof(Instant), |
||||
|
typeof(Instant?), |
||||
|
typeof(Guid), |
||||
|
typeof(Guid?) |
||||
|
}; |
||||
|
|
||||
|
public object RawValue |
||||
|
{ |
||||
|
get { return rawValue; } |
||||
|
} |
||||
|
|
||||
|
internal PropertyValue(object rawValue) |
||||
|
{ |
||||
|
if (rawValue != null && !AllowedTypes.Contains(rawValue.GetType())) |
||||
|
{ |
||||
|
throw new ArgumentException("The type is not supported.", nameof(rawValue)); |
||||
|
} |
||||
|
|
||||
|
this.rawValue = rawValue; |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return rawValue?.ToString(); |
||||
|
} |
||||
|
|
||||
|
public bool ToBoolean(CultureInfo culture) |
||||
|
{ |
||||
|
return ToOrParseValue(culture, bool.Parse); |
||||
|
} |
||||
|
|
||||
|
public bool? ToNullableBoolean(CultureInfo culture) |
||||
|
{ |
||||
|
return ToNullableOrParseValue(culture, bool.Parse); |
||||
|
} |
||||
|
|
||||
|
public float ToSingle(CultureInfo culture) |
||||
|
{ |
||||
|
return ToOrParseValue(culture, x => float.Parse(x, culture)); |
||||
|
} |
||||
|
|
||||
|
public float? ToNullableSingle(CultureInfo culture) |
||||
|
{ |
||||
|
return ToNullableOrParseValue(culture, x => float.Parse(x, culture)); |
||||
|
} |
||||
|
|
||||
|
public double ToDouble(CultureInfo culture) |
||||
|
{ |
||||
|
return ToOrParseValue(culture, x => double.Parse(x, culture)); |
||||
|
} |
||||
|
|
||||
|
public double? ToNullableDouble(CultureInfo culture) |
||||
|
{ |
||||
|
return ToNullableOrParseValue(culture, x => double.Parse(x, culture)); |
||||
|
} |
||||
|
|
||||
|
public int ToInt32(CultureInfo culture) |
||||
|
{ |
||||
|
return ToOrParseValue(culture, x => int.Parse(x, culture)); |
||||
|
} |
||||
|
|
||||
|
public int? ToNullableInt32(CultureInfo culture) |
||||
|
{ |
||||
|
return ToNullableOrParseValue(culture, x => int.Parse(x, culture)); |
||||
|
} |
||||
|
|
||||
|
public long ToInt64(CultureInfo culture) |
||||
|
{ |
||||
|
return ToOrParseValue(culture, x => long.Parse(x, culture)); |
||||
|
} |
||||
|
|
||||
|
public long? ToNullableInt64(CultureInfo culture) |
||||
|
{ |
||||
|
return ToNullableOrParseValue(culture, x => long.Parse(x, culture)); |
||||
|
} |
||||
|
|
||||
|
public Instant ToInstant(CultureInfo culture) |
||||
|
{ |
||||
|
return ToOrParseValue(culture, x => InstantPattern.GeneralPattern.Parse(x).Value); |
||||
|
} |
||||
|
|
||||
|
public Instant? ToNullableInstant(CultureInfo culture) |
||||
|
{ |
||||
|
return ToNullableOrParseValue(culture, x => InstantPattern.GeneralPattern.Parse(x).Value); |
||||
|
} |
||||
|
|
||||
|
public Guid ToGuid(CultureInfo culture) |
||||
|
{ |
||||
|
return ToOrParseValue(culture, Guid.Parse); |
||||
|
} |
||||
|
|
||||
|
public Guid? ToNullableGuid(CultureInfo culture) |
||||
|
{ |
||||
|
return ToNullableOrParseValue(culture, Guid.Parse); |
||||
|
} |
||||
|
|
||||
|
private T? ToNullableOrParseValue<T>(IFormatProvider culture, Func<string, T> parser) where T : struct |
||||
|
{ |
||||
|
T result; |
||||
|
|
||||
|
return TryParse(culture, parser, out result) ? result : (T?)null; |
||||
|
} |
||||
|
|
||||
|
private T ToOrParseValue<T>(IFormatProvider culture, Func<string, T> parser) |
||||
|
{ |
||||
|
T result; |
||||
|
|
||||
|
return TryParse(culture, parser, out result) ? result : default(T); |
||||
|
} |
||||
|
|
||||
|
private bool TryParse<T>(IFormatProvider culture, Func<string, T> parser, out T result) |
||||
|
{ |
||||
|
var value = rawValue; |
||||
|
|
||||
|
if (value != null) |
||||
|
{ |
||||
|
var valueType = value.GetType(); |
||||
|
|
||||
|
if (valueType == typeof(T)) |
||||
|
{ |
||||
|
result = (T)value; |
||||
|
} |
||||
|
else if (valueType == typeof(string)) |
||||
|
{ |
||||
|
result = Parse(parser, valueType, value); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = Convert<T>(culture, value, valueType); |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
result = default(T); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private static T Convert<T>(IFormatProvider culture, object value, Type valueType) |
||||
|
{ |
||||
|
var requestedType = typeof(T); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
return (T)System.Convert.ChangeType(value, requestedType, culture); |
||||
|
} |
||||
|
catch (OverflowException) |
||||
|
{ |
||||
|
string message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}' because it is either too small or large."; |
||||
|
|
||||
|
throw new InvalidCastException(message); |
||||
|
} |
||||
|
catch (InvalidCastException) |
||||
|
{ |
||||
|
string message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}'."; |
||||
|
|
||||
|
throw new InvalidCastException(message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static T Parse<T>(Func<string, T> parser, Type valueType, object value) |
||||
|
{ |
||||
|
var requestedType = typeof(T); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
return parser(value.ToString()); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
string message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}'."; |
||||
|
|
||||
|
throw new InvalidCastException(message, e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// TaskExtensions.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure.Tasks |
||||
|
{ |
||||
|
public static class TaskExtensions |
||||
|
{ |
||||
|
public static void Forget(this Task task) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
// ==========================================================================
|
||||
|
// TaskHelper.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace PinkParrot.Infrastructure.Tasks |
||||
|
{ |
||||
|
public static class TaskHelper |
||||
|
{ |
||||
|
public static readonly Task Done = CreateDoneTask(); |
||||
|
|
||||
|
private static Task CreateDoneTask() |
||||
|
{ |
||||
|
var result = new TaskCompletionSource<object>(); |
||||
|
|
||||
|
result.SetResult(null); |
||||
|
|
||||
|
return result.Task; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
{ |
||||
|
"version": "1.0.0-*", |
||||
|
"dependencies": { |
||||
|
"NodaTime": "2.0.0-alpha20160729", |
||||
|
"NETStandard.Library": "1.6.0" |
||||
|
}, |
||||
|
"frameworks": { |
||||
|
"netcoreapp1.0": { |
||||
|
"dependencies": { |
||||
|
"Microsoft.NETCore.App": { |
||||
|
"type": "platform", |
||||
|
"version": "1.0.0" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"tooling": { |
||||
|
"defaultNamespace": "PinkParrot.Infrastructure" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,234 @@ |
|||||
|
## Ignore Visual Studio temporary files, build results, and |
||||
|
## files generated by popular Visual Studio add-ons. |
||||
|
|
||||
|
# User-specific files |
||||
|
*.suo |
||||
|
*.user |
||||
|
*.userosscache |
||||
|
*.sln.docstates |
||||
|
|
||||
|
# User-specific files (MonoDevelop/Xamarin Studio) |
||||
|
*.userprefs |
||||
|
|
||||
|
# Build results |
||||
|
[Dd]ebug/ |
||||
|
[Dd]ebugPublic/ |
||||
|
[Rr]elease/ |
||||
|
[Rr]eleases/ |
||||
|
x64/ |
||||
|
x86/ |
||||
|
build/ |
||||
|
bld/ |
||||
|
[Bb]in/ |
||||
|
[Oo]bj/ |
||||
|
|
||||
|
# Visual Studio 2015 cache/options directory |
||||
|
.vs/ |
||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot |
||||
|
#wwwroot/ |
||||
|
|
||||
|
# MSTest test Results |
||||
|
[Tt]est[Rr]esult*/ |
||||
|
[Bb]uild[Ll]og.* |
||||
|
|
||||
|
# NUNIT |
||||
|
*.VisualState.xml |
||||
|
TestResult.xml |
||||
|
|
||||
|
# Build Results of an ATL Project |
||||
|
[Dd]ebugPS/ |
||||
|
[Rr]eleasePS/ |
||||
|
dlldata.c |
||||
|
|
||||
|
# DNX |
||||
|
project.lock.json |
||||
|
artifacts/ |
||||
|
|
||||
|
*_i.c |
||||
|
*_p.c |
||||
|
*_i.h |
||||
|
*.ilk |
||||
|
*.meta |
||||
|
*.obj |
||||
|
*.pch |
||||
|
*.pdb |
||||
|
*.pgc |
||||
|
*.pgd |
||||
|
*.rsp |
||||
|
*.sbr |
||||
|
*.tlb |
||||
|
*.tli |
||||
|
*.tlh |
||||
|
*.tmp |
||||
|
*.tmp_proj |
||||
|
*.log |
||||
|
*.vspscc |
||||
|
*.vssscc |
||||
|
.builds |
||||
|
*.pidb |
||||
|
*.svclog |
||||
|
*.scc |
||||
|
|
||||
|
# Chutzpah Test files |
||||
|
_Chutzpah* |
||||
|
|
||||
|
# Visual C++ cache files |
||||
|
ipch/ |
||||
|
*.aps |
||||
|
*.ncb |
||||
|
*.opendb |
||||
|
*.opensdf |
||||
|
*.sdf |
||||
|
*.cachefile |
||||
|
|
||||
|
# Visual Studio profiler |
||||
|
*.psess |
||||
|
*.vsp |
||||
|
*.vspx |
||||
|
*.sap |
||||
|
|
||||
|
# TFS 2012 Local Workspace |
||||
|
$tf/ |
||||
|
|
||||
|
# Guidance Automation Toolkit |
||||
|
*.gpState |
||||
|
|
||||
|
# ReSharper is a .NET coding add-in |
||||
|
_ReSharper*/ |
||||
|
*.[Rr]e[Ss]harper |
||||
|
*.DotSettings.user |
||||
|
|
||||
|
# JustCode is a .NET coding add-in |
||||
|
.JustCode |
||||
|
|
||||
|
# TeamCity is a build add-in |
||||
|
_TeamCity* |
||||
|
|
||||
|
# DotCover is a Code Coverage Tool |
||||
|
*.dotCover |
||||
|
|
||||
|
# NCrunch |
||||
|
_NCrunch_* |
||||
|
.*crunch*.local.xml |
||||
|
nCrunchTemp_* |
||||
|
|
||||
|
# MightyMoose |
||||
|
*.mm.* |
||||
|
AutoTest.Net/ |
||||
|
|
||||
|
# Web workbench (sass) |
||||
|
.sass-cache/ |
||||
|
|
||||
|
# Installshield output folder |
||||
|
[Ee]xpress/ |
||||
|
|
||||
|
# DocProject is a documentation generator add-in |
||||
|
DocProject/buildhelp/ |
||||
|
DocProject/Help/*.HxT |
||||
|
DocProject/Help/*.HxC |
||||
|
DocProject/Help/*.hhc |
||||
|
DocProject/Help/*.hhk |
||||
|
DocProject/Help/*.hhp |
||||
|
DocProject/Help/Html2 |
||||
|
DocProject/Help/html |
||||
|
|
||||
|
# Click-Once directory |
||||
|
publish/ |
||||
|
|
||||
|
# Publish Web Output |
||||
|
*.[Pp]ublish.xml |
||||
|
*.azurePubxml |
||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings |
||||
|
# but database connection strings (with potential passwords) will be unencrypted |
||||
|
*.pubxml |
||||
|
*.publishproj |
||||
|
|
||||
|
# NuGet Packages |
||||
|
*.nupkg |
||||
|
# The packages folder can be ignored because of Package Restore |
||||
|
**/packages/* |
||||
|
# except build/, which is used as an MSBuild target. |
||||
|
!**/packages/build/ |
||||
|
# Uncomment if necessary however generally it will be regenerated when needed |
||||
|
#!**/packages/repositories.config |
||||
|
|
||||
|
# Microsoft Azure Build Output |
||||
|
csx/ |
||||
|
*.build.csdef |
||||
|
|
||||
|
# Microsoft Azure Emulator |
||||
|
ecf/ |
||||
|
rcf/ |
||||
|
|
||||
|
# Microsoft Azure ApplicationInsights config file |
||||
|
ApplicationInsights.config |
||||
|
|
||||
|
# Windows Store app package directory |
||||
|
AppPackages/ |
||||
|
BundleArtifacts/ |
||||
|
|
||||
|
# Visual Studio cache files |
||||
|
# files ending in .cache can be ignored |
||||
|
*.[Cc]ache |
||||
|
# but keep track of directories ending in .cache |
||||
|
!*.[Cc]ache/ |
||||
|
|
||||
|
# Others |
||||
|
ClientBin/ |
||||
|
~$* |
||||
|
*~ |
||||
|
*.dbmdl |
||||
|
*.dbproj.schemaview |
||||
|
*.pfx |
||||
|
*.publishsettings |
||||
|
node_modules/ |
||||
|
orleans.codegen.cs |
||||
|
|
||||
|
# RIA/Silverlight projects |
||||
|
Generated_Code/ |
||||
|
|
||||
|
# Backup & report files from converting an old project file |
||||
|
# to a newer Visual Studio version. Backup files are not needed, |
||||
|
# because we have git ;-) |
||||
|
_UpgradeReport_Files/ |
||||
|
Backup*/ |
||||
|
UpgradeLog*.XML |
||||
|
UpgradeLog*.htm |
||||
|
|
||||
|
# SQL Server files |
||||
|
*.mdf |
||||
|
*.ldf |
||||
|
|
||||
|
# Business Intelligence projects |
||||
|
*.rdl.data |
||||
|
*.bim.layout |
||||
|
*.bim_*.settings |
||||
|
|
||||
|
# Microsoft Fakes |
||||
|
FakesAssemblies/ |
||||
|
|
||||
|
# GhostDoc plugin setting file |
||||
|
*.GhostDoc.xml |
||||
|
|
||||
|
# Node.js Tools for Visual Studio |
||||
|
.ntvs_analysis.dat |
||||
|
|
||||
|
# Visual Studio 6 build log |
||||
|
*.plg |
||||
|
|
||||
|
# Visual Studio 6 workspace options file |
||||
|
*.opt |
||||
|
|
||||
|
# Visual Studio LightSwitch build output |
||||
|
**/*.HTMLClient/GeneratedArtifacts |
||||
|
**/*.DesktopClient/GeneratedArtifacts |
||||
|
**/*.DesktopClient/ModelManifest.xml |
||||
|
**/*.Server/GeneratedArtifacts |
||||
|
**/*.Server/ModelManifest.xml |
||||
|
_Pvt_Extensions |
||||
|
|
||||
|
# Paket dependency manager |
||||
|
.paket/paket.exe |
||||
|
|
||||
|
# FAKE - F# Make |
||||
|
.fake/ |
||||
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion> |
||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>a85201c6-6af8-4b63-8365-08f741050438</ProjectGuid> |
||||
|
<RootNamespace>PinkParrot.Write</RootNamespace> |
||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> |
||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> |
||||
|
</Project> |
||||
@ -0,0 +1,21 @@ |
|||||
|
// ==========================================================================
|
||||
|
// AddModelField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class AddModelField |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public string FieldType; |
||||
|
|
||||
|
public string FieldName; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// CreateModelSchema.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class CreateModelSchema |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public string Name; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// DeleteModelField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class DeleteModelField |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// ==========================================================================
|
||||
|
// DeleteModelSchema.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class DeleteModelSchema |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// DisableModelField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class DisableModelField |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// EnableModelField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class EnableModelField |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// HideModelField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class HideModelField |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ShowModelField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class ShowModelField |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public Guid FieldId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
// ==========================================================================
|
||||
|
// UpdateModelField.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class UpdateModelField |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public Guid FieldId; |
||||
|
|
||||
|
public PropertiesBag Settings; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
// ==========================================================================
|
||||
|
// UpdateModelSchema.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Infrastructure; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema.Commands |
||||
|
{ |
||||
|
public class UpdateModelSchema |
||||
|
{ |
||||
|
public Guid AggregateId; |
||||
|
|
||||
|
public string NewName; |
||||
|
|
||||
|
public PropertiesBag Settings { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,196 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ModelSchemaDomainObject.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using PinkParrot.Core.Schema; |
||||
|
using PinkParrot.Events.Schema; |
||||
|
using PinkParrot.Infrastructure.CQRS; |
||||
|
using PinkParrot.Write.Schema.Commands; |
||||
|
|
||||
|
namespace PinkParrot.Write.Schema |
||||
|
{ |
||||
|
public class ModelSchemaDomainObject : DomainObject |
||||
|
{ |
||||
|
private readonly ModelFieldFactory fieldFactory; |
||||
|
private bool isDeleted; |
||||
|
private ModelSchema schema; |
||||
|
|
||||
|
public ModelSchemaDomainObject(Guid id, int version, ModelFieldFactory fieldFactory) |
||||
|
: base(id, version) |
||||
|
{ |
||||
|
this.fieldFactory = fieldFactory; |
||||
|
} |
||||
|
|
||||
|
public ModelSchema Schema |
||||
|
{ |
||||
|
get { return schema; } |
||||
|
} |
||||
|
|
||||
|
public bool IsDeleted |
||||
|
{ |
||||
|
get { return isDeleted; } |
||||
|
} |
||||
|
|
||||
|
protected void Apply(ModelSchemaCreated @event) |
||||
|
{ |
||||
|
schema = ModelSchema.Create(@event.Name); |
||||
|
} |
||||
|
|
||||
|
protected void Apply(ModelFieldAdded @event) |
||||
|
{ |
||||
|
schema = schema.AddField(@event.FieldId, @event.FieldType, @event.FieldName, fieldFactory); |
||||
|
} |
||||
|
|
||||
|
protected void Apply(ModelFieldUpdated @event) |
||||
|
{ |
||||
|
schema = schema.SetField(@event.FieldId, @event.Settings); |
||||
|
} |
||||
|
|
||||
|
public void Apply(ModelFieldHidden @event) |
||||
|
{ |
||||
|
schema = schema.HideField(@event.FieldId); |
||||
|
} |
||||
|
|
||||
|
public void Apply(ModelFieldShown @event) |
||||
|
{ |
||||
|
schema = schema.ShowField(@event.FieldId); |
||||
|
} |
||||
|
|
||||
|
public void Apply(ModelFieldDisabled @event) |
||||
|
{ |
||||
|
schema = schema.DisableField(@event.FieldId); |
||||
|
} |
||||
|
|
||||
|
public void Apply(ModelFieldEnabled @event) |
||||
|
{ |
||||
|
schema = schema.EnableField(@event.FieldId); |
||||
|
} |
||||
|
|
||||
|
protected void Apply(ModelSchemaUpdated @event) |
||||
|
{ |
||||
|
schema = schema.Update(schema.Metadata.Configure(@event.NewName, @event.Settings)); |
||||
|
} |
||||
|
|
||||
|
protected void Apply(ModelFieldDeleted @event) |
||||
|
{ |
||||
|
schema = schema.DeleteField(@event.FieldId); |
||||
|
} |
||||
|
|
||||
|
protected void Apply(ModelSchemaDeleted @event) |
||||
|
{ |
||||
|
isDeleted = false; |
||||
|
} |
||||
|
|
||||
|
public void Create(CreateModelSchema command) |
||||
|
{ |
||||
|
VerifyNotCreated(); |
||||
|
|
||||
|
schema = ModelSchema.Create(command.Name); |
||||
|
|
||||
|
RaiseEvent(new ModelSchemaCreated {Name = command.Name}, true); |
||||
|
} |
||||
|
|
||||
|
public void AddField(Guid id, AddModelField command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
schema = schema.AddField(id, command.FieldType, command.FieldName, fieldFactory); |
||||
|
|
||||
|
RaiseEvent( |
||||
|
new ModelFieldAdded {FieldId = id, FieldType = command.FieldType, FieldName = command.FieldName}, true); |
||||
|
} |
||||
|
|
||||
|
public void Update(UpdateModelSchema command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
schema = schema.Update(schema.Metadata.Configure(command.NewName, command.Settings)); |
||||
|
|
||||
|
RaiseEvent(new ModelSchemaUpdated {NewName = command.NewName, Settings = command.Settings}, true); |
||||
|
} |
||||
|
|
||||
|
public void UpdateField(UpdateModelField command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
schema = schema.SetField(command.FieldId, command.Settings); |
||||
|
|
||||
|
RaiseEvent(new ModelFieldUpdated {FieldId = command.FieldId, Settings = command.Settings}, true); |
||||
|
} |
||||
|
|
||||
|
public void HideField(HideModelField command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
schema = schema.HideField(command.FieldId); |
||||
|
|
||||
|
RaiseEvent(new ModelFieldHidden {FieldId = command.FieldId}, true); |
||||
|
} |
||||
|
|
||||
|
public void ShowField(HideModelField command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
schema = schema.ShowField(command.FieldId); |
||||
|
|
||||
|
RaiseEvent(new ModelFieldShown {FieldId = command.FieldId}, true); |
||||
|
} |
||||
|
|
||||
|
public void DisableField(DisableModelField command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
schema = schema.DisableField(command.FieldId); |
||||
|
|
||||
|
RaiseEvent(new ModelFieldDisabled {FieldId = command.FieldId}, true); |
||||
|
} |
||||
|
|
||||
|
public void EnableField(EnableModelField command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
schema = schema.EnableField(command.FieldId); |
||||
|
|
||||
|
RaiseEvent(new ModelFieldEnabled {FieldId = command.FieldId}, true); |
||||
|
} |
||||
|
|
||||
|
public void Delete(DeleteModelSchema command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
isDeleted = true; |
||||
|
|
||||
|
RaiseEvent(new ModelSchemaDeleted(), true); |
||||
|
} |
||||
|
|
||||
|
public void DeleteField(DeleteModelField command) |
||||
|
{ |
||||
|
VerifyCreatedAndNotDeleted(); |
||||
|
|
||||
|
schema = schema.DeleteField(command.FieldId); |
||||
|
|
||||
|
RaiseEvent(new ModelFieldDeleted {FieldId = command.FieldId}, true); |
||||
|
} |
||||
|
|
||||
|
private void VerifyNotCreated() |
||||
|
{ |
||||
|
if (schema != null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Schema has already been created."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void VerifyCreatedAndNotDeleted() |
||||
|
{ |
||||
|
if (isDeleted || schema == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Schema has already been deleted or not created yet."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
{ |
||||
|
"version": "1.0.0-*", |
||||
|
|
||||
|
"dependencies": { |
||||
|
"NETStandard.Library": "1.6.0", |
||||
|
"NodaTime": "2.0.0-alpha20160729", |
||||
|
"PinkParrot.Core": "1.0.0-*", |
||||
|
"PinkParrot.Events": "1.0.0-*", |
||||
|
"PinkParrot.Infrastructure": "1.0.0-*" |
||||
|
}, |
||||
|
|
||||
|
"frameworks": { |
||||
|
"netcoreapp1.0": { |
||||
|
"dependencies": { |
||||
|
"Microsoft.NETCore.App": { |
||||
|
"type": "platform", |
||||
|
"version": "1.0.0" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
"tooling": { |
||||
|
"defaultNamespace": "PinkParrot.Write" |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue