This repository was archived by the owner on Nov 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathOperationHandler.cs
157 lines (137 loc) · 6.05 KB
/
OperationHandler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Exceptions;
using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Extensions;
using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Models.KnownStrings;
using Microsoft.OpenApi.Models;
namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration
{
internal static class OperationHandler
{
/// <summary>
/// Checks whether the optional operation id tag is present in the operation element.
/// </summary>
/// <param name="operationElement"></param>
/// <returns>True if the operationId tag is in the operation element, otherwise false.</returns>
public static bool HasOperationId(XElement operationElement)
{
return operationElement?.Elements().Where(p => p.Name == KnownXmlStrings.OperationId).Count() > 0;
}
/// <summary>
/// Extracts the operation id from the operation element.
/// </summary>
/// <param name="operationElement"></param>
/// <returns>Operation id.</returns>
/// <exception cref="InvalidOperationIdException">Thrown if the operationId tag is missing or
/// there are more than one tags.</exception>
public static string GetOperationId(XElement operationElement)
{
var operationIdList = operationElement?.Elements().Where(p => p.Name == KnownXmlStrings.OperationId).ToList();
if (operationIdList?.Count == 1)
{
return operationIdList[0].Value;
}
else
{
string error = operationIdList.Count > 1
? SpecificationGenerationMessages.MultipleOperationId
: SpecificationGenerationMessages.NoOperationId;
throw new InvalidOperationIdException(error);
}
}
/// <summary>
/// Generates the operation id by parsing segments out of the absolute path.
/// </summary>
public static string GenerateOperationId(string absolutePath, OperationType operationMethod)
{
var operationId = new StringBuilder(operationMethod.ToString().ToLowerInvariant());
foreach (var segment in absolutePath.Split('/'))
{
if (string.IsNullOrWhiteSpace(segment))
{
continue;
}
var current = string.Empty;
// In order to build an operation id, extract the path parameters
// and prepend By to these before adding them to the operationId.
// e.g. for GET /v6/products/{productId} -> getV6ProductsByProductId
var matches = new Regex(@"\{(.*?)\}").Matches(segment);
if (matches.Count > 0)
{
foreach (Match match in matches)
{
current += "By" + match.Groups[1].Value.ToTitleCase();
}
}
else
{
current = segment.ToTitleCase();
}
// OpenAPI spec recommends following "programming naming conventions"
// for operationId to allow tools to utilize this property.
// To be safe, we only allow alphanumeric characters.
operationId.Append(Regex.Replace(current, "[^a-zA-Z0-9]", string.Empty));
}
return operationId.ToString();
}
/// <summary>
/// Extracts the operation method from the operation element
/// </summary>
/// <param name="operationElement">The xml element representing an operation in the annotation xml.</param>
/// <exception cref="InvalidVerbException">Thrown if the verb is missing or has invalid format.</exception>
public static OperationType GetOperationMethod(XElement operationElement)
{
var verbElement = operationElement.Descendants().FirstOrDefault(i => i.Name == KnownXmlStrings.Verb);
var verb = verbElement?.Value.Trim();
OperationType operationMethod;
if (Enum.TryParse(verb, true, out operationMethod))
{
return operationMethod;
}
throw new InvalidVerbException(verb);
}
/// <summary>
/// Extracts the URL from the operation element
/// </summary>
/// <param name="operationElement">The xml element representing an operation in the annotation xml.</param>
/// <exception cref="InvalidUrlException">Thrown if the URL is missing or has invalid format.</exception>
public static string GetUrl(
XElement operationElement)
{
var urls = operationElement.Elements(KnownXmlStrings.Url).Select(i => i.Value);
// Can't process further if no url is documented, so skip the operation.
var url = urls.FirstOrDefault();
if (url == null)
{
throw new InvalidUrlException(
url,
SpecificationGenerationMessages.NullUrl);
}
try
{
url = WebUtility.UrlDecode(new Uri(WebUtility.UrlDecode(url)).AbsolutePath);
}
catch (UriFormatException)
{
throw new InvalidUrlException(
url,
SpecificationGenerationMessages.MalformattedUrl);
}
catch (Exception e)
{
throw new InvalidUrlException(
url,
e.Message);
}
return url;
}
}
}