Skip to content

Commit

Permalink
[dart-dio] Incorrect hashCode and == overide for fields withList (#18198
Browse files Browse the repository at this point in the history
)

* [dart-dio] Incorrect hashCode and == overide for fields withList

* fix

* extend description

---------

Co-authored-by: Vasiliy Ditsyak <vasilich6107@users.noreply.github.com>
  • Loading branch information
vasilich6107 and vasilich6107 authored May 19, 2024
1 parent 8924083 commit 3d15864
Show file tree
Hide file tree
Showing 59 changed files with 684 additions and 510 deletions.
1 change: 1 addition & 0 deletions docs/generators/dart-dio.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|<dl><dt>**false**</dt><dd>No changes to the enum's are made, this is the default option.</dd><dt>**true**</dt><dd>With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.</dd></dl>|false|
|equalityCheckMethod|Specify equality check method. Takes effect only in case if serializationLibrary is json_serializable.|<dl><dt>**default**</dt><dd>[DEFAULT] Built in hash code generation method</dd><dt>**equatable**</dt><dd>Uses equatable library for equality checking</dd></dl>|default|
|finalProperties|Whether properties are marked as final when using Json Serializable for serialization| |true|
|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8545,7 +8545,7 @@ protected void handleConstantParams(CodegenOperation operation) {
// Also, adds back non constant params to allParams.
for (CodegenParameter p : copy) {
if (p.isEnum && p.required && p._enum != null && p._enum.size() == 1) {
// Add to constantParams for use in the code generation templates.
// Add to constantParams for use in the code generation templates.
operation.constantParams.add(p);
if (p.isQueryParam) {
operation.queryParams.removeIf(param -> param.baseName.equals(p.baseName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public class DartDioClientCodegen extends AbstractDartCodegen {
public static final String DATE_LIBRARY_TIME_MACHINE = "timemachine";
public static final String DATE_LIBRARY_DEFAULT = DATE_LIBRARY_CORE;

public static final String EQUALITY_CHECK_METHOD = "equalityCheckMethod";
public static final String EQUALITY_CHECK_METHOD_DEFAULT = "default";
public static final String EQUALITY_CHECK_METHOD_EQUATABLE = "equatable";
public static final String SERIALIZATION_LIBRARY_BUILT_VALUE = "built_value";
public static final String SERIALIZATION_LIBRARY_JSON_SERIALIZABLE = "json_serializable";
public static final String SERIALIZATION_LIBRARY_DEFAULT = SERIALIZATION_LIBRARY_BUILT_VALUE;
Expand All @@ -71,6 +74,7 @@ public class DartDioClientCodegen extends AbstractDartCodegen {
private static final String CLIENT_NAME = "clientName";

private String dateLibrary;
private String equalityCheckMethod;

private String clientName;

Expand Down Expand Up @@ -107,19 +111,30 @@ public DartDioClientCodegen() {
serializationLibrary.setDefault(SERIALIZATION_LIBRARY_DEFAULT);
cliOptions.add(serializationLibrary);

// Equality check option
final CliOption equalityCheckOption = CliOption.newString(EQUALITY_CHECK_METHOD, "Specify equality check method. Takes effect only in case if serializationLibrary is json_serializable.");
equalityCheckOption.setDefault(EQUALITY_CHECK_METHOD_DEFAULT);

final Map<String, String> equalityCheckOptions = new HashMap<>();
equalityCheckOptions.put(EQUALITY_CHECK_METHOD_DEFAULT, "[DEFAULT] Built in hash code generation method");
equalityCheckOptions.put(EQUALITY_CHECK_METHOD_EQUATABLE, "Uses equatable library for equality checking");
equalityCheckOption.setEnum(equalityCheckOptions);
cliOptions.add(equalityCheckOption);

// Date Library Option
final CliOption dateOption = CliOption.newString(DATE_LIBRARY, "Specify Date library");
dateOption.setDefault(DATE_LIBRARY_DEFAULT);

final CliOption finalProperties = CliOption.newBoolean(FINAL_PROPERTIES, "Whether properties are marked as final when using Json Serializable for serialization");
finalProperties.setDefault("true");
cliOptions.add(finalProperties);

final Map<String, String> dateOptions = new HashMap<>();
dateOptions.put(DATE_LIBRARY_CORE, "[DEFAULT] Dart core library (DateTime)");
dateOptions.put(DATE_LIBRARY_TIME_MACHINE, "Time Machine is date and time library for Flutter, Web, and Server with support for timezones, calendars, cultures, formatting and parsing.");
dateOption.setEnum(dateOptions);
cliOptions.add(dateOption);

// Final Properties Option
final CliOption finalProperties = CliOption.newBoolean(FINAL_PROPERTIES, "Whether properties are marked as final when using Json Serializable for serialization");
finalProperties.setDefault("true");
cliOptions.add(finalProperties);
}

public String getDateLibrary() {
Expand All @@ -130,6 +145,14 @@ public void setDateLibrary(String library) {
this.dateLibrary = library;
}

public String getEqualityCheckMethod() {
return equalityCheckMethod;
}

public void setEqualityCheckMethod(String equalityCheckMethod) {
this.equalityCheckMethod = equalityCheckMethod;
}

public String getClientName() {
return clientName;
}
Expand Down Expand Up @@ -172,6 +195,12 @@ public void processOpts() {
}
setDateLibrary(additionalProperties.get(DATE_LIBRARY).toString());

if (!additionalProperties.containsKey(EQUALITY_CHECK_METHOD)) {
additionalProperties.put(EQUALITY_CHECK_METHOD, EQUALITY_CHECK_METHOD_DEFAULT);
LOGGER.debug("Equality check method not set, using default {}", EQUALITY_CHECK_METHOD_DEFAULT);
}
setEqualityCheckMethod(additionalProperties.get(EQUALITY_CHECK_METHOD).toString());

if (!additionalProperties.containsKey(FINAL_PROPERTIES)) {
additionalProperties.put(FINAL_PROPERTIES, Boolean.parseBoolean(FINAL_PROPERTIES_DEFAULT_VALUE));
LOGGER.debug("finalProperties not set, using default {}", FINAL_PROPERTIES_DEFAULT_VALUE);
Expand Down Expand Up @@ -205,6 +234,7 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("auth/auth.mustache", authFolder, "auth.dart"));

configureSerializationLibrary(srcFolder);
configureEqualityCheckMethod(srcFolder);
configureDateLibrary(srcFolder);
}

Expand Down Expand Up @@ -278,6 +308,17 @@ private void configureSerializationLibraryJsonSerializable(String srcFolder) {
imports.put("MultipartFile", DIO_IMPORT);
}

private void configureEqualityCheckMethod(String srcFolder) {

This comment has been minimized.

Copy link
@Philzen

Philzen May 19, 2024

Contributor

@vasilich6107 The parameter srcFolder is unused in this method – was this by mistake? Otherwise, i'd recommend to remove in order to simplify.

This comment has been minimized.

Copy link
@vasilich6107

vasilich6107 May 19, 2024

Author Contributor

Hi. Yeah, will remove in next PR.

switch (equalityCheckMethod) {
case EQUALITY_CHECK_METHOD_EQUATABLE:
additionalProperties.put("useEquatable", "true");
break;
default:
case EQUALITY_CHECK_METHOD_DEFAULT:
break;
}
}

private void configureDateLibrary(String srcFolder) {
switch (dateLibrary) {
case DATE_LIBRARY_TIME_MACHINE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ dependencies:
built_value: '>=8.4.0 <9.0.0'
built_collection: '>=5.1.1 <6.0.0'
{{/useBuiltValue}}
{{#useEquatable}}
equatable: '^2.0.5'
{{/useEquatable}}
{{#useJsonSerializable}}
json_annotation: '^4.4.0'
{{/useJsonSerializable}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'package:json_annotation/json_annotation.dart';
{{#useEquatable}}
import 'package:equatable/src/equatable_utils.dart';
{{/useEquatable}}

part '{{classFilename}}.g.dart';

Expand Down Expand Up @@ -61,17 +64,50 @@ class {{{classname}}} {


{{/vars}}
@override
bool operator ==(Object other) => identical(this, other) || other is {{{classname}}} &&
{{#vars}}
other.{{{name}}} == {{{name}}}{{^-last}} &&{{/-last}}{{#-last}};{{/-last}}
{{/vars}}

@override
int get hashCode =>
{{#vars}}
{{#isNullable}}({{{name}}} == null ? 0 : {{{name}}}.hashCode){{/isNullable}}{{^isNullable}}{{{name}}}.hashCode{{/isNullable}}{{^-last}} +{{/-last}}{{#-last}};{{/-last}}
{{/vars}}
{{#useEquatable}}
bool operator ==(Object other) {
return identical(this, other) ||
other is {{{classname}}} &&
runtimeType == other.runtimeType &&
equals(
[
{{#vars}}
{{{name}}},
{{/vars}}
],
[
{{#vars}}
other.{{{name}}},
{{/vars}}
]
);
}
{{/useEquatable}}

{{^useEquatable}}
@override
bool operator ==(Object other) => identical(this, other) || other is {{{classname}}} &&
{{#vars}}
other.{{{name}}} == {{{name}}}{{^-last}} &&{{/-last}}{{#-last}};{{/-last}}
{{/vars}}
{{/useEquatable}}

{{#useEquatable}}
@override
int get hashCode => runtimeType.hashCode ^ mapPropsToHashCode([
{{#vars}}
{{{name}}},
{{/vars}}
],);
{{/useEquatable}}
{{^useEquatable}}
@override
int get hashCode =>
{{#vars}}
{{#isNullable}}({{{name}}} == null ? 0 : {{{name}}}.hashCode){{/isNullable}}{{^isNullable}}{{{name}}}.hashCode{{/isNullable}}{{^-last}} +{{/-last}}{{#-last}};{{/-last}}
{{/vars}}
{{/useEquatable}}

factory {{{classname}}}.fromJson(Map<String, dynamic> json) => _${{{classname}}}FromJson(json);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import 'package:json_annotation/json_annotation.dart';
enum {{{classname}}} {
{{#allowableValues}}
{{#enumVars}}
{{#description}}
/// {{{.}}}
{{/description}}
@JsonValue({{#isString}}r{{/isString}}{{{value}}})
{{{name}}}({{^isString}}'{{/isString}}{{#isString}}r{{/isString}}{{{value}}}{{^isString}}'{{/isString}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
{{^isNull}}
{{#description}}
/// {{{.}}}
{{/description}}
@JsonValue({{#isString}}r{{/isString}}{{{value}}})
{{{name}}}({{^isString}}'{{/isString}}{{#isString}}r{{/isString}}{{{value}}}{{^isString}}'{{/isString}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
{{/isNull}}
{{/enumVars}}
{{/allowableValues}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.openapitools.codegen.AbstractOptionsTest;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.languages.DartClientCodegen;
import org.openapitools.codegen.languages.DartDioClientCodegen;
import org.openapitools.codegen.options.DartClientOptionsProvider;

import static org.mockito.Mockito.mock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ protected void verifyOptions() {
verify(clientCodegen).setUseEnumExtension(Boolean.parseBoolean(DartDioClientOptionsProvider.USE_ENUM_EXTENSION));
verify(clientCodegen).setDateLibrary(DartDioClientCodegen.DATE_LIBRARY_DEFAULT);
verify(clientCodegen).setLibrary(DartDioClientCodegen.SERIALIZATION_LIBRARY_DEFAULT);
verify(clientCodegen).setEqualityCheckMethod(DartDioClientCodegen.EQUALITY_CHECK_METHOD_DEFAULT);
verify(clientCodegen).setEnumUnknownDefaultCase(Boolean.parseBoolean(DartDioClientOptionsProvider.ENUM_UNKNOWN_DEFAULT_CASE_VALUE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public Map<String, String> createOptions() {
.put(CodegenConstants.SERIALIZATION_LIBRARY, DartDioClientCodegen.SERIALIZATION_LIBRARY_DEFAULT)
.put(DartDioClientCodegen.DATE_LIBRARY, DartDioClientCodegen.DATE_LIBRARY_DEFAULT)
.put(DartDioClientCodegen.FINAL_PROPERTIES, DartDioClientCodegen.FINAL_PROPERTIES_DEFAULT_VALUE)
.put(DartDioClientCodegen.EQUALITY_CHECK_METHOD, DartDioClientCodegen.EQUALITY_CHECK_METHOD_DEFAULT)
.put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER_VALUE)
.put(DartDioClientCodegen.USE_ENUM_EXTENSION, USE_ENUM_EXTENSION)
.put(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,17 @@ class AdditionalPropertiesClass {



@override
bool operator ==(Object other) => identical(this, other) || other is AdditionalPropertiesClass &&
other.mapProperty == mapProperty &&
other.mapOfMapProperty == mapOfMapProperty;

@override
int get hashCode =>
mapProperty.hashCode +
mapOfMapProperty.hashCode;

@override
bool operator ==(Object other) => identical(this, other) || other is AdditionalPropertiesClass &&
other.mapProperty == mapProperty &&
other.mapOfMapProperty == mapOfMapProperty;

@override
int get hashCode =>
mapProperty.hashCode +
mapOfMapProperty.hashCode;

factory AdditionalPropertiesClass.fromJson(Map<String, dynamic> json) => _$AdditionalPropertiesClassFromJson(json);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,17 @@ class AllOfWithSingleRef {



@override
bool operator ==(Object other) => identical(this, other) || other is AllOfWithSingleRef &&
other.username == username &&
other.singleRefType == singleRefType;

@override
int get hashCode =>
username.hashCode +
singleRefType.hashCode;

@override
bool operator ==(Object other) => identical(this, other) || other is AllOfWithSingleRef &&
other.username == username &&
other.singleRefType == singleRefType;

@override
int get hashCode =>
username.hashCode +
singleRefType.hashCode;

factory AllOfWithSingleRef.fromJson(Map<String, dynamic> json) => _$AllOfWithSingleRefFromJson(json);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,17 @@ class Animal {



@override
bool operator ==(Object other) => identical(this, other) || other is Animal &&
other.className == className &&
other.color == color;

@override
int get hashCode =>
className.hashCode +
color.hashCode;

@override
bool operator ==(Object other) => identical(this, other) || other is Animal &&
other.className == className &&
other.color == color;

@override
int get hashCode =>
className.hashCode +
color.hashCode;

factory Animal.fromJson(Map<String, dynamic> json) => _$AnimalFromJson(json);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,19 @@ class ApiResponse {



@override
bool operator ==(Object other) => identical(this, other) || other is ApiResponse &&
other.code == code &&
other.type == type &&
other.message == message;

@override
int get hashCode =>
code.hashCode +
type.hashCode +
message.hashCode;

@override
bool operator ==(Object other) => identical(this, other) || other is ApiResponse &&
other.code == code &&
other.type == type &&
other.message == message;

@override
int get hashCode =>
code.hashCode +
type.hashCode +
message.hashCode;

factory ApiResponse.fromJson(Map<String, dynamic> json) => _$ApiResponseFromJson(json);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ class ArrayOfArrayOfNumberOnly {



@override
bool operator ==(Object other) => identical(this, other) || other is ArrayOfArrayOfNumberOnly &&
other.arrayArrayNumber == arrayArrayNumber;

@override
int get hashCode =>
arrayArrayNumber.hashCode;

@override
bool operator ==(Object other) => identical(this, other) || other is ArrayOfArrayOfNumberOnly &&
other.arrayArrayNumber == arrayArrayNumber;

@override
int get hashCode =>
arrayArrayNumber.hashCode;

factory ArrayOfArrayOfNumberOnly.fromJson(Map<String, dynamic> json) => _$ArrayOfArrayOfNumberOnlyFromJson(json);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ class ArrayOfNumberOnly {



@override
bool operator ==(Object other) => identical(this, other) || other is ArrayOfNumberOnly &&
other.arrayNumber == arrayNumber;

@override
int get hashCode =>
arrayNumber.hashCode;

@override
bool operator ==(Object other) => identical(this, other) || other is ArrayOfNumberOnly &&
other.arrayNumber == arrayNumber;

@override
int get hashCode =>
arrayNumber.hashCode;

factory ArrayOfNumberOnly.fromJson(Map<String, dynamic> json) => _$ArrayOfNumberOnlyFromJson(json);

Expand Down
Loading

0 comments on commit 3d15864

Please sign in to comment.