diff --git a/packages/sdk-codegen/src/codeGen.ts b/packages/sdk-codegen/src/codeGen.ts index 6de05d534..98e6de39e 100644 --- a/packages/sdk-codegen/src/codeGen.ts +++ b/packages/sdk-codegen/src/codeGen.ts @@ -168,6 +168,9 @@ export interface ICodeGen { */ commentStr: string + /** Generate comments in source code? */ + noComment: boolean + /** * string representation of null value * e.g. Python None, C# null, Delphi nil @@ -735,6 +738,7 @@ export abstract class CodeGen implements ICodeGen { indentStr = ' ' commentStr = '// ' + noComment = false nullStr = 'null' endTypeStr = '' transport = 'rtl' @@ -1106,6 +1110,7 @@ export abstract class CodeGen implements ICodeGen { } comment(indent: string, description: string) { + if (this.noComment) return '' return commentBlock(description, indent, this.commentStr) } @@ -1114,6 +1119,7 @@ export abstract class CodeGen implements ICodeGen { text: string | undefined, _commentStr?: string ) { + if (this.noComment) return '' return text ? `${this.comment(indent, text)}\n` : '' } diff --git a/packages/sdk-codegen/src/csharp.gen.spec.ts b/packages/sdk-codegen/src/csharp.gen.spec.ts index c0966ef51..e81f6e5d3 100644 --- a/packages/sdk-codegen/src/csharp.gen.spec.ts +++ b/packages/sdk-codegen/src/csharp.gen.spec.ts @@ -89,6 +89,21 @@ describe('c# generator', () => { expect(actual).toEqual(expected) }) + it('noComment type', () => { + const type = apiTestModel.types.AccessToken + const expected = `public class AccessToken : SdkModel +{ + public string? access_token { get; set; } = null; + public string? token_type { get; set; } = null; + public long? expires_in { get; set; } = null; + public string? refresh_token { get; set; } = null; +}` + gen.noComment = true + const actual = gen.declareType(indent, type) + gen.noComment = false + expect(actual).toEqual(expected) + }) + it('with special names', () => { const type = apiTestModel.types.HyphenType const actual = gen.declareType(indent, type) @@ -204,6 +219,24 @@ public async Task> run_sql_query( const actual = gen.declareMethod(indent, method) expect(actual).toEqual(expected) }) + it('noComment method with multiple return types', () => { + const method = apiTestModel.methods.run_sql_query + const expected = `public async Task> run_sql_query( + string slug, + string result_format, + string? download = null, + ITransportSettings? options = null) where TSuccess : class +{ + slug = SdkUtils.EncodeParam(slug); + result_format = SdkUtils.EncodeParam(result_format); + return await AuthRequest(HttpMethod.Post, $"/sql_queries/{slug}/run/{result_format}", new Values { + { "download", download }},null,options); +}` + gen.noComment = true + const actual = gen.declareMethod(indent, method) + gen.noComment = false + expect(actual).toEqual(expected) + }) it('generates a method with a single return type', () => { const method = apiTestModel.methods.query_task_multi_results const expected = `/// ### Fetch results of multiple async queries diff --git a/packages/sdk-codegen/src/csharp.gen.ts b/packages/sdk-codegen/src/csharp.gen.ts index 0d5d7fd95..9e336b517 100644 --- a/packages/sdk-codegen/src/csharp.gen.ts +++ b/packages/sdk-codegen/src/csharp.gen.ts @@ -141,6 +141,7 @@ export class CSharpGen extends CodeGen { codeQuote = '"' commentHeader(indent: string, text: string | undefined) { + if (this.noComment) return '' return text ? `${commentBlock(text, indent, '/// ')}\n` : '' } @@ -381,7 +382,7 @@ namespace Looker.SDK.API${this.apiRef} } summary(indent: string, summary: string) { - if (!summary) return '' + if (this.noComment || !summary) return '' const nl = summary.indexOf('\n') >= 0 ? '\n' : '' return this.commentHeader(indent, `${nl}${summary}${nl}`) } diff --git a/packages/sdk-codegen/src/go.gen.spec.ts b/packages/sdk-codegen/src/go.gen.spec.ts new file mode 100644 index 000000000..9ac6eaff5 --- /dev/null +++ b/packages/sdk-codegen/src/go.gen.spec.ts @@ -0,0 +1,92 @@ +/* + + MIT License + + Copyright (c) 2021 Looker Data Sciences, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + */ + +import { TestConfig } from './testUtils' +import { IEnumType } from './sdkModels' +import { GoGen } from './go.gen' + +const config = TestConfig() +const apiTestModel = config.apiTestModel +const gen = new GoGen(apiTestModel) +const indent = '' + +describe('Go generator', () => { + describe('comment header', () => { + it('is empty with no comment', () => { + expect(gen.commentHeader(indent, '')).toEqual('') + }) + + it('is multiple lines with a two line comment', () => { + const expected = `/* + +foo +bar + +*/ +` + expect(gen.commentHeader(indent, 'foo\nbar')).toEqual(expected) + }) + }) + + describe('types', () => { + it('enum type', () => { + const type = apiTestModel.types.PermissionType as IEnumType + expect(type).toBeDefined() + expect(type.values).toEqual(['view', 'edit']) + const expected = `type PermissionType string +const PermissionType_View PermissionType = "view" +const PermissionType_Edit PermissionType = "edit" +` + const actual = gen.declareType('', type) + expect(actual).toEqual(expected) + }) + + it('special needs', () => { + const type = apiTestModel.types.HyphenType + const expected = ` +type HyphenType struct { + ProjectName *string \`json:"project_name,omitempty"\` // A normal variable name + ProjectDigest *string \`json:"project_digest,omitempty"\` // A hyphenated property name + ComputationTime *float32 \`json:"computation_time,omitempty"\` // A spaced out property name +}` + const actual = gen.declareType('', type) + expect(actual).toEqual(expected) + }) + it('noComment special needs', () => { + const type = apiTestModel.types.HyphenType + const expected = ` +type HyphenType struct { + ProjectName *string \`json:"project_name,omitempty"\` + ProjectDigest *string \`json:"project_digest,omitempty"\` + ComputationTime *float32 \`json:"computation_time,omitempty"\` +}` + gen.noComment = true + const actual = gen.declareType('', type) + gen.noComment = false + expect(actual).toEqual(expected) + }) + }) +}) diff --git a/packages/sdk-codegen/src/go.gen.ts b/packages/sdk-codegen/src/go.gen.ts index 1c02457bd..09e35228f 100644 --- a/packages/sdk-codegen/src/go.gen.ts +++ b/packages/sdk-codegen/src/go.gen.ts @@ -64,7 +64,7 @@ class AlignColumnWriter { result += rowSize - 1 === colIndex ? col : align(col, this.sizes[colIndex]) }) - result += '\n' + result = result.trimRight() + '\n' }) return result } @@ -133,12 +133,14 @@ export class GoGen extends CodeGen { } commentHeader(indent: string, text: string | undefined) { + if (this.noComment) return '' return text ? `${indent}/*\n\n${commentBlock(text, indent, '')}\n\n${indent}*/\n` : '' } comment(indent: string, description: string) { + if (this.noComment) return '' return commentBlock(description, indent, this.commentStr) } @@ -481,7 +483,7 @@ import ( let propertyValues = '' const num = type as EnumType const typeName = this.capitalize(type.name) - props.push(`type ${typeName} string`) // todo: handle other types then string + props.push(`type ${typeName} string`) // todo: handle other types than string num.values.forEach((value) => { // props.push(this.declareEnumValue(bump, value, typeName)) this.declareEnumValue(bump, value, typeName, writer) diff --git a/packages/sdk-codegen/src/kotlin.gen.spec.ts b/packages/sdk-codegen/src/kotlin.gen.spec.ts index 229088c90..2195b5c14 100644 --- a/packages/sdk-codegen/src/kotlin.gen.spec.ts +++ b/packages/sdk-codegen/src/kotlin.gen.spec.ts @@ -54,7 +54,6 @@ describe('Kotlin generator', () => { const type = apiTestModel.types.PermissionType as IEnumType expect(type).toBeDefined() expect(type.values).toEqual(['view', 'edit']) - const actual = gen.declareType('', type) const expected = `/** * Type of permission: "view" or "edit" Valid values are: "view", "edit". */ @@ -62,9 +61,23 @@ enum class PermissionType : Serializable { view, edit }` + const actual = gen.declareType('', type) expect(actual).toEqual(expected) }) + it('noComment enum type', () => { + const type = apiTestModel.types.PermissionType as IEnumType + expect(type).toBeDefined() + expect(type.values).toEqual(['view', 'edit']) + const expected = `enum class PermissionType : Serializable { + view, + edit +}` + gen.noComment = true + const actual = gen.declareType('', type) + gen.noComment = false + expect(actual).toEqual(expected) + }) it('special needs', () => { const type = apiTestModel.types.HyphenType const actual = gen.declareType('', type) diff --git a/packages/sdk-codegen/src/kotlin.gen.ts b/packages/sdk-codegen/src/kotlin.gen.ts index 7c0b1f441..4deae4e0f 100644 --- a/packages/sdk-codegen/src/kotlin.gen.ts +++ b/packages/sdk-codegen/src/kotlin.gen.ts @@ -131,7 +131,7 @@ import java.util.* // TODO create methodHeader(IMethod) and typeHeader(IType) https://kotlinlang.org/docs/reference/kotlin-doc.html commentHeader(indent: string, text: string | undefined, commentStr = ' * ') { - if (!text) return '' + if (this.noComment || !text) return '' if (commentStr === ' ') { return `${indent}/**\n\n${commentBlock( text, diff --git a/packages/sdk-codegen/src/pseudo.gen.spec.ts b/packages/sdk-codegen/src/pseudo.gen.spec.ts index a79277e74..cc4cfa611 100644 --- a/packages/sdk-codegen/src/pseudo.gen.spec.ts +++ b/packages/sdk-codegen/src/pseudo.gen.spec.ts @@ -34,13 +34,29 @@ const gen = new PseudoGen(apiTestModel) describe('pseudocode', () => { describe('method signature', () => { - it('optional body and additional param', () => { + it('noComment optional body and additional param', () => { const method = apiTestModel.methods.create_user_credentials_email expect(method).toBeDefined() const expected = `create_user_credentials_email( user_id: int64, body: CredentialsEmail, [fields: string] +): CredentialsEmail` + gen.noComment = true + const actual = gen.methodSignature('', method) + gen.noComment = false + expect(actual).toEqual(expected) + }) + it('optional body and additional param', () => { + const method = apiTestModel.methods.create_user_credentials_email + expect(method).toBeDefined() + const expected = `"### Email/password login information for the specified user." +create_user_credentials_email( + "id of user" +user_id: int64, + body: CredentialsEmail, + "Requested fields." +[fields: string] ): CredentialsEmail` const actual = gen.methodSignature('', method) expect(actual).toEqual(expected) @@ -48,17 +64,33 @@ describe('pseudocode', () => { it('no params', () => { const method = apiTestModel.methods.all_datagroups expect(method).toBeDefined() - const expected = `all_datagroups(): Datagroup[]` + const expected = `"### Get information about all datagroups." +all_datagroups(): Datagroup[]` const actual = gen.methodSignature('', method) expect(actual).toEqual(expected) }) test('import_lookml_dashboard', () => { const method = apiTestModel.methods.import_lookml_dashboard - const expected = `import_lookml_dashboard( - lookml_dashboard_id: string, - space_id: string, + const expected = `"### Import a LookML dashboard to a space as a UDD +"Creates a UDD (a dashboard which exists in the Looker database rather than as a LookML file) from the LookML dashboard +"and puts it in the space specified. The created UDD will have a lookml_link_id which links to the original LookML dashboard. +" +"To give the imported dashboard specify a (e.g. title: "my title") in the body of your request, otherwise the imported +"dashboard will have the same title as the original LookML dashboard. +" +"For this operation to succeed the user must have permission to see the LookML dashboard in question, and have permission to +"create content in the space the dashboard is being imported to. +" +"**Sync** a linked UDD with [sync_lookml_dashboard()](#!/Dashboard/sync_lookml_dashboard) +"**Unlink** a linked UDD by setting lookml_link_id to null with [update_dashboard()](#!/Dashboard/update_dashboard)" +import_lookml_dashboard( + "Id of LookML dashboard" +lookml_dashboard_id: string, + "Id of space to import the dashboard to" +space_id: string, [body: Dashboard], - [raw_locale: boolean] + "If true, and this dashboard is localized, export it with the raw keys, not localized." +[raw_locale: boolean] ): Dashboard` const actual = gen.methodSignature('', method) expect(actual).toEqual(expected) @@ -69,15 +101,25 @@ describe('pseudocode', () => { const type = apiTestModel.types.Datagroup expect(type).toBeDefined() const expected = `Datagroup { + "Operations the current user is able to perform on this object" [can: Hash[boolean]] + "UNIX timestamp at which this entry was created." [created_at: int64] + "Unique ID of the datagroup" [id: int64] + "Name of the model containing the datagroup. Unique when combined with name." [model_name: string] + "Name of the datagroup. Unique when combined with model_name." [name: string] + "UNIX timestamp before which cache entries are considered stale. Cannot be in the future." [stale_before: int64] + "UNIX timestamp at which this entry trigger was last checked." [trigger_check_at: int64] + "The message returned with the error of the last trigger check." [trigger_error: string] + "The value of the trigger when last checked." [trigger_value: string] + "UNIX timestamp at which this entry became triggered. Cannot be in the future." [triggered_at: int64]}` const actual = gen.declareType('', type) expect(actual).toEqual(expected) diff --git a/packages/sdk-codegen/src/pseudo.gen.ts b/packages/sdk-codegen/src/pseudo.gen.ts index b1780379f..28cc65e4f 100644 --- a/packages/sdk-codegen/src/pseudo.gen.ts +++ b/packages/sdk-codegen/src/pseudo.gen.ts @@ -33,18 +33,17 @@ import { ArrayType, IMethod, IParameter, IProperty, IType } from './sdkModels' * Pseudocde generator */ export class PseudoGen extends CodeGen { + commentStr = '"' endTypeStr = '}' /** * Generic prototype-style method signature generator * * NOTE: Every language generator should override this methodSignature function * - * @param {string} indent indentation for code - * @param {IMethod} method for signature - * @returns {string} prototype declaration of method + * @param indent indentation for code + * @param method for signature */ methodSignature(indent: string, method: IMethod): string { - indent = '' const params = method.allParams const args = params.map((p) => this.declareParameter(indent, method, p)) const bump = this.bumper(indent) @@ -52,7 +51,21 @@ export class PseudoGen extends CodeGen { args.length === 0 ? '' : `\n${bump}${args.join(',\n' + bump).trim()}${indent}\n` - return `${indent}${method.operationId}(${fragment}): ${method.primaryResponse.type.name}` + return ( + this.commentHeader(indent, method.description) + + `${indent}${method.operationId}(${fragment}): ${method.primaryResponse.type.name}` + ) + } + + commentHeader( + indent: string, + text: string | undefined, + _commentStr?: string + ): string | string { + if (this.noComment) return '' + const comment = super.commentHeader(indent, text, _commentStr).trimRight() + if (!comment) return '' + return `${comment}${this.commentStr}\n` } construct(_indent: string, _type: IType): string { @@ -68,15 +81,18 @@ export class PseudoGen extends CodeGen { _method: IMethod, param: IParameter ): string { - const result = `${indent}${param.name}: ${param.type.name}` - if (param.required) return result - return `[${result}]` + let result = `${indent}${param.name}: ${param.type.name}` + if (!param.required) result = `[${result}]` + return this.commentHeader(indent, param.description) + result } declareProperty(indent: string, property: IProperty): string { const lb = property.required ? '' : '[' const rb = property.required ? '' : ']' - return `${indent}${lb}${property.name}: ${property.type.name}${rb}` + return ( + this.commentHeader(indent, property.description) + + `${indent}${lb}${property.name}: ${property.type.name}${rb}` + ) } encodePathParams(_indent: string, _method: IMethod): string { @@ -104,7 +120,10 @@ export class PseudoGen extends CodeGen { } typeSignature(indent: string, type: IType): string { - return `${indent}${type.name} ${this.typeOpen}\n` + return ( + this.commentHeader(indent, type.description) + + `${indent}${type.name} ${this.typeOpen}\n` + ) } declareType(indent: string, type: IType): string { diff --git a/packages/sdk-codegen/src/python.gen.spec.ts b/packages/sdk-codegen/src/python.gen.spec.ts index e11ca65d8..0e37178d2 100644 --- a/packages/sdk-codegen/src/python.gen.spec.ts +++ b/packages/sdk-codegen/src/python.gen.spec.ts @@ -285,6 +285,19 @@ def render_task_results( expect(actual).toEqual(expected) }) + it('noComment binary return type render_task_results', () => { + const method = apiTestModel.methods.render_task_results + const expected = `def render_task_results( + self, + render_task_id: str, + transport_options: Optional[transport.TransportOptions] = None, +) -> bytes: +` + gen.noComment = true + const actual = gen.methodSignature('', method) + gen.noComment = false + expect(actual).toEqual(expected) + }) it('binary or string return type run_url_encoded_query', () => { const method = apiTestModel.methods.run_url_encoded_query const expected = `# ### Run an URL encoded query. diff --git a/packages/sdk-codegen/src/swift.gen.spec.ts b/packages/sdk-codegen/src/swift.gen.spec.ts index 2da7edd2d..1f7c6b767 100644 --- a/packages/sdk-codegen/src/swift.gen.spec.ts +++ b/packages/sdk-codegen/src/swift.gen.spec.ts @@ -54,7 +54,6 @@ describe('swift generator', () => { const type = apiTestModel.types.PermissionType as IEnumType expect(type).toBeDefined() expect(type.values).toEqual(['view', 'edit']) - const actual = gen.declareType('', type) const expected = `/** * Type of permission: "view" or "edit" Valid values are: "view", "edit". */ @@ -62,6 +61,20 @@ public enum PermissionType: String, Codable { case view = "view" case edit = "edit" }` + const actual = gen.declareType('', type) + expect(actual).toEqual(expected) + }) + it('noComment enum type', () => { + const type = apiTestModel.types.PermissionType as IEnumType + expect(type).toBeDefined() + expect(type.values).toEqual(['view', 'edit']) + const expected = `public enum PermissionType: String, Codable { + case view = "view" + case edit = "edit" +}` + gen.noComment = true + const actual = gen.declareType('', type) + gen.noComment = false expect(actual).toEqual(expected) }) }) diff --git a/packages/sdk-codegen/src/swift.gen.ts b/packages/sdk-codegen/src/swift.gen.ts index b11a2a645..e4d8e7531 100644 --- a/packages/sdk-codegen/src/swift.gen.ts +++ b/packages/sdk-codegen/src/swift.gen.ts @@ -210,7 +210,7 @@ import Foundation } commentHeader(indent: string, text: string | undefined, commentStr = ' * ') { - if (!text) return '' + if (this.noComment || !text) return '' if (commentStr === ' ') { return `${indent}/**\n\n${commentBlock( text, diff --git a/packages/sdk-codegen/src/typescript.gen.spec.ts b/packages/sdk-codegen/src/typescript.gen.spec.ts index b48c7b4ea..f6b082cf6 100644 --- a/packages/sdk-codegen/src/typescript.gen.spec.ts +++ b/packages/sdk-codegen/src/typescript.gen.spec.ts @@ -586,6 +586,19 @@ async create_user_credentials_email( const actual = gen.methodSignature('', method) expect(actual).toEqual(expected) }) + it('noComment optional body and additional param', () => { + const method = apiTestModel.methods.create_user_credentials_email + expect(method).toBeDefined() + const expected = `async create_user_credentials_email( + user_id: number, + body: Partial, + fields?: string, options?: Partial): Promise> { +` + gen.noComment = true + const actual = gen.methodSignature('', method) + gen.noComment = false + expect(actual).toEqual(expected) + }) it('no params', () => { const method = apiTestModel.methods.all_datagroups expect(method).toBeDefined() diff --git a/packages/sdk-codegen/src/typescript.gen.ts b/packages/sdk-codegen/src/typescript.gen.ts index 17af1bd26..8a79c863e 100644 --- a/packages/sdk-codegen/src/typescript.gen.ts +++ b/packages/sdk-codegen/src/typescript.gen.ts @@ -210,7 +210,7 @@ export class ${this.packageName}Stream extends APIMethods { } commentHeader(indent: string, text: string | undefined, commentStr = ' * ') { - if (!text) return '' + if (this.noComment || !text) return '' const commentPrefix = text.includes(' License') && text.includes('Copyright (c)') ? '/*' : '/**' if (commentStr === ' ') {