curly braces in control flow

This commit is contained in:
Tobe O 2019-05-30 23:49:00 -04:00
parent f29050f7df
commit 49942cc841
19 changed files with 113 additions and 69 deletions

View file

@ -5,5 +5,6 @@ analyzer:
linter: linter:
rules: rules:
- avoid_slow_async_io - avoid_slow_async_io
- curly_braces_in_flow_control_structures
- unnecessary_const - unnecessary_const
- unnecessary_new - unnecessary_new

View file

@ -105,8 +105,8 @@ class Controller {
var injection = preInject(reflectedMethod, app.container.reflector); var injection = preInject(reflectedMethod, app.container.reflector);
if (exposeDecl?.allowNull?.isNotEmpty == true) if (exposeDecl?.allowNull?.isNotEmpty == true) {
injection.optional?.addAll(exposeDecl.allowNull); injection.optional?.addAll(exposeDecl.allowNull);}
routeMappings[name] = routable.addRoute(exposeDecl.method, routeMappings[name] = routable.addRoute(exposeDecl.method,
exposeDecl.path, handleContained(reflectedMethod, injection), exposeDecl.path, handleContained(reflectedMethod, injection),

View file

@ -149,9 +149,10 @@ abstract class Driver<
} }
return f.catchError((e, StackTrace st) { return f.catchError((e, StackTrace st) {
if (e is FormatException) if (e is FormatException) {
throw AngelHttpException.badRequest(message: e.message) throw AngelHttpException.badRequest(message: e.message)
..stackTrace = st; ..stackTrace = st;
}
throw AngelHttpException(e, throw AngelHttpException(e,
stackTrace: st, stackTrace: st,
statusCode: 500, statusCode: 500,
@ -172,10 +173,11 @@ abstract class Driver<
} else { } else {
var zoneSpec = ZoneSpecification( var zoneSpec = ZoneSpecification(
print: (self, parent, zone, line) { print: (self, parent, zone, line) {
if (app.logger != null) if (app.logger != null) {
app.logger.info(line); app.logger.info(line);
else } else {
parent.print(zone, line); parent.print(zone, line);
}
}, },
handleUncaughtError: (self, parent, zone, error, stackTrace) { handleUncaughtError: (self, parent, zone, error, stackTrace) {
var trace = Trace.from(stackTrace ?? StackTrace.current).terse; var trace = Trace.from(stackTrace ?? StackTrace.current).terse;
@ -259,9 +261,9 @@ abstract class Driver<
Future handleError; Future handleError;
if (!res.isOpen) if (!res.isOpen) {
handleError = Future.value(); handleError = Future.value();
else { } else {
res.statusCode = e.statusCode; res.statusCode = e.statusCode;
handleError = handleError =
Future.sync(() => app.errorHandler(e, req, res)).then((result) { Future.sync(() => app.errorHandler(e, req, res)).then((result) {

View file

@ -63,13 +63,14 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
} }
Map<String, dynamic> _stripReq(Map<String, dynamic> params) { Map<String, dynamic> _stripReq(Map<String, dynamic> params) {
if (params == null) if (params == null) {
return params; return params;
else } else {
return params.keys return params.keys
.where((key) => key != '__requestctx' && key != '__responsectx') .where((key) => key != '__requestctx' && key != '__responsectx')
.fold<Map<String, dynamic>>( .fold<Map<String, dynamic>>(
{}, (map, key) => map..[key] = params[key]); {}, (map, key) => map..[key] = params[key]);
}
} }
/// Closes any open [StreamController]s on this instance. **Internal use only**. /// Closes any open [StreamController]s on this instance. **Internal use only**.
@ -109,8 +110,9 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
final listeners = <HookedServiceEventListener<Id, Data, T>>[] final listeners = <HookedServiceEventListener<Id, Data, T>>[]
..addAll(isAfter == true ? after : before); ..addAll(isAfter == true ? after : before);
if (hooks != null) if (hooks != null) {
listeners.addAll(isAfter == true ? hooks.after : hooks.before); listeners.addAll(isAfter == true ? hooks.after : hooks.before);
}
listeners.forEach(dispatcher.listen); listeners.forEach(dispatcher.listen);
} }
@ -469,8 +471,9 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
HookedServiceEvent<Id, Data, T> event, HookedServiceEvent<Id, Data, T> event,
[HookedServiceEventListener<Id, Data, T> callback]) { [HookedServiceEventListener<Id, Data, T> callback]) {
Future f; Future f;
if (callback != null && event?._canceled != true) if (callback != null && event?._canceled != true) {
f = Future.sync(() => callback(event)); f = Future.sync(() => callback(event));
}
f ??= Future.value(); f ??= Future.value();
return f.then((_) => dispatcher._emit(event)); return f.then((_) => dispatcher._emit(event));
} }
@ -533,7 +536,7 @@ class HookedServiceEvent<Id, Data, T extends Service<Id, Data>> {
T service; T service;
HookedServiceEvent(this._isAfter, this._request, this._response, this.service, HookedServiceEvent(this._isAfter, this._request, this._response, this.service,
String this._eventName, this._eventName,
{Id id, this.data, Map<String, dynamic> params, this.result}) { {Id id, this.data, Map<String, dynamic> params, this.result}) {
_id = id; _id = id;
_params = params ?? {}; _params = params ?? {};
@ -557,8 +560,9 @@ class HookedServiceEventDispatcher<Id, Data, T extends Service<Id, Data>> {
/// Fires an event, and returns it once it is either canceled, or all listeners have run. /// Fires an event, and returns it once it is either canceled, or all listeners have run.
Future<HookedServiceEvent<Id, Data, T>> _emit( Future<HookedServiceEvent<Id, Data, T>> _emit(
HookedServiceEvent<Id, Data, T> event) { HookedServiceEvent<Id, Data, T> event) {
if (event?._canceled == true || event == null || listeners.isEmpty) if (event?._canceled == true || event == null || listeners.isEmpty) {
return Future.value(event); return Future.value(event);
}
var f = Future<HookedServiceEvent<Id, Data, T>>.value(event); var f = Future<HookedServiceEvent<Id, Data, T>>.value(event);

View file

@ -39,8 +39,9 @@ resolveInjection(requirement, InjectionRequest injection, RequestContext req,
if (value == null && param.required != false) throw param.error; if (value == null && param.required != false) throw param.error;
return value; return value;
} else if (requirement is String) { } else if (requirement is String) {
if (req.container.hasNamed(requirement)) if (req.container.hasNamed(requirement)) {
return req.container.findByName(requirement); return req.container.findByName(requirement);
}
if (req.params.containsKey(requirement)) { if (req.params.containsKey(requirement)) {
return req.params[requirement]; return req.params[requirement];
} else if ((propFromApp = req.app.findProperty(requirement)) != null) } else if ((propFromApp = req.app.findProperty(requirement)) != null)
@ -62,14 +63,16 @@ resolveInjection(requirement, InjectionRequest injection, RequestContext req,
_primitiveTypes.contains(type)) { _primitiveTypes.contains(type)) {
return await resolveInjection( return await resolveInjection(
key, injection, req, res, throwOnUnresolved, container); key, injection, req, res, throwOnUnresolved, container);
} else } else {
return await resolveInjection( return await resolveInjection(
type, injection, req, res, throwOnUnresolved, container); type, injection, req, res, throwOnUnresolved, container);
}
} else if (requirement is Type && requirement != dynamic) { } else if (requirement is Type && requirement != dynamic) {
try { try {
var futureType = container.reflector.reflectFutureOf(requirement); var futureType = container.reflector.reflectFutureOf(requirement);
if (container.has(futureType.reflectedType)) if (container.has(futureType.reflectedType)) {
return await container.make(futureType.reflectedType); return await container.make(futureType.reflectedType);
}
} on UnsupportedError { } on UnsupportedError {
// Ignore. // Ignore.
} }

View file

@ -39,18 +39,19 @@ class MapService extends Service<String, Map<String, dynamic>> {
return (Map<String, dynamic> item) { return (Map<String, dynamic> item) {
if (item['id'] == null) if (item['id'] == null)
return false; return false;
else if (autoIdAndDateFields != false) else if (autoIdAndDateFields != false) {
return item['id'] == id?.toString(); return item['id'] == id?.toString();
else } else {
return item['id'] == id; return item['id'] == id;
}
}; };
} }
@override @override
Future<List<Map<String, dynamic>>> index([Map<String, dynamic> params]) { Future<List<Map<String, dynamic>>> index([Map<String, dynamic> params]) {
if (allowQuery == false || params == null || params['query'] is! Map) if (allowQuery == false || params == null || params['query'] is! Map) {
return Future.value(items); return Future.value(items);
else { } else {
var query = params['query'] as Map; var query = params['query'] as Map;
return Future.value(items.where((item) { return Future.value(items.where((item) {
@ -75,10 +76,11 @@ class MapService extends Service<String, Map<String, dynamic>> {
@override @override
Future<Map<String, dynamic>> create(Map<String, dynamic> data, Future<Map<String, dynamic>> create(Map<String, dynamic> data,
[Map<String, dynamic> params]) { [Map<String, dynamic> params]) {
if (data is! Map) if (data is! Map) {
throw AngelHttpException.badRequest( throw AngelHttpException.badRequest(
message: message:
'MapService does not support `create` with ${data.runtimeType}.'); 'MapService does not support `create` with ${data.runtimeType}.');
}
var now = DateTime.now().toIso8601String(); var now = DateTime.now().toIso8601String();
var result = Map<String, dynamic>.from(data); var result = Map<String, dynamic>.from(data);
@ -95,10 +97,11 @@ class MapService extends Service<String, Map<String, dynamic>> {
@override @override
Future<Map<String, dynamic>> modify(String id, Map<String, dynamic> data, Future<Map<String, dynamic>> modify(String id, Map<String, dynamic> data,
[Map<String, dynamic> params]) { [Map<String, dynamic> params]) {
if (data is! Map) if (data is! Map) {
throw AngelHttpException.badRequest( throw AngelHttpException.badRequest(
message: message:
'MapService does not support `modify` with ${data.runtimeType}.'); 'MapService does not support `modify` with ${data.runtimeType}.');
}
if (!items.any(_matchesId(id))) return create(data, params); if (!items.any(_matchesId(id))) return create(data, params);
return read(id).then((item) { return read(id).then((item) {
@ -106,10 +109,11 @@ class MapService extends Service<String, Map<String, dynamic>> {
if (idx < 0) return create(data, params); if (idx < 0) return create(data, params);
var result = Map<String, dynamic>.from(item)..addAll(data); var result = Map<String, dynamic>.from(item)..addAll(data);
if (autoIdAndDateFields == true) if (autoIdAndDateFields == true) {
result result
..[autoSnakeCaseNames == false ? 'updatedAt' : 'updated_at'] = ..[autoSnakeCaseNames == false ? 'updatedAt' : 'updated_at'] =
DateTime.now().toIso8601String(); DateTime.now().toIso8601String();
}
return Future.value(items[idx] = result); return Future.value(items[idx] = result);
}); });
} }
@ -117,16 +121,18 @@ class MapService extends Service<String, Map<String, dynamic>> {
@override @override
Future<Map<String, dynamic>> update(String id, Map<String, dynamic> data, Future<Map<String, dynamic>> update(String id, Map<String, dynamic> data,
[Map<String, dynamic> params]) { [Map<String, dynamic> params]) {
if (data is! Map) if (data is! Map) {
throw AngelHttpException.badRequest( throw AngelHttpException.badRequest(
message: message:
'MapService does not support `update` with ${data.runtimeType}.'); 'MapService does not support `update` with ${data.runtimeType}.');
}
if (!items.any(_matchesId(id))) return create(data, params); if (!items.any(_matchesId(id))) return create(data, params);
return read(id).then((old) { return read(id).then((old) {
if (!items.remove(old)) if (!items.remove(old)) {
throw AngelHttpException.notFound( throw AngelHttpException.notFound(
message: 'No record found for ID $id'); message: 'No record found for ID $id');
}
var result = Map<String, dynamic>.from(data); var result = Map<String, dynamic>.from(data);
if (autoIdAndDateFields == true) { if (autoIdAndDateFields == true) {
@ -158,11 +164,12 @@ class MapService extends Service<String, Map<String, dynamic>> {
} }
return read(id, params).then((result) { return read(id, params).then((result) {
if (items.remove(result)) if (items.remove(result)) {
return result; return result;
else } else {
throw AngelHttpException.notFound( throw AngelHttpException.notFound(
message: 'No record found for ID $id'); message: 'No record found for ID $id');
}
}); });
} }
} }

View file

@ -70,30 +70,38 @@ class Parameter {
/// Returns an error that can be thrown when the parameter is not present. /// Returns an error that can be thrown when the parameter is not present.
get error { get error {
if (cookie?.isNotEmpty == true) if (cookie?.isNotEmpty == true) {
return AngelHttpException.badRequest( return AngelHttpException.badRequest(
message: 'Missing required cookie "$cookie".'); message: 'Missing required cookie "$cookie".');
if (header?.isNotEmpty == true) }
if (header?.isNotEmpty == true) {
return AngelHttpException.badRequest( return AngelHttpException.badRequest(
message: 'Missing required header "$header".'); message: 'Missing required header "$header".');
if (query?.isNotEmpty == true) }
if (query?.isNotEmpty == true) {
return AngelHttpException.badRequest( return AngelHttpException.badRequest(
message: 'Missing required query parameter "$query".'); message: 'Missing required query parameter "$query".');
if (session?.isNotEmpty == true) }
if (session?.isNotEmpty == true) {
return StateError('Session does not contain required key "$session".'); return StateError('Session does not contain required key "$session".');
}
} }
/// Obtains a value for this parameter from a [RequestContext]. /// Obtains a value for this parameter from a [RequestContext].
getValue(RequestContext req) { getValue(RequestContext req) {
if (cookie?.isNotEmpty == true) if (cookie?.isNotEmpty == true) {
return req.cookies.firstWhere((c) => c.name == cookie)?.value ?? return req.cookies.firstWhere((c) => c.name == cookie)?.value ??
defaultValue; defaultValue;
if (header?.isNotEmpty == true) }
if (header?.isNotEmpty == true) {
return req.headers.value(header) ?? defaultValue; return req.headers.value(header) ?? defaultValue;
if (session?.isNotEmpty == true) }
if (session?.isNotEmpty == true) {
return req.session[session] ?? defaultValue; return req.session[session] ?? defaultValue;
if (query?.isNotEmpty == true) }
if (query?.isNotEmpty == true) {
return req.uri.queryParameters[query] ?? defaultValue; return req.uri.queryParameters[query] ?? defaultValue;
}
return defaultValue; return defaultValue;
} }
} }

View file

@ -202,18 +202,20 @@ abstract class RequestContext<RawRequest> {
: contentType?.toString(); : contentType?.toString();
// Change to assert // Change to assert
if (contentTypeString == null) if (contentTypeString == null) {
throw ArgumentError( throw ArgumentError(
'RequestContext.accepts expects the `contentType` parameter to NOT be null.'); 'RequestContext.accepts expects the `contentType` parameter to NOT be null.');
}
_acceptHeaderCache ??= headers.value('accept'); _acceptHeaderCache ??= headers.value('accept');
if (_acceptHeaderCache == null) if (_acceptHeaderCache == null)
return false; return false;
else if (strict != true && _acceptHeaderCache.contains('*/*')) else if (strict != true && _acceptHeaderCache.contains('*/*')) {
return true; return true;
else } else {
return _acceptHeaderCache.contains(contentTypeString); return _acceptHeaderCache.contains(contentTypeString);
}
} }
/// Returns as `true` if the client's `Accept` header indicates that it will accept any response content type. /// Returns as `true` if the client's `Accept` header indicates that it will accept any response content type.

View file

@ -76,10 +76,11 @@ abstract class ResponseContext<RawResponse>
int get statusCode => _statusCode; int get statusCode => _statusCode;
set statusCode(int value) { set statusCode(int value) {
if (!isOpen) if (!isOpen) {
throw closed(); throw closed();
else } else {
_statusCode = value ?? 200; _statusCode = value ?? 200;
}
} }
/// Returns `true` if the response is still available for processing by Angel. /// Returns `true` if the response is still available for processing by Angel.
@ -114,10 +115,11 @@ abstract class ResponseContext<RawResponse>
/// ///
/// If [value] is `null`, then the header will be removed. /// If [value] is `null`, then the header will be removed.
set contentLength(int value) { set contentLength(int value) {
if (value == null) if (value == null) {
headers.remove('content-length'); headers.remove('content-length');
else } else {
headers['content-length'] = value.toString(); headers['content-length'] = value.toString();
}
} }
/// Gets or sets the content type to send back to a client. /// Gets or sets the content type to send back to a client.
@ -262,21 +264,24 @@ abstract class ResponseContext<RawResponse>
// UserController@show // UserController@show
List<String> split = action.split("@"); List<String> split = action.split("@");
if (split.length < 2) if (split.length < 2) {
throw Exception( throw Exception(
"Controller redirects must take the form of 'Controller@action'. You gave: $action"); "Controller redirects must take the form of 'Controller@action'. You gave: $action");
}
Controller controller = Controller controller =
app.controllers[split[0].replaceAll(_straySlashes, '')]; app.controllers[split[0].replaceAll(_straySlashes, '')];
if (controller == null) if (controller == null) {
throw Exception("Could not find a controller named '${split[0]}'"); throw Exception("Could not find a controller named '${split[0]}'");
}
Route matched = controller.routeMappings[split[1]]; Route matched = controller.routeMappings[split[1]];
if (matched == null) if (matched == null) {
throw Exception( throw Exception(
"Controller '${split[0]}' does not contain any action named '${split[1]}'"); "Controller '${split[0]}' does not contain any action named '${split[1]}'");
}
final head = controller final head = controller
.findExpose(app.container.reflector) .findExpose(app.container.reflector)
@ -314,8 +319,9 @@ abstract class ResponseContext<RawResponse>
? MediaType('application', 'octet-stream') ? MediaType('application', 'octet-stream')
: MediaType.parse(mimeType); : MediaType.parse(mimeType);
if (correspondingRequest.method != 'HEAD') if (correspondingRequest.method != 'HEAD') {
return file.openRead().pipe(this); return file.openRead().pipe(this);
}
} }
/// Configure the response to write to an intermediate response buffer, rather than to the stream directly. /// Configure the response to write to an intermediate response buffer, rather than to the stream directly.
@ -352,10 +358,11 @@ abstract class ResponseContext<RawResponse>
void writeCharCode(int charCode) { void writeCharCode(int charCode) {
if (!isOpen && isBuffered) if (!isOpen && isBuffered)
throw closed(); throw closed();
else if (!isBuffered) else if (!isBuffered) {
add([charCode]); add([charCode]);
else } else {
buffer.addByte(charCode); buffer.addByte(charCode);
}
} }
@override @override
@ -392,18 +399,20 @@ class _LockableBytesBuilderImpl implements LockableBytesBuilder {
@override @override
void add(List<int> bytes) { void add(List<int> bytes) {
if (_closed) if (_closed) {
throw _deny(); throw _deny();
else } else {
_buf.add(bytes); _buf.add(bytes);
}
} }
@override @override
void addByte(int byte) { void addByte(int byte) {
if (_closed) if (_closed) {
throw _deny(); throw _deny();
else } else {
_buf.addByte(byte); _buf.addByte(byte);
}
} }
@override @override

View file

@ -26,9 +26,9 @@ RequestHandler chain(Iterable<RequestHandler> handlers) {
for (var handler in handlers) { for (var handler in handlers) {
if (handler == null) break; if (handler == null) break;
if (runPipeline == null) if (runPipeline == null) {
runPipeline = () => Future.sync(() => handler(req, res)); runPipeline = () => Future.sync(() => handler(req, res));
else { } else {
var current = runPipeline; var current = runPipeline;
runPipeline = () => current().then((result) => !res.isOpen runPipeline = () => current().then((result) => !res.isOpen
? Future.value(result) ? Future.value(result)

View file

@ -275,8 +275,9 @@ class Angel extends Routable {
return result; return result;
} else if (result != null) { } else if (result != null) {
return res.serialize(result); return res.serialize(result);
} else } else {
return res.isOpen; return res.isOpen;
}
}); });
} }

View file

@ -21,7 +21,7 @@ class Providers {
/// The transport through which the client is accessing this service. /// The transport through which the client is accessing this service.
final String via; final String via;
const Providers(String this.via); const Providers(this.via);
static const String viaRest = "rest"; static const String viaRest = "rest";
static const String viaWebsocket = "websocket"; static const String viaWebsocket = "websocket";
@ -206,10 +206,11 @@ class Service<Id, Data> extends Routable {
return (id == true || id?.toString() == 'true') as T; return (id == true || id?.toString() == 'true') as T;
else if (T == double) else if (T == double)
return double.parse(id.toString()) as T; return double.parse(id.toString()) as T;
else if (T == num) else if (T == num) {
return num.parse(id.toString()) as T; return num.parse(id.toString()) as T;
else } else {
return id as T; return id as T;
}
} }
/// Generates RESTful routes pointing to this class's methods. /// Generates RESTful routes pointing to this class's methods.

View file

@ -80,10 +80,11 @@ class HttpRequestContext extends RequestContext<HttpRequest> {
String override = request.method; String override = request.method;
if (app.allowMethodOverrides == true) if (app.allowMethodOverrides == true) {
override = override =
request.headers.value('x-http-method-override')?.toUpperCase() ?? request.headers.value('x-http-method-override')?.toUpperCase() ??
request.method; request.method;
}
ctx.app = app; ctx.app = app;
ctx._contentType = request.headers.contentType == null ctx._contentType = request.headers.contentType == null

View file

@ -73,9 +73,10 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
@override @override
set contentType(MediaType value) { set contentType(MediaType value) {
super.contentType = value; super.contentType = value;
if (!_streamInitialized) if (!_streamInitialized) {
rawResponse.headers.contentType = rawResponse.headers.contentType =
ContentType(value.type, value.subtype, parameters: value.parameters); ContentType(value.type, value.subtype, parameters: value.parameters);
}
} }
bool _openStream() { bool _openStream() {
@ -184,8 +185,9 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
rawResponse.add(data); rawResponse.add(data);
} }
} else } else {
buffer.add(data); buffer.add(data);
}
} }
@override @override

View file

@ -189,8 +189,9 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
stream.sendData(data); stream.sendData(data);
} }
} else } else {
buffer.add(data); buffer.add(data);
}
} }
@override @override

View file

@ -62,9 +62,9 @@ class _SingleSafeCtrl<T> implements SafeCtrl<T> {
@override @override
void whenInitialized(void callback()) { void whenInitialized(void callback()) {
if (!_initialized) { if (!_initialized) {
if (!_hasListener) if (!_hasListener) {
_initializer = callback; _initializer = callback;
else { } else {
_initializer(); _initializer();
_initialized = true; _initialized = true;
} }
@ -112,9 +112,9 @@ class _BroadcastSafeCtrl<T> implements SafeCtrl<T> {
@override @override
void whenInitialized(void callback()) { void whenInitialized(void callback()) {
if (!_initialized) { if (!_initialized) {
if (_listeners <= 0) if (_listeners <= 0) {
_initializer = callback; _initializer = callback;
else { } else {
_initializer(); _initializer();
_initialized = true; _initialized = true;
} }

View file

@ -7,7 +7,7 @@ class Todo extends Model {
String text; String text;
String over; String over;
Todo({String this.text, String this.over}); Todo({this.text, this.over});
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {

View file

@ -63,8 +63,9 @@ main() {
res.redirectToAction("TodoController@foo", {"foo": "world"})); res.redirectToAction("TodoController@foo", {"foo": "world"}));
// Register as a singleton, just for the purpose of this test // Register as a singleton, just for the purpose of this test
if (!app.container.has<TodoController>()) if (!app.container.has<TodoController>()) {
app.container.registerSingleton(ctrl = TodoController()); app.container.registerSingleton(ctrl = TodoController());
}
// Using mountController<T>(); // Using mountController<T>();
await app.mountController<TodoController>(); await app.mountController<TodoController>();

View file

@ -94,8 +94,9 @@ main() {
await http.handleRequest(rq); await http.handleRequest(rq);
var body = await rq.response.transform(utf8.decoder).join(); var body = await rq.response.transform(utf8.decoder).join();
if (rq.response.statusCode != 32) if (rq.response.statusCode != 32) {
throw 'overwrite should throw error; response: $body'; throw 'overwrite should throw error; response: $body';
}
} on StateError { } on StateError {
// Success // Success
} }