@@ -97,6 +97,7 @@ public partial class {endPoint.ClassName}
9797
9898{ GenerateMethod ( endPoint ) }
9999{ ( ShouldGenerateResponseWrapperMethod ( endPoint ) ? GenerateMethod ( endPoint , returnResponseWrapper : true ) : TrimmedLine ) }
100+ { GeneratePollingMethods ( endPoint ) }
100101{ GenerateExtensionMethod ( endPoint ) }
101102 }}
102103}}" . RemoveBlankLinesWhereOnlyWhitespaces ( ) ;
@@ -121,6 +122,7 @@ public partial interface I{endPoint.ClassName}
121122 {{
122123{ GenerateMethod ( endPoint , isInterface : true ) }
123124{ ( ShouldGenerateResponseWrapperMethod ( endPoint ) ? GenerateMethod ( endPoint , isInterface : true , returnResponseWrapper : true ) : TrimmedLine ) }
125+ { GeneratePollingMethods ( endPoint , isInterface : true ) }
124126{ GenerateExtensionMethod ( endPoint , isInterface : true ) }
125127 }}
126128}}" . RemoveBlankLinesWhereOnlyWhitespaces ( ) ;
@@ -272,6 +274,170 @@ private static string GetResponseWrapperMethodName(EndPoint endPoint)
272274 return $ "{ endPoint . NotAsyncMethodName } AsResponseAsync";
273275 }
274276
277+ private static string GetPollingMethodName (
278+ EndPoint endPoint ,
279+ PollingOperation pollingOperation )
280+ {
281+ return $ "{ endPoint . NotAsyncMethodName } { pollingOperation . Name . ToPropertyName ( ) } Async";
282+ }
283+
284+ private static bool SupportsPollingOperation (
285+ EndPoint endPoint ,
286+ PollingOperation pollingOperation )
287+ {
288+ if ( string . IsNullOrWhiteSpace ( pollingOperation . Name ) ||
289+ pollingOperation . SuccessCriteria . IsEmpty ||
290+ endPoint . RawStream ||
291+ endPoint . EnumerableStream )
292+ {
293+ return false ;
294+ }
295+
296+ if ( ! string . IsNullOrWhiteSpace ( endPoint . SuccessResponse . Type . CSharpType ) )
297+ {
298+ return true ;
299+ }
300+
301+ return ! pollingOperation . SuccessCriteria . Any ( static x => x . ContextType == PollingCriterionContextType . ResponseBody ) &&
302+ ! pollingOperation . FailureCriteria . Any ( static x => x . ContextType == PollingCriterionContextType . ResponseBody ) ;
303+ }
304+
305+ private static string GeneratePollingMethods (
306+ EndPoint endPoint ,
307+ bool isInterface = false )
308+ {
309+ return endPoint . PollingOperations
310+ . Where ( x => SupportsPollingOperation ( endPoint , x ) )
311+ . Select ( x => GeneratePollingMethod ( endPoint , x , isInterface ) )
312+ . Inject ( ) ;
313+ }
314+
315+ private static string GeneratePollingMethod (
316+ EndPoint endPoint ,
317+ PollingOperation pollingOperation ,
318+ bool isInterface = false )
319+ {
320+ var methodName = GetPollingMethodName ( endPoint , pollingOperation ) ;
321+ var hasBody = ! string . IsNullOrWhiteSpace ( endPoint . SuccessResponse . Type . CSharpType ) ;
322+ var bodyReturnType = hasBody
323+ ? $ "global::System.Threading.Tasks.Task<{ endPoint . SuccessResponse . Type . CSharpTypeWithoutNullability } >"
324+ : "global::System.Threading.Tasks.Task" ;
325+ var successExpression = GeneratePollingCriteriaExpression ( endPoint , pollingOperation . SuccessCriteria , "__pollingResponse" ) ;
326+ var failureExpression = GeneratePollingCriteriaExpression ( endPoint , pollingOperation . FailureCriteria , "__pollingResponse" ) ;
327+ var body = isInterface
328+ ? ";"
329+ : $@ "
330+ {{
331+ var __pollingOptions = global::{ endPoint . Settings . Namespace } .AutoSDKPollingSupport.ResolvePollingOptions(
332+ pollingOptions: pollingOptions,
333+ defaultInitialDelay: global::System.TimeSpan.FromSeconds({ pollingOperation . DelaySeconds } ),
334+ defaultInterval: global::System.TimeSpan.FromSeconds({ pollingOperation . IntervalSeconds } ),
335+ defaultMaxAttempts: { pollingOperation . LimitCount } );
336+ global::{ endPoint . Settings . Namespace } .AutoSDKHttpResponse? __lastResponse = null;
337+
338+ if (__pollingOptions.InitialDelay > global::System.TimeSpan.Zero)
339+ {{
340+ await global::{ endPoint . Settings . Namespace } .AutoSDKPollingSupport.DelayAsync(
341+ delay: __pollingOptions.InitialDelay,
342+ cancellationToken: cancellationToken).ConfigureAwait(false);
343+ }}
344+
345+ for (var __pollAttempt = 1; __pollAttempt <= __pollingOptions.MaxAttempts; __pollAttempt++)
346+ {{
347+ var __pollingResponse = await { GetResponseWrapperMethodName ( endPoint ) } (
348+ { GenerateMethodInvocationArguments ( endPoint ) }
349+ ).ConfigureAwait(false);
350+ __lastResponse = __pollingResponse;
351+
352+ { ( ! pollingOperation . FailureCriteria . IsEmpty ? $@ " if ({ failureExpression } )
353+ {{
354+ throw new global::{ endPoint . Settings . Namespace } .AutoSDKPollingException(
355+ message: $""Polling helper '{ methodName } ' matched a configured failure criterion on attempt {{__pollAttempt}}."",
356+ response: __pollingResponse);
357+ }}
358+ " : TrimmedLine ) }
359+ if ({ successExpression } )
360+ {{
361+ { ( hasBody ? " return __pollingResponse.Body;" : " return;" ) }
362+ }}
363+
364+ if (__pollAttempt == __pollingOptions.MaxAttempts)
365+ {{
366+ break;
367+ }}
368+
369+ await global::{ endPoint . Settings . Namespace } .AutoSDKPollingSupport.DelayAsync(
370+ delay: __pollingOptions.Interval,
371+ cancellationToken: cancellationToken).ConfigureAwait(false);
372+ }}
373+
374+ throw new global::{ endPoint . Settings . Namespace } .AutoSDKPollingException(
375+ message: $""Polling helper '{ methodName } ' did not satisfy its success criteria after {{__pollingOptions.MaxAttempts}} attempts."",
376+ response: __lastResponse);
377+ }}" ;
378+
379+ return $@ "
380+ { "Polls the endpoint until the configured polling criteria are satisfied." . ToXmlDocumentationSummary ( level : 8 ) }
381+ { endPoint . Parameters . Where ( x => x . Location != null ) . Select ( x => $@ "
382+ { x . Summary . ToXmlDocumentationForParam ( x . ParameterName , level : 8 ) } " ) . Inject ( ) }
383+ { ( string . IsNullOrWhiteSpace ( endPoint . RequestType . CSharpType ) ? TrimmedLine : @"
384+ /// <param name=""request""></param>" ) }
385+ /// <param name=""pollingOptions"">Overrides the generated polling delay, interval, and attempt limit.</param>
386+ /// <param name=""requestOptions"">Per-request overrides applied to each poll attempt.</param>
387+ /// <param name=""cancellationToken"">The token to cancel the polling operation with</param>
388+ /// <exception cref=""global::{ endPoint . Settings . Namespace } .AutoSDKPollingException""></exception>
389+ { ( isInterface ? "" : "public async " ) } { bodyReturnType } { methodName } (
390+ { endPoint . Parameters . Where ( x => x is { Location : not null , IsRequired : true } && ! x . HasSchemaDefault ) . Select ( x => $@ "
391+ { x . Type . CSharpType } { x . ParameterName } ," ) . Inject ( ) }
392+ { ( string . IsNullOrWhiteSpace ( endPoint . RequestType . CSharpType ) ? TrimmedLine : $@ "
393+ { endPoint . RequestType . CSharpTypeWithoutNullability } request," ) }
394+ { endPoint . Parameters . Where ( x => x is { Location : not null } && ( ! x . IsRequired || x . HasSchemaDefault ) ) . Select ( x => $@ "
395+ { x . Type . CSharpType } { x . ParameterName } = { x . ParameterDefaultValue } ," ) . Inject ( ) }
396+ global::{ endPoint . Settings . Namespace } .AutoSDKPollingOptions? pollingOptions = default,
397+ global::{ endPoint . Settings . Namespace } .AutoSDKRequestOptions? requestOptions = default,
398+ global::System.Threading.CancellationToken cancellationToken = default){ body }
399+ " . RemoveBlankLinesWhereOnlyWhitespaces ( ) ;
400+ }
401+
402+ private static string GeneratePollingCriteriaExpression (
403+ EndPoint endPoint ,
404+ EquatableArray < PollingCriterion > criteria ,
405+ string responseVariableName )
406+ {
407+ return criteria . IsEmpty
408+ ? "false"
409+ : string . Join (
410+ " && " ,
411+ criteria . Select ( x => GeneratePollingCriterionExpression ( endPoint , x , responseVariableName ) ) ) ;
412+ }
413+
414+ private static string GeneratePollingCriterionExpression (
415+ EndPoint endPoint ,
416+ PollingCriterion criterion ,
417+ string responseVariableName )
418+ {
419+ return criterion switch
420+ {
421+ { Type : PollingCriterionType . Simple , ContextType : PollingCriterionContextType . StatusCode } => $@ "global::{ endPoint . Settings . Namespace } .AutoSDKPollingSupport.MatchesStatusCode(
422+ statusCode: { responseVariableName } .StatusCode,
423+ @operator: { criterion . Operator . ToCSharpStringLiteral ( ) } ,
424+ expectedValue: { criterion . ExpectedValue . ToCSharpStringLiteral ( ) } )" ,
425+ { Type : PollingCriterionType . Regex , ContextType : PollingCriterionContextType . StatusCode } => $@ "global::{ endPoint . Settings . Namespace } .AutoSDKPollingSupport.MatchesRegexValue(
426+ value: global::{ endPoint . Settings . Namespace } .AutoSDKPollingSupport.GetStatusCodeValue({ responseVariableName } .StatusCode),
427+ pattern: { criterion . Pattern . ToCSharpStringLiteral ( ) } )" ,
428+ { Type : PollingCriterionType . Simple , ContextType : PollingCriterionContextType . ResponseBody } => $@ "global::{ endPoint . Settings . Namespace } .AutoSDKPollingSupport.MatchesSimpleCondition(
429+ body: { responseVariableName } { ( string . IsNullOrWhiteSpace ( endPoint . SuccessResponse . Type . CSharpType ) ? string . Empty : ".Body" ) } ,
430+ jsonPointer: { criterion . JsonPointer . ToCSharpStringLiteral ( ) } ,
431+ @operator: { criterion . Operator . ToCSharpStringLiteral ( ) } ,
432+ expectedValue: { criterion . ExpectedValue . ToCSharpStringLiteral ( ) } )" ,
433+ { Type : PollingCriterionType . Regex , ContextType : PollingCriterionContextType . ResponseBody } => $@ "global::{ endPoint . Settings . Namespace } .AutoSDKPollingSupport.MatchesRegexCondition(
434+ body: { responseVariableName } { ( string . IsNullOrWhiteSpace ( endPoint . SuccessResponse . Type . CSharpType ) ? string . Empty : ".Body" ) } ,
435+ jsonPointer: { criterion . JsonPointer . ToCSharpStringLiteral ( ) } ,
436+ pattern: { criterion . Pattern . ToCSharpStringLiteral ( ) } )" ,
437+ _ => "false" ,
438+ } ;
439+ }
440+
275441 private static string GetSendExpression (
276442 EndPoint endPoint ,
277443 string requestVariableName ,
0 commit comments