Skip to content

Commit

Permalink
Updating LRO design (#396)
Browse files Browse the repository at this point in the history
* Adding initial redesign for LROs

* Commenting out LRO related tests

* Removing unnecessary code and update generated code

* Adding updated generated code

* Updating response handling in operation methods

* Revert removing Begin prefix

* Adding updated pager code

* Adding status code exception for pagers until functionality is added

* Remove time import for pagers

* Updating Http to HTTP

* Updating needsTimeAndContext to isLRO

* Calling poller error handler when creating polling trackers

* Removing error handler from pollers.go and using existing operation error handlers

* Adding status code check for codes specified in swagger responses for LROs

* Adding 204 status to response handlers, adding resume method for each lro operation

* Changing GetPoller() closure on response envelopes to Poller type field

* Change field funcType to lroPointerException

* Refactoring PollUntilDone

Co-authored-by: Catalina Peralta <[email protected]>
Co-authored-by: Joel Hendrix <[email protected]>
  • Loading branch information
3 people authored May 20, 2020
1 parent b5fe01f commit 6d8ed32
Show file tree
Hide file tree
Showing 17 changed files with 4,413 additions and 8,866 deletions.
1 change: 1 addition & 0 deletions src/common/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function isPageableOperation(op: Operation): boolean {

export interface PollerInfo {
name: string;
responseType: string;
op: Operation;
}

Expand Down
10 changes: 7 additions & 3 deletions src/generator/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class StructDef {
tag = '';
}
let pointer = '*';
if (prop.schema.language.go!.discriminatorInterface) {
if (prop.schema.language.go!.discriminatorInterface || prop.schema.language.go!.lroPointerException) {
// pointer-to-interface introduces very clunky code
pointer = '';
}
Expand Down Expand Up @@ -268,7 +268,11 @@ function generateStruct(lang: Language, props?: Property[]): StructDef {
imports.add('fmt');
}
if (lang.responseType) {
imports.add("net/http");
imports.add('net/http');
}
if (lang.isLRO) {
imports.add('time');
imports.add('context');
}
if (lang.needsDateTimeMarshalling) {
imports.add('encoding/' + lang.marshallingFormat);
Expand Down Expand Up @@ -435,7 +439,7 @@ function generateMarshaller(structDef: StructDef) {
let formatSig = 'JSON() ([]byte, error)';
let methodName = 'MarshalJSON';
if (structDef.Language.marshallingFormat === 'xml') {
formatSig = 'XML(e *xml.Encoder, start xml.StartElement) error'
formatSig = 'XML(e *xml.Encoder, start xml.StartElement) error';
methodName = 'MarshalXML';
}
let text = `func (${receiver} ${structDef.Language.name}) Marshal${formatSig} {\n`;
Expand Down
70 changes: 47 additions & 23 deletions src/generator/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ function generateOperation(clientName: string, op: Operation, imports: ImportMan
text += '}\n\n';
return text;
}
imports.add('time');
text += `\treq, err := client.${info.protocolNaming.requestMethod}(${reqParams.join(', ')})\n`;
text += `\tif err != nil {\n`;
text += `\t\treturn nil, err\n`;
Expand All @@ -284,15 +285,30 @@ function generateOperation(clientName: string, op: Operation, imports: ImportMan
text += `\tif err != nil {\n`;
text += `\t\treturn nil, err\n`;
text += `\t}\n`;
if (!op.responses) {
text += '\tresult := &HTTPResponse{\n';
text += '\t\tresult.RawResponse: resp\n';
text += '\t}\n';
} else {
text += `\tresult, err := client.${info.protocolNaming.responseMethod}(resp)\n`;
text += `\tif err != nil {\n`;
text += `\t\treturn nil, err\n`;
text += `\t}\n`;
}
text += `\tpt, err := createPollingTracker("${op.language.go!.pollerType.name}", resp, client.${info.protocolNaming.errorMethod})\n`;
text += `\tif err != nil {\n`;
text += `\t\treturn nil, err\n`;
text += `\t}\n`;
text += `\tresult.Poller = &${camelCase(op.language.go!.pollerType.name)}{\n`;
text += `\t\t\tpt: pt,\n`;
text += `\t\t\tpipeline: client.p,\n`;
text += `\t\t\tresponse: client.${info.protocolNaming.responseMethod},\n`;
text += `\t}\n`;
text += `\tresult.PollUntilDone = func(ctx context.Context, frequency time.Duration)(*${op.language.go!.pollerType.responseType}Response, error) {\n`;
text += `\t\treturn ${camelCase(op.language.go!.pollerType.name)}PollUntilDone(ctx, result.Poller, frequency)\n`;
text += `\t}\n`;
text += `\treturn result, nil\n`;
// closing braces
text += `\treturn &${op.language.go!.pollerType.name}{\n`;
text += `\t\tpt: pt,\n`;
text += `\t\tclient: client,\n`;
text += `\t}, nil\n`;
text += '}\n\n';
text += addResumePollerMethod(op, clientName);
return text;
Expand Down Expand Up @@ -593,6 +609,7 @@ function createProtocolResponse(client: string, op: Operation, imports: ImportMa
// this is to support operations that specify multiple response codes
// that return the same schema (or no schema).
// TODO: handle response codes with different schemas
// TODO: remove pageable LRO exception
let statusCodes = new Array<string>();
statusCodes = statusCodes.concat(firstResp.protocol.http?.statusCodes);
for (let i = 1; i < op.responses.length; ++i) {
Expand All @@ -605,15 +622,22 @@ function createProtocolResponse(client: string, op: Operation, imports: ImportMa
// same schemas, append status codes
statusCodes = statusCodes.concat(op.responses[i].protocol.http?.statusCodes);
}
} else if (isLROOperation(op)) {
statusCodes = statusCodes.concat(op.responses[i].protocol.http?.statusCodes);
}
}
// LROs will skip this check since the status code is checked by the poller
if (!isLROOperation(op)) {
text += `\tif !resp.HasStatusCode(${formatStatusCodes(statusCodes)}) {\n`;
text += `\t\treturn nil, client.${info.protocolNaming.errorMethod}(resp)\n`;
text += '\t}\n';
if (isLROOperation(op) && statusCodes.find(element => element === '204') === undefined) {
statusCodes = statusCodes.concat('204');
}
text += `\tif !resp.HasStatusCode(${formatStatusCodes(statusCodes)}) {\n`;
text += `\t\treturn nil, client.${info.protocolNaming.errorMethod}(resp)\n`;
text += '\t}\n';
if (!isSchemaResponse(firstResp)) {
if (isLROOperation(op)) {
text += '\treturn &HTTPResponse{RawResponse: resp.Response}, nil\n';
text += '}\n\n';
return text;
}
// no response body, return the *http.Response
text += `\treturn resp.Response, nil\n`;
text += '}\n\n';
Expand Down Expand Up @@ -747,8 +771,8 @@ function createInterfaceDefinition(group: OperationGroup, imports: ImportManager
interfaceText += `\t${opName}(${getAPIParametersSig(op, imports)}) (${returns.join(', ')})\n`;
// Add resume LRO poller method for each Begin poller method
if (isLROOperation(op) && !op.extensions!['x-ms-pageable']) {
interfaceText += `\t// Resume${pascalCase(op.language.go!.pollerType.name)} - Used to create a new instance of this poller from the resume token of a previous instance of this poller type.\n`;
interfaceText += `\tResume${pascalCase(op.language.go!.pollerType.name)}(id string) (${pascalCase(op.language.go!.pollerType.name)}, error)\n`;
interfaceText += `\t// Resume${op.language.go!.name} - Used to create a new instance of this poller from the resume token of a previous instance of this poller type.\n`;
interfaceText += `\tResume${op.language.go!.name}(token string) (${op.language.go!.pollerType.name}, error)\n`;
}
}
interfaceText += '}\n\n';
Expand Down Expand Up @@ -912,26 +936,26 @@ function generateReturnsInfo(op: Operation, forHandler: boolean): string[] {
// must check pageable first as all pageable operations are also schema responses
if (!forHandler && isPageableOperation(op)) {
returnType = op.language.go!.pageableType.name;
} else if (!forHandler && isLROOperation(op)) {
returnType = pascalCase(op.language.go!.pollerType.name);
} else if (isSchemaResponse(firstResp)) {
returnType = '*' + firstResp.schema.language.go!.responseType.name;
} else if (isLROOperation(op)) {
returnType = '*HTTPResponse';
}
return [returnType, 'error'];
}

function addResumePollerMethod(op: Operation, clientName: string): string {
const pollerName = pascalCase(op.language.go!.pollerType.name);
const info = <OperationNaming>op.language.go!;
let text = `func (client *${clientName}) Resume${pollerName}(token string) (${pollerName}, error) {\n`;
let text = `func (client *${clientName}) Resume${op.language.go!.name}(token string) (${op.language.go!.pollerType.name}, error) {\n`;
text += `\tpt, err := resumePollingTracker("${op.language.go!.pollerType.name}", token, client.${info.protocolNaming.errorMethod})\n`;
text += `\tif err != nil {\n`;
text += `\t\treturn nil, err\n`;
text += `\t}\n`;
text += `\treturn &${op.language.go!.pollerType.name}{\n`;
text += `\t\tclient: client,\n`;
text += '\t\tpt: pt,\n'
text += `\t}, nil\n`;
text += `}\n`;
text += '\tif err != nil {\n';
text += '\t\treturn nil, err\n';
text += '\t}\n';
text += `\treturn &${camelCase(op.language.go!.pollerType.name)}{\n`;
text += '\t\tpipeline: client.p,\n';
text += '\t\tpt: pt,\n';
text += `\t\tresponse: client.${info.protocolNaming.responseMethod},\n`;
text += '\t}, nil\n';
text += '}\n';
return text;
}
Loading

0 comments on commit 6d8ed32

Please sign in to comment.