**Migrating to Brick 3 from Brick 2**
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.devstandards.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 donebrick_offline_first/offline_first.dartis nowbrick_offline_first/brick_offline_first.dartbrick_offline_first_with_rest/offline_first_with_rest.dartis nowbrick_offline_first_with_rest/brick_offline_first_with_rest.dartbrick_offline_first_with_graphql/brick_offline_first_with_graphql.dartis nowbrick_offline_first_with_graphql/brick_offline_first_with_graphql.dartbrick_graphql/graphql.dartis nowbrick_rest/brick_graphql.dartbrick_rest/rest.dartis nowbrick_rest/brick_rest.dartbrick_sqlite/sqlite.dartis nowbrick_sqlite/brick_sqlite.dart
brick_sqlite_abstract/db.dartis nowbrick_sqlite/db.dart.brick_sqlite_abstract/sqlite_model.dartandbrick_sqlite_abstract/annotations.dartare now exported bybrick_sqlite/brick_sqlite.dartfor 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
providerArgsin Brick Rest have changed:'topLevelKey'and'headers'and'supplementalTopLevelData'have been removed (use'request') and'request'now accepts aRestRequestinstead of the HTTP method string.providerArgsin Brick Graphql have changed:'document'and'variables'have been removed. Instead, use'operation'.analyzeris now>= 5
Brick Offline First with Graphql
FieldRename,GraphqlGraphqlProvider, andGraphqlSerializableare 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, andRestSerializableare no longer exported byoffline_first_with_rest.dart. Instead, import these file frompackage:brick_rest/brick_rest.dartOfflineFirstWithRestRepository#reattemptForStatusCodeshas been removed from instance-level access. The constructor argument forwards to theRestOfflineQueueClient, where it can be accessed if needed.OfflineFirstWithRestRepository#throwTunnerNotFoundExceptionshas 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'sfromKeyandtoKeyhave 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.
endpointwas a string, which removed analysis in IDEs, permitting errors to escape during runtime. With endpoints as classes,Queryandinstanceobjects 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
PATCHin 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,
)
)
:bulb: 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).