forked from loafle/openapi-generator-original
Added support for enums in Dart. (#6516)
* Added support for enums in Dart. * Pick non-private names for enum values. The _ prefix denotes a private member in Dart, so avoid generating enum values starting with this character. * Properly encode enum values into query paramters. * Various cleanups. * Add support for x-enum-values extension. Use class instead of enum for better ergonomy. Better generated enum names. * Fixed test. * Support enum descriptions.
This commit is contained in:
@@ -3,27 +3,35 @@ package io.swagger.codegen.languages;
|
||||
import io.swagger.codegen.CliOption;
|
||||
import io.swagger.codegen.CodegenConfig;
|
||||
import io.swagger.codegen.CodegenConstants;
|
||||
import io.swagger.codegen.CodegenModel;
|
||||
import io.swagger.codegen.CodegenProperty;
|
||||
import io.swagger.codegen.CodegenType;
|
||||
import io.swagger.codegen.DefaultCodegen;
|
||||
import io.swagger.codegen.SupportingFile;
|
||||
import io.swagger.models.Model;
|
||||
import io.swagger.models.properties.ArrayProperty;
|
||||
import io.swagger.models.properties.MapProperty;
|
||||
import io.swagger.models.properties.Property;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
public static final String BROWSER_CLIENT = "browserClient";
|
||||
public static final String PUB_NAME = "pubName";
|
||||
public static final String PUB_VERSION = "pubVersion";
|
||||
public static final String PUB_DESCRIPTION = "pubDescription";
|
||||
public static final String USE_ENUM_EXTENSION = "useEnumExtension";
|
||||
protected boolean browserClient = true;
|
||||
protected String pubName = "swagger";
|
||||
protected String pubVersion = "1.0.0";
|
||||
protected String pubDescription = "Swagger API client";
|
||||
protected boolean useEnumExtension = false;
|
||||
protected String sourceFolder = "";
|
||||
protected String apiDocPath = "docs/";
|
||||
protected String modelDocPath = "docs/";
|
||||
@@ -95,6 +103,7 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
cliOptions.add(new CliOption(PUB_NAME, "Name in generated pubspec"));
|
||||
cliOptions.add(new CliOption(PUB_VERSION, "Version in generated pubspec"));
|
||||
cliOptions.add(new CliOption(PUB_DESCRIPTION, "Description in generated pubspec"));
|
||||
cliOptions.add(new CliOption(USE_ENUM_EXTENSION, "Allow the 'x-enum-values' extension for enums"));
|
||||
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, "source folder for generated code"));
|
||||
}
|
||||
|
||||
@@ -145,6 +154,13 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
additionalProperties.put(PUB_DESCRIPTION, pubDescription);
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(USE_ENUM_EXTENSION)) {
|
||||
this.setUseEnumExtension(convertPropertyToBooleanAndWriteBack(USE_ENUM_EXTENSION));
|
||||
} else {
|
||||
// Not set, use to be passed to template.
|
||||
additionalProperties.put(USE_ENUM_EXTENSION, useEnumExtension);
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
|
||||
this.setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
|
||||
}
|
||||
@@ -177,16 +193,11 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
|
||||
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
|
||||
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String escapeReservedWord(String name) {
|
||||
if(this.reservedWordsMappings().containsKey(name)) {
|
||||
return this.reservedWordsMappings().get(name);
|
||||
}
|
||||
return "_" + name;
|
||||
public String escapeReservedWord(String name) {
|
||||
return name + "_";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -223,8 +234,11 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
// pet_id => petId
|
||||
name = camelize(name, true);
|
||||
|
||||
// for reserved word or word starting with number, append _
|
||||
if (isReservedWord(name) || name.matches("^\\d.*")) {
|
||||
if (name.matches("^\\d.*")) {
|
||||
name = "n" + name;
|
||||
}
|
||||
|
||||
if (isReservedWord(name)) {
|
||||
name = escapeReservedWord(name);
|
||||
}
|
||||
|
||||
@@ -300,6 +314,117 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
return toModelName(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
||||
return postProcessModelsEnum(objs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessModelsEnum(Map<String, Object> objs) {
|
||||
List<Object> models = (List<Object>) objs.get("models");
|
||||
for (Object _mo : models) {
|
||||
Map<String, Object> mo = (Map<String, Object>) _mo;
|
||||
CodegenModel cm = (CodegenModel) mo.get("model");
|
||||
boolean succes = buildEnumFromVendorExtension(cm) ||
|
||||
buildEnumFromValues(cm);
|
||||
for (CodegenProperty var : cm.vars) {
|
||||
updateCodegenPropertyEnum(var);
|
||||
}
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the set of enum members from their declared value.
|
||||
*
|
||||
* @return {@code true} if the enum was built
|
||||
*/
|
||||
private boolean buildEnumFromValues(CodegenModel cm) {
|
||||
if (!cm.isEnum || cm.allowableValues == null) {
|
||||
return false;
|
||||
}
|
||||
Map<String, Object> allowableValues = cm.allowableValues;
|
||||
List<Object> values = (List<Object>) allowableValues.get("values");
|
||||
List<Map<String, String>> enumVars =
|
||||
new ArrayList<Map<String, String>>();
|
||||
String commonPrefix = findCommonPrefixOfVars(values);
|
||||
int truncateIdx = commonPrefix.length();
|
||||
for (Object value : values) {
|
||||
Map<String, String> enumVar = new HashMap<String, String>();
|
||||
String enumName;
|
||||
if (truncateIdx == 0) {
|
||||
enumName = value.toString();
|
||||
} else {
|
||||
enumName = value.toString().substring(truncateIdx);
|
||||
if ("".equals(enumName)) {
|
||||
enumName = value.toString();
|
||||
}
|
||||
}
|
||||
enumVar.put("name", toEnumVarName(enumName, cm.dataType));
|
||||
enumVar.put("value", toEnumValue(value.toString(), cm.dataType));
|
||||
enumVars.add(enumVar);
|
||||
}
|
||||
cm.allowableValues.put("enumVars", enumVars);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the set of enum members from a vendor extension.
|
||||
*
|
||||
* @return {@code true} if the enum was built
|
||||
*/
|
||||
private boolean buildEnumFromVendorExtension(CodegenModel cm) {
|
||||
if (!cm.isEnum || cm.allowableValues == null ||
|
||||
!useEnumExtension ||
|
||||
!cm.vendorExtensions.containsKey("x-enum-values")) {
|
||||
return false;
|
||||
}
|
||||
Object extension = cm.vendorExtensions.get("x-enum-values");
|
||||
List<Map<String, Object>> values =
|
||||
(List<Map<String, Object>>) extension;
|
||||
List<Map<String, String>> enumVars =
|
||||
new ArrayList<Map<String, String>>();
|
||||
for (Map<String, Object> value : values) {
|
||||
Map<String, String> enumVar = new HashMap<String, String>();
|
||||
String name = camelize((String) value.get("identifier"), true);
|
||||
if (isReservedWord(name)) {
|
||||
name = escapeReservedWord(name);
|
||||
}
|
||||
enumVar.put("name", name);
|
||||
enumVar.put("value", toEnumValue(
|
||||
value.get("numericValue").toString(), cm.dataType));
|
||||
if (value.containsKey("description")) {
|
||||
enumVar.put("description", value.get("description").toString());
|
||||
}
|
||||
enumVars.add(enumVar);
|
||||
}
|
||||
cm.allowableValues.put("enumVars", enumVars);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toEnumVarName(String value, String datatype) {
|
||||
if (value.length() == 0) {
|
||||
return "empty";
|
||||
}
|
||||
String var = value.replaceAll("\\W+", "_");
|
||||
if ("number".equalsIgnoreCase(datatype) ||
|
||||
"int".equalsIgnoreCase(datatype)) {
|
||||
var = "Number" + var;
|
||||
}
|
||||
return escapeReservedWord(camelize(var, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toEnumValue(String value, String datatype) {
|
||||
if ("number".equalsIgnoreCase(datatype) ||
|
||||
"int".equalsIgnoreCase(datatype)) {
|
||||
return value;
|
||||
} else {
|
||||
return "\"" + escapeText(value) + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toOperationId(String operationId) {
|
||||
// method name cannot use reserved keyword, e.g. return
|
||||
@@ -328,6 +453,10 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
this.pubDescription = pubDescription;
|
||||
}
|
||||
|
||||
public void setUseEnumExtension(boolean useEnumExtension) {
|
||||
this.useEnumExtension = useEnumExtension;
|
||||
}
|
||||
|
||||
public void setSourceFolder(String sourceFolder) {
|
||||
this.sourceFolder = sourceFolder;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,14 @@ class ApiClient {
|
||||
Map<String, Authentication> _authentications = {};
|
||||
|
||||
final dson = new Dartson.JSON()
|
||||
..addTransformer(new DateTimeParser(), DateTime);
|
||||
{{#models}}
|
||||
{{#model}}
|
||||
{{#isEnum}}
|
||||
..addTransformer(new {{classname}}TypeTransformer(), {{classname}})
|
||||
{{/isEnum}}
|
||||
{{/model}}
|
||||
{{/models}}
|
||||
..addTransformer(new DateTimeParser(), DateTime);
|
||||
|
||||
final _RegList = new RegExp(r'^List<(.*)>$');
|
||||
final _RegMap = new RegExp(r'^Map<String,(.*)>$');
|
||||
@@ -46,7 +53,16 @@ class ApiClient {
|
||||
{{#models}}
|
||||
{{#model}}
|
||||
case '{{classname}}':
|
||||
{{#isEnum}}
|
||||
// Enclose the value in a list so that Dartson can use a transformer
|
||||
// to decode it.
|
||||
final listValue = [value];
|
||||
final List<dynamic> listResult = dson.map(listValue, []);
|
||||
return listResult[0];
|
||||
{{/isEnum}}
|
||||
{{^isEnum}}
|
||||
return dson.map(value, new {{classname}}());
|
||||
{{/isEnum}}
|
||||
{{/model}}
|
||||
{{/models}}
|
||||
default:
|
||||
@@ -116,7 +132,7 @@ class ApiClient {
|
||||
headerParams['Content-Type'] = contentType;
|
||||
|
||||
if(body is MultipartRequest) {
|
||||
var request = new MultipartRequest(method, Uri.parse(url));
|
||||
var request = new MultipartRequest(method, Uri.parse(url));
|
||||
request.fields.addAll(body.fields);
|
||||
request.files.addAll(body.files);
|
||||
request.headers.addAll(body.headers);
|
||||
@@ -141,7 +157,7 @@ class ApiClient {
|
||||
}
|
||||
|
||||
/// Update query and header parameters based on authentication settings.
|
||||
/// @param authNames The authentications to apply
|
||||
/// @param authNames The authentications to apply
|
||||
void _updateParamsForAuth(List<String> authNames, List<QueryParam> queryParams, Map<String, String> headerParams) {
|
||||
authNames.forEach((authName) {
|
||||
Authentication auth = _authentications[authName];
|
||||
|
||||
@@ -38,7 +38,15 @@ String _parameterToString(dynamic value) {
|
||||
return '';
|
||||
} else if (value is DateTime) {
|
||||
return value.toUtc().toIso8601String();
|
||||
{{#models}}
|
||||
{{#model}}
|
||||
{{#isEnum}}
|
||||
} else if (value is {{classname}}) {
|
||||
return new {{classname}}TypeTransformer().encode(value).toString();
|
||||
{{/isEnum}}
|
||||
{{/model}}
|
||||
{{/models}}
|
||||
} else {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:http/browser_client.dart';{{/browserClient}}
|
||||
import 'package:http/http.dart';
|
||||
import 'package:dartson/dartson.dart';
|
||||
import 'package:dartson/transformers/date_time.dart';
|
||||
import 'package:dartson/type_transformer.dart';
|
||||
|
||||
part 'api_client.dart';
|
||||
part 'api_helper.dart';
|
||||
@@ -21,4 +22,3 @@ part 'auth/http_basic_auth.dart';
|
||||
{{/model}}{{/models}}
|
||||
|
||||
ApiClient defaultApiClient = new ApiClient();
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
@Entity()
|
||||
class {{classname}} {
|
||||
{{#vars}}{{#description}}/* {{{description}}} */{{/description}}
|
||||
@Property(name: '{{baseName}}')
|
||||
{{{datatype}}} {{name}} = {{{defaultValue}}};
|
||||
{{#allowableValues}}{{#min}} // range from {{min}} to {{max}}{{/min}}//{{^min}}enum {{name}}Enum { {{#values}} {{.}}, {{/values}} };{{/min}}{{/allowableValues}}
|
||||
{{/vars}}
|
||||
{{classname}}();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{{classname}}[{{#vars}}{{name}}=${{name}}, {{/vars}}]';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
@Entity()
|
||||
class {{classname}} {
|
||||
/// The underlying value of this enum member.
|
||||
final {{dataType}} value;
|
||||
|
||||
const {{classname}}._internal(this.value);
|
||||
|
||||
{{#allowableValues}}
|
||||
{{#enumVars}}
|
||||
{{#description}}
|
||||
/// {{description}}
|
||||
{{/description}}
|
||||
static const {{classname}} {{name}} = const {{classname}}._internal({{value}});
|
||||
{{/enumVars}}
|
||||
{{/allowableValues}}
|
||||
}
|
||||
|
||||
class {{classname}}TypeTransformer extends TypeTransformer<{{classname}}> {
|
||||
|
||||
@override
|
||||
dynamic encode({{classname}} data) {
|
||||
return data.value;
|
||||
}
|
||||
|
||||
@override
|
||||
{{classname}} decode(dynamic data) {
|
||||
switch (data) {
|
||||
{{#allowableValues}}
|
||||
{{#enumVars}}
|
||||
case {{value}}: return {{classname}}.{{name}};
|
||||
{{/enumVars}}
|
||||
{{/allowableValues}}
|
||||
default: throw('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,7 @@
|
||||
part of {{pubName}}.api;
|
||||
|
||||
{{#models}}{{#model}}
|
||||
@Entity()
|
||||
class {{classname}} {
|
||||
{{#vars}}{{#description}}/* {{{description}}} */{{/description}}
|
||||
@Property(name: '{{baseName}}')
|
||||
{{{datatype}}} {{name}} = {{{defaultValue}}};
|
||||
{{#allowableValues}}{{#min}} // range from {{min}} to {{max}}{{/min}}//{{^min}}enum {{name}}Enum { {{#values}} {{.}}, {{/values}} };{{/min}}{{/allowableValues}}
|
||||
{{/vars}}
|
||||
{{classname}}();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{{classname}}[{{#vars}}{{name}}=${{name}}, {{/vars}}]';
|
||||
}
|
||||
|
||||
}
|
||||
{{/model}}{{/models}}
|
||||
{{#models}}
|
||||
{{#model}}
|
||||
{{#isEnum}}{{>enum}}{{/isEnum}}{{^isEnum}}{{>class}}{{/isEnum}}
|
||||
{{/model}}
|
||||
{{/models}}
|
||||
|
||||
@@ -38,7 +38,8 @@ public class DartClientOptionsTest extends AbstractOptionsTest {
|
||||
times = 1;
|
||||
clientCodegen.setSourceFolder(DartClientOptionsProvider.SOURCE_FOLDER_VALUE);
|
||||
times = 1;
|
||||
clientCodegen.setUseEnumExtension(Boolean.valueOf(DartClientOptionsProvider.USE_ENUM_EXTENSION));
|
||||
times = 1;
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public class DartClientOptionsProvider implements OptionsProvider {
|
||||
public static final String PUB_VERSION_VALUE = "1.0.0-SNAPSHOT";
|
||||
public static final String PUB_DESCRIPTION_VALUE = "Swagger API client dart";
|
||||
public static final String SOURCE_FOLDER_VALUE = "src";
|
||||
public static final String USE_ENUM_EXTENSION = "true";
|
||||
public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false";
|
||||
|
||||
|
||||
@@ -33,6 +34,7 @@ public class DartClientOptionsProvider implements OptionsProvider {
|
||||
.put(DartClientCodegen.PUB_VERSION, PUB_VERSION_VALUE)
|
||||
.put(DartClientCodegen.PUB_DESCRIPTION, PUB_DESCRIPTION_VALUE)
|
||||
.put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER_VALUE)
|
||||
.put(DartClientCodegen.USE_ENUM_EXTENSION, USE_ENUM_EXTENSION)
|
||||
.put(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.2.3-SNAPSHOT
|
||||
2.3.0-SNAPSHOT
|
||||
@@ -87,7 +87,7 @@ This endpoint does not need any parameter.
|
||||
|
||||
### Return type
|
||||
|
||||
[**Map<String, int>**](Map.md)
|
||||
**Map<String, int>**
|
||||
|
||||
### Authorization
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:http/browser_client.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:dartson/dartson.dart';
|
||||
import 'package:dartson/transformers/date_time.dart';
|
||||
import 'package:dartson/type_transformer.dart';
|
||||
|
||||
part 'api_client.dart';
|
||||
part 'api_helper.dart';
|
||||
@@ -28,4 +29,3 @@ part 'model/user.dart';
|
||||
|
||||
|
||||
ApiClient defaultApiClient = new ApiClient();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class ApiClient {
|
||||
Map<String, Authentication> _authentications = {};
|
||||
|
||||
final dson = new Dartson.JSON()
|
||||
..addTransformer(new DateTimeParser(), DateTime);
|
||||
..addTransformer(new DateTimeParser(), DateTime);
|
||||
|
||||
final _RegList = new RegExp(r'^List<(.*)>$');
|
||||
final _RegMap = new RegExp(r'^Map<String,(.*)>$');
|
||||
@@ -121,7 +121,7 @@ class ApiClient {
|
||||
headerParams['Content-Type'] = contentType;
|
||||
|
||||
if(body is MultipartRequest) {
|
||||
var request = new MultipartRequest(method, Uri.parse(url));
|
||||
var request = new MultipartRequest(method, Uri.parse(url));
|
||||
request.fields.addAll(body.fields);
|
||||
request.files.addAll(body.files);
|
||||
request.headers.addAll(body.headers);
|
||||
@@ -146,7 +146,7 @@ class ApiClient {
|
||||
}
|
||||
|
||||
/// Update query and header parameters based on authentication settings.
|
||||
/// @param authNames The authentications to apply
|
||||
/// @param authNames The authentications to apply
|
||||
void _updateParamsForAuth(List<String> authNames, List<QueryParam> queryParams, Map<String, String> headerParams) {
|
||||
authNames.forEach((authName) {
|
||||
Authentication auth = _authentications[authName];
|
||||
|
||||
@@ -41,4 +41,4 @@ String _parameterToString(dynamic value) {
|
||||
} else {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
part of swagger.api;
|
||||
|
||||
|
||||
@Entity()
|
||||
class ApiResponse {
|
||||
|
||||
@@ -21,6 +20,5 @@ class ApiResponse {
|
||||
String toString() {
|
||||
return 'ApiResponse[code=$code, type=$type, message=$message, ]';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
part of swagger.api;
|
||||
|
||||
|
||||
@Entity()
|
||||
class Category {
|
||||
|
||||
@@ -17,6 +16,5 @@ class Category {
|
||||
String toString() {
|
||||
return 'Category[id=$id, name=$name, ]';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
part of swagger.api;
|
||||
|
||||
|
||||
@Entity()
|
||||
class Order {
|
||||
|
||||
@@ -33,6 +32,5 @@ class Order {
|
||||
String toString() {
|
||||
return 'Order[id=$id, petId=$petId, quantity=$quantity, shipDate=$shipDate, status=$status, complete=$complete, ]';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
part of swagger.api;
|
||||
|
||||
|
||||
@Entity()
|
||||
class Pet {
|
||||
|
||||
@@ -33,6 +32,5 @@ class Pet {
|
||||
String toString() {
|
||||
return 'Pet[id=$id, category=$category, name=$name, photoUrls=$photoUrls, tags=$tags, status=$status, ]';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
part of swagger.api;
|
||||
|
||||
|
||||
@Entity()
|
||||
class Tag {
|
||||
|
||||
@@ -17,6 +16,5 @@ class Tag {
|
||||
String toString() {
|
||||
return 'Tag[id=$id, name=$name, ]';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
part of swagger.api;
|
||||
|
||||
|
||||
@Entity()
|
||||
class User {
|
||||
|
||||
@@ -41,6 +40,5 @@ class User {
|
||||
String toString() {
|
||||
return 'User[id=$id, username=$username, firstName=$firstName, lastName=$lastName, email=$email, password=$password, phone=$phone, userStatus=$userStatus, ]';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user