From c62afcc684919966109d3e0ed4d1374299de6ef7 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Tue, 29 Apr 2025 20:54:02 -0600 Subject: [PATCH 01/24] attempt to do overloads --- ...retrieveUserByLoginIdWithLoginIdTypes.json | 30 ++++++++++ ...ieveUserLoginReportByLoginIdWithTypes.json | 58 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json create mode 100644 src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json new file mode 100644 index 000000000..658acd829 --- /dev/null +++ b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json @@ -0,0 +1,30 @@ +{ + "uri": "/api/user", + "comments": [ + "Retrieves the user for the loginId for specific loginIdTypes." + ], + "method": "get", + "methodName": "retrieveUserByLoginId", + "successResponse": "UserResponse", + "errorResponse": "Errors", + "params": [ + { + "name": "loginId", + "comments": [ + "The email or username of the user." + ], + "type": "urlParameter", + "parameterName": "loginId", + "javaType": "String" + }, + { + "name": "loginIdTypes", + "comments": [ + "The identity types that FusionAuth will compare the loginId to." + ], + "type": "urlParameter", + "parameterName": "loginIdTypes", + "javaType": "String[]" + } + ] +} diff --git a/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json b/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json new file mode 100644 index 000000000..483674fa4 --- /dev/null +++ b/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json @@ -0,0 +1,58 @@ +{ + "uri": "/api/report/login", + "comments": [ + "Retrieves the login report between the two instants for a particular user by login Id. If you specify an application id, it will only return the", + "login counts for that application." + ], + "method": "get", + "methodName": "retrieveUserLoginReportByLoginId", + "successResponse": "LoginReportResponse", + "errorResponse": "Errors", + "params": [ + { + "name": "applicationId", + "comments": [ + "(Optional) The application id." + ], + "type": "urlParameter", + "parameterName": "applicationId", + "javaType": "UUID" + }, + { + "name": "loginId", + "comments": [ + "The userId id." + ], + "type": "urlParameter", + "parameterName": "loginId", + "javaType": "String" + }, + { + "name": "loginIdTypes", + "comments": [ + "The identity types that FusionAuth will compare the loginId to." + ], + "type": "urlParameter", + "parameterName": "loginIdTypes", + "javaType": "String[]" + }, + { + "name": "start", + "comments": [ + "The start instant as UTC milliseconds since Epoch." + ], + "type": "urlParameter", + "parameterName": "start", + "javaType": "long" + }, + { + "name": "end", + "comments": [ + "The end instant as UTC milliseconds since Epoch." + ], + "type": "urlParameter", + "parameterName": "end", + "javaType": "long" + } + ] +} From db867f418fa248e0d58cd0f0f595ad22bc8b639a Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Tue, 29 Apr 2025 21:04:41 -0600 Subject: [PATCH 02/24] arrays do not work --- src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json | 2 +- src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json index 658acd829..441b2312d 100644 --- a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json +++ b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json @@ -24,7 +24,7 @@ ], "type": "urlParameter", "parameterName": "loginIdTypes", - "javaType": "String[]" + "javaType": "List" } ] } diff --git a/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json b/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json index 483674fa4..266b77b91 100644 --- a/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json +++ b/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json @@ -34,7 +34,7 @@ ], "type": "urlParameter", "parameterName": "loginIdTypes", - "javaType": "String[]" + "javaType": "List" }, { "name": "start", From d1d39ff6656fbab5080cc4d405129a99bc527250 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Wed, 30 Apr 2025 12:12:25 -0600 Subject: [PATCH 03/24] Try doing this with same method instead - works for typescript --- src/main/api/retrieveUserByLoginId.json | 14 ++++- ...retrieveUserByLoginIdWithLoginIdTypes.json | 30 ---------- ...ieveUserLoginReportByLoginIdWithTypes.json | 58 ------------------- src/main/client/_macros.ftl | 7 ++- src/main/client/typescript.client.ftl | 4 ++ 5 files changed, 22 insertions(+), 91 deletions(-) delete mode 100644 src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json delete mode 100644 src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json diff --git a/src/main/api/retrieveUserByLoginId.json b/src/main/api/retrieveUserByLoginId.json index e2380eaa7..8deff7722 100644 --- a/src/main/api/retrieveUserByLoginId.json +++ b/src/main/api/retrieveUserByLoginId.json @@ -1,7 +1,7 @@ { "uri": "/api/user", "comments": [ - "Retrieves the user for the loginId. The loginId can be either the username or the email." + "Retrieves the user for the loginId, using specific loginIdTypes. If no loginIdTypes are provided, the default is [email, username]" ], "method": "get", "methodName": "retrieveUserByLoginId", @@ -16,6 +16,16 @@ "type": "urlParameter", "parameterName": "loginId", "javaType": "String" + }, + { + "name": "loginIdTypes", + "comments": [ + "(Optional) the identity types that FusionAuth will compare the loginId to." + ], + "type": "urlParameter", + "parameterName": "loginIdTypes", + "javaType": "Collection", + "optional": true } ] -} \ No newline at end of file +} diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json deleted file mode 100644 index 441b2312d..000000000 --- a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "uri": "/api/user", - "comments": [ - "Retrieves the user for the loginId for specific loginIdTypes." - ], - "method": "get", - "methodName": "retrieveUserByLoginId", - "successResponse": "UserResponse", - "errorResponse": "Errors", - "params": [ - { - "name": "loginId", - "comments": [ - "The email or username of the user." - ], - "type": "urlParameter", - "parameterName": "loginId", - "javaType": "String" - }, - { - "name": "loginIdTypes", - "comments": [ - "The identity types that FusionAuth will compare the loginId to." - ], - "type": "urlParameter", - "parameterName": "loginIdTypes", - "javaType": "List" - } - ] -} diff --git a/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json b/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json deleted file mode 100644 index 266b77b91..000000000 --- a/src/main/api/retrieveUserLoginReportByLoginIdWithTypes.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "uri": "/api/report/login", - "comments": [ - "Retrieves the login report between the two instants for a particular user by login Id. If you specify an application id, it will only return the", - "login counts for that application." - ], - "method": "get", - "methodName": "retrieveUserLoginReportByLoginId", - "successResponse": "LoginReportResponse", - "errorResponse": "Errors", - "params": [ - { - "name": "applicationId", - "comments": [ - "(Optional) The application id." - ], - "type": "urlParameter", - "parameterName": "applicationId", - "javaType": "UUID" - }, - { - "name": "loginId", - "comments": [ - "The userId id." - ], - "type": "urlParameter", - "parameterName": "loginId", - "javaType": "String" - }, - { - "name": "loginIdTypes", - "comments": [ - "The identity types that FusionAuth will compare the loginId to." - ], - "type": "urlParameter", - "parameterName": "loginIdTypes", - "javaType": "List" - }, - { - "name": "start", - "comments": [ - "The start instant as UTC milliseconds since Epoch." - ], - "type": "urlParameter", - "parameterName": "start", - "javaType": "long" - }, - { - "name": "end", - "comments": [ - "The end instant as UTC milliseconds since Epoch." - ], - "type": "urlParameter", - "parameterName": "end", - "javaType": "long" - } - ] -} diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index f52d0400b..bce958e9d 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -226,7 +226,12 @@ [#local result = result + [param.name]/] [#elseif language == "ts"] [#local convertedType = convertType(param.javaType, language)/] - [#local result = result + [param.name + (convertedType != "Object")?then(': ' + convertedType, '')]] + [#if param.optional!false] + [#local suffix = "?"/] + [#else] + [#local suffix = ""/] + [/#if] + [#local result = result + [param.name + suffix + (convertedType != "Object")?then(': ' + convertedType, '')]] [#elseif language == "go"] [#local convertedType = convertType(param.javaType, language)/] [#if api.method == "patch" && convertedType?ends_with("Request")] diff --git a/src/main/client/typescript.client.ftl b/src/main/client/typescript.client.ftl index b76bf8231..c8e266f6c 100644 --- a/src/main/client/typescript.client.ftl +++ b/src/main/client/typescript.client.ftl @@ -100,7 +100,11 @@ export class FusionAuthClient { [#if param.type == "urlSegment"] .withUriSegment(${(param.constant?? && param.constant)?then(param.value, param.name)}) [#elseif param.type == "urlParameter"] + [#if param.optional!false] + .withOptionalParameter('${param.parameterName}', ${(param.constant?? && param.constant)?then(param.value, param.name)}) + [#else] .withParameter('${param.parameterName}', ${(param.constant?? && param.constant)?then(param.value, param.name)}) + [/#if] [#elseif param.type == "body"] .withJSONBody(${param.name}) [/#if] From bc5d29296b81d3e3d86586fb38180e74914b7811 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 10:21:59 -0600 Subject: [PATCH 04/24] this approach seems to work well --- src/main/api/retrieveUserByLoginId.json | 13 ++------ ...retrieveUserByLoginIdWithLoginIdTypes.json | 32 +++++++++++++++++++ src/main/client/java.client.ftl | 13 ++++++-- 3 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json diff --git a/src/main/api/retrieveUserByLoginId.json b/src/main/api/retrieveUserByLoginId.json index 8deff7722..6e0596fb7 100644 --- a/src/main/api/retrieveUserByLoginId.json +++ b/src/main/api/retrieveUserByLoginId.json @@ -1,12 +1,13 @@ { "uri": "/api/user", "comments": [ - "Retrieves the user for the loginId, using specific loginIdTypes. If no loginIdTypes are provided, the default is [email, username]" + "Retrieves the user for the loginId. The loginId can be either the username or the email." ], "method": "get", "methodName": "retrieveUserByLoginId", "successResponse": "UserResponse", "errorResponse": "Errors", + "overloads": true, "params": [ { "name": "loginId", @@ -16,16 +17,6 @@ "type": "urlParameter", "parameterName": "loginId", "javaType": "String" - }, - { - "name": "loginIdTypes", - "comments": [ - "(Optional) the identity types that FusionAuth will compare the loginId to." - ], - "type": "urlParameter", - "parameterName": "loginIdTypes", - "javaType": "Collection", - "optional": true } ] } diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json new file mode 100644 index 000000000..35967000b --- /dev/null +++ b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json @@ -0,0 +1,32 @@ +{ + "uri": "/api/user", + "comments": [ + "Retrieves the user for the loginId, using specific loginIdTypes." + ], + "method": "get", + "methodName": "retrieveUserByLoginIdWithLoginIdTypes", + "successResponse": "UserResponse", + "errorResponse": "Errors", + "overload": "retrieveUserByLoginId", + "params": [ + { + "name": "loginId", + "comments": [ + "The email or username of the user." + ], + "type": "urlParameter", + "parameterName": "loginId", + "javaType": "String" + }, + { + "name": "loginIdTypes", + "comments": [ + "the identity types that FusionAuth will compare the loginId to." + ], + "type": "urlParameter", + "parameterName": "loginIdTypes", + "javaType": "Collection", + "optional": true + } + ] +} diff --git a/src/main/client/java.client.ftl b/src/main/client/java.client.ftl index 4f8ec68fb..e8bbcd8bc 100644 --- a/src/main/client/java.client.ftl +++ b/src/main/client/java.client.ftl @@ -334,7 +334,15 @@ public class FusionAuthClient { return new FusionAuthClient(apiKey, baseURL, connectTimeout, readTimeout, tenantId, objectMapper); } -[#list apis as api] +[#-- Java supports method overloading, so exclude apis that are just overloads --] +[#list apis?filter(api -> !(api.overload??)) as top_level_api] + [#if top_level_api.overloads!false] + [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] + [#else] + [#assign overloads=[top_level_api] /] + [/#if] + [#list overloads as api] + [#assign is_overload = api.overload??/] /** [#list api.comments as comment] * [#if comment?has_content]${comment}[#else]

[/#if] @@ -353,7 +361,7 @@ public class FusionAuthClient { [#if api.deprecated??] @Deprecated [/#if] - public ClientResponse<${api.successResponse}, ${api.errorResponse}> ${api.methodName}(${global.methodParameters(api, "java")}) { + public ClientResponse<${api.successResponse}, ${api.errorResponse}> ${is_overload?then(api.overload, api.methodName)}(${global.methodParameters(api, "java")}) { [#assign formPost = false/] [#list api.params![] as param] [#if param.type == "form"][#assign formPost = true/][/#if] @@ -387,6 +395,7 @@ public class FusionAuthClient { .go(); } +[/#list] [/#list] protected RESTClient start(Class type, Class errorType) { From 73768269729a65085c33130160ef2fb4eff35eb8 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 10:46:20 -0600 Subject: [PATCH 05/24] Handle Ruby --- src/main/client/_macros.ftl | 4 ++++ src/main/client/ruby.client.ftl | 11 ++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index bce958e9d..cb614fae2 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -249,7 +249,11 @@ [#if param.name == "end"] [#local result = result + ["_end"]/] [#else] + [#if param.optional!false] + [#local result = result + [camel_to_underscores(param.name) + '=nil']/] + [#else] [#local result = result + [camel_to_underscores(param.name)]/] + [/#if] [/#if] [#elseif language == "csharp"] [#local convertedType = convertType(param.javaType, language)/] diff --git a/src/main/client/ruby.client.ftl b/src/main/client/ruby.client.ftl index 375dbd7ce..a7d0285d2 100644 --- a/src/main/client/ruby.client.ftl +++ b/src/main/client/ruby.client.ftl @@ -42,22 +42,27 @@ module FusionAuth @tenant_id = tenant_id end -[#list apis as api] +[#-- Ruby supports default positional arguments, so exclude apis that are just overloads --] +[#list apis?filter(api -> !(api.overloads!false)) as api] # [#list api.comments as comment] # ${comment} [/#list] # [#list api.params![] as param] + [#assign paramComments = param.comments![]/] + [#if param.optional!false] + [#assign paramComments = ["(Optional) "+param.comments[0]] + param.comments[1..]/] + [/#if] [#if !param.constant??] - # @param ${camel_to_underscores(param.name?replace("end", "_end"))} [${global.convertType(param.javaType, "ruby")}] ${param.comments?join("\n # ")} + # @param ${camel_to_underscores(param.name?replace("end", "_end"))} [${global.convertType(param.javaType, "ruby")}] ${paramComments?join("\n # ")} [/#if] [/#list] # @return [FusionAuth::ClientResponse] The ClientResponse object. [#if api.deprecated??] # @deprecated ${api.deprecated?replace("{{renamedMethod}}", camel_to_underscores(api.renamedMethod!''))} [/#if] - def ${camel_to_underscores(api.methodName)}[#if (api.params![])?filter(p -> !p.constant??)?has_content](${global.methodParameters(api, "ruby")})[/#if] + def ${camel_to_underscores(api.overload???then(api.overload, api.methodName))}[#if (api.params![])?filter(p -> !p.constant??)?has_content](${global.methodParameters(api, "ruby")})[/#if] [#assign formPost = false/] [#list api.params![] as param] [#if param.type == "form"][#assign formPost = true/][/#if] From a4d313eac15ca30eb3ddabba4ca184f3152f3ac5 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 11:18:49 -0600 Subject: [PATCH 06/24] Python updates + opt text --- .../api/retrieveUserByLoginIdWithLoginIdTypes.json | 2 +- src/main/client/_macros.ftl | 5 +++-- src/main/client/python.client.ftl | 11 ++++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json index 35967000b..0a3753496 100644 --- a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json +++ b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json @@ -21,7 +21,7 @@ { "name": "loginIdTypes", "comments": [ - "the identity types that FusionAuth will compare the loginId to." + "the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" ], "type": "urlParameter", "parameterName": "loginIdTypes", diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index cb614fae2..44a00b2fd 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -239,8 +239,9 @@ [/#if] [#local result = result + [convertValue(param.name, language) + (convertedType != "interface{}")?then(' ' + convertedType, ' interface{}')]] [#elseif language == "python"] - [#-- If the parameter is optional, give it a default value --] - [#if optional] + [#-- If the parameter is optional, give it a default value. Ideally we'd not use optional, which is driven by string comments, but + changing that now would cause a lot of disruption --] + [#if optional || param.optional!false] [#local result = result + [convertValue(param, language) + "=None"]/] [#else] [#local result = result + [convertValue(param, language)]/] diff --git a/src/main/client/python.client.ftl b/src/main/client/python.client.ftl index 6b42fb815..ffb0f3570 100644 --- a/src/main/client/python.client.ftl +++ b/src/main/client/python.client.ftl @@ -37,18 +37,23 @@ class FusionAuthClient: """Sets the tenant_id on the client""" self.tenant_id = tenant_id -[#list apis as api] +[#-- Python supports default positional arguments, so exclude apis that are just overloads --] +[#list apis?filter(api -> !(api.overloads!false)) as api] [#if api.deprecated??] @deprecated("${api.deprecated?replace("{{renamedMethod}}", camel_to_underscores(api.renamedMethod!''))}") [/#if] - def ${camel_to_underscores(api.methodName)}(${global.methodParameters(api, "python")}): + def ${camel_to_underscores(api.overload???then(api.overload, api.methodName))}(${global.methodParameters(api, "python")}): """ ${api.comments?join("\n ")} Attributes: [#list api.params![] as param] + [#assign paramComments = param.comments![]/] + [#if param.optional!false] + [#assign paramComments = ["(Optional) "+param.comments[0]] + param.comments[1..]/] + [/#if] [#if !param.constant??] - ${global.convertValue(param, "python")}: ${param.comments?join("\n ")} + ${global.convertValue(param, "python")}: ${paramComments?join("\n ")} [/#if] [/#list] """ From 3ae5d219e162b450ed0eddd0553ceafd1ec60500 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 11:34:35 -0600 Subject: [PATCH 07/24] Most of this openapi stuff should work --- bin/build-openapi-yaml.rb | 265 +++++++++++++++++++++----------------- 1 file changed, 144 insertions(+), 121 deletions(-) diff --git a/bin/build-openapi-yaml.rb b/bin/build-openapi-yaml.rb index 4152a27eb..870158311 100755 --- a/bin/build-openapi-yaml.rb +++ b/bin/build-openapi-yaml.rb @@ -1,3 +1,17 @@ +# Copyright (c) 2025, FusionAuth, All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + #!/usr/bin/env ruby require 'json' @@ -54,41 +68,41 @@ def is_primitive(type) def convert_primitive(type) if type == "boolean" - return {"type" => "boolean"} + return { "type" => "boolean" } end if type == "Array" - return {"type" => "string", "format" => "binary"} + return { "type" => "string", "format" => "binary" } end if type == "URI" - return {"type" => "string", "format" => "URI"} # TODO not sure if that is correct? + return { "type" => "string", "format" => "URI" } # TODO not sure if that is correct? end if type == "Object" - return {"type" => "object"} + return { "type" => "object" } end if type == "Set" || type == "SortedSet" - return {"type" => "array", "uniqueItems" => true, "items" => {}} + return { "type" => "array", "uniqueItems" => true, "items" => {} } end if type == "UUID" - return {"type" => "string", "format" => "uuid"} + return { "type" => "string", "format" => "uuid" } end if type == "String" || type == "char" - return {"type" => "string"} + return { "type" => "string" } end if type == "integer" || type == "int" - return {"type" => "integer"} + return { "type" => "integer" } end if type == "long" - return {"type" => "integer", "format" => "int64"} + return { "type" => "integer", "format" => "int64" } end if type == "double" - return {"type" => "number", "format" => "double"} + return { "type" => "number", "format" => "double" } end end @@ -101,26 +115,26 @@ def add_identity_provider_field(schemas, identity_providers) } identity_providers.each do |idp| ref = make_ref(idp) - schemas["IdentityProviderField"]["oneOf"] << {"$ref" => ref } - schemas["IdentityProviderField"]["discriminator"]["mapping"][idp.sub("IdentityProvider","")] = ref + schemas["IdentityProviderField"]["oneOf"] << { "$ref" => ref } + schemas["IdentityProviderField"]["discriminator"]["mapping"][idp.sub("IdentityProvider", "")] = ref end end -def make_ref(type,packageName = nil) +def make_ref(type, packageName = nil) if type == "Void" return nil end - objectName = modify_type(packageName,type) - return '#/components/schemas/'+objectName + objectName = modify_type(packageName, type) + return '#/components/schemas/' + objectName end -def modify_type(packageName,objectName) +def modify_type(packageName, objectName) # collisions in a few cases if objectName == "LambdaConfiguration" if packageName.match(/connector/) - return "Connector"+objectName + return "Connector" + objectName elsif packageName.match(/provider/) - return "Provider"+objectName + return "Provider" + objectName end end return objectName @@ -129,8 +143,8 @@ def modify_type(packageName,objectName) def process_domain_file(fn, schemas, options, identity_providers, domain_files) # This method is working on domain_files, but also needs to read them. Do not modify the list! - if options[:verbose] - puts "processing "+fn + if options[:verbose] + puts "processing " + fn end f = File.open(fn) @@ -139,20 +153,19 @@ def process_domain_file(fn, schemas, options, identity_providers, domain_files) f.close packagename = json["packageName"] - objectname = modify_type(packagename,json["type"]) + objectname = modify_type(packagename, json["type"]) openapiobj = {} if json["description"] - openapiobj["description"] = json["description"].gsub('/','').gsub(/@au.*/,'').gsub('*','').gsub(/\n/,'').gsub("\n",'').delete("\n").strip + openapiobj["description"] = json["description"].gsub('/', '').gsub(/@au.*/, '').gsub('*', '').gsub(/\n/, '').gsub("\n", '').delete("\n").strip end openapiobj["type"] = "object" - # TODO What about ENUMS in an existing data model with fields? - if json["enum"] + if json["enum"] openapiobj["type"] = "string" # some enums have name attribute, other are just plain strings if json["enum"][0] && json["enum"][0]["name"] - openapiobj["enum"] = json["enum"].map {|e| e["name"] } + openapiobj["enum"] = json["enum"].map { |e| e["name"] } else openapiobj["enum"] = json["enum"] end @@ -186,16 +199,16 @@ def process_domain_file(fn, schemas, options, identity_providers, domain_files) if fields properties = {} openapiobj["properties"] = properties - fields.each do |k,v| + fields.each do |k, v| properties[k] = {} - v.each do |k2,v2| - if k2 == "type" + v.each do |k2, v2| + if k2 == "type" if is_primitive(v2) properties[k] = convert_primitive(v2) elsif v2 == "List" listElementType = fields[k]["typeArguments"][0]["type"] - addListValue(properties[k],k2,listElementType,identity_providers, k, objectname) - + addListValue(properties[k], k2, listElementType, identity_providers, k, objectname) + elsif v2 == "Map" properties[k][k2] = "object" properties[k]["additionalProperties"] = {} @@ -209,13 +222,13 @@ def process_domain_file(fn, schemas, options, identity_providers, domain_files) properties[k]["additionalProperties"] = convert_primitive(mapValueType) elsif mapValueType == "List" listElementType = fields[k]["typeArguments"][1]["typeArguments"][0]["type"] - addListValue(properties[k],k2,listElementType,identity_providers) - elsif mapValueType == "D" && k == "applicationConfiguration" && objectname.match(/IdentityProvider$/) + addListValue(properties[k], k2, listElementType, identity_providers) + elsif mapValueType == "D" && k == "applicationConfiguration" && objectname.match(/IdentityProvider$/) if objectname.match(/BaseIdentityProvider$/) or objectname.match(/BaseSAMLv2IdentityProvider$/) # remove this one, we don't need to provide anything for the BaseIdentityProvider or BaseSAMLv2IdentityProvider application config.properties, I think properties.delete(k) else - identityproviderconfigrefname = objectname.sub("IdentityProvider","") + "ApplicationConfiguration" + identityproviderconfigrefname = objectname.sub("IdentityProvider", "") + "ApplicationConfiguration" properties[k]["additionalProperties"]['$ref'] = make_ref(identityproviderconfigrefname) end else @@ -228,10 +241,10 @@ def process_domain_file(fn, schemas, options, identity_providers, domain_files) end else if v2.match(/BaseIdentityProvider$/) - # special handling of this. We create IdentityProviderField elsewhere - # see https://github.com/OpenAPITools/openapi-generator/issues/10880#issuecomment-995243186 for why - properties[k]["$ref"] = make_ref('IdentityProviderField') - + # special handling of this. We create IdentityProviderField elsewhere + # see https://github.com/OpenAPITools/openapi-generator/issues/10880#issuecomment-995243186 for why + properties[k]["$ref"] = make_ref('IdentityProviderField') + else # put in ref properties[k]['$ref'] = make_ref(v2, packagename) @@ -242,18 +255,18 @@ def process_domain_file(fn, schemas, options, identity_providers, domain_files) end end - schemas[objectname] = openapiobj + schemas[objectname] = openapiobj end -def addListValue(hash,key,listElementType,identity_providers,rootkey=nil,objectname=nil) +def addListValue(hash, key, listElementType, identity_providers, rootkey = nil, objectname = nil) hash[key] = "array" hash["items"] = {} if is_primitive(listElementType) hash["items"] = convert_primitive(listElementType) elsif listElementType == "T" && rootkey == "results" && objectname == "SearchResults" # TODO not sure this works, test it out - hash["items"] = {"type" => "object"} + hash["items"] = { "type" => "object" } elsif listElementType.match(/BaseIdentityProvider$/) # special handling of this. We create IdentityProviderField elsewhere # see https://github.com/OpenAPITools/openapi-generator/issues/10880#issuecomment-995243186 for why @@ -264,11 +277,12 @@ def addListValue(hash,key,listElementType,identity_providers,rootkey=nil,objectn end end -def param_optional(comments_arr) - if comments_arr && comments_arr[0].include?("(Optional)") +def param_optional(param) + if param['comments']&.[](0)&.include?("(Optional)") return true end - return false + + return param['optional'] end def process_rawpaths(rawpaths, options) @@ -282,21 +296,21 @@ def process_rawpaths(rawpaths, options) rawpaths.each do |uri, methods| new_paths[uri] = {} - methods.each do |method, pathobjs| + methods.each do |method, pathobjs| if pathobjs.length == 1 # no merging needed new_paths[uri][method] = pathobjs[0] else orig_object = pathobjs[0] - + pathobjs.drop(1).each do |pathobj| - if options[:verbose] - puts "merging in " + uri.to_s + " " + method.to_s + " operationId: " + pathobj["operationId"].to_s + if options[:verbose] + puts "merging in " + uri.to_s + " " + method.to_s + " operationId: " + pathobj["operationId"].to_s end orig_object = merge_operations(pathobj, orig_object, uri, method) end - new_paths[uri][method] = orig_object + new_paths[uri][method] = orig_object end end end @@ -305,8 +319,8 @@ def process_rawpaths(rawpaths, options) end def process_api_file(fn, paths, options, deferred) - if options[:verbose] - puts "processing "+fn + if options[:verbose] + puts "processing " + fn end f = File.open(fn) fs = f.read @@ -314,36 +328,36 @@ def process_api_file(fn, paths, options, deferred) f.close if json["deprecated"] - if options[:verbose] - puts "skipping deprecated "+fn + if options[:verbose] + puts "skipping deprecated " + fn end return end method = json["method"] uri = json["uri"] - + # check to see if the url segments are optional if json["params"] uri_without_optional = uri - segmentparams = json["params"].select{|p| p["type"] == "urlSegment"} + segmentparams = json["params"].select { |p| p["type"] == "urlSegment" } segmentparams.each do |p| if p["constant"] == true - uri = uri + "/"+p["value"].delete('"') - uri_without_optional = uri_without_optional + "/"+p["value"].delete('"') + uri = uri + "/" + p["value"].delete('"') + uri_without_optional = uri_without_optional + "/" + p["value"].delete('"') next end - - if param_optional(p["comments"]) - uri = uri + "/{"+p["name"]+"}" + + if param_optional(p) + uri = uri + "/{" + p["name"] + "}" else - uri = uri + "/{"+p["name"]+"}" - uri_without_optional = uri_without_optional + "/{"+p["name"]+"}" + uri = uri + "/{" + p["name"] + "}" + uri_without_optional = uri_without_optional + "/{" + p["name"] + "}" end end - if options[:verbose] + if options[:verbose] puts "adding path for " + uri end @@ -352,7 +366,7 @@ def process_api_file(fn, paths, options, deferred) # only support an optional parameter on the last url segment if uri_without_optional != uri - if options[:verbose] + if options[:verbose] puts "adding path for " + uri_without_optional end build_path(uri_without_optional, json, paths, false, options) @@ -412,7 +426,7 @@ def build_operation_id(new_api_object, old_api_object, uri, method) operation_name += "-" + uri_array[3] end end - operation_name = operation_name.split("-").map{|e| e.capitalize}.join("") + operation_name = operation_name.split("-").map { |e| e.capitalize }.join("") operation_name[0] = operation_name[0].capitalize # just capitalize first letter @@ -423,18 +437,18 @@ def build_operation_id(new_api_object, old_api_object, uri, method) def merge_operations(new_api_object, old_api_object, uri, method) new_api_object["description"] += " OR " + old_api_object["description"] new_api_object["operationId"] = build_operation_id(new_api_object, old_api_object, uri, method) - queryparamstoadd = old_api_object["parameters"].select{|p| p["in"] == "query"} + queryparamstoadd = old_api_object["parameters"].select { |p| p["in"] == "query" } # query params we add # path params are handled in the if uri_without_optional != uri code path # only problem would be if two different operations took different path params in same location but we don't have that - oldparamstohandle = old_api_object["parameters"].select{|p| p["in"] != "query" && p["in"] != "path" } - newparamstohandle = new_api_object["parameters"].select{|p| p["in"] != "query" && p["in"] != "path"} - if oldparamstohandle & newparamstohandle != newparamstohandle + oldparamstohandle = old_api_object["parameters"].select { |p| p["in"] != "query" && p["in"] != "path" } + newparamstohandle = new_api_object["parameters"].select { |p| p["in"] != "query" && p["in"] != "path" } + if oldparamstohandle & newparamstohandle != newparamstohandle p "Saw some new params that were not query params. Doh! " end new_api_object["parameters"] += queryparamstoadd - #remove dups + # remove dups new_api_object["parameters"] = new_api_object["parameters"].uniq { |p| p["name"] } # make sure if we have a requestBody, we pass that along in the merge. This will blow up if we have two operations that take request bodies but don't have the same request body @@ -455,7 +469,7 @@ def build_path(uri, json, paths, include_optional_segment_param, options) end jsonparams = json["params"] - if not paths[uri] + if not paths[uri] paths[uri] = {} end @@ -473,19 +487,18 @@ def build_path(uri, json, paths, include_optional_segment_param, options) # sometimes anonymous requests can take JWT bearer tokens. We shouldn't see that on any other request definitions, which all default to API key auth if json["authorization"] and json["authorization"].include? 'Bearer' openapiobj["security"] = [] - openapiobj["security"].push({BEARER_AUTH_SCHEME_NAME => []}) + openapiobj["security"].push({ BEARER_AUTH_SCHEME_NAME => [] }) end end - params = [] openapiobj["parameters"] = params if jsonparams - segmentparams = jsonparams.select{|p| p["type"] == "urlSegment"} - queryparams = jsonparams.select{|p| p["type"] == "urlParameter"} - bodyparams = jsonparams.select{|p| p["type"] == "body"} - + segmentparams = jsonparams.select { |p| p["type"] == "urlSegment" } + queryparams = jsonparams.select { |p| p["type"] == "urlParameter" } + bodyparams = jsonparams.select { |p| p["type"] == "body" } + queryparams.each do |p| params << build_openapi_paramobj(p, "query") end @@ -496,7 +509,7 @@ def build_path(uri, json, paths, include_optional_segment_param, options) next end - if param_optional(p["comments"]) + if param_optional(p) if include_optional_segment_param # we have an optional param but it is in the URI, so we want to add it to the parameters params << build_openapi_paramobj(p, "path") @@ -515,7 +528,7 @@ def build_path(uri, json, paths, include_optional_segment_param, options) end end - add_header_params(params,json) + add_header_params(params, json) openapiobj["responses"] = {} openapiobj["responses"]['200'] = {} @@ -528,57 +541,68 @@ def build_path(uri, json, paths, include_optional_segment_param, options) def add_header_params(params, json) - # TODO this info should be shoved into the api definition JSON in client builder. This is currently absent and only available in the docs or code or here - apis_requiring_tenant_header = ["tenant","user-action","entity","user/family","user","two-factor","user/comment","application","email/template","user/registration","group","consent"] - - apis_with_optional_tenant_header = ["login", "passwordless", "identity-provider/login","jwt"] - - no_header_needed = true - required = false - if apis_requiring_tenant_header.include? json["uri"].gsub("/api/","") - required = true - no_header_needed = false - end - if apis_with_optional_tenant_header.include? json["uri"].gsub("/api/","") - required = false - no_header_needed = false - end - - if no_header_needed - # no tenant header there - return - end - header_param = {} - header_param["in"] = "header" - header_param["name"] = "X-FusionAuth-TenantId" - header_param["description"] = "The unique Id of the tenant used to scope this API request. Only required when there is more than one tenant and the API key is not tenant-scoped." - header_param["required"] = false - header_param["schema"] = {} - header_param["schema"]["type"] = "string" - header_param["schema"]["format"] = "UUID" - - params << header_param + # TODO this info should be shoved into the api definition JSON in client builder. This is currently absent and only available in the docs or code or here + apis_requiring_tenant_header = ["tenant", "user-action", "entity", "user/family", "user", "two-factor", "user/comment", "application", "email/template", "user/registration", "group", "consent"] + + apis_with_optional_tenant_header = ["login", "passwordless", "identity-provider/login", "jwt"] + + no_header_needed = true + required = false + if apis_requiring_tenant_header.include? json["uri"].gsub("/api/", "") + required = true + no_header_needed = false + end + if apis_with_optional_tenant_header.include? json["uri"].gsub("/api/", "") + required = false + no_header_needed = false + end + + if no_header_needed + # no tenant header there + return + end + header_param = {} + header_param["in"] = "header" + header_param["name"] = "X-FusionAuth-TenantId" + header_param["description"] = "The unique Id of the tenant used to scope this API request. Only required when there is more than one tenant and the API key is not tenant-scoped." + header_param["required"] = false + header_param["schema"] = {} + header_param["schema"]["type"] = "string" + header_param["schema"]["format"] = "UUID" + + params << header_param end def build_openapi_paramobj(jsonparamobj, paramtype) paramobj = {} paramobj["name"] = jsonparamobj["name"] paramobj["in"] = paramtype - paramobj["schema"] = {} - paramobj["schema"]["type"] = "string" + paramobj["schema"] = if jsonparamobj['javaType'] == 'Collection' + { + 'type' => 'array', + 'items' => { + 'type' => 'string' + } + } + else + { 'type' => 'string' } + end if paramtype == "path" paramobj["required"] = true end + if jsonparamobj['optional'] + paramobj['required'] = false + end if jsonparamobj["comments"] && jsonparamobj["comments"][0] - paramobj["description"] = jsonparamobj["comments"].join(" ").gsub('(Optional)','').gsub("\n",'').delete("\n").strip + paramobj["description"] = jsonparamobj["comments"].join(" ").gsub('(Optional)', '').gsub("\n", '').delete("\n").strip end return paramobj end def build_nested_content_response(hash, ref, description) hash["description"] = description - + if ref hash["content"] = {} hash["content"]["application/json"] = {} @@ -606,7 +630,6 @@ def add_jwt_bearer_security_scheme(bearer_jwt_scheme_name, security_schemes) return security_schemes end - ######### processing starts domain_files = [] @@ -622,15 +645,15 @@ def add_jwt_bearer_security_scheme(bearer_jwt_scheme_name, security_schemes) spec["components"] = components if options[:file] - api_files = Dir.glob(options[:sourcedir]+"/main/api/*"+options[:file]+"*") - #domain_files = Dir.glob(options[:sourcedir]+"/main/domain/*"+options[:file]+".json") - domain_files = Dir.glob(options[:sourcedir]+"/main/domain/*") + api_files = Dir.glob(options[:sourcedir] + "/main/api/*" + options[:file] + "*") + # domain_files = Dir.glob(options[:sourcedir]+"/main/domain/*"+options[:file]+".json") + domain_files = Dir.glob(options[:sourcedir] + "/main/domain/*") else - api_files = Dir.glob(options[:sourcedir]+"/main/api/*") - domain_files = Dir.glob(options[:sourcedir]+"/main/domain/*") + api_files = Dir.glob(options[:sourcedir] + "/main/api/*") + domain_files = Dir.glob(options[:sourcedir] + "/main/domain/*") end -if options[:verbose] +if options[:verbose] puts "Processing files: " puts api_files puts domain_files @@ -658,7 +681,7 @@ def add_jwt_bearer_security_scheme(bearer_jwt_scheme_name, security_schemes) if fn.match(/io.fusionauth.domain.provider.BaseSAMLv2IdentityProvider.json/) next end - process_domain_file(fn, schemas, options,identity_providers, domain_files) + process_domain_file(fn, schemas, options, identity_providers, domain_files) end schemas["ZonedDateTime"] = {} @@ -728,7 +751,7 @@ def add_jwt_bearer_security_scheme(bearer_jwt_scheme_name, security_schemes) # components and paths # https://stackoverflow.com/questions/21251309/how-to-remove-on-top-of-a-yaml-file - f.write spec.to_yaml.gsub(/^---/,'') + f.write spec.to_yaml.gsub(/^---/, '') end # TODO handle {} in component schema ? From 1182789cdfb8cfe936eac3cb69bdd02c746fb48b Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 12:04:24 -0600 Subject: [PATCH 08/24] try deterministic sorting in the right places # Conflicts: # bin/build-openapi-yaml.rb --- bin/build-openapi-yaml.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/build-openapi-yaml.rb b/bin/build-openapi-yaml.rb index 870158311..ff100ea25 100755 --- a/bin/build-openapi-yaml.rb +++ b/bin/build-openapi-yaml.rb @@ -113,7 +113,7 @@ def add_identity_provider_field(schemas, identity_providers) "propertyName" => "type", "mapping" => {} } - identity_providers.each do |idp| + identity_providers.sort.each do |idp| ref = make_ref(idp) schemas["IdentityProviderField"]["oneOf"] << { "$ref" => ref } schemas["IdentityProviderField"]["discriminator"]["mapping"][idp.sub("IdentityProvider", "")] = ref @@ -293,7 +293,7 @@ def process_rawpaths(rawpaths, options) # walk keys of paths to get uri # then walk keys of paths[uri] to get methods # then walk array of paths[uri][method] to get things to merge - rawpaths.each do |uri, methods| + rawpaths.sort_by { |uri, methods| uri }.each do |uri, methods| new_paths[uri] = {} methods.each do |method, pathobjs| @@ -652,6 +652,8 @@ def add_jwt_bearer_security_scheme(bearer_jwt_scheme_name, security_schemes) api_files = Dir.glob(options[:sourcedir] + "/main/api/*") domain_files = Dir.glob(options[:sourcedir] + "/main/domain/*") end +api_files.sort! +domain_files.sort! if options[:verbose] puts "Processing files: " From 97829027b3345979361e1fbe3d124e05b461575b Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 12:35:50 -0600 Subject: [PATCH 09/24] go generic String->string issue --- src/main/client/_macros.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index 44a00b2fd..e558c5579 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -53,7 +53,7 @@ [#elseif type == "Void"] [#return "nil"/] [#elseif type?starts_with("Collection")] - [#return type?replace("Collection", "[]")?replace("UUID", "string")?replace("<", "")?replace(">", "")/] + [#return type?replace("Collection", "[]")?replace("UUID", "string")?replace("<", "")?replace(">", "")?replace("String", "string")/] [#elseif type == "String" || type = "UUID" || type == "ZoneId" || type == "URI" || type == "Locale" || type == "LocalDate" || type == "char" || type == "IdentityType" ] [#return "string"/] [#elseif type == "Object" || type == "D" || type == "T"] From e88de1bf152590bf3a2d647b70f70c7a0923c8ca Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 12:54:20 -0600 Subject: [PATCH 10/24] .net fixes --- src/main/client/_macros.ftl | 2 +- src/main/client/netcore.client.ftl | 13 +++++++-- src/main/client/netcore.client.interface.ftl | 28 ++++++++++++++++---- src/main/client/netcore.client.sync.ftl | 17 +++++++++--- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index e558c5579..a11efdf08 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -34,7 +34,7 @@ [#case "KeyAlgorithm"][#return "KeyAlgorithm?"/] [#default] [#if type?starts_with("Collection")] - [#return type?replace("Collection", "List")?replace("UUID", "string")/] + [#return type?replace("Collection", "List")?replace("UUID", "string")?replace("String", "string")/] [#else] [#return type/] [/#if] diff --git a/src/main/client/netcore.client.ftl b/src/main/client/netcore.client.ftl index 948a9434b..3107ce0cf 100644 --- a/src/main/client/netcore.client.ftl +++ b/src/main/client/netcore.client.ftl @@ -90,13 +90,21 @@ namespace io.fusionauth { return client; } - [#list apis as api] + [#-- C# supports method overloading, so exclude apis that are just overloads --] + [#list apis?filter(api -> !(api.overload??)) as top_level_api] + [#if top_level_api.overloads!false] + [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] + [#else] + [#assign overloads=[top_level_api] /] + [/#if] + [#list overloads as api] + [#assign is_overload = api.overload??/] /// [#if api.deprecated??] [Obsolete("${api.deprecated?replace("{{renamedMethod}}",(api.renamedMethod!'')?cap_first + "Async")}")] [/#if] - public Task> ${api.methodName?cap_first}Async(${global.methodParameters(api, "csharp")}) { + public Task> ${is_overload?then(api.overload, api.methodName)?cap_first}Async(${global.methodParameters(api, "csharp")}) { [#assign formPost = false/] [#list api.params![] as param] [#if param.type == "form"][#assign formPost = true/][/#if] @@ -131,6 +139,7 @@ namespace io.fusionauth { .goAsync<${global.convertType(api.successResponse, "csharp")}>(); } [/#list] + [/#list] } internal class DefaultRESTClientBuilder : IRESTClientBuilder { diff --git a/src/main/client/netcore.client.interface.ftl b/src/main/client/netcore.client.interface.ftl index 9a33c3561..326bb01a7 100644 --- a/src/main/client/netcore.client.interface.ftl +++ b/src/main/client/netcore.client.interface.ftl @@ -34,8 +34,15 @@ using io.fusionauth.domain.reactor; namespace io.fusionauth { public interface IFusionAuthAsyncClient { - [#list apis as api] - + [#-- C# supports method overloading, so exclude apis that are just overloads --] + [#list apis?filter(api -> !(api.overload??)) as top_level_api] + [#if top_level_api.overloads!false] + [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] + [#else] + [#assign overloads=[top_level_api] /] + [/#if] + [#list overloads as api] + [#assign is_overload = api.overload??/] ///

[#list api.comments as comment] /// ${comment} @@ -56,12 +63,22 @@ namespace io.fusionauth { [#if api.deprecated??] [Obsolete("${api.deprecated?replace("{{renamedMethod}}",(api.renamedMethod!'')?cap_first + "Async")}")] [/#if] - Task> ${api.methodName?cap_first}Async(${global.methodParameters(api, "csharp")}); + Task> ${is_overload?then(api.overload, api.methodName)?cap_first}Async(${global.methodParameters(api, "csharp")}); + + [/#list] [/#list] } public interface IFusionAuthSyncClient { - [#list apis as api] + [#-- C# supports method overloading, so exclude apis that are just overloads --] + [#list apis?filter(api -> !(api.overload??)) as top_level_api] + [#if top_level_api.overloads!false] + [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] + [#else] + [#assign overloads=[top_level_api] /] + [/#if] + [#list overloads as api] + [#assign is_overload = api.overload??/] /// [#list api.comments as comment] @@ -82,7 +99,8 @@ namespace io.fusionauth { [#if api.deprecated??] [Obsolete("${api.deprecated?replace("{{renamedMethod}}",(api.renamedMethod!'')?cap_first + "Async")}")] [/#if] - ClientResponse<${global.convertType(api.successResponse, "csharp")}> ${api.methodName?cap_first}(${global.methodParameters(api, "csharp")}); + ClientResponse<${global.convertType(api.successResponse, "csharp")}> ${is_overload?then(api.overload, api.methodName)?cap_first}(${global.methodParameters(api, "csharp")}); + [/#list] [/#list] } } diff --git a/src/main/client/netcore.client.sync.ftl b/src/main/client/netcore.client.sync.ftl index f15add8a6..45c97f296 100644 --- a/src/main/client/netcore.client.sync.ftl +++ b/src/main/client/netcore.client.sync.ftl @@ -72,16 +72,27 @@ namespace io.fusionauth { public FusionAuthSyncClient withTenantId(Guid? tenantId) { return tenantId == null ? this : new FusionAuthSyncClient(client.apiKey, client.host, tenantId.ToString()); } - [#list apis as api] + [#-- C# supports method overloading, so exclude apis that are just overloads --] + [#list apis?filter(api -> !(api.overload??)) as top_level_api] + [#if top_level_api.overloads!false] + [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] + [#else] + [#assign overloads=[top_level_api] /] + [/#if] + [#list overloads as api] + [#assign is_overload = api.overload??/] + [#assign params = removeConstantParams(api.params![])] /// [#if api.deprecated??] [Obsolete("${api.deprecated?replace("{{renamedMethod}}", (api.renamedMethod!'')?cap_first)}")] [/#if] - public ClientResponse<${global.convertType(api.successResponse, "csharp")}> ${api.methodName?cap_first}(${global.methodParameters(api, "csharp")}) { - return client.${api.methodName?cap_first}Async(${getParamNames(params)}).GetAwaiter().GetResult(); + public ClientResponse<${global.convertType(api.successResponse, "csharp")}> ${is_overload?then(api.overload, api.methodName)?cap_first}(${global.methodParameters(api, "csharp")}) { + return client.${is_overload?then(api.overload, api.methodName)?cap_first}Async(${getParamNames(params)}).GetAwaiter().GetResult(); } + + [/#list] [/#list] } } From cb8cd3b5095cdd71093be8f198c35a6ae91943af Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 15:18:07 -0600 Subject: [PATCH 11/24] typescript fixes --- src/main/client/typescript.client.ftl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/client/typescript.client.ftl b/src/main/client/typescript.client.ftl index c8e266f6c..e24f7dfa5 100644 --- a/src/main/client/typescript.client.ftl +++ b/src/main/client/typescript.client.ftl @@ -56,15 +56,20 @@ export class FusionAuthClient { } [#-- @formatter:off --] -[#list apis as api] +[#-- TypeScript supports default arguments, so exclude apis that are just overloads --] +[#list apis?filter(api -> !(api.overloads!false)) as api] /** [#list api.comments as comment] * ${comment} [/#list] * [#list api.params![] as param] + [#assign paramComments = param.comments![]/] + [#if param.optional!false] + [#assign paramComments = ["(Optional) "+param.comments[0]] + param.comments[1..]/] + [/#if] [#if !param.constant??] - * @param {${global.optional(param, "ts")}${global.convertType(param.javaType, "ts")}} ${param.name} ${param.comments?join("\n * ")} + * @param {${global.optional(param, "ts")}${global.convertType(param.javaType, "ts")}} ${param.name} ${paramComments?join("\n * ")} [/#if] [/#list] * @returns {Promise>} @@ -74,7 +79,7 @@ export class FusionAuthClient { [/#if] */ [#assign parameters = global.methodParameters(api, "ts")/] - ${api.methodName}(${parameters}): Promise> { + ${api.overload???then(api.overload, api.methodName)}(${parameters}): Promise> { [#assign formPost = false/] [#list api.params![] as param] [#if param.type == "form"][#assign formPost = true/][/#if] From ec901909474af273e12e87bee10b90f7539e8582 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 15:24:28 -0600 Subject: [PATCH 12/24] add report method --- src/main/api/retrieveUserLoginReportByLoginId.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/api/retrieveUserLoginReportByLoginId.json b/src/main/api/retrieveUserLoginReportByLoginId.json index 7adfc5ad8..3759beddd 100644 --- a/src/main/api/retrieveUserLoginReportByLoginId.json +++ b/src/main/api/retrieveUserLoginReportByLoginId.json @@ -8,6 +8,7 @@ "methodName": "retrieveUserLoginReportByLoginId", "successResponse": "LoginReportResponse", "errorResponse": "Errors", + "overloads": true, "params": [ { "name": "applicationId", @@ -46,4 +47,4 @@ "javaType": "long" } ] -} \ No newline at end of file +} From 1c625ef78457da323bed78a37befe2b92487b03d Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 15:24:37 -0600 Subject: [PATCH 13/24] Add report method --- ...erLoginReportByLoginIdAndLoginIdTypes.json | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json diff --git a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json new file mode 100644 index 000000000..ddc5a2def --- /dev/null +++ b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json @@ -0,0 +1,60 @@ +{ + "uri": "/api/report/login", + "comments": [ + "Retrieves the login report between the two instants for a particular user by login Id, using specific loginIdTypes. If you specify an application id, it will only return the", + "login counts for that application." + ], + "method": "get", + "methodName": "retrieveUserLoginReportByLoginIdAndLoginIdTypes", + "successResponse": "LoginReportResponse", + "errorResponse": "Errors", + "overload": "retrieveUserLoginReportByLoginId", + "params": [ + { + "name": "applicationId", + "comments": [ + "(Optional) The application id." + ], + "type": "urlParameter", + "parameterName": "applicationId", + "javaType": "UUID" + }, + { + "name": "loginId", + "comments": [ + "The userId id." + ], + "type": "urlParameter", + "parameterName": "loginId", + "javaType": "String" + }, + { + "name": "start", + "comments": [ + "The start instant as UTC milliseconds since Epoch." + ], + "type": "urlParameter", + "parameterName": "start", + "javaType": "long" + }, + { + "name": "end", + "comments": [ + "The end instant as UTC milliseconds since Epoch." + ], + "type": "urlParameter", + "parameterName": "end", + "javaType": "long" + }, + { + "name": "loginIdTypes", + "comments": [ + "the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" + ], + "type": "urlParameter", + "parameterName": "loginIdTypes", + "javaType": "Collection", + "optional": true + } + ] +} From 617687f9f3504351d3608265e876cd79577dc9d4 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 16:34:42 -0600 Subject: [PATCH 14/24] Explain --- bin/build-openapi-yaml.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/build-openapi-yaml.rb b/bin/build-openapi-yaml.rb index bb7783018..3bcdfdcea 100755 --- a/bin/build-openapi-yaml.rb +++ b/bin/build-openapi-yaml.rb @@ -277,6 +277,7 @@ def addListValue(hash, key, listElementType, identity_providers, rootkey = nil, end end +# Returns true if the 1st comment include (Optional) or an explicit optional boolean exists def param_optional(param) if param['comments']&.[](0)&.include?("(Optional)") return true @@ -293,7 +294,7 @@ def process_rawpaths(rawpaths, options) # walk keys of paths to get uri # then walk keys of paths[uri] to get methods # then walk array of paths[uri][method] to get things to merge - rawpaths.sort_by { |uri, methods| uri }.each do |uri, methods| + rawpaths.sort_by { |uri, _| uri }.each do |uri, methods| new_paths[uri] = {} methods.each do |method, pathobjs| @@ -577,6 +578,7 @@ def build_openapi_paramobj(jsonparamobj, paramtype) paramobj = {} paramobj["name"] = jsonparamobj["name"] paramobj["in"] = paramtype + # Cover Java generics here paramobj["schema"] = if jsonparamobj['javaType'] == 'Collection' { 'type' => 'array', From cbbe0865c1f676bdd3f1ea950f2ae2471f6900a3 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Fri, 2 May 2025 16:56:01 -0600 Subject: [PATCH 15/24] forgot PHP --- src/main/client/_macros.ftl | 8 +++++--- src/main/client/php.client.ftl | 13 +++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index a11efdf08..25bde3e8b 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -217,7 +217,9 @@ [#local optional = param.comments[0]?starts_with("(Optional)")/] [#if language == "php"] [#-- If the parameter is the last one and is optional, give it a default value --] - [#if !param_has_next && optional] + [#-- If the parameter is optional, give it a default value. Ideally we'd remove the assign boolean in this FTL, + which is driven by string comments, but changing that now would cause a lot of disruption --] + [#if !param_has_next && (optional || param.optional!false)] [#local result = result + ["$" + param.name + " = NULL"]/] [#else] [#local result = result + ["$" + param.name]/] @@ -239,8 +241,8 @@ [/#if] [#local result = result + [convertValue(param.name, language) + (convertedType != "interface{}")?then(' ' + convertedType, ' interface{}')]] [#elseif language == "python"] - [#-- If the parameter is optional, give it a default value. Ideally we'd not use optional, which is driven by string comments, but - changing that now would cause a lot of disruption --] + [#-- If the parameter is optional, give it a default value. Ideally we'd remove the assign boolean in this FTL, + which is driven by string comments, but changing that now would cause a lot of disruption --] [#if optional || param.optional!false] [#local result = result + [convertValue(param, language) + "=None"]/] [#else] diff --git a/src/main/client/php.client.ftl b/src/main/client/php.client.ftl index d65f56c0d..6d75adbb6 100644 --- a/src/main/client/php.client.ftl +++ b/src/main/client/php.client.ftl @@ -79,15 +79,20 @@ class FusionAuthClient return $this; } -[#list apis as api] +[#-- PHP supports default positional arguments, so exclude apis that are just overloads --] +[#list apis?filter(api -> !(api.overloads!false)) as api] /** [#list api.comments as comment] * ${comment} [/#list] * [#list api.params![] as param] + [#assign paramComments = param.comments![]/] + [#if param.optional!false] + [#assign paramComments = ["(Optional) "+param.comments[0]] + param.comments[1..]/] + [/#if] [#if !param.constant??] - * @param ${global.convertType(param.javaType, "php")} $${param.name} ${param.comments?join("\n * ")} + * @param ${global.convertType(param.javaType, "php")} $${param.name} ${paramComments?join("\n * ")} [/#if] [/#list] * @@ -97,7 +102,7 @@ class FusionAuthClient * @deprecated ${api.deprecated?replace("{{renamedMethod}}", api.renamedMethod!'')} [/#if] */ - public function ${api.methodName}(${global.methodParameters(api, "php")}) + public function ${api.overload???then(api.overload, api.methodName)}(${global.methodParameters(api, "php")}) { [#assign formPost = false/] [#list api.params![] as param] @@ -151,4 +156,4 @@ class FusionAuthClient ->successResponseHandler(new JSONResponseHandler()) ->errorResponseHandler(new JSONResponseHandler()); } -} \ No newline at end of file +} From 425a7f5a7f924f59db386e1dd13afa182d1aa6a2 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Mon, 5 May 2025 16:02:16 -0600 Subject: [PATCH 16/24] use lists when possible, order is significant here --- bin/build-openapi-yaml.rb | 2 +- .../api/retrieveUserByLoginIdWithLoginIdTypes.json | 2 +- ...eveUserLoginReportByLoginIdAndLoginIdTypes.json | 2 +- src/main/client/_macros.ftl | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bin/build-openapi-yaml.rb b/bin/build-openapi-yaml.rb index 3bcdfdcea..2d40adf99 100755 --- a/bin/build-openapi-yaml.rb +++ b/bin/build-openapi-yaml.rb @@ -579,7 +579,7 @@ def build_openapi_paramobj(jsonparamobj, paramtype) paramobj["name"] = jsonparamobj["name"] paramobj["in"] = paramtype # Cover Java generics here - paramobj["schema"] = if jsonparamobj['javaType'] == 'Collection' + paramobj["schema"] = if %w[Collection List].include? jsonparamobj['javaType'] { 'type' => 'array', 'items' => { diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json index 0a3753496..b5e447992 100644 --- a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json +++ b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json @@ -25,7 +25,7 @@ ], "type": "urlParameter", "parameterName": "loginIdTypes", - "javaType": "Collection", + "javaType": "List", "optional": true } ] diff --git a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json index ddc5a2def..e566be44f 100644 --- a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json +++ b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json @@ -53,7 +53,7 @@ ], "type": "urlParameter", "parameterName": "loginIdTypes", - "javaType": "Collection", + "javaType": "List", "optional": true } ] diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index 25bde3e8b..cf85d926b 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -33,7 +33,7 @@ [#case "KeyType"][#return "KeyType?"/] [#case "KeyAlgorithm"][#return "KeyAlgorithm?"/] [#default] - [#if type?starts_with("Collection")] + [#if type?starts_with("Collection") || type?starts_with("List")] [#return type?replace("Collection", "List")?replace("UUID", "string")?replace("String", "string")/] [#else] [#return type/] @@ -52,8 +52,8 @@ [#return "int64"/] [#elseif type == "Void"] [#return "nil"/] - [#elseif type?starts_with("Collection")] - [#return type?replace("Collection", "[]")?replace("UUID", "string")?replace("<", "")?replace(">", "")?replace("String", "string")/] + [#elseif type?starts_with("Collection<") || type?starts_with("List<")] + [#return type?replace("Collection", "[]")?replace("List", "[]")?replace("UUID", "string")?replace("<", "")?replace(">", "")?replace("String", "string")/] [#elseif type == "String" || type = "UUID" || type == "ZoneId" || type == "URI" || type == "Locale" || type == "LocalDate" || type == "char" || type == "IdentityType" ] [#return "string"/] [#elseif type == "Object" || type == "D" || type == "T"] @@ -97,7 +97,7 @@ [#case "JWT"][#return "JWT | object"/] [#case "Void"][#return "void"/] [#default] - [#if type?starts_with("Collection")] + [#if type?starts_with("Collection<") || type?starts_with("List<")] [#return type?replace("Collection", "Array")?replace("UUID", "string")/] [#else] [#return type/] @@ -132,8 +132,8 @@ [#case "Object"][#return "any"/] [#case "Void"][#return "void"/] [#default] - [#if type?starts_with("Collection")] - [#return type?replace("Collection", "Array")?replace("UUID", "string")/] + [#if type?starts_with("Collection<") || type?starts_with("List<")] + [#return type?replace("Collection", "Array")?replace("List", "Array")?replace("UUID", "string")/] [#else] [#return type/] [/#if] @@ -155,7 +155,7 @@ [#elseif language == "ruby"] [#if type == "UUID" || type == "String" || type == "IdentityProviderType" || type == "LambdaType"] [#return "string"/] - [#elseif type?starts_with("Collection")] + [#elseif type?starts_with("Collection<") || type?starts_with("List<")] [#return "Array"/] [#elseif type == "boolean" || type == "Boolean"] [#return "Boolean"/] From 9e4f4abbb061576c07384fbc9b6b9dc59ad2d6ae Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Tue, 6 May 2025 16:28:08 -0600 Subject: [PATCH 17/24] Revert --- src/main/client/_macros.ftl | 36 +++++++------------- src/main/client/java.client.ftl | 13 ++----- src/main/client/netcore.client.ftl | 13 ++----- src/main/client/netcore.client.interface.ftl | 28 +++------------ src/main/client/netcore.client.sync.ftl | 17 ++------- src/main/client/php.client.ftl | 13 +++---- src/main/client/python.client.ftl | 11 ++---- src/main/client/ruby.client.ftl | 11 ++---- src/main/client/typescript.client.ftl | 15 ++------ 9 files changed, 37 insertions(+), 120 deletions(-) diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index cf85d926b..f52d0400b 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -33,8 +33,8 @@ [#case "KeyType"][#return "KeyType?"/] [#case "KeyAlgorithm"][#return "KeyAlgorithm?"/] [#default] - [#if type?starts_with("Collection") || type?starts_with("List")] - [#return type?replace("Collection", "List")?replace("UUID", "string")?replace("String", "string")/] + [#if type?starts_with("Collection")] + [#return type?replace("Collection", "List")?replace("UUID", "string")/] [#else] [#return type/] [/#if] @@ -52,8 +52,8 @@ [#return "int64"/] [#elseif type == "Void"] [#return "nil"/] - [#elseif type?starts_with("Collection<") || type?starts_with("List<")] - [#return type?replace("Collection", "[]")?replace("List", "[]")?replace("UUID", "string")?replace("<", "")?replace(">", "")?replace("String", "string")/] + [#elseif type?starts_with("Collection")] + [#return type?replace("Collection", "[]")?replace("UUID", "string")?replace("<", "")?replace(">", "")/] [#elseif type == "String" || type = "UUID" || type == "ZoneId" || type == "URI" || type == "Locale" || type == "LocalDate" || type == "char" || type == "IdentityType" ] [#return "string"/] [#elseif type == "Object" || type == "D" || type == "T"] @@ -97,7 +97,7 @@ [#case "JWT"][#return "JWT | object"/] [#case "Void"][#return "void"/] [#default] - [#if type?starts_with("Collection<") || type?starts_with("List<")] + [#if type?starts_with("Collection")] [#return type?replace("Collection", "Array")?replace("UUID", "string")/] [#else] [#return type/] @@ -132,8 +132,8 @@ [#case "Object"][#return "any"/] [#case "Void"][#return "void"/] [#default] - [#if type?starts_with("Collection<") || type?starts_with("List<")] - [#return type?replace("Collection", "Array")?replace("List", "Array")?replace("UUID", "string")/] + [#if type?starts_with("Collection")] + [#return type?replace("Collection", "Array")?replace("UUID", "string")/] [#else] [#return type/] [/#if] @@ -155,7 +155,7 @@ [#elseif language == "ruby"] [#if type == "UUID" || type == "String" || type == "IdentityProviderType" || type == "LambdaType"] [#return "string"/] - [#elseif type?starts_with("Collection<") || type?starts_with("List<")] + [#elseif type?starts_with("Collection")] [#return "Array"/] [#elseif type == "boolean" || type == "Boolean"] [#return "Boolean"/] @@ -217,9 +217,7 @@ [#local optional = param.comments[0]?starts_with("(Optional)")/] [#if language == "php"] [#-- If the parameter is the last one and is optional, give it a default value --] - [#-- If the parameter is optional, give it a default value. Ideally we'd remove the assign boolean in this FTL, - which is driven by string comments, but changing that now would cause a lot of disruption --] - [#if !param_has_next && (optional || param.optional!false)] + [#if !param_has_next && optional] [#local result = result + ["$" + param.name + " = NULL"]/] [#else] [#local result = result + ["$" + param.name]/] @@ -228,12 +226,7 @@ [#local result = result + [param.name]/] [#elseif language == "ts"] [#local convertedType = convertType(param.javaType, language)/] - [#if param.optional!false] - [#local suffix = "?"/] - [#else] - [#local suffix = ""/] - [/#if] - [#local result = result + [param.name + suffix + (convertedType != "Object")?then(': ' + convertedType, '')]] + [#local result = result + [param.name + (convertedType != "Object")?then(': ' + convertedType, '')]] [#elseif language == "go"] [#local convertedType = convertType(param.javaType, language)/] [#if api.method == "patch" && convertedType?ends_with("Request")] @@ -241,9 +234,8 @@ [/#if] [#local result = result + [convertValue(param.name, language) + (convertedType != "interface{}")?then(' ' + convertedType, ' interface{}')]] [#elseif language == "python"] - [#-- If the parameter is optional, give it a default value. Ideally we'd remove the assign boolean in this FTL, - which is driven by string comments, but changing that now would cause a lot of disruption --] - [#if optional || param.optional!false] + [#-- If the parameter is optional, give it a default value --] + [#if optional] [#local result = result + [convertValue(param, language) + "=None"]/] [#else] [#local result = result + [convertValue(param, language)]/] @@ -252,11 +244,7 @@ [#if param.name == "end"] [#local result = result + ["_end"]/] [#else] - [#if param.optional!false] - [#local result = result + [camel_to_underscores(param.name) + '=nil']/] - [#else] [#local result = result + [camel_to_underscores(param.name)]/] - [/#if] [/#if] [#elseif language == "csharp"] [#local convertedType = convertType(param.javaType, language)/] diff --git a/src/main/client/java.client.ftl b/src/main/client/java.client.ftl index e8bbcd8bc..4f8ec68fb 100644 --- a/src/main/client/java.client.ftl +++ b/src/main/client/java.client.ftl @@ -334,15 +334,7 @@ public class FusionAuthClient { return new FusionAuthClient(apiKey, baseURL, connectTimeout, readTimeout, tenantId, objectMapper); } -[#-- Java supports method overloading, so exclude apis that are just overloads --] -[#list apis?filter(api -> !(api.overload??)) as top_level_api] - [#if top_level_api.overloads!false] - [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] - [#else] - [#assign overloads=[top_level_api] /] - [/#if] - [#list overloads as api] - [#assign is_overload = api.overload??/] +[#list apis as api] /** [#list api.comments as comment] * [#if comment?has_content]${comment}[#else]

[/#if] @@ -361,7 +353,7 @@ public class FusionAuthClient { [#if api.deprecated??] @Deprecated [/#if] - public ClientResponse<${api.successResponse}, ${api.errorResponse}> ${is_overload?then(api.overload, api.methodName)}(${global.methodParameters(api, "java")}) { + public ClientResponse<${api.successResponse}, ${api.errorResponse}> ${api.methodName}(${global.methodParameters(api, "java")}) { [#assign formPost = false/] [#list api.params![] as param] [#if param.type == "form"][#assign formPost = true/][/#if] @@ -395,7 +387,6 @@ public class FusionAuthClient { .go(); } -[/#list] [/#list] protected RESTClient start(Class type, Class errorType) { diff --git a/src/main/client/netcore.client.ftl b/src/main/client/netcore.client.ftl index 3107ce0cf..948a9434b 100644 --- a/src/main/client/netcore.client.ftl +++ b/src/main/client/netcore.client.ftl @@ -90,21 +90,13 @@ namespace io.fusionauth { return client; } - [#-- C# supports method overloading, so exclude apis that are just overloads --] - [#list apis?filter(api -> !(api.overload??)) as top_level_api] - [#if top_level_api.overloads!false] - [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] - [#else] - [#assign overloads=[top_level_api] /] - [/#if] - [#list overloads as api] - [#assign is_overload = api.overload??/] + [#list apis as api] /// [#if api.deprecated??] [Obsolete("${api.deprecated?replace("{{renamedMethod}}",(api.renamedMethod!'')?cap_first + "Async")}")] [/#if] - public Task> ${is_overload?then(api.overload, api.methodName)?cap_first}Async(${global.methodParameters(api, "csharp")}) { + public Task> ${api.methodName?cap_first}Async(${global.methodParameters(api, "csharp")}) { [#assign formPost = false/] [#list api.params![] as param] [#if param.type == "form"][#assign formPost = true/][/#if] @@ -139,7 +131,6 @@ namespace io.fusionauth { .goAsync<${global.convertType(api.successResponse, "csharp")}>(); } [/#list] - [/#list] } internal class DefaultRESTClientBuilder : IRESTClientBuilder { diff --git a/src/main/client/netcore.client.interface.ftl b/src/main/client/netcore.client.interface.ftl index 326bb01a7..9a33c3561 100644 --- a/src/main/client/netcore.client.interface.ftl +++ b/src/main/client/netcore.client.interface.ftl @@ -34,15 +34,8 @@ using io.fusionauth.domain.reactor; namespace io.fusionauth { public interface IFusionAuthAsyncClient { - [#-- C# supports method overloading, so exclude apis that are just overloads --] - [#list apis?filter(api -> !(api.overload??)) as top_level_api] - [#if top_level_api.overloads!false] - [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] - [#else] - [#assign overloads=[top_level_api] /] - [/#if] - [#list overloads as api] - [#assign is_overload = api.overload??/] + [#list apis as api] + ///

[#list api.comments as comment] /// ${comment} @@ -63,22 +56,12 @@ namespace io.fusionauth { [#if api.deprecated??] [Obsolete("${api.deprecated?replace("{{renamedMethod}}",(api.renamedMethod!'')?cap_first + "Async")}")] [/#if] - Task> ${is_overload?then(api.overload, api.methodName)?cap_first}Async(${global.methodParameters(api, "csharp")}); - - [/#list] + Task> ${api.methodName?cap_first}Async(${global.methodParameters(api, "csharp")}); [/#list] } public interface IFusionAuthSyncClient { - [#-- C# supports method overloading, so exclude apis that are just overloads --] - [#list apis?filter(api -> !(api.overload??)) as top_level_api] - [#if top_level_api.overloads!false] - [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] - [#else] - [#assign overloads=[top_level_api] /] - [/#if] - [#list overloads as api] - [#assign is_overload = api.overload??/] + [#list apis as api] /// [#list api.comments as comment] @@ -99,8 +82,7 @@ namespace io.fusionauth { [#if api.deprecated??] [Obsolete("${api.deprecated?replace("{{renamedMethod}}",(api.renamedMethod!'')?cap_first + "Async")}")] [/#if] - ClientResponse<${global.convertType(api.successResponse, "csharp")}> ${is_overload?then(api.overload, api.methodName)?cap_first}(${global.methodParameters(api, "csharp")}); - [/#list] + ClientResponse<${global.convertType(api.successResponse, "csharp")}> ${api.methodName?cap_first}(${global.methodParameters(api, "csharp")}); [/#list] } } diff --git a/src/main/client/netcore.client.sync.ftl b/src/main/client/netcore.client.sync.ftl index 45c97f296..f15add8a6 100644 --- a/src/main/client/netcore.client.sync.ftl +++ b/src/main/client/netcore.client.sync.ftl @@ -72,27 +72,16 @@ namespace io.fusionauth { public FusionAuthSyncClient withTenantId(Guid? tenantId) { return tenantId == null ? this : new FusionAuthSyncClient(client.apiKey, client.host, tenantId.ToString()); } - [#-- C# supports method overloading, so exclude apis that are just overloads --] - [#list apis?filter(api -> !(api.overload??)) as top_level_api] - [#if top_level_api.overloads!false] - [#assign overloads=[top_level_api] + apis?filter(other -> other.overload?? && other.overload == top_level_api.methodName) /] - [#else] - [#assign overloads=[top_level_api] /] - [/#if] - [#list overloads as api] - [#assign is_overload = api.overload??/] - + [#list apis as api] [#assign params = removeConstantParams(api.params![])] /// [#if api.deprecated??] [Obsolete("${api.deprecated?replace("{{renamedMethod}}", (api.renamedMethod!'')?cap_first)}")] [/#if] - public ClientResponse<${global.convertType(api.successResponse, "csharp")}> ${is_overload?then(api.overload, api.methodName)?cap_first}(${global.methodParameters(api, "csharp")}) { - return client.${is_overload?then(api.overload, api.methodName)?cap_first}Async(${getParamNames(params)}).GetAwaiter().GetResult(); + public ClientResponse<${global.convertType(api.successResponse, "csharp")}> ${api.methodName?cap_first}(${global.methodParameters(api, "csharp")}) { + return client.${api.methodName?cap_first}Async(${getParamNames(params)}).GetAwaiter().GetResult(); } - - [/#list] [/#list] } } diff --git a/src/main/client/php.client.ftl b/src/main/client/php.client.ftl index 6d75adbb6..d65f56c0d 100644 --- a/src/main/client/php.client.ftl +++ b/src/main/client/php.client.ftl @@ -79,20 +79,15 @@ class FusionAuthClient return $this; } -[#-- PHP supports default positional arguments, so exclude apis that are just overloads --] -[#list apis?filter(api -> !(api.overloads!false)) as api] +[#list apis as api] /** [#list api.comments as comment] * ${comment} [/#list] * [#list api.params![] as param] - [#assign paramComments = param.comments![]/] - [#if param.optional!false] - [#assign paramComments = ["(Optional) "+param.comments[0]] + param.comments[1..]/] - [/#if] [#if !param.constant??] - * @param ${global.convertType(param.javaType, "php")} $${param.name} ${paramComments?join("\n * ")} + * @param ${global.convertType(param.javaType, "php")} $${param.name} ${param.comments?join("\n * ")} [/#if] [/#list] * @@ -102,7 +97,7 @@ class FusionAuthClient * @deprecated ${api.deprecated?replace("{{renamedMethod}}", api.renamedMethod!'')} [/#if] */ - public function ${api.overload???then(api.overload, api.methodName)}(${global.methodParameters(api, "php")}) + public function ${api.methodName}(${global.methodParameters(api, "php")}) { [#assign formPost = false/] [#list api.params![] as param] @@ -156,4 +151,4 @@ class FusionAuthClient ->successResponseHandler(new JSONResponseHandler()) ->errorResponseHandler(new JSONResponseHandler()); } -} +} \ No newline at end of file diff --git a/src/main/client/python.client.ftl b/src/main/client/python.client.ftl index ffb0f3570..6b42fb815 100644 --- a/src/main/client/python.client.ftl +++ b/src/main/client/python.client.ftl @@ -37,23 +37,18 @@ class FusionAuthClient: """Sets the tenant_id on the client""" self.tenant_id = tenant_id -[#-- Python supports default positional arguments, so exclude apis that are just overloads --] -[#list apis?filter(api -> !(api.overloads!false)) as api] +[#list apis as api] [#if api.deprecated??] @deprecated("${api.deprecated?replace("{{renamedMethod}}", camel_to_underscores(api.renamedMethod!''))}") [/#if] - def ${camel_to_underscores(api.overload???then(api.overload, api.methodName))}(${global.methodParameters(api, "python")}): + def ${camel_to_underscores(api.methodName)}(${global.methodParameters(api, "python")}): """ ${api.comments?join("\n ")} Attributes: [#list api.params![] as param] - [#assign paramComments = param.comments![]/] - [#if param.optional!false] - [#assign paramComments = ["(Optional) "+param.comments[0]] + param.comments[1..]/] - [/#if] [#if !param.constant??] - ${global.convertValue(param, "python")}: ${paramComments?join("\n ")} + ${global.convertValue(param, "python")}: ${param.comments?join("\n ")} [/#if] [/#list] """ diff --git a/src/main/client/ruby.client.ftl b/src/main/client/ruby.client.ftl index a7d0285d2..375dbd7ce 100644 --- a/src/main/client/ruby.client.ftl +++ b/src/main/client/ruby.client.ftl @@ -42,27 +42,22 @@ module FusionAuth @tenant_id = tenant_id end -[#-- Ruby supports default positional arguments, so exclude apis that are just overloads --] -[#list apis?filter(api -> !(api.overloads!false)) as api] +[#list apis as api] # [#list api.comments as comment] # ${comment} [/#list] # [#list api.params![] as param] - [#assign paramComments = param.comments![]/] - [#if param.optional!false] - [#assign paramComments = ["(Optional) "+param.comments[0]] + param.comments[1..]/] - [/#if] [#if !param.constant??] - # @param ${camel_to_underscores(param.name?replace("end", "_end"))} [${global.convertType(param.javaType, "ruby")}] ${paramComments?join("\n # ")} + # @param ${camel_to_underscores(param.name?replace("end", "_end"))} [${global.convertType(param.javaType, "ruby")}] ${param.comments?join("\n # ")} [/#if] [/#list] # @return [FusionAuth::ClientResponse] The ClientResponse object. [#if api.deprecated??] # @deprecated ${api.deprecated?replace("{{renamedMethod}}", camel_to_underscores(api.renamedMethod!''))} [/#if] - def ${camel_to_underscores(api.overload???then(api.overload, api.methodName))}[#if (api.params![])?filter(p -> !p.constant??)?has_content](${global.methodParameters(api, "ruby")})[/#if] + def ${camel_to_underscores(api.methodName)}[#if (api.params![])?filter(p -> !p.constant??)?has_content](${global.methodParameters(api, "ruby")})[/#if] [#assign formPost = false/] [#list api.params![] as param] [#if param.type == "form"][#assign formPost = true/][/#if] diff --git a/src/main/client/typescript.client.ftl b/src/main/client/typescript.client.ftl index e24f7dfa5..b76bf8231 100644 --- a/src/main/client/typescript.client.ftl +++ b/src/main/client/typescript.client.ftl @@ -56,20 +56,15 @@ export class FusionAuthClient { } [#-- @formatter:off --] -[#-- TypeScript supports default arguments, so exclude apis that are just overloads --] -[#list apis?filter(api -> !(api.overloads!false)) as api] +[#list apis as api] /** [#list api.comments as comment] * ${comment} [/#list] * [#list api.params![] as param] - [#assign paramComments = param.comments![]/] - [#if param.optional!false] - [#assign paramComments = ["(Optional) "+param.comments[0]] + param.comments[1..]/] - [/#if] [#if !param.constant??] - * @param {${global.optional(param, "ts")}${global.convertType(param.javaType, "ts")}} ${param.name} ${paramComments?join("\n * ")} + * @param {${global.optional(param, "ts")}${global.convertType(param.javaType, "ts")}} ${param.name} ${param.comments?join("\n * ")} [/#if] [/#list] * @returns {Promise>} @@ -79,7 +74,7 @@ export class FusionAuthClient { [/#if] */ [#assign parameters = global.methodParameters(api, "ts")/] - ${api.overload???then(api.overload, api.methodName)}(${parameters}): Promise> { + ${api.methodName}(${parameters}): Promise> { [#assign formPost = false/] [#list api.params![] as param] [#if param.type == "form"][#assign formPost = true/][/#if] @@ -105,11 +100,7 @@ export class FusionAuthClient { [#if param.type == "urlSegment"] .withUriSegment(${(param.constant?? && param.constant)?then(param.value, param.name)}) [#elseif param.type == "urlParameter"] - [#if param.optional!false] - .withOptionalParameter('${param.parameterName}', ${(param.constant?? && param.constant)?then(param.value, param.name)}) - [#else] .withParameter('${param.parameterName}', ${(param.constant?? && param.constant)?then(param.value, param.name)}) - [/#if] [#elseif param.type == "body"] .withJSONBody(${param.name}) [/#if] From 16601ca76949e69222e704fe2cb1be30a5e84582 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Tue, 6 May 2025 16:30:03 -0600 Subject: [PATCH 18/24] Remove overload stuff --- src/main/api/retrieveUserByLoginId.json | 1 - src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json | 6 ++---- src/main/api/retrieveUserLoginReportByLoginId.json | 1 - .../retrieveUserLoginReportByLoginIdAndLoginIdTypes.json | 6 ++---- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/api/retrieveUserByLoginId.json b/src/main/api/retrieveUserByLoginId.json index 6e0596fb7..d86b4f934 100644 --- a/src/main/api/retrieveUserByLoginId.json +++ b/src/main/api/retrieveUserByLoginId.json @@ -7,7 +7,6 @@ "methodName": "retrieveUserByLoginId", "successResponse": "UserResponse", "errorResponse": "Errors", - "overloads": true, "params": [ { "name": "loginId", diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json index b5e447992..70af2bdc5 100644 --- a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json +++ b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json @@ -7,7 +7,6 @@ "methodName": "retrieveUserByLoginIdWithLoginIdTypes", "successResponse": "UserResponse", "errorResponse": "Errors", - "overload": "retrieveUserByLoginId", "params": [ { "name": "loginId", @@ -21,12 +20,11 @@ { "name": "loginIdTypes", "comments": [ - "the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" + "(Optional) the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" ], "type": "urlParameter", "parameterName": "loginIdTypes", - "javaType": "List", - "optional": true + "javaType": "List" } ] } diff --git a/src/main/api/retrieveUserLoginReportByLoginId.json b/src/main/api/retrieveUserLoginReportByLoginId.json index 3759beddd..e36b93ad0 100644 --- a/src/main/api/retrieveUserLoginReportByLoginId.json +++ b/src/main/api/retrieveUserLoginReportByLoginId.json @@ -8,7 +8,6 @@ "methodName": "retrieveUserLoginReportByLoginId", "successResponse": "LoginReportResponse", "errorResponse": "Errors", - "overloads": true, "params": [ { "name": "applicationId", diff --git a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json index e566be44f..13cc41672 100644 --- a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json +++ b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json @@ -8,7 +8,6 @@ "methodName": "retrieveUserLoginReportByLoginIdAndLoginIdTypes", "successResponse": "LoginReportResponse", "errorResponse": "Errors", - "overload": "retrieveUserLoginReportByLoginId", "params": [ { "name": "applicationId", @@ -49,12 +48,11 @@ { "name": "loginIdTypes", "comments": [ - "the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" + "(Optional) the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" ], "type": "urlParameter", "parameterName": "loginIdTypes", - "javaType": "List", - "optional": true + "javaType": "List" } ] } From a6f502b180f1496ee2962ab28f79e36731d58a59 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Tue, 6 May 2025 16:31:10 -0600 Subject: [PATCH 19/24] cover more java basics --- bin/build-openapi-yaml.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/build-openapi-yaml.rb b/bin/build-openapi-yaml.rb index 2d40adf99..414a75a9f 100755 --- a/bin/build-openapi-yaml.rb +++ b/bin/build-openapi-yaml.rb @@ -579,7 +579,7 @@ def build_openapi_paramobj(jsonparamobj, paramtype) paramobj["name"] = jsonparamobj["name"] paramobj["in"] = paramtype # Cover Java generics here - paramobj["schema"] = if %w[Collection List].include? jsonparamobj['javaType'] + paramobj["schema"] = if %w[Collection List Set].include? jsonparamobj['javaType'] { 'type' => 'array', 'items' => { From 34ea1422de3d7aabb09d035bf61d836d8f943a38 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Tue, 6 May 2025 16:31:18 -0600 Subject: [PATCH 20/24] remove more optional stuff --- bin/build-openapi-yaml.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bin/build-openapi-yaml.rb b/bin/build-openapi-yaml.rb index 414a75a9f..00b9cfb1d 100755 --- a/bin/build-openapi-yaml.rb +++ b/bin/build-openapi-yaml.rb @@ -279,11 +279,7 @@ def addListValue(hash, key, listElementType, identity_providers, rootkey = nil, # Returns true if the 1st comment include (Optional) or an explicit optional boolean exists def param_optional(param) - if param['comments']&.[](0)&.include?("(Optional)") - return true - end - - return param['optional'] + param['comments']&.[](0)&.include?("(Optional)") end def process_rawpaths(rawpaths, options) From 21838ec91e84676567488d7a98ef7bd2798248a4 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Wed, 7 May 2025 07:59:05 -0600 Subject: [PATCH 21/24] on this flavor, these are not optional --- src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json | 2 +- .../api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json index 70af2bdc5..d118771e3 100644 --- a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json +++ b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json @@ -20,7 +20,7 @@ { "name": "loginIdTypes", "comments": [ - "(Optional) the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" + "the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" ], "type": "urlParameter", "parameterName": "loginIdTypes", diff --git a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json index 13cc41672..71f76054d 100644 --- a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json +++ b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json @@ -48,7 +48,7 @@ { "name": "loginIdTypes", "comments": [ - "(Optional) the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" + "the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" ], "type": "urlParameter", "parameterName": "loginIdTypes", From d2180a578523928c48634fcc51063a1b3a507e04 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Wed, 7 May 2025 07:59:44 -0600 Subject: [PATCH 22/24] Correct our generic macros for our use cases --- src/main/client/_macros.ftl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/client/_macros.ftl b/src/main/client/_macros.ftl index f52d0400b..941747a44 100644 --- a/src/main/client/_macros.ftl +++ b/src/main/client/_macros.ftl @@ -33,8 +33,8 @@ [#case "KeyType"][#return "KeyType?"/] [#case "KeyAlgorithm"][#return "KeyAlgorithm?"/] [#default] - [#if type?starts_with("Collection")] - [#return type?replace("Collection", "List")?replace("UUID", "string")/] + [#if type?starts_with("Collection") || type?starts_with("List")] + [#return type?replace("Collection", "List")?replace("UUID", "string")?replace("String", "string")/] [#else] [#return type/] [/#if] @@ -52,8 +52,8 @@ [#return "int64"/] [#elseif type == "Void"] [#return "nil"/] - [#elseif type?starts_with("Collection")] - [#return type?replace("Collection", "[]")?replace("UUID", "string")?replace("<", "")?replace(">", "")/] + [#elseif type?starts_with("Collection<") || type?starts_with("List<")] + [#return type?replace("Collection", "[]")?replace("List", "[]")?replace("UUID", "string")?replace("<", "")?replace(">", "")?replace("String", "string")/] [#elseif type == "String" || type = "UUID" || type == "ZoneId" || type == "URI" || type == "Locale" || type == "LocalDate" || type == "char" || type == "IdentityType" ] [#return "string"/] [#elseif type == "Object" || type == "D" || type == "T"] @@ -97,7 +97,7 @@ [#case "JWT"][#return "JWT | object"/] [#case "Void"][#return "void"/] [#default] - [#if type?starts_with("Collection")] + [#if type?starts_with("Collection<") || type?starts_with("List<")] [#return type?replace("Collection", "Array")?replace("UUID", "string")/] [#else] [#return type/] @@ -132,8 +132,8 @@ [#case "Object"][#return "any"/] [#case "Void"][#return "void"/] [#default] - [#if type?starts_with("Collection")] - [#return type?replace("Collection", "Array")?replace("UUID", "string")/] + [#if type?starts_with("Collection<") || type?starts_with("List<")] + [#return type?replace("Collection", "Array")?replace("List", "Array")?replace("UUID", "string")/] [#else] [#return type/] [/#if] @@ -155,7 +155,7 @@ [#elseif language == "ruby"] [#if type == "UUID" || type == "String" || type == "IdentityProviderType" || type == "LambdaType"] [#return "string"/] - [#elseif type?starts_with("Collection")] + [#elseif type?starts_with("Collection<") || type?starts_with("List<")] [#return "Array"/] [#elseif type == "boolean" || type == "Boolean"] [#return "Boolean"/] From f6735a01df653cff1eff25d46d2fba0f4ac0c705 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Wed, 7 May 2025 08:01:23 -0600 Subject: [PATCH 23/24] correct comment --- bin/build-openapi-yaml.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/build-openapi-yaml.rb b/bin/build-openapi-yaml.rb index 00b9cfb1d..dc0a6c91c 100755 --- a/bin/build-openapi-yaml.rb +++ b/bin/build-openapi-yaml.rb @@ -277,7 +277,7 @@ def addListValue(hash, key, listElementType, identity_providers, rootkey = nil, end end -# Returns true if the 1st comment include (Optional) or an explicit optional boolean exists +# Returns true if the 1st comment includes (Optional) def param_optional(param) param['comments']&.[](0)&.include?("(Optional)") end From 0b5b166e16910ec1cbf82105f869e15867002a68 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Wed, 7 May 2025 08:33:07 -0600 Subject: [PATCH 24/24] now that this overload does not have opt params, remove defaults --- src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json | 2 +- .../api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json index d118771e3..343d90971 100644 --- a/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json +++ b/src/main/api/retrieveUserByLoginIdWithLoginIdTypes.json @@ -20,7 +20,7 @@ { "name": "loginIdTypes", "comments": [ - "the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" + "the identity types that FusionAuth will compare the loginId to." ], "type": "urlParameter", "parameterName": "loginIdTypes", diff --git a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json index 71f76054d..5417cf732 100644 --- a/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json +++ b/src/main/api/retrieveUserLoginReportByLoginIdAndLoginIdTypes.json @@ -48,7 +48,7 @@ { "name": "loginIdTypes", "comments": [ - "the identity types that FusionAuth will compare the loginId to. Defaults to [email, username]" + "the identity types that FusionAuth will compare the loginId to." ], "type": "urlParameter", "parameterName": "loginIdTypes",