Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,99 @@ final messages = await supabase
upgrading and leave this change out of the upgrade itself.

`nullsFirst` is unchanged and still defaults to `false`.

### Every deprecated API is gone

v3 drops the whole deprecated surface that accumulated over v1 and v2. Where an entry has a
replacement, that replacement has been available for at least one minor version. The rest were
already inert: unused types, options the client ignored, or values the server never sent.

| Removed | Replacement | Package |
| --- | --- | --- |
| `AuthChangeEvent.userDeleted` | none, it was never emitted | `gotrue` |
| `OAuthProvider.snakeCase` | `OAuthProvider.name` | `gotrue` |
| `User.confirmedAt` | `User.emailConfirmedAt` | `gotrue` |
| `ReturningOption` | none, it was unused | `postgrest` |
| `PostgrestClient.auth()` | `PostgrestClient.setAuth()` | `postgrest` |
| `RealtimeClient.longpollerTimeout` | none, there is no longpoll transport | `realtime_client` |
| `ChannelResponse.rateLimited` | none, it was never returned | `realtime_client` |
| `FileObject.lastAccessedAt`, `FileObjectV2.lastAccessedAt` | none, the server does not populate it | `storage_client` |
| `AuthUser` | `User` | `supabase` |
| `RealtimeClientOptions.eventsPerSecond` | none, it was already ignored | `supabase` |
| `RemoveSubscriptionResult` | none | `supabase` |
| `SupabaseRealtimeError` | none | `supabase` |
| `SupabaseEventTypes` and `SupabaseEventTypesName` | none, it was unused | `supabase` |
| `SupabaseStreamBuilder.execute()` | listen to the builder directly | `supabase` |

Both `lastAccessedAt` fields were also required constructor parameters, so any code that builds a
`FileObject` or `FileObjectV2` by hand drops that argument.

Two of these need more than a rename.

`User.confirmedAt` mirrored `emailConfirmedAt` and is no longer parsed from or written to JSON, so
`toJson()` output no longer contains a `confirmed_at` key:

```dart
// Before
final confirmed = user.confirmedAt != null;

// After
final confirmed = user.emailConfirmedAt != null;
```

`SupabaseStreamBuilder` has been a `Stream` since 1.0.0, so `execute()` only returned the builder's
own stream:

```dart
// Before
supabase.from('users').stream(primaryKey: ['id']).execute().listen(handle);

// After
supabase.from('users').stream(primaryKey: ['id']).listen(handle);
```

### `publishableKey` is required on `Supabase.initialize`

The deprecated `anonKey` parameter is removed and `publishableKey` is now required. A legacy anon
key is still a valid value, it just goes under the new name.

```dart
// Before
await Supabase.initialize(url: url, anonKey: anonKey);

// After
await Supabase.initialize(url: url, publishableKey: anonKey);
```

### `createSignedUrls` reports per-path failures

The old `createSignedUrls` returned `List<SignedUrl>` and silently dropped paths the server could
not sign, so there was no way to tell a missing file from a successful one. It is removed, and
`createSignedUrlsResult` takes over the name. Each entry in the returned list is either a
`SignedUrlSuccess` or a `SignedUrlFailure`.

```dart
// Before
final urls = await supabase.storage
.from('avatars')
.createSignedUrls(['a.png', 'b.png'], 60);
for (final url in urls) {
print(url.signedUrl);
}

// After
final results = await supabase.storage
.from('avatars')
.createSignedUrls(['a.png', 'b.png'], 60);
for (final result in results) {
switch (result) {
case SignedUrlSuccess(:final signedUrl):
print(signedUrl);
case SignedUrlFailure(:final path, :final error):
print('could not sign $path: $error');
}
}
```

If you were already on `createSignedUrlsResult`, drop the `Result` suffix from the call.

3 changes: 0 additions & 3 deletions packages/gotrue/lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ enum AuthChangeEvent {
signedOut('SIGNED_OUT'),
tokenRefreshed('TOKEN_REFRESHED'),
userUpdated('USER_UPDATED'),

@Deprecated('Was never in use and might be removed in the future.')
userDeleted(''),
mfaChallengeVerified('MFA_CHALLENGE_VERIFIED');

final String jsName;
Expand Down
9 changes: 0 additions & 9 deletions packages/gotrue/lib/src/types/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,6 @@ final class OAuthProvider {
zoom,
];

/// The API wire value for this provider.
///
/// Returns [name] as-is without any case conversion. The getter name is
/// misleading for custom providers whose names may not be snake_case.
/// Use [name] directly instead.
@Deprecated('Use name instead.')
// ignore: match-getter-setter-field-names
String get snakeCase => name;

@override
bool operator ==(Object other) =>
identical(this, other) || (other is OAuthProvider && other.name == name);
Expand Down
15 changes: 3 additions & 12 deletions packages/gotrue/lib/src/types/user.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ignore_for_file: deprecated_member_use_from_same_package

import 'package:collection/collection.dart';
import 'package:gotrue/src/types/mfa.dart';

Expand All @@ -17,8 +15,6 @@ class User {
final String? email;
final String? phone;
final String createdAt;
@Deprecated('Use emailConfirmedAt instead')
final String? confirmedAt;
final String? emailConfirmedAt;
final String? phoneConfirmedAt;
final String? lastSignInAt;
Expand All @@ -42,7 +38,6 @@ class User {
this.email,
this.phone,
required this.createdAt,
@Deprecated('Use emailConfirmedAt instead') this.confirmedAt,
this.emailConfirmedAt,
this.phoneConfirmedAt,
this.lastSignInAt,
Expand Down Expand Up @@ -74,7 +69,6 @@ class User {
email: json['email'],
phone: json['phone'],
createdAt: json['created_at'] ?? '',
confirmedAt: json['confirmed_at'],
emailConfirmedAt: json['email_confirmed_at'],
phoneConfirmedAt: json['phone_confirmed_at'],
lastSignInAt: json['last_sign_in_at'],
Expand Down Expand Up @@ -107,7 +101,6 @@ class User {
'email': email,
'phone': phone,
'created_at': createdAt,
'confirmed_at': confirmedAt,
'email_confirmed_at': emailConfirmedAt,
'phone_confirmed_at': phoneConfirmedAt,
'last_sign_in_at': lastSignInAt,
Expand All @@ -126,9 +119,9 @@ class User {
'recoverySentAt: $recoverySentAt, emailChangeSentAt: '
'$emailChangeSentAt, newEmail: $newEmail, invitedAt: $invitedAt, '
'actionLink: $actionLink, email: $email, phone: $phone, createdAt: '
'$createdAt, confirmedAt: $confirmedAt, emailConfirmedAt: '
'$emailConfirmedAt, phoneConfirmedAt: $phoneConfirmedAt, lastSignInAt: '
'$lastSignInAt, role: $role, updatedAt: $updatedAt, identities: '
'$createdAt, emailConfirmedAt: $emailConfirmedAt, phoneConfirmedAt: '
'$phoneConfirmedAt, lastSignInAt: $lastSignInAt, role: $role, '
'updatedAt: $updatedAt, identities: '
'$identities, factors: $factors, isAnonymous: $isAnonymous)';
}

Expand All @@ -151,7 +144,6 @@ class User {
other.email == email &&
other.phone == phone &&
other.createdAt == createdAt &&
other.confirmedAt == confirmedAt &&
other.emailConfirmedAt == emailConfirmedAt &&
other.phoneConfirmedAt == phoneConfirmedAt &&
other.lastSignInAt == lastSignInAt &&
Expand All @@ -177,7 +169,6 @@ class User {
email.hashCode ^
phone.hashCode ^
createdAt.hashCode ^
confirmedAt.hashCode ^
emailConfirmedAt.hashCode ^
phoneConfirmedAt.hashCode ^
lastSignInAt.hashCode ^
Expand Down
8 changes: 0 additions & 8 deletions packages/gotrue/test/custom_oauth_provider_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ void main() {
expect(provider.name, 'custom:my-provider');
});

test('snakeCase of a custom provider returns its raw value', () {
final provider = OAuthProvider('custom:my-provider');
expect(provider.snakeCase, 'custom:my-provider');
});

test(
'getOAuthSignInUrl builds correct URL for a custom provider',
() async {
Expand All @@ -47,11 +42,8 @@ void main() {

test('built-in providers still work as static constants', () {
expect(OAuthProvider.google.name, 'google');
expect(OAuthProvider.google.snakeCase, 'google');
expect(OAuthProvider.linkedinOidc.name, 'linkedin_oidc');
expect(OAuthProvider.linkedinOidc.snakeCase, 'linkedin_oidc');
expect(OAuthProvider.slackOidc.name, 'slack_oidc');
expect(OAuthProvider.slackOidc.snakeCase, 'slack_oidc');
});

test('equality is value-based on name', () {
Expand Down
19 changes: 2 additions & 17 deletions packages/gotrue/test/src/constants_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ignore_for_file: deprecated_member_use_from_same_package

import 'package:gotrue/src/constants.dart';
import 'package:gotrue/src/version.dart';
import 'package:supabase_common/supabase_common.dart';
Expand Down Expand Up @@ -60,7 +58,7 @@ void main() {

group('AuthChangeEvent', () {
test('has correct enum values', () {
expect(AuthChangeEvent.values, hasLength(8));
expect(AuthChangeEvent.values, hasLength(7));
expect(AuthChangeEvent.values, contains(AuthChangeEvent.initialSession));
expect(
AuthChangeEvent.values,
Expand All @@ -70,8 +68,6 @@ void main() {
expect(AuthChangeEvent.values, contains(AuthChangeEvent.signedOut));
expect(AuthChangeEvent.values, contains(AuthChangeEvent.tokenRefreshed));
expect(AuthChangeEvent.values, contains(AuthChangeEvent.userUpdated));
// ignore: deprecated_member_use
expect(AuthChangeEvent.values, contains(AuthChangeEvent.userDeleted));
expect(
AuthChangeEvent.values,
contains(AuthChangeEvent.mfaChallengeVerified),
Expand All @@ -88,19 +84,12 @@ void main() {
expect(AuthChangeEvent.signedOut.jsName, equals('SIGNED_OUT'));
expect(AuthChangeEvent.tokenRefreshed.jsName, equals('TOKEN_REFRESHED'));
expect(AuthChangeEvent.userUpdated.jsName, equals('USER_UPDATED'));
// ignore: deprecated_member_use
expect(AuthChangeEvent.userDeleted.jsName, equals(''));
expect(
AuthChangeEvent.mfaChallengeVerified.jsName,
equals('MFA_CHALLENGE_VERIFIED'),
);
});

test('userDeleted is deprecated', () {
// ignore: deprecated_member_use
expect(AuthChangeEvent.userDeleted.jsName, equals(''));
});

group('AuthChangeEvent', () {
test('fromString returns correct event for valid names', () {
expect(
Expand All @@ -127,11 +116,6 @@ void main() {
AuthChangeEvent.fromString('userUpdated'),
equals(AuthChangeEvent.userUpdated),
);
// ignore: deprecated_member_use
expect(
AuthChangeEvent.fromString('userDeleted'),
equals(AuthChangeEvent.userDeleted),
);
expect(
AuthChangeEvent.fromString('mfaChallengeVerified'),
equals(AuthChangeEvent.mfaChallengeVerified),
Expand All @@ -140,6 +124,7 @@ void main() {

test('fromString returns null for invalid names', () {
expect(AuthChangeEvent.fromString('invalid'), isNull);
expect(AuthChangeEvent.fromString('userDeleted'), isNull);
expect(AuthChangeEvent.fromString('SIGNED_IN'), isNull);
expect(AuthChangeEvent.fromString('signed_in'), isNull);
expect(AuthChangeEvent.fromString(''), isNull);
Expand Down
9 changes: 0 additions & 9 deletions packages/gotrue/test/src/types/user_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ignore_for_file: deprecated_member_use_from_same_package

import 'package:gotrue/src/types/user.dart';
import 'package:test/test.dart';

Expand Down Expand Up @@ -68,8 +66,6 @@ void main() {
'email': 'test@example.com',
'phone': '+1234567890',
'created_at': '2023-01-01T00:00:00Z',
// ignore: deprecated_member_use
'confirmed_at': '2023-01-01T04:00:00Z',
'email_confirmed_at': '2023-01-01T05:00:00Z',
'phone_confirmed_at': '2023-01-01T06:00:00Z',
'last_sign_in_at': '2023-01-01T07:00:00Z',
Expand Down Expand Up @@ -100,8 +96,6 @@ void main() {
expect(user.email, equals('test@example.com'));
expect(user.phone, equals('+1234567890'));
expect(user.createdAt, equals('2023-01-01T00:00:00Z'));
// ignore: deprecated_member_use
expect(user.confirmedAt, equals('2023-01-01T04:00:00Z'));
expect(user.emailConfirmedAt, equals('2023-01-01T05:00:00Z'));
expect(user.phoneConfirmedAt, equals('2023-01-01T06:00:00Z'));
expect(user.lastSignInAt, equals('2023-01-01T07:00:00Z'));
Expand Down Expand Up @@ -275,7 +269,6 @@ void main() {
email: 'test@example.com',
phone: '+1234567890',
createdAt: '2023-01-01T00:00:00Z',
confirmedAt: '2023-01-01T04:00:00Z',
emailConfirmedAt: '2023-01-01T05:00:00Z',
phoneConfirmedAt: '2023-01-01T06:00:00Z',
lastSignInAt: '2023-01-01T07:00:00Z',
Expand All @@ -299,8 +292,6 @@ void main() {
expect(json['email'], equals('test@example.com'));
expect(json['phone'], equals('+1234567890'));
expect(json['created_at'], equals('2023-01-01T00:00:00Z'));
// ignore: deprecated_member_use
expect(json['confirmed_at'], equals('2023-01-01T04:00:00Z'));
expect(json['email_confirmed_at'], equals('2023-01-01T05:00:00Z'));
expect(json['phone_confirmed_at'], equals('2023-01-01T06:00:00Z'));
expect(json['last_sign_in_at'], equals('2023-01-01T07:00:00Z'));
Expand Down
8 changes: 2 additions & 6 deletions packages/postgrest/lib/src/postgrest.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,8 @@ class PostgrestClient {
}

/// Authenticates the request with JWT.
@Deprecated("Use setAuth() instead")
PostgrestClient auth(String token) {
headers['Authorization'] = 'Bearer $token';
return this;
}

///
/// Passing `null` clears the `Authorization` header.
Comment thread
spydon marked this conversation as resolved.
PostgrestClient setAuth(String? token) {
_log.finest("setAuth with: $token");
if (token != null) {
Expand Down
10 changes: 0 additions & 10 deletions packages/postgrest/lib/src/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,6 @@ enum ExplainFormat {
json,
}

// coverage:ignore-[start]
/// Controls whether the affected row's representation is returned in the
/// response.
@Deprecated('Not used anywhere. Will be removed in the next major version.')
enum ReturningOption {
minimal,
representation,
}
// coverage:ignore-[end]

/// The type of tsquery conversion to use on [query].
enum TextSearchType {
/// Uses PostgreSQL's plainto_tsquery function.
Expand Down
4 changes: 0 additions & 4 deletions packages/realtime_client/lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,4 @@ enum ChannelEvent {
};
}

class Transports {
static const String websocket = 'websocket';
}

enum RealtimeLogLevel { info, debug, warn, error }
6 changes: 1 addition & 5 deletions packages/realtime_client/lib/src/realtime_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,6 @@ class RealtimeClient {
final _heartbeatController =
StreamController<RealtimeHeartbeatStatus>.broadcast();

@Deprecated("No longer used. Will be removed in the next major version.")
int longpollerTimeout = 20000;

/// The current state of the socket, or `null` before the first [connect].
SocketState? connectionState;
Future<String?> Function()? customAccessToken;
Expand Down Expand Up @@ -218,12 +215,11 @@ class RealtimeClient {
TimerCalculation? reconnectAfterMs,
Map<String, String>? headers,
this.params = const {},
this.longpollerTimeout = 20000,
RealtimeLogLevel? logLevel,
this.httpClient,
this.customAccessToken,
this.version = RealtimeProtocolVersion.v2,
}) : endPoint = Uri.parse('$endPoint/${Transports.websocket}')
}) : endPoint = Uri.parse('$endPoint/websocket')
.replace(
queryParameters: logLevel == null
? null
Expand Down
Loading
Loading