1+ using System ;
2+ using System . Linq ;
3+ using System . Runtime . CompilerServices ;
4+ using System . Xml . Linq ;
5+ using NLog ;
6+
7+ namespace Typewriter
8+ {
9+ /// <summary>
10+ /// Contains a set of rules for altering CSDL metadata. These rules contain known hacks,
11+ /// fixes, and workarounds for issues in the metadata. Why the metadata has these issues
12+ /// is a long story.
13+ /// </summary>
14+
15+ internal class MetadataPreprocessor
16+ {
17+ private static Logger Logger => LogManager . GetLogger ( "MetadataPreprocessor" ) ;
18+ private static XDocument xMetadata ;
19+
20+
21+ internal static XDocument GetXMetadata ( )
22+ {
23+ return xMetadata ;
24+ }
25+
26+ // Added for tests.
27+ internal static void SetXMetadata ( XDocument metadata )
28+ {
29+ xMetadata = metadata ;
30+ }
31+
32+ /// <summary>
33+ /// Cleans metadata to match the assumptions in the generator and templates.
34+ /// </summary>
35+ /// <param name="csdlContents">Metadata content.</param>
36+ /// <returns>A string of metadata that should work with the generator.</returns>
37+ internal static string CleanMetadata ( string csdlContents )
38+ {
39+ if ( csdlContents == "" )
40+ throw new ArgumentException ( "The CSDL string is empty." ) ;
41+
42+ xMetadata = XDocument . Parse ( csdlContents ) ;
43+
44+ // Rules to apply to the {csdlContents} metadata.
45+ RemoveCapabilityAnnotations ( ) ;
46+ AddLongDescriptionToThumbnail ( ) ;
47+ RemoveHasStream ( "onenotePage" ) ;
48+ RemoveHasStream ( "onenoteResource" ) ;
49+ AddContainsTarget ( "plannerBucket" ) ;
50+ AddContainsTarget ( "plannerTask" ) ;
51+ AddContainsTarget ( "plannerPlan" ) ;
52+ AddContainsTarget ( "plannerDelta" ) ;
53+
54+ return xMetadata . ToString ( ) ;
55+ }
56+
57+ /// <summary>
58+ /// Removes the HasStream attribute from an entity.
59+ /// We do this since the metadata does't properly describe the stream nature of this resources.
60+ /// Examples of this are the onenotePage and onenoteResource entities.
61+ /// </summary>
62+ /// <param name="entityTypeName">The value of an EntityType/Name attribute that will have the HasStream attribute removed.</param>
63+ internal static void RemoveHasStream ( string entityTypeName )
64+ {
65+ try
66+ {
67+ xMetadata . Descendants ( )
68+ . Where ( x => x . Name . LocalName == "EntityType" )
69+ . Where ( x => x . Attribute ( "Name" ) . Value . Equals ( entityTypeName ) )
70+ . ToList ( ) . ForEach ( x => x . Attribute ( "HasStream" ) . Remove ( ) ) ;
71+
72+ Logger . Info ( "RemoveHasStream rule was applied so that we removed the HasStream attribute from the {0} entityType." , entityTypeName ) ;
73+ }
74+ catch
75+ {
76+ Logger . Warn ( "RemoveHasStream rule was not applied so we could not remove the HasStream attribute from the {0} entityType." , entityTypeName ) ;
77+ }
78+ }
79+
80+ /// <summary>
81+ /// Add ContainsTarget="true" to all navigationProperties whose type is a collection of a given entityType.
82+ /// This means that the navigation is to an entity and not to an entity reference.
83+ /// This also means that the contained entity is part of entity set of the parentEntity; an implied entity set.
84+ /// </summary>
85+ /// <param name="entityTypeName">The type of entity to self-contain</param>
86+ internal static void AddContainsTarget ( string entityTypeName )
87+ {
88+ var list = xMetadata . Descendants ( )
89+ . Where ( x => x . Name . LocalName == "NavigationProperty" )
90+ . Where ( x => x . Attribute ( "ContainsTarget" ) == null || x . Attribute ( "ContainsTarget" ) . Value . Equals ( "false" ) )
91+ . Where ( x => x . Attribute ( "Type" ) . Value == "Collection(microsoft.graph." + entityTypeName + ")" )
92+ . ToList ( ) ;
93+
94+ if ( list . Count == 0 )
95+ {
96+ Logger . Warn ( "AddContainsTarget rule was not applied. No entity type named {0} found with missing navigation property containment." , entityTypeName ) ;
97+ }
98+ else
99+ {
100+ list . ForEach ( x =>
101+ {
102+ x . SetAttributeValue ( "ContainsTarget" , "true" ) ;
103+
104+ var parentEntityName = x . Parent . Attribute ( "Name" ) . Value ;
105+ var navigationPropertyName = x . Attribute ( "Name" ) . Value ;
106+
107+ Logger . Info ( "AddContainsTarget rule applied so that ContainsTarget=true was set on the {0} entity's {1} navigation property." , parentEntityName , navigationPropertyName ) ;
108+ } ) ;
109+ }
110+ }
111+
112+ /// <summary>
113+ /// Remove all capability annotations since the metadata doesn't describe
114+ /// them properly and the generator doesn't process them properly.
115+ /// </summary>
116+ internal static void RemoveCapabilityAnnotations ( )
117+ {
118+ xMetadata . Descendants ( )
119+ . Where ( x => ( string ) x . Name . LocalName == "Annotation" )
120+ . Where ( x => x . Attribute ( "Term" ) . Value . StartsWith ( "Org.OData.Capabilities" ) )
121+ . Remove ( ) ;
122+
123+ Logger . Info ( "RemoveCapabilityAnnotations rule was applied so that capability annotations are removed from the metadata." ) ;
124+ }
125+
126+ /// <summary>
127+ /// Adds a long description annotation to the thumbnail complex type. This annotation is a
128+ /// generation hint for the generator.
129+ /// </summary>
130+ internal static void AddLongDescriptionToThumbnail ( )
131+ {
132+ // Thumbnail hack - add LongDescription annotation
133+ XElement thumbnailComplexType = xMetadata . Descendants ( )
134+ . Where ( x => ( string ) x . Name . LocalName == "ComplexType" )
135+ . Where ( x => x . Attribute ( "Name" ) . Value == "thumbnail" )
136+ . First ( ) ;
137+
138+ if ( thumbnailComplexType != null )
139+ {
140+ // need to specify namespace so default xmlns="" isn't added that breaks VIPR
141+ XElement thumbnailAnnotation = new XElement ( thumbnailComplexType . Name . Namespace + "Annotation" ) ;
142+
143+ thumbnailAnnotation . Add ( new XAttribute ( "Term" , "Org.OData.Core.V1.LongDescription" ) ) ;
144+ thumbnailAnnotation . Add ( new XAttribute ( "String" , "navigable" ) ) ;
145+ thumbnailComplexType . Add ( thumbnailAnnotation ) ;
146+
147+ Logger . Info ( "AddLongDescriptionToThumbnail rule was applied to the thumbnail complex type." ) ;
148+ }
149+ else
150+ {
151+ Logger . Error ( "AddLongDescriptionToThumbnail rule was not applied to the thumbnail complex type because the type wasn't found." ) ;
152+ }
153+ }
154+ }
155+ }
0 commit comments