11using Microsoft . AspNetCore . Http ;
22using Microsoft . Extensions . Logging ;
33using Umbraco . Cms . Core . DeliveryApi ;
4- using Umbraco . Cms . Core . Models . PublishedContent ;
5- using Umbraco . Extensions ;
4+ using Umbraco . Cms . Web . Common . Rendering ;
65
76namespace Umbraco . Cms . Api . Delivery . Rendering ;
87
9- internal sealed class RequestContextOutputExpansionStrategyV2 : IOutputExpansionStrategy
8+ internal sealed class RequestContextOutputExpansionStrategyV2 : ElementOnlyOutputExpansionStrategy , IOutputExpansionStrategy
109{
11- private const string All = "$all" ;
12- private const string None = "" ;
13- private const string ExpandParameterName = "expand" ;
14- private const string FieldsParameterName = "fields" ;
15-
16- private readonly IApiPropertyRenderer _propertyRenderer ;
1710 private readonly ILogger < RequestContextOutputExpansionStrategyV2 > _logger ;
1811
19- private readonly Stack < Node ? > _expandProperties ;
20- private readonly Stack < Node ? > _includeProperties ;
21-
2212 public RequestContextOutputExpansionStrategyV2 (
2313 IHttpContextAccessor httpContextAccessor ,
2414 IApiPropertyRenderer propertyRenderer ,
2515 ILogger < RequestContextOutputExpansionStrategyV2 > logger )
16+ : base ( propertyRenderer )
2617 {
27- _propertyRenderer = propertyRenderer ;
2818 _logger = logger ;
29- _expandProperties = new Stack < Node ? > ( ) ;
30- _includeProperties = new Stack < Node ? > ( ) ;
31-
3219 InitializeExpandAndInclude ( httpContextAccessor ) ;
3320 }
3421
35- public IDictionary < string , object ? > MapContentProperties ( IPublishedContent content )
36- => content . ItemType == PublishedItemType . Content
37- ? MapProperties ( content . Properties )
38- : throw new ArgumentException ( $ "Invalid item type. This method can only be used with item type { nameof ( PublishedItemType . Content ) } , got: { content . ItemType } ") ;
39-
40- public IDictionary < string , object ? > MapMediaProperties ( IPublishedContent media , bool skipUmbracoProperties = true )
41- {
42- if ( media . ItemType != PublishedItemType . Media )
43- {
44- throw new ArgumentException ( $ "Invalid item type. This method can only be used with item type { PublishedItemType . Media } , got: { media . ItemType } ") ;
45- }
46-
47- IPublishedProperty [ ] properties = media
48- . Properties
49- . Where ( p => skipUmbracoProperties is false || p . Alias . StartsWith ( "umbraco" ) is false )
50- . ToArray ( ) ;
51-
52- return properties . Any ( )
53- ? MapProperties ( properties )
54- : new Dictionary < string , object ? > ( ) ;
55- }
56-
57- public IDictionary < string , object ? > MapElementProperties ( IPublishedElement element )
58- => MapProperties ( element . Properties , true ) ;
59-
6022 private void InitializeExpandAndInclude ( IHttpContextAccessor httpContextAccessor )
6123 {
6224 string ? QueryValue ( string key ) => httpContextAccessor . HttpContext ? . Request . Query [ key ] ;
@@ -66,7 +28,7 @@ private void InitializeExpandAndInclude(IHttpContextAccessor httpContextAccessor
6628
6729 try
6830 {
69- _expandProperties . Push ( Node . Parse ( toExpand ) ) ;
31+ ExpandProperties . Push ( Node . Parse ( toExpand ) ) ;
7032 }
7133 catch ( ArgumentException ex )
7234 {
@@ -76,110 +38,12 @@ private void InitializeExpandAndInclude(IHttpContextAccessor httpContextAccessor
7638
7739 try
7840 {
79- _includeProperties . Push ( Node . Parse ( toInclude ) ) ;
41+ IncludeProperties . Push ( Node . Parse ( toInclude ) ) ;
8042 }
8143 catch ( ArgumentException ex )
8244 {
8345 _logger . LogError ( ex , $ "Could not parse the '{ FieldsParameterName } ' parameter. See exception for details.") ;
8446 throw new ArgumentException ( $ "Could not parse the '{ FieldsParameterName } ' parameter: { ex . Message } ") ;
8547 }
8648 }
87-
88- private IDictionary < string , object ? > MapProperties ( IEnumerable < IPublishedProperty > properties , bool forceExpandProperties = false )
89- {
90- Node ? currentExpandProperties = _expandProperties . Peek ( ) ;
91- if ( _expandProperties . Count > 1 && currentExpandProperties is null && forceExpandProperties is false )
92- {
93- return new Dictionary < string , object ? > ( ) ;
94- }
95-
96- Node ? currentIncludeProperties = _includeProperties . Peek ( ) ;
97- var result = new Dictionary < string , object ? > ( ) ;
98- foreach ( IPublishedProperty property in properties )
99- {
100- Node ? nextIncludeProperties = GetNextProperties ( currentIncludeProperties , property . Alias ) ;
101- if ( currentIncludeProperties is not null && currentIncludeProperties . Items . Any ( ) && nextIncludeProperties is null )
102- {
103- continue ;
104- }
105-
106- Node ? nextExpandProperties = GetNextProperties ( currentExpandProperties , property . Alias ) ;
107-
108- _includeProperties . Push ( nextIncludeProperties ) ;
109- _expandProperties . Push ( nextExpandProperties ) ;
110-
111- result [ property . Alias ] = GetPropertyValue ( property ) ;
112-
113- _expandProperties . Pop ( ) ;
114- _includeProperties . Pop ( ) ;
115- }
116-
117- return result ;
118- }
119-
120- private Node ? GetNextProperties ( Node ? currentProperties , string propertyAlias )
121- => currentProperties ? . Items . FirstOrDefault ( i => i . Key == All )
122- ?? currentProperties ? . Items . FirstOrDefault ( i => i . Key == "properties" ) ? . Items . FirstOrDefault ( i => i . Key == All || i . Key == propertyAlias ) ;
123-
124- private object ? GetPropertyValue ( IPublishedProperty property )
125- => _propertyRenderer . GetPropertyValue ( property , _expandProperties . Peek ( ) is not null ) ;
126-
127- private class Node
128- {
129- public string Key { get ; private set ; } = string . Empty ;
130-
131- public List < Node > Items { get ; } = new ( ) ;
132-
133- public static Node Parse ( string value )
134- {
135- // verify that there are as many start brackets as there are end brackets
136- if ( value . CountOccurrences ( "[" ) != value . CountOccurrences ( "]" ) )
137- {
138- throw new ArgumentException ( "Value did not contain an equal number of start and end brackets" ) ;
139- }
140-
141- // verify that the value does not start with a start bracket
142- if ( value . StartsWith ( "[" ) )
143- {
144- throw new ArgumentException ( "Value cannot start with a bracket" ) ;
145- }
146-
147- // verify that there are no empty brackets
148- if ( value . Contains ( "[]" ) )
149- {
150- throw new ArgumentException ( "Value cannot contain empty brackets" ) ;
151- }
152-
153- var stack = new Stack < Node > ( ) ;
154- var root = new Node { Key = "root" } ;
155- stack . Push ( root ) ;
156-
157- var currentNode = new Node ( ) ;
158- root . Items . Add ( currentNode ) ;
159-
160- foreach ( char c in value )
161- {
162- switch ( c )
163- {
164- case '[' : // Start a new node, child of the current node
165- stack . Push ( currentNode ) ;
166- currentNode = new Node ( ) ;
167- stack . Peek ( ) . Items . Add ( currentNode ) ;
168- break ;
169- case ',' : // Start a new node, but at the same level of the current node
170- currentNode = new Node ( ) ;
171- stack . Peek ( ) . Items . Add ( currentNode ) ;
172- break ;
173- case ']' : // Back to parent of the current node
174- currentNode = stack . Pop ( ) ;
175- break ;
176- default : // Add char to current node key
177- currentNode . Key += c ;
178- break ;
179- }
180- }
181-
182- return root ;
183- }
184- }
18549}
0 commit comments