**Migrating to Brick 3 from Brick 2**
tshedor opened this issue · comments
Brick v3 has arrived. This release focuses on bringing GraphQL parity to REST. It also consolidates the usage of providerArgs
and expands the latest features in Dart, like enhanced enums and the latest analyzer.
BrickOfflineFirstWithRest
now has a subscribe
method. Invoking this method will return a stream populated as events hit the local SQLite database. For example, when a user is upserted
, all repository.subscribe<User>()
listeners will receive the copy of that user.
Additionally, the RestSerializable
configuration has been overhauled. endpoint
has been replaced by requestTransformer
. This will permit even more granular control over individual requests and code analysis to catch errors ahead of runtime.
Abstract packages have been removed since Sqflite has abstracted its Flutter dependency to a "common" API. brick_offline_first_with_graphql_abstract
, brick_offline_first_with_rest_abstract
, brick_sqlite_abstract
, and brick_offline_first_abstract
will remain on pub.dev since publishing is forever. While this change is internal, parent packages no longer export the contents of child packages. Some adjustments may need to be made.
And much less interesting but extremely helpful for local dev: melos has been added to simplify local package development. All commits following v3 will use conventional commits to simplify release and changelog management.
As always, the migration guide and scripts are below:
Breaking Changes
- Primary package files are renamed in line with
pub.dev
standards.for FILE in $(find "lib" -type f -name "*.dart"); do sed -i '' 's/package:brick_offline_first\/offline_first.dart/package:brick_offline_first\/brick_offline_first.dart/g' $FILE sed -i '' 's/package:brick_offline_first_with_rest\/offline_first_with_rest.dart/package:brick_offline_first_with_rest\/brick_offline_first_with_rest.dart/g' $FILE sed -i '' 's/package:brick_offline_first_with_graphql\/offline_first_with_graphql.dart/package:brick_offline_first_with_graphql\/brick_offline_first_with_graphql.dart/g' $FILE sed -i '' 's/package:brick_rest\/rest.dart/package:brick_rest\/brick_rest.dart/g' $FILE sed -i '' 's/package:brick_sqlite\/sqlite.dart/package:brick_sqlite\/brick_sqlite.dart/g' $FILE sed -i '' 's/package:brick_graphql\/graphql.dart/package:brick_graphql\/brick_graphql.dart/g' $FILE done
brick_offline_first/offline_first.dart
is nowbrick_offline_first/brick_offline_first.dart
brick_offline_first_with_rest/offline_first_with_rest.dart
is nowbrick_offline_first_with_rest/brick_offline_first_with_rest.dart
brick_offline_first_with_graphql/brick_offline_first_with_graphql.dart
is nowbrick_offline_first_with_graphql/brick_offline_first_with_graphql.dart
brick_graphql/graphql.dart
is nowbrick_rest/brick_graphql.dart
brick_rest/rest.dart
is nowbrick_rest/brick_rest.dart
brick_sqlite/sqlite.dart
is nowbrick_sqlite/brick_sqlite.dart
brick_sqlite_abstract/db.dart
is nowbrick_sqlite/db.dart
.brick_sqlite_abstract/sqlite_model.dart
andbrick_sqlite_abstract/annotations.dart
are now exported bybrick_sqlite/brick_sqlite.dart
for FILE in $(find "lib" -type f -name "*.dart"); do sed -i '' 's/package:brick_sqlite_abstract\/annotations.dart/package:brick_sqlite\/brick_sqlite.dart/g' $FILE sed -i '' 's/package:brick_sqlite_abstract\/sqlite_model.dart/package:brick_sqlite\/brick_sqlite.dart/g' $FILE sed -i '' 's/package:brick_sqlite_abstract\/db.dart/package:brick_sqlite\/db.dart/g' $FILE done
- The minimum Dart version has been increased to 2.18
providerArgs
in Brick Rest have changed:'topLevelKey'
and'headers'
and'supplementalTopLevelData'
have been removed (use'request'
) and'request'
now accepts aRestRequest
instead of the HTTP method string.providerArgs
in Brick Graphql have changed:'document'
and'variables'
have been removed. Instead, use'operation'
.analyzer
is now>= 5
Brick Offline First with Graphql
FieldRename
,Graphql
GraphqlProvider
, andGraphqlSerializable
are no longer exported byoffline_first_with_graphql.dart
. Instead, import these file frompackage:brick_graphql/brick_graphql.dart
Brick Offline First with Rest
FieldRename
,Rest
,RestProvider
, andRestSerializable
are no longer exported byoffline_first_with_rest.dart
. Instead, import these file frompackage:brick_rest/brick_rest.dart
OfflineFirstWithRestRepository#reattemptForStatusCodes
has been removed from instance-level access. The constructor argument forwards to theRestOfflineQueueClient
, where it can be accessed if needed.OfflineFirstWithRestRepository#throwTunnerNotFoundExceptions
has been removed. This value was duplicated fromofflineQueueManager
; the queue manager is where the property exclusively lives now.
Improvements
- Listen for SQLite changes via
OfflineFirstWithRestRepository#subscribe
Brick Graphql
This breaking change migration is less automatable. The script below is a best attempt and should be manually confirmed after running.
for FILE in $(find "lib" -type f -name "*.dart"); do
# `sed` regex capture may work for you; it didn't on Mac for me
# sed -i '' "s/\'document\': (.*)/\'operation\': GraphqlOperation\(document: \1\) \/\/ TODO verify migration to GraphqlOperation /g" $FILE
perl -0777 -i -pe "s/'document': (.*)/'operation': GraphqlOperation\(document: \1\) \/\/ TODO verify migration to GraphqlOperation /igs" $FILE
# sed -i '' "s/\'variables\': (.*)/\'operation\': GraphqlOperation\(variables: \1\) \/\/ TODO verify migration to GraphqlOperation /g" $FILE
perl -0777 -i -pe "s/'variables': (.*)/'operation': GraphqlOperation\(variables: \1\) \/\/ TODO verify migration to GraphqlOperation /igs" $FILE
done
providerArgs['document']
This has been consolidated to 'operation'
. For example: providerArgs: { 'operation': GraphqlOperation(document: r'''mutation UpdateUser(id: ....)''')}
.
providerArgs['variables']
This has been consolidated to 'operation'
. For example: providerArgs: { 'operation': GraphqlOperation(variables: {'id': '1'}) }
.
Brick Rest
This breaking change migration is less automatable. The script below is a best attempt and should be manually confirmed after running.
for FILE in $(find "lib" -type f -name "*.dart"); do
# `sed` regex capture may work for you; it didn't on Mac for me
# sed -i '' "s/\'request\': (.*)/\'request\': RestRequest\(method: \1\) \/\/ TODO verify migration to RestRequest /g" $FILE
perl -0777 -i -pe "s/'request': (.*)/'request': RestRequest\(method: \1\) \/\/ TODO verify migration to RestRequest /igs" $FILE
# sed -i '' "s/\'headers\': (.*)/\'request\': RestRequest\(headers: \1\) \/\/ TODO verify migration to RestRequest /g" $FILE
perl -0777 -i -pe "s/'headers': (.*)/'request': RestRequest\(headers: \1\) \/\/ TODO verify migration to RestRequest /igs" $FILE
# sed -i '' "s/\'topLevelKey\': (.*)/\'request\': RestRequest\(topLevelKey: \1\) \/\/ TODO verify migration to RestRequest /g" $FILE
perl -0777 -i -pe "s/'topLevelKey': (.*)/'request': RestRequest\(topLevelKey: \1\) \/\/ TODO verify migration to RestRequest /igs" $FILE
# sed -i '' "s/\'supplementalTopLevelData\': (.*)/\'request\': RestRequest\(supplementalTopLevelData: \1\) \/\/ TODO verify migration to RestRequest /g" $FILE
perl -0777 -i -pe "s/'supplementalTopLevelData': (.*)/'request': RestRequest\(supplementalTopLevelData: \1\) \/\/ TODO verify migration to RestRequest /igs" $FILE
done
providerArgs['request']
This key now accepts a RestRequest
class instead of an HTTP method name.
providerArgs['headers']
This has been consolidated to 'request'
. For example: providerArgs: { 'request': RestRequest(headers: {'Authorization': 'Bearer'})}
.
providerArgs['topLevelKey']
This has been consolidated to 'request'
. For example: providerArgs: { 'request': RestRequest(topLevelKey: 'myKey' )}
.
providerArgs['supplementalTopLevelData']
This has been consolidated to 'request'
. For example: providerArgs: { 'request': RestRequest(supplementalTopLevelData: {'myKey': {'myData': 1}}) }
.
RestSerializable(requestTransformer:)
RestSerializable
'sfromKey
andtoKey
have been consolidated toRestRequest(topLevelKey:)
RestSerializable(endpoint:)
has been replaced in this release byRestSerializable(requestTransformer:)
. It will be painful to upgrade though with good reason.
- Strongly-typed classes.
endpoint
was a string, which removed analysis in IDEs, permitting errors to escape during runtime. With endpoints as classes,Query
andinstance
objects will receive type hinting. - Fine control over REST requests. Define on a request-level basis what key to pull from or push to. Declare specific HTTP methods like
PATCH
in a class that manages request instead of in distributedproviderArgs
. - Future-proof development. Enhancing REST's configuration will be on a class object instead of in untyped string keys on
providerArgs
. The REST interface is consolidated to this subclass.
Since all APIs are different, and endpoint
used stringified code, the migration cannot be scripted for all users. Instead, examples are provided below to illustrate how to refactor from Brick 2's endpoint
to Brick 3's requestTransformer
. Some examples:
// BEFORE
@ConnectOfflineFirstWithRest(
restConfig: RestSerializable(
endpoint: '"/users";'
fromKey: 'users',
)
)
// AFTER
class UserRequestTransformer extends RestRequestTransformer {
final get = const RestRequest(url: '/users', topLevelKey: 'users');
const UserRequestTransformer(Query? query, RestModel? instance) : super(query, instance);
}
@ConnectOfflineFirstWithRest(
restConfig: RestSerializable(
requestTransformer: UserRequestTransformer.new,
)
)
Some cases are more complex:
// BEFORE
@ConnectOfflineFirstWithRest(
restConfig: RestSerializable(
endpoint: r'''{
if (query?.action == QueryAction.delete) return "/users/${instance.id}";
if (query?.action == QueryAction.get &&
query?.providerArgs.isNotEmpty &&
query?.providerArgs['limit'] != null) {
return "/users?limit=${query.providerArgs['limit']}";
}
return "/users";
}''';
)
)
// AFTER
class UserRequestTransformer extends RestRequestTransformer {
RestRequest? get get {
if (query?.providerArgs.isNotEmpty && query.providerArgs['limit'] != null) {
return RestRequest(url: "/users?limit=${query.providerArgs['limit']}");
}
const RestRequest(url: '/users');
}
final delete = RestRequest(url: '/users/${instance.id}');
const UserRequestTransformer(Query? query, RestModel? instance) : super(query, instance);
}
@ConnectOfflineFirstWithRest(
restConfig: RestSerializable(
requestTransformer: UserRequestTransformer.new,
)
)
💡 For ease of illustration, the code is provided as if the transformer and model logic live in the same file. It's strongly recommended to include the request transformer logic in its own, colocated file (such as user.model.request.dart
).