From 1cf82962fb3206d53defc1bcee8cb4c42025d5fb Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 11:35:04 +0200 Subject: [PATCH 01/18] test(fdc): add more tests to FDC --- .../lib/src/core/ref.dart | 2 +- .../lib/src/network/grpc_transport.dart | 1 + .../test/src/core/ref_test.dart | 117 +- .../test/src/core/ref_test.mocks.dart | 247 ++++ .../src/firebase_data_connect_test.mocks.dart | 4 - .../test/src/network/grpc_transport_test.dart | 133 +++ .../network/grpc_transport_test.mocks.dart | 1062 +++++++++++++++++ .../test/src/network/rest_transport_test.dart | 28 +- .../network/rest_transport_test.mocks.dart | 4 - 9 files changed, 1585 insertions(+), 13 deletions(-) create mode 100644 packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart create mode 100644 packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart create mode 100644 packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/core/ref.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/core/ref.dart index 6f0a437fc775..299737d8b0e1 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/core/ref.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/core/ref.dart @@ -125,7 +125,7 @@ class QueryRef extends OperationRef { .cast>(); if (_queryManager.containsQuery(operationName, variables, varsSerialized)) { try { - this.execute(); + unawaited(this.execute()); } catch (_) { // Call to `execute` should properly pass the error to the Stream. log("Error thrown by execute. The error will propagate via onError."); diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart index acbe34e91668..e8173608a17e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart @@ -106,6 +106,7 @@ class GRPCTransport implements DataConnectTransport { try { response = await stub.executeQuery(request, options: CallOptions(metadata: await getMetadata())); + print("coucou response $response"); return deserializer(jsonEncode(response.data.toProto3Json())); } on Exception catch (e) { throw DataConnectError(DataConnectErrorCode.other, diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart index eb42b669c8a3..fe602f006a70 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart @@ -10,10 +10,10 @@ import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'package:firebase_data_connect/src/common/common_library.dart'; import 'package:firebase_data_connect/src/core/ref.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -// Mock classes -class MockDataConnectTransport extends Mock implements DataConnectTransport {} +import 'ref_test.mocks.dart'; class MockFirebaseDataConnect extends Mock implements FirebaseDataConnect {} @@ -21,6 +21,14 @@ class MockQueryManager extends Mock implements QueryManager {} class MockOperationRef extends Mock implements OperationRef {} +class MockQueryRef extends Mock + implements QueryRef {} + +class MockStreamController extends Mock implements StreamController {} + +@GenerateMocks([ + DataConnectTransport, +]) void main() { group('OperationResult', () { test('should initialize correctly with provided data and ref', () { @@ -71,6 +79,111 @@ void main() { expect(queryManager.trackedQueries['testQuery']!['varsAsStr'], isNotNull); expect(stream, isA()); }); + + test('addQuery should return existing StreamController if query exists', + () { + final stream1 = + queryManager.addQuery('testQuery', 'variables', 'varsAsStr'); + final stream2 = + queryManager.addQuery('testQuery', 'variables', 'varsAsStr'); + + expect(stream1, stream2); + }); + + test( + 'subscribe should propagate errors via the stream', + () async { + final mockTransport = MockDataConnectTransport(); + final queryManager = QueryManager(mockDataConnect); + final mockStreamController = MockStreamController(); + final completer = Completer(); + + when(mockTransport.invokeQuery('testQuery', any, any, null)) + .thenThrow(Exception('Test Error')); + + final streamController = StreamController.broadcast(); + + queryManager.trackedQueries['testQuery'] = {'': streamController}; + + final queryRef = QueryRef( + mockDataConnect, + 'testQuery', + mockTransport, + emptySerializer, + queryManager, + emptySerializer, + null, + ); + + streamController.stream.listen( + (event) { + fail('Error was not propagated to the stream'); + }, + onError: (error) { + expect(error, isA()); + expect(error.toString(), contains('Test Error')); + completer.complete(); + }, + ); + + // Manually trigger the callback since subscribe is not working in unit tests + // TODO(Lyokone): find a way of using subscribe in unit tests + queryManager.triggerCallback( + 'testQuery', '', queryRef, null, Exception('Test Error')); + + await completer.future.timeout(Duration(seconds: 2), onTimeout: () { + fail('Error was not propagated to the stream'); + }); + }, + ); + + test( + 'should propagate data via the stream', + () async { + final mockTransport = MockDataConnectTransport(); + final queryManager = QueryManager(mockDataConnect); + final mockStreamController = MockStreamController(); + final completer = Completer(); + + when(mockTransport.invokeQuery('testQuery', any, any, null)) + .thenAnswer((_) async => 'Deserialized Data'); + + final streamController = StreamController.broadcast(); + + queryManager.trackedQueries['testQuery'] = {'': streamController}; + + final queryRef = QueryRef( + mockDataConnect, + 'testQuery', + mockTransport, + emptySerializer, + queryManager, + emptySerializer, + null, + ); + + streamController.stream.listen( + (event) { + expect(event.data, 'Deserialized Data'); + expect(event.ref, queryRef); + expect(event.dataConnect, mockDataConnect); + completer.complete(); + }, + onError: (error) { + fail('Error was propagated to the stream'); + }, + ); + + // Manually trigger the callback since subscribe is not working in unit tests + // TODO(Lyokone): find a way of using subscribe in unit tests + queryManager.triggerCallback( + 'testQuery', '', queryRef, 'Deserialized Data', null); + + await completer.future.timeout(Duration(seconds: 2), onTimeout: () { + fail('Data was not propagated to the stream'); + }); + }, + ); }); group('MutationRef', () { diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart new file mode 100644 index 000000000000..e70983d3d58f --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart @@ -0,0 +1,247 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in firebase_data_connect/test/src/core/ref_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:firebase_app_check/firebase_app_check.dart' as _i5; +import 'package:firebase_auth/firebase_auth.dart' as _i4; +import 'package:firebase_data_connect/src/common/common_library.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeTransportOptions_0 extends _i1.SmartFake + implements _i2.TransportOptions { + _FakeTransportOptions_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDataConnectOptions_1 extends _i1.SmartFake + implements _i2.DataConnectOptions { + _FakeDataConnectOptions_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFuture_2 extends _i1.SmartFake implements _i3.Future { + _FakeFuture_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [DataConnectTransport]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDataConnectTransport extends _i1.Mock + implements _i2.DataConnectTransport { + MockDataConnectTransport() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.TransportOptions get transportOptions => (super.noSuchMethod( + Invocation.getter(#transportOptions), + returnValue: _FakeTransportOptions_0( + this, + Invocation.getter(#transportOptions), + ), + ) as _i2.TransportOptions); + + @override + set transportOptions(_i2.TransportOptions? _transportOptions) => + super.noSuchMethod( + Invocation.setter( + #transportOptions, + _transportOptions, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.DataConnectOptions get options => (super.noSuchMethod( + Invocation.getter(#options), + returnValue: _FakeDataConnectOptions_1( + this, + Invocation.getter(#options), + ), + ) as _i2.DataConnectOptions); + + @override + set options(_i2.DataConnectOptions? _options) => super.noSuchMethod( + Invocation.setter( + #options, + _options, + ), + returnValueForMissingStub: null, + ); + + @override + set auth(_i4.FirebaseAuth? _auth) => super.noSuchMethod( + Invocation.setter( + #auth, + _auth, + ), + returnValueForMissingStub: null, + ); + + @override + set appCheck(_i5.FirebaseAppCheck? _appCheck) => super.noSuchMethod( + Invocation.setter( + #appCheck, + _appCheck, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.CallerSDKType get sdkType => (super.noSuchMethod( + Invocation.getter(#sdkType), + returnValue: _i2.CallerSDKType.core, + ) as _i2.CallerSDKType); + + @override + set sdkType(_i2.CallerSDKType? _sdkType) => super.noSuchMethod( + Invocation.setter( + #sdkType, + _sdkType, + ), + returnValueForMissingStub: null, + ); + + @override + String get appId => (super.noSuchMethod( + Invocation.getter(#appId), + returnValue: _i6.dummyValue( + this, + Invocation.getter(#appId), + ), + ) as String); + + @override + set appId(String? _appId) => super.noSuchMethod( + Invocation.setter( + #appId, + _appId, + ), + returnValueForMissingStub: null, + ); + + @override + _i3.Future invokeQuery( + String? queryName, + _i2.Deserializer? deserializer, + _i2.Serializer? serializer, + Variables? vars, + ) => + (super.noSuchMethod( + Invocation.method( + #invokeQuery, + [ + queryName, + deserializer, + serializer, + vars, + ], + ), + returnValue: _i6.ifNotNull( + _i6.dummyValueOrNull( + this, + Invocation.method( + #invokeQuery, + [ + queryName, + deserializer, + serializer, + vars, + ], + ), + ), + (Data v) => _i3.Future.value(v), + ) ?? + _FakeFuture_2( + this, + Invocation.method( + #invokeQuery, + [ + queryName, + deserializer, + serializer, + vars, + ], + ), + ), + ) as _i3.Future); + + @override + _i3.Future invokeMutation( + String? queryName, + _i2.Deserializer? deserializer, + _i2.Serializer? serializer, + Variables? vars, + ) => + (super.noSuchMethod( + Invocation.method( + #invokeMutation, + [ + queryName, + deserializer, + serializer, + vars, + ], + ), + returnValue: _i6.ifNotNull( + _i6.dummyValueOrNull( + this, + Invocation.method( + #invokeMutation, + [ + queryName, + deserializer, + serializer, + vars, + ], + ), + ), + (Data v) => _i3.Future.value(v), + ) ?? + _FakeFuture_2( + this, + Invocation.method( + #invokeMutation, + [ + queryName, + deserializer, + serializer, + vars, + ], + ), + ), + ) as _i3.Future); +} diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart index 3365f28aadab..ef96fe7c1e26 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart @@ -1,7 +1,3 @@ -// Copyright 2024, the Chromium project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - // Mocks generated by Mockito 5.4.4 from annotations // in firebase_data_connect/test/src/firebase_data_connect_test.dart. // Do not manually edit this file. diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart new file mode 100644 index 000000000000..631aa135fc39 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart @@ -0,0 +1,133 @@ +import 'package:firebase_app_check/firebase_app_check.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_data_connect/src/common/common_library.dart'; +import 'package:firebase_data_connect/src/generated/connector_service.pbgrpc.dart'; +import 'package:firebase_data_connect/src/network/grpc_library.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:grpc/grpc.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'grpc_transport_test.mocks.dart'; + +class MockFirebaseAuth extends Mock implements FirebaseAuth {} + +@GenerateMocks([ + ClientChannel, + FirebaseAppCheck, + User, + ConnectorServiceClient, + ResponseFuture +]) +void main() { + late GRPCTransport transport; + late MockFirebaseAuth mockAuth; + late MockFirebaseAppCheck mockAppCheck; + late MockUser mockUser; + late MockConnectorServiceClient mockStub; + + setUp(() { + mockAuth = MockFirebaseAuth(); + mockAppCheck = MockFirebaseAppCheck(); + mockUser = MockUser(); + mockStub = MockConnectorServiceClient(); + + when(mockAuth.currentUser).thenReturn(mockUser); + + transport = GRPCTransport( + TransportOptions('testhost', 443, true), + DataConnectOptions( + 'testProject', + 'testLocation', + 'testConnector', + 'testService', + ), + 'testAppId', + CallerSDKType.core, + mockAuth, + mockAppCheck, + ); + + transport.stub = mockStub; + }); + + group('GRPCTransport', () { + test('should correctly initialize secure GRPC channel', () { + expect( + transport.channel.options.credentials, + const ChannelCredentials.secure(), + ); + }); + + test('should correctly initialize insecure GRPC channel', () { + final insecureTransport = GRPCTransport( + TransportOptions('testhost', 443, false), + DataConnectOptions( + 'testProject', + 'testLocation', + 'testConnector', + 'testService', + ), + 'testAppId', + CallerSDKType.core, + mockAuth, + mockAppCheck, + ); + + expect( + insecureTransport.channel.options.credentials, + const ChannelCredentials.insecure(), + ); + }); + + test('invokeQuery should throw an error on failed query execution', + () async { + when(mockStub.executeQuery(any, options: anyNamed('options'))) + .thenThrow(Exception('GRPC error')); + + final deserializer = (String data) => 'Deserialized Data'; + + expect( + () => transport.invokeQuery('testQuery', deserializer, null, null), + throwsA(isA()), + ); + }); + + test('invokeMutation should throw an error on failed mutation execution', + () async { + when(mockStub.executeMutation(any, options: anyNamed('options'))) + .thenThrow(Exception('GRPC error')); + + final deserializer = (String data) => 'Deserialized Data'; + + expect( + () => + transport.invokeMutation('testMutation', deserializer, null, null), + throwsA(isA()), + ); + }); + + test('getMetadata should include auth and appCheck tokens in metadata', + () async { + when(mockUser.getIdToken()).thenAnswer((_) async => 'authToken123'); + when(mockAppCheck.getToken()).thenAnswer((_) async => 'appCheckToken123'); + + final metadata = await transport.getMetadata(); + + expect(metadata['x-firebase-auth-token'], 'authToken123'); + expect(metadata['X-Firebase-AppCheck'], 'appCheckToken123'); + }); + + test( + 'getMetadata should handle missing auth and appCheck tokens gracefully', + () async { + when(mockUser.getIdToken()).thenThrow(Exception('Auth error')); + when(mockAppCheck.getToken()).thenThrow(Exception('AppCheck error')); + + final metadata = await transport.getMetadata(); + + expect(metadata.containsKey('x-firebase-auth-token'), isFalse); + expect(metadata.containsKey('X-Firebase-AppCheck'), isFalse); + }); + }); +} diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart new file mode 100644 index 000000000000..48ec8af42b40 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart @@ -0,0 +1,1062 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in firebase_data_connect/test/src/network/grpc_transport_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i9; + +import 'package:firebase_app_check/firebase_app_check.dart' as _i12; +import 'package:firebase_app_check_platform_interface/firebase_app_check_platform_interface.dart' + as _i13; +import 'package:firebase_auth/firebase_auth.dart' as _i7; +import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart' + as _i6; +import 'package:firebase_core/firebase_core.dart' as _i5; +import 'package:firebase_data_connect/src/generated/connector_service.pb.dart' + as _i16; +import 'package:firebase_data_connect/src/generated/connector_service.pbgrpc.dart' + as _i15; +import 'package:grpc/service_api.dart' as _i8; +import 'package:grpc/src/client/call.dart' as _i4; +import 'package:grpc/src/client/connection.dart' as _i3; +import 'package:grpc/src/client/http2_channel.dart' as _i10; +import 'package:grpc/src/client/method.dart' as _i11; +import 'package:grpc/src/client/options.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i14; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeObject_0 extends _i1.SmartFake implements Object { + _FakeObject_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeChannelOptions_1 extends _i1.SmartFake + implements _i2.ChannelOptions { + _FakeChannelOptions_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeClientConnection_2 extends _i1.SmartFake + implements _i3.ClientConnection { + _FakeClientConnection_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeClientCall_3 extends _i1.SmartFake + implements _i4.ClientCall { + _FakeClientCall_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFirebaseApp_4 extends _i1.SmartFake implements _i5.FirebaseApp { + _FakeFirebaseApp_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeUserMetadata_5 extends _i1.SmartFake implements _i6.UserMetadata { + _FakeUserMetadata_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeMultiFactor_6 extends _i1.SmartFake implements _i7.MultiFactor { + _FakeMultiFactor_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeIdTokenResult_7 extends _i1.SmartFake implements _i6.IdTokenResult { + _FakeIdTokenResult_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeUserCredential_8 extends _i1.SmartFake + implements _i7.UserCredential { + _FakeUserCredential_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeConfirmationResult_9 extends _i1.SmartFake + implements _i7.ConfirmationResult { + _FakeConfirmationResult_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeUser_10 extends _i1.SmartFake implements _i7.User { + _FakeUser_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeResponseFuture_11 extends _i1.SmartFake + implements _i8.ResponseFuture { + _FakeResponseFuture_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeResponseStream_12 extends _i1.SmartFake + implements _i8.ResponseStream { + _FakeResponseStream_12( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFuture_13 extends _i1.SmartFake implements _i9.Future { + _FakeFuture_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [ClientChannel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockClientChannel extends _i1.Mock implements _i10.ClientChannel { + MockClientChannel() { + _i1.throwOnMissingStub(this); + } + + @override + Object get host => (super.noSuchMethod( + Invocation.getter(#host), + returnValue: _FakeObject_0( + this, + Invocation.getter(#host), + ), + ) as Object); + + @override + int get port => (super.noSuchMethod( + Invocation.getter(#port), + returnValue: 0, + ) as int); + + @override + _i2.ChannelOptions get options => (super.noSuchMethod( + Invocation.getter(#options), + returnValue: _FakeChannelOptions_1( + this, + Invocation.getter(#options), + ), + ) as _i2.ChannelOptions); + + @override + _i9.Stream<_i3.ConnectionState> get onConnectionStateChanged => + (super.noSuchMethod( + Invocation.getter(#onConnectionStateChanged), + returnValue: _i9.Stream<_i3.ConnectionState>.empty(), + ) as _i9.Stream<_i3.ConnectionState>); + + @override + _i3.ClientConnection createConnection() => (super.noSuchMethod( + Invocation.method( + #createConnection, + [], + ), + returnValue: _FakeClientConnection_2( + this, + Invocation.method( + #createConnection, + [], + ), + ), + ) as _i3.ClientConnection); + + @override + _i9.Future shutdown() => (super.noSuchMethod( + Invocation.method( + #shutdown, + [], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future terminate() => (super.noSuchMethod( + Invocation.method( + #terminate, + [], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future<_i3.ClientConnection> getConnection() => (super.noSuchMethod( + Invocation.method( + #getConnection, + [], + ), + returnValue: + _i9.Future<_i3.ClientConnection>.value(_FakeClientConnection_2( + this, + Invocation.method( + #getConnection, + [], + ), + )), + ) as _i9.Future<_i3.ClientConnection>); + + @override + _i4.ClientCall createCall( + _i11.ClientMethod? method, + _i9.Stream? requests, + _i4.CallOptions? options, + ) => + (super.noSuchMethod( + Invocation.method( + #createCall, + [ + method, + requests, + options, + ], + ), + returnValue: _FakeClientCall_3( + this, + Invocation.method( + #createCall, + [ + method, + requests, + options, + ], + ), + ), + ) as _i4.ClientCall); +} + +/// A class which mocks [FirebaseAppCheck]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFirebaseAppCheck extends _i1.Mock implements _i12.FirebaseAppCheck { + MockFirebaseAppCheck() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.FirebaseApp get app => (super.noSuchMethod( + Invocation.getter(#app), + returnValue: _FakeFirebaseApp_4( + this, + Invocation.getter(#app), + ), + ) as _i5.FirebaseApp); + + @override + set app(_i5.FirebaseApp? _app) => super.noSuchMethod( + Invocation.setter( + #app, + _app, + ), + returnValueForMissingStub: null, + ); + + @override + _i9.Stream get onTokenChange => (super.noSuchMethod( + Invocation.getter(#onTokenChange), + returnValue: _i9.Stream.empty(), + ) as _i9.Stream); + + @override + Map get pluginConstants => (super.noSuchMethod( + Invocation.getter(#pluginConstants), + returnValue: {}, + ) as Map); + + @override + _i9.Future activate({ + _i13.WebProvider? webProvider, + _i13.AndroidProvider? androidProvider = _i13.AndroidProvider.playIntegrity, + _i13.AppleProvider? appleProvider = _i13.AppleProvider.deviceCheck, + }) => + (super.noSuchMethod( + Invocation.method( + #activate, + [], + { + #webProvider: webProvider, + #androidProvider: androidProvider, + #appleProvider: appleProvider, + }, + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future getToken([bool? forceRefresh]) => (super.noSuchMethod( + Invocation.method( + #getToken, + [forceRefresh], + ), + returnValue: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future setTokenAutoRefreshEnabled( + bool? isTokenAutoRefreshEnabled) => + (super.noSuchMethod( + Invocation.method( + #setTokenAutoRefreshEnabled, + [isTokenAutoRefreshEnabled], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future getLimitedUseToken() => (super.noSuchMethod( + Invocation.method( + #getLimitedUseToken, + [], + ), + returnValue: _i9.Future.value(_i14.dummyValue( + this, + Invocation.method( + #getLimitedUseToken, + [], + ), + )), + ) as _i9.Future); +} + +/// A class which mocks [User]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockUser extends _i1.Mock implements _i7.User { + MockUser() { + _i1.throwOnMissingStub(this); + } + + @override + bool get emailVerified => (super.noSuchMethod( + Invocation.getter(#emailVerified), + returnValue: false, + ) as bool); + + @override + bool get isAnonymous => (super.noSuchMethod( + Invocation.getter(#isAnonymous), + returnValue: false, + ) as bool); + + @override + _i6.UserMetadata get metadata => (super.noSuchMethod( + Invocation.getter(#metadata), + returnValue: _FakeUserMetadata_5( + this, + Invocation.getter(#metadata), + ), + ) as _i6.UserMetadata); + + @override + List<_i6.UserInfo> get providerData => (super.noSuchMethod( + Invocation.getter(#providerData), + returnValue: <_i6.UserInfo>[], + ) as List<_i6.UserInfo>); + + @override + String get uid => (super.noSuchMethod( + Invocation.getter(#uid), + returnValue: _i14.dummyValue( + this, + Invocation.getter(#uid), + ), + ) as String); + + @override + _i7.MultiFactor get multiFactor => (super.noSuchMethod( + Invocation.getter(#multiFactor), + returnValue: _FakeMultiFactor_6( + this, + Invocation.getter(#multiFactor), + ), + ) as _i7.MultiFactor); + + @override + _i9.Future delete() => (super.noSuchMethod( + Invocation.method( + #delete, + [], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future getIdToken([bool? forceRefresh = false]) => + (super.noSuchMethod( + Invocation.method( + #getIdToken, + [forceRefresh], + ), + returnValue: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future<_i6.IdTokenResult> getIdTokenResult( + [bool? forceRefresh = false]) => + (super.noSuchMethod( + Invocation.method( + #getIdTokenResult, + [forceRefresh], + ), + returnValue: _i9.Future<_i6.IdTokenResult>.value(_FakeIdTokenResult_7( + this, + Invocation.method( + #getIdTokenResult, + [forceRefresh], + ), + )), + ) as _i9.Future<_i6.IdTokenResult>); + + @override + _i9.Future<_i7.UserCredential> linkWithCredential( + _i6.AuthCredential? credential) => + (super.noSuchMethod( + Invocation.method( + #linkWithCredential, + [credential], + ), + returnValue: _i9.Future<_i7.UserCredential>.value(_FakeUserCredential_8( + this, + Invocation.method( + #linkWithCredential, + [credential], + ), + )), + ) as _i9.Future<_i7.UserCredential>); + + @override + _i9.Future<_i7.UserCredential> linkWithProvider(_i6.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #linkWithProvider, + [provider], + ), + returnValue: _i9.Future<_i7.UserCredential>.value(_FakeUserCredential_8( + this, + Invocation.method( + #linkWithProvider, + [provider], + ), + )), + ) as _i9.Future<_i7.UserCredential>); + + @override + _i9.Future<_i7.UserCredential> reauthenticateWithProvider( + _i6.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #reauthenticateWithProvider, + [provider], + ), + returnValue: _i9.Future<_i7.UserCredential>.value(_FakeUserCredential_8( + this, + Invocation.method( + #reauthenticateWithProvider, + [provider], + ), + )), + ) as _i9.Future<_i7.UserCredential>); + + @override + _i9.Future<_i7.UserCredential> reauthenticateWithPopup( + _i6.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #reauthenticateWithPopup, + [provider], + ), + returnValue: _i9.Future<_i7.UserCredential>.value(_FakeUserCredential_8( + this, + Invocation.method( + #reauthenticateWithPopup, + [provider], + ), + )), + ) as _i9.Future<_i7.UserCredential>); + + @override + _i9.Future reauthenticateWithRedirect(_i6.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #reauthenticateWithRedirect, + [provider], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future<_i7.UserCredential> linkWithPopup(_i6.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #linkWithPopup, + [provider], + ), + returnValue: _i9.Future<_i7.UserCredential>.value(_FakeUserCredential_8( + this, + Invocation.method( + #linkWithPopup, + [provider], + ), + )), + ) as _i9.Future<_i7.UserCredential>); + + @override + _i9.Future linkWithRedirect(_i6.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #linkWithRedirect, + [provider], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future<_i7.ConfirmationResult> linkWithPhoneNumber( + String? phoneNumber, [ + _i7.RecaptchaVerifier? verifier, + ]) => + (super.noSuchMethod( + Invocation.method( + #linkWithPhoneNumber, + [ + phoneNumber, + verifier, + ], + ), + returnValue: + _i9.Future<_i7.ConfirmationResult>.value(_FakeConfirmationResult_9( + this, + Invocation.method( + #linkWithPhoneNumber, + [ + phoneNumber, + verifier, + ], + ), + )), + ) as _i9.Future<_i7.ConfirmationResult>); + + @override + _i9.Future<_i7.UserCredential> reauthenticateWithCredential( + _i6.AuthCredential? credential) => + (super.noSuchMethod( + Invocation.method( + #reauthenticateWithCredential, + [credential], + ), + returnValue: _i9.Future<_i7.UserCredential>.value(_FakeUserCredential_8( + this, + Invocation.method( + #reauthenticateWithCredential, + [credential], + ), + )), + ) as _i9.Future<_i7.UserCredential>); + + @override + _i9.Future reload() => (super.noSuchMethod( + Invocation.method( + #reload, + [], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future sendEmailVerification( + [_i6.ActionCodeSettings? actionCodeSettings]) => + (super.noSuchMethod( + Invocation.method( + #sendEmailVerification, + [actionCodeSettings], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future<_i7.User> unlink(String? providerId) => (super.noSuchMethod( + Invocation.method( + #unlink, + [providerId], + ), + returnValue: _i9.Future<_i7.User>.value(_FakeUser_10( + this, + Invocation.method( + #unlink, + [providerId], + ), + )), + ) as _i9.Future<_i7.User>); + + @override + _i9.Future updateEmail(String? newEmail) => (super.noSuchMethod( + Invocation.method( + #updateEmail, + [newEmail], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future updatePassword(String? newPassword) => (super.noSuchMethod( + Invocation.method( + #updatePassword, + [newPassword], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future updatePhoneNumber( + _i6.PhoneAuthCredential? phoneCredential) => + (super.noSuchMethod( + Invocation.method( + #updatePhoneNumber, + [phoneCredential], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future updateDisplayName(String? displayName) => + (super.noSuchMethod( + Invocation.method( + #updateDisplayName, + [displayName], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future updatePhotoURL(String? photoURL) => (super.noSuchMethod( + Invocation.method( + #updatePhotoURL, + [photoURL], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future updateProfile({ + String? displayName, + String? photoURL, + }) => + (super.noSuchMethod( + Invocation.method( + #updateProfile, + [], + { + #displayName: displayName, + #photoURL: photoURL, + }, + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + + @override + _i9.Future verifyBeforeUpdateEmail( + String? newEmail, [ + _i6.ActionCodeSettings? actionCodeSettings, + ]) => + (super.noSuchMethod( + Invocation.method( + #verifyBeforeUpdateEmail, + [ + newEmail, + actionCodeSettings, + ], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); +} + +/// A class which mocks [ConnectorServiceClient]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockConnectorServiceClient extends _i1.Mock + implements _i15.ConnectorServiceClient { + MockConnectorServiceClient() { + _i1.throwOnMissingStub(this); + } + + @override + _i8.ResponseFuture<_i16.ExecuteQueryResponse> executeQuery( + _i16.ExecuteQueryRequest? request, { + _i4.CallOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #executeQuery, + [request], + {#options: options}, + ), + returnValue: _FakeResponseFuture_11<_i16.ExecuteQueryResponse>( + this, + Invocation.method( + #executeQuery, + [request], + {#options: options}, + ), + ), + ) as _i8.ResponseFuture<_i16.ExecuteQueryResponse>); + + @override + _i8.ResponseFuture<_i16.ExecuteMutationResponse> executeMutation( + _i16.ExecuteMutationRequest? request, { + _i4.CallOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #executeMutation, + [request], + {#options: options}, + ), + returnValue: _FakeResponseFuture_11<_i16.ExecuteMutationResponse>( + this, + Invocation.method( + #executeMutation, + [request], + {#options: options}, + ), + ), + ) as _i8.ResponseFuture<_i16.ExecuteMutationResponse>); + + @override + _i4.ClientCall $createCall( + _i11.ClientMethod? method, + _i9.Stream? requests, { + _i4.CallOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #$createCall, + [ + method, + requests, + ], + {#options: options}, + ), + returnValue: _FakeClientCall_3( + this, + Invocation.method( + #$createCall, + [ + method, + requests, + ], + {#options: options}, + ), + ), + ) as _i4.ClientCall); + + @override + _i8.ResponseFuture $createUnaryCall( + _i11.ClientMethod? method, + Q? request, { + _i4.CallOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #$createUnaryCall, + [ + method, + request, + ], + {#options: options}, + ), + returnValue: _FakeResponseFuture_11( + this, + Invocation.method( + #$createUnaryCall, + [ + method, + request, + ], + {#options: options}, + ), + ), + ) as _i8.ResponseFuture); + + @override + _i8.ResponseStream $createStreamingCall( + _i11.ClientMethod? method, + _i9.Stream? requests, { + _i4.CallOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #$createStreamingCall, + [ + method, + requests, + ], + {#options: options}, + ), + returnValue: _FakeResponseStream_12( + this, + Invocation.method( + #$createStreamingCall, + [ + method, + requests, + ], + {#options: options}, + ), + ), + ) as _i8.ResponseStream); +} + +/// A class which mocks [ResponseFuture]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockResponseFuture extends _i1.Mock implements _i8.ResponseFuture { + MockResponseFuture() { + _i1.throwOnMissingStub(this); + } + + @override + _i9.Future> get headers => (super.noSuchMethod( + Invocation.getter(#headers), + returnValue: _i9.Future>.value({}), + ) as _i9.Future>); + + @override + _i9.Future> get trailers => (super.noSuchMethod( + Invocation.getter(#trailers), + returnValue: _i9.Future>.value({}), + ) as _i9.Future>); + + @override + _i9.Stream asStream() => (super.noSuchMethod( + Invocation.method( + #asStream, + [], + ), + returnValue: _i9.Stream.empty(), + ) as _i9.Stream); + + @override + _i9.Future catchError( + Function? onError, { + bool Function(Object)? test, + }) => + (super.noSuchMethod( + Invocation.method( + #catchError, + [onError], + {#test: test}, + ), + returnValue: _i14.ifNotNull( + _i14.dummyValueOrNull( + this, + Invocation.method( + #catchError, + [onError], + {#test: test}, + ), + ), + (R v) => _i9.Future.value(v), + ) ?? + _FakeFuture_13( + this, + Invocation.method( + #catchError, + [onError], + {#test: test}, + ), + ), + ) as _i9.Future); + + @override + _i9.Future then( + _i9.FutureOr Function(R)? onValue, { + Function? onError, + }) => + (super.noSuchMethod( + Invocation.method( + #then, + [onValue], + {#onError: onError}, + ), + returnValue: _i14.ifNotNull( + _i14.dummyValueOrNull( + this, + Invocation.method( + #then, + [onValue], + {#onError: onError}, + ), + ), + (S v) => _i9.Future.value(v), + ) ?? + _FakeFuture_13( + this, + Invocation.method( + #then, + [onValue], + {#onError: onError}, + ), + ), + ) as _i9.Future); + + @override + _i9.Future whenComplete(_i9.FutureOr Function()? action) => + (super.noSuchMethod( + Invocation.method( + #whenComplete, + [action], + ), + returnValue: _i14.ifNotNull( + _i14.dummyValueOrNull( + this, + Invocation.method( + #whenComplete, + [action], + ), + ), + (R v) => _i9.Future.value(v), + ) ?? + _FakeFuture_13( + this, + Invocation.method( + #whenComplete, + [action], + ), + ), + ) as _i9.Future); + + @override + _i9.Future timeout( + Duration? timeLimit, { + _i9.FutureOr Function()? onTimeout, + }) => + (super.noSuchMethod( + Invocation.method( + #timeout, + [timeLimit], + {#onTimeout: onTimeout}, + ), + returnValue: _i14.ifNotNull( + _i14.dummyValueOrNull( + this, + Invocation.method( + #timeout, + [timeLimit], + {#onTimeout: onTimeout}, + ), + ), + (R v) => _i9.Future.value(v), + ) ?? + _FakeFuture_13( + this, + Invocation.method( + #timeout, + [timeLimit], + {#onTimeout: onTimeout}, + ), + ), + ) as _i9.Future); + + @override + _i9.Future cancel() => (super.noSuchMethod( + Invocation.method( + #cancel, + [], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); +} diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart index 4720ac78ece7..05d0466bc530 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart @@ -173,8 +173,7 @@ void main() { )).called(1); }); - test('invokeOperation should include auth and appCheck tokens in headers', - () async { + test('invokeOperation should include auth tokens in headers', () async { final mockResponse = http.Response('{"data": {"key": "value"}}', 200); when(mockHttpClient.post(any, headers: anyNamed('headers'), body: anyNamed('body'))) @@ -198,6 +197,30 @@ void main() { )).called(1); }); + test('invokeOperation should include appcheck tokens in headers', () async { + final mockResponse = http.Response('{"data": {"key": "value"}}', 200); + when(mockHttpClient.post(any, + headers: anyNamed('headers'), body: anyNamed('body'))) + .thenAnswer((_) async => mockResponse); + + when(mockUser.getIdToken()).thenAnswer((_) async => 'authToken123'); + when(mockAppCheck.getToken()).thenAnswer((_) async => 'appCheckToken123'); + + final deserializer = (String data) => 'Deserialized Data'; + + await transport.invokeOperation( + 'testQuery', deserializer, null, null, 'executeQuery'); + + verify(mockHttpClient.post( + any, + headers: argThat( + containsPair('X-Firebase-AppCheck', 'appCheckToken123'), + named: 'headers', + ), + body: anyNamed('body'), + )).called(1); + }); + test( 'invokeOperation should handle missing auth and appCheck tokens gracefully', () async { @@ -223,6 +246,7 @@ void main() { body: anyNamed('body'), )).called(1); }); + test('invokeOperation should throw an error if the server throws one', () async { final mockResponse = http.Response(""" diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart index 02270373a449..244aad50e971 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart @@ -1,7 +1,3 @@ -// Copyright 2024, the Chromium project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - // Mocks generated by Mockito 5.4.4 from annotations // in firebase_data_connect/test/src/network/rest_transport_test.dart. // Do not manually edit this file. From 7c92a877d7a81f2d10bb7edff67d298a58941ed8 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 11:41:22 +0200 Subject: [PATCH 02/18] more --- .../test/src/core/ref_test.dart | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart index fe602f006a70..95bfbd148aa0 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart @@ -184,6 +184,36 @@ void main() { }); }, ); + + test( + 'execute should propagate error as string when server responds with error', + () async { + final mockTransport = MockDataConnectTransport(); + final queryManager = QueryManager(mockDataConnect); + + // Simulate server throwing an exception + when(mockTransport.invokeQuery(any, any, any, any)) + .thenThrow(Exception('Server Error')); + + final queryRef = QueryRef( + mockDataConnect, + 'testQuery', + mockTransport, + emptySerializer, + queryManager, + emptySerializer, + 'variables', + ); + + try { + await queryRef.execute(); + fail('Expected execute to throw an exception.'); + } catch (error) { + expect(error, isA()); + expect(error.toString(), contains('Server Error')); + } + }, + ); }); group('MutationRef', () { From f1382b60a2dee8b5c1f67cf6662882f7774a4421 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 12:02:02 +0200 Subject: [PATCH 03/18] update to accomodate for latest plugin versino --- .../.dataconnect/schema/main/implicit.gql | 34 + .../.dataconnect/schema/main/input.gql | 644 ++++++ .../.dataconnect/schema/main/mutation.gql | 507 +++++ .../.dataconnect/schema/main/query.gql | 212 ++ .../.dataconnect/schema/main/relation.gql | 100 + .../.dataconnect/schema/prelude.gql | 1865 +++++++++++++++++ .../example/dataconnect/connector/queries.gql | 30 +- .../integration_test/generation_e2e.dart | 21 +- .../example/integration_test/listen_e2e.dart | 14 +- .../example/integration_test/query_e2e.dart | 56 +- .../lib/generated/add_date_and_timestamp.dart | 24 +- .../lib/generated/add_director_to_movie.dart | 19 +- .../example/lib/generated/add_person.dart | 18 +- .../example/lib/generated/add_timestamp.dart | 21 +- .../example/lib/generated/create_movie.dart | 25 +- .../example/lib/generated/delete_movie.dart | 25 +- .../example/lib/generated/list_movies.dart | 22 +- .../lib/generated/list_movies_by_genre.dart | 155 ++ .../list_movies_by_partial_title.dart | 27 +- .../example/lib/generated/list_persons.dart | 18 +- .../example/lib/generated/list_thing.dart | 18 +- .../lib/generated/list_timestamps.dart | 22 +- .../example/lib/generated/movies.dart | 113 +- .../example/lib/generated/seed_data.dart | 18 +- .../example/lib/generated/seed_movies.dart | 18 +- .../example/lib/generated/thing.dart | 18 +- .../example/lib/main.dart | 10 +- 27 files changed, 3758 insertions(+), 296 deletions(-) create mode 100644 packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql create mode 100644 packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql create mode 100644 packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql create mode 100644 packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql create mode 100644 packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/relation.gql create mode 100644 packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/prelude.gql create mode 100644 packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_genre.dart diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql new file mode 100644 index 000000000000..f3cc340c8b71 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql @@ -0,0 +1,34 @@ +extend type DirectedBy { + """ + ✨ Implicit foreign key field based on `DirectedBy`.`movie`. It must match the value of `Movie`.`id`. See `@ref` for how to customize it. + """ + movieId: UUID! @fdc_generated(from: "DirectedBy.movie", purpose: IMPLICIT_REF_FIELD) + """ + ✨ Implicit foreign key field based on `DirectedBy`.`directedby`. It must match the value of `Person`.`id`. See `@ref` for how to customize it. + """ + directedbyId: UUID! @fdc_generated(from: "DirectedBy.directedby", purpose: IMPLICIT_REF_FIELD) +} +extend type Movie { + """ + ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. + """ + id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "Movie", purpose: IMPLICIT_KEY_FIELD) +} +extend type Person { + """ + ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. + """ + id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "Person", purpose: IMPLICIT_KEY_FIELD) +} +extend type Thing { + """ + ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. + """ + id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "Thing", purpose: IMPLICIT_KEY_FIELD) +} +extend type TimestampHolder { + """ + ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. + """ + id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "TimestampHolder", purpose: IMPLICIT_KEY_FIELD) +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql new file mode 100644 index 000000000000..cd0123266442 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql @@ -0,0 +1,644 @@ +""" +✨ `DirectedBy_KeyOutput` returns the primary key fields of table type `DirectedBy`. + +It has the same format as `DirectedBy_Key`, but is only used as mutation return value. +""" +scalar DirectedBy_KeyOutput +""" +✨ `Movie_KeyOutput` returns the primary key fields of table type `Movie`. + +It has the same format as `Movie_Key`, but is only used as mutation return value. +""" +scalar Movie_KeyOutput +""" +✨ `Person_KeyOutput` returns the primary key fields of table type `Person`. + +It has the same format as `Person_Key`, but is only used as mutation return value. +""" +scalar Person_KeyOutput +""" +✨ `Thing_KeyOutput` returns the primary key fields of table type `Thing`. + +It has the same format as `Thing_Key`, but is only used as mutation return value. +""" +scalar Thing_KeyOutput +""" +✨ `TimestampHolder_KeyOutput` returns the primary key fields of table type `TimestampHolder`. + +It has the same format as `TimestampHolder_Key`, but is only used as mutation return value. +""" +scalar TimestampHolder_KeyOutput +""" +✨ Generated data input type for table 'DirectedBy'. It includes all necessary fields for creating or upserting rows into table. +""" +input DirectedBy_Data { + """ + ✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!` + """ + movieId: UUID + """ + ✨ `_expr` server value variant of `movieId` (✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!`) + """ + movieId_expr: UUID_Expr + """ + ✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!` + """ + directedbyId: UUID + """ + ✨ `_expr` server value variant of `directedbyId` (✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!`) + """ + directedbyId_expr: UUID_Expr + """ + ✨ Generated from Field `DirectedBy`.`directedby` of type `Person!` + """ + directedby: Person_Key + """ + ✨ Generated from Field `DirectedBy`.`movie` of type `Movie!` + """ + movie: Movie_Key +} +""" +✨ Generated filter input type for table 'DirectedBy'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input DirectedBy_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [DirectedBy_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: DirectedBy_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [DirectedBy_Filter!] + """ + ✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!` + """ + movieId: UUID_Filter + """ + ✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!` + """ + directedbyId: UUID_Filter + """ + ✨ Generated from Field `DirectedBy`.`directedby` of type `Person!` + """ + directedby: Person_Filter + """ + ✨ Generated from Field `DirectedBy`.`movie` of type `Movie!` + """ + movie: Movie_Filter +} +""" +✨ Generated first-row input type for table 'DirectedBy'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input DirectedBy_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [DirectedBy_Order!] + """ + Filters rows based on the specified conditions. + """ + where: DirectedBy_Filter +} +""" +✨ Generated key input type for table 'DirectedBy'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input DirectedBy_Key { + """ + ✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!` + """ + movieId: UUID + """ + ✨ `_expr` server value variant of `movieId` (✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!`) + """ + movieId_expr: UUID_Expr + """ + ✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!` + """ + directedbyId: UUID + """ + ✨ `_expr` server value variant of `directedbyId` (✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!`) + """ + directedbyId_expr: UUID_Expr +} +""" +✨ Generated list filter input type for table 'DirectedBy'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input DirectedBy_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: DirectedBy_Filter +} +""" +✨ Generated order input type for table 'DirectedBy'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input DirectedBy_Order { + """ + ✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!` + """ + movieId: OrderDirection + """ + ✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!` + """ + directedbyId: OrderDirection + """ + ✨ Generated from Field `DirectedBy`.`directedby` of type `Person!` + """ + directedby: Person_Order + """ + ✨ Generated from Field `DirectedBy`.`movie` of type `Movie!` + """ + movie: Movie_Order +} +""" +✨ Generated data input type for table 'Movie'. It includes all necessary fields for creating or upserting rows into table. +""" +input Movie_Data { + """ + ✨ Generated from Field `Movie`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Movie`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr + """ + ✨ Generated from Field `Movie`.`description` of type `String` + """ + description: String + """ + ✨ `_expr` server value variant of `description` (✨ Generated from Field `Movie`.`description` of type `String`) + """ + description_expr: String_Expr + """ + ✨ Generated from Field `Movie`.`genre` of type `String!` + """ + genre: String + """ + ✨ `_expr` server value variant of `genre` (✨ Generated from Field `Movie`.`genre` of type `String!`) + """ + genre_expr: String_Expr + """ + ✨ Generated from Field `Movie`.`rating` of type `Float` + """ + rating: Float + """ + ✨ Generated from Field `Movie`.`releaseYear` of type `Int` + """ + releaseYear: Int + """ + ✨ Generated from Field `Movie`.`title` of type `String!` + """ + title: String + """ + ✨ `_expr` server value variant of `title` (✨ Generated from Field `Movie`.`title` of type `String!`) + """ + title_expr: String_Expr +} +""" +✨ Generated filter input type for table 'Movie'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Movie_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [Movie_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: Movie_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [Movie_Filter!] + """ + ✨ Generated from Field `Movie`.`id` of type `UUID!` + """ + id: UUID_Filter + """ + ✨ Generated from Field `Movie`.`description` of type `String` + """ + description: String_Filter + """ + ✨ Generated from Field `Movie`.`genre` of type `String!` + """ + genre: String_Filter + """ + ✨ Generated from Field `Movie`.`rating` of type `Float` + """ + rating: Float_Filter + """ + ✨ Generated from Field `Movie`.`releaseYear` of type `Int` + """ + releaseYear: Int_Filter + """ + ✨ Generated from Field `Movie`.`title` of type `String!` + """ + title: String_Filter + """ + ✨ Generated from Field `Movie`.`directedBies_on_movie` of type `[DirectedBy!]!` + """ + directedBies_on_movie: DirectedBy_ListFilter + """ + ✨ Generated from Field `Movie`.`people_via_DirectedBy` of type `[Person!]!` + """ + people_via_DirectedBy: Person_ListFilter +} +""" +✨ Generated first-row input type for table 'Movie'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input Movie_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [Movie_Order!] + """ + Filters rows based on the specified conditions. + """ + where: Movie_Filter +} +""" +✨ Generated key input type for table 'Movie'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input Movie_Key { + """ + ✨ Generated from Field `Movie`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Movie`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr +} +""" +✨ Generated list filter input type for table 'Movie'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input Movie_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: Movie_Filter +} +""" +✨ Generated order input type for table 'Movie'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input Movie_Order { + """ + ✨ Generated from Field `Movie`.`id` of type `UUID!` + """ + id: OrderDirection + """ + ✨ Generated from Field `Movie`.`description` of type `String` + """ + description: OrderDirection + """ + ✨ Generated from Field `Movie`.`genre` of type `String!` + """ + genre: OrderDirection + """ + ✨ Generated from Field `Movie`.`rating` of type `Float` + """ + rating: OrderDirection + """ + ✨ Generated from Field `Movie`.`releaseYear` of type `Int` + """ + releaseYear: OrderDirection + """ + ✨ Generated from Field `Movie`.`title` of type `String!` + """ + title: OrderDirection +} +""" +✨ Generated data input type for table 'Person'. It includes all necessary fields for creating or upserting rows into table. +""" +input Person_Data { + """ + ✨ Generated from Field `Person`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Person`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr + """ + ✨ Generated from Field `Person`.`name` of type `String!` + """ + name: String + """ + ✨ `_expr` server value variant of `name` (✨ Generated from Field `Person`.`name` of type `String!`) + """ + name_expr: String_Expr +} +""" +✨ Generated filter input type for table 'Person'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Person_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [Person_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: Person_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [Person_Filter!] + """ + ✨ Generated from Field `Person`.`id` of type `UUID!` + """ + id: UUID_Filter + """ + ✨ Generated from Field `Person`.`name` of type `String!` + """ + name: String_Filter + """ + ✨ Generated from Field `Person`.`directedBies_on_directedby` of type `[DirectedBy!]!` + """ + directedBies_on_directedby: DirectedBy_ListFilter + """ + ✨ Generated from Field `Person`.`movies_via_DirectedBy` of type `[Movie!]!` + """ + movies_via_DirectedBy: Movie_ListFilter +} +""" +✨ Generated first-row input type for table 'Person'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input Person_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [Person_Order!] + """ + Filters rows based on the specified conditions. + """ + where: Person_Filter +} +""" +✨ Generated key input type for table 'Person'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input Person_Key { + """ + ✨ Generated from Field `Person`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Person`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr +} +""" +✨ Generated list filter input type for table 'Person'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input Person_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: Person_Filter +} +""" +✨ Generated order input type for table 'Person'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input Person_Order { + """ + ✨ Generated from Field `Person`.`id` of type `UUID!` + """ + id: OrderDirection + """ + ✨ Generated from Field `Person`.`name` of type `String!` + """ + name: OrderDirection +} +""" +✨ Generated data input type for table 'Thing'. It includes all necessary fields for creating or upserting rows into table. +""" +input Thing_Data { + """ + ✨ Generated from Field `Thing`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Thing`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr + """ + ✨ Generated from Field `Thing`.`title` of type `Any!` + """ + title: Any +} +""" +✨ Generated filter input type for table 'Thing'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Thing_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [Thing_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: Thing_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [Thing_Filter!] + """ + ✨ Generated from Field `Thing`.`id` of type `UUID!` + """ + id: UUID_Filter + """ + ✨ Generated from Field `Thing`.`title` of type `Any!` + """ + title: Any_Filter +} +""" +✨ Generated first-row input type for table 'Thing'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input Thing_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [Thing_Order!] + """ + Filters rows based on the specified conditions. + """ + where: Thing_Filter +} +""" +✨ Generated key input type for table 'Thing'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input Thing_Key { + """ + ✨ Generated from Field `Thing`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Thing`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr +} +""" +✨ Generated list filter input type for table 'Thing'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input Thing_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: Thing_Filter +} +""" +✨ Generated order input type for table 'Thing'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input Thing_Order { + """ + ✨ Generated from Field `Thing`.`id` of type `UUID!` + """ + id: OrderDirection + """ + ✨ Generated from Field `Thing`.`title` of type `Any!` + """ + title: OrderDirection +} +""" +✨ Generated data input type for table 'TimestampHolder'. It includes all necessary fields for creating or upserting rows into table. +""" +input TimestampHolder_Data { + """ + ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `TimestampHolder`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr + """ + ✨ Generated from Field `TimestampHolder`.`date` of type `Date` + """ + date: Date + """ + ✨ `_date` server value variant of `date` (✨ Generated from Field `TimestampHolder`.`date` of type `Date`) + """ + date_date: Date_Relative + """ + ✨ `_expr` server value variant of `date` (✨ Generated from Field `TimestampHolder`.`date` of type `Date`) + """ + date_expr: Date_Expr + """ + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` + """ + timestamp: Timestamp + """ + ✨ `_expr` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) + """ + timestamp_expr: Timestamp_Expr + """ + ✨ `_time` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) + """ + timestamp_time: Timestamp_Relative +} +""" +✨ Generated filter input type for table 'TimestampHolder'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input TimestampHolder_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [TimestampHolder_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: TimestampHolder_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [TimestampHolder_Filter!] + """ + ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + """ + id: UUID_Filter + """ + ✨ Generated from Field `TimestampHolder`.`date` of type `Date` + """ + date: Date_Filter + """ + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` + """ + timestamp: Timestamp_Filter +} +""" +✨ Generated first-row input type for table 'TimestampHolder'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input TimestampHolder_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [TimestampHolder_Order!] + """ + Filters rows based on the specified conditions. + """ + where: TimestampHolder_Filter +} +""" +✨ Generated key input type for table 'TimestampHolder'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input TimestampHolder_Key { + """ + ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `TimestampHolder`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr +} +""" +✨ Generated list filter input type for table 'TimestampHolder'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input TimestampHolder_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: TimestampHolder_Filter +} +""" +✨ Generated order input type for table 'TimestampHolder'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input TimestampHolder_Order { + """ + ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + """ + id: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`date` of type `Date` + """ + date: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` + """ + timestamp: OrderDirection +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql new file mode 100644 index 000000000000..4628c9b8401c --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql @@ -0,0 +1,507 @@ +extend type Mutation { + """ + ✨ Insert a single `DirectedBy` into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + directedBy_insert( + """ + Data object to insert into the table. + """ + data: DirectedBy_Data! + ): DirectedBy_KeyOutput! @fdc_generated(from: "DirectedBy", purpose: INSERT_SINGLE) + """ + ✨ Insert a single `Movie` into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + movie_insert( + """ + Data object to insert into the table. + """ + data: Movie_Data! + ): Movie_KeyOutput! @fdc_generated(from: "Movie", purpose: INSERT_SINGLE) + """ + ✨ Insert a single `Person` into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + person_insert( + """ + Data object to insert into the table. + """ + data: Person_Data! + ): Person_KeyOutput! @fdc_generated(from: "Person", purpose: INSERT_SINGLE) + """ + ✨ Insert a single `Thing` into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + thing_insert( + """ + Data object to insert into the table. + """ + data: Thing_Data! + ): Thing_KeyOutput! @fdc_generated(from: "Thing", purpose: INSERT_SINGLE) + """ + ✨ Insert a single `TimestampHolder` into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + timestampHolder_insert( + """ + Data object to insert into the table. + """ + data: TimestampHolder_Data! + ): TimestampHolder_KeyOutput! @fdc_generated(from: "TimestampHolder", purpose: INSERT_SINGLE) + """ + ✨ Insert `DirectedBy` objects into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + directedBy_insertMany( + """ + List of data objects to insert into the table. + """ + data: [DirectedBy_Data!]! + ): [DirectedBy_KeyOutput!]! @fdc_generated(from: "DirectedBy", purpose: INSERT_MULTIPLE) + """ + ✨ Insert `Movie` objects into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + movie_insertMany( + """ + List of data objects to insert into the table. + """ + data: [Movie_Data!]! + ): [Movie_KeyOutput!]! @fdc_generated(from: "Movie", purpose: INSERT_MULTIPLE) + """ + ✨ Insert `Person` objects into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + person_insertMany( + """ + List of data objects to insert into the table. + """ + data: [Person_Data!]! + ): [Person_KeyOutput!]! @fdc_generated(from: "Person", purpose: INSERT_MULTIPLE) + """ + ✨ Insert `Thing` objects into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + thing_insertMany( + """ + List of data objects to insert into the table. + """ + data: [Thing_Data!]! + ): [Thing_KeyOutput!]! @fdc_generated(from: "Thing", purpose: INSERT_MULTIPLE) + """ + ✨ Insert `TimestampHolder` objects into the table. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + timestampHolder_insertMany( + """ + List of data objects to insert into the table. + """ + data: [TimestampHolder_Data!]! + ): [TimestampHolder_KeyOutput!]! @fdc_generated(from: "TimestampHolder", purpose: INSERT_MULTIPLE) + """ + ✨ Insert or update a single `DirectedBy` into the table, based on the primary key. Returns the key of the newly inserted `DirectedBy`. + """ + directedBy_upsert( + """ + Data object to insert or update if it already exists. + """ + data: DirectedBy_Data! + ): DirectedBy_KeyOutput! @fdc_generated(from: "DirectedBy", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update a single `Movie` into the table, based on the primary key. Returns the key of the newly inserted `Movie`. + """ + movie_upsert( + """ + Data object to insert or update if it already exists. + """ + data: Movie_Data! + ): Movie_KeyOutput! @fdc_generated(from: "Movie", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update a single `Person` into the table, based on the primary key. Returns the key of the newly inserted `Person`. + """ + person_upsert( + """ + Data object to insert or update if it already exists. + """ + data: Person_Data! + ): Person_KeyOutput! @fdc_generated(from: "Person", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update a single `Thing` into the table, based on the primary key. Returns the key of the newly inserted `Thing`. + """ + thing_upsert( + """ + Data object to insert or update if it already exists. + """ + data: Thing_Data! + ): Thing_KeyOutput! @fdc_generated(from: "Thing", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update a single `TimestampHolder` into the table, based on the primary key. Returns the key of the newly inserted `TimestampHolder`. + """ + timestampHolder_upsert( + """ + Data object to insert or update if it already exists. + """ + data: TimestampHolder_Data! + ): TimestampHolder_KeyOutput! @fdc_generated(from: "TimestampHolder", purpose: UPSERT_SINGLE) + """ + ✨ Update a single `DirectedBy` based on `id`, `key` or `first`, setting columns specified in `data`. Returns `null` if not found. + """ + directedBy_update( + """ + The key used to identify the object. + """ + key: DirectedBy_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: DirectedBy_FirstRow + + """ + Data object containing fields to be updated. + """ + data: DirectedBy_Data! + ): DirectedBy_KeyOutput @fdc_generated(from: "DirectedBy", purpose: UPDATE_SINGLE) + """ + ✨ Update a single `Movie` based on `id`, `key` or `first`, setting columns specified in `data`. Returns `null` if not found. + """ + movie_update( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Movie_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Movie_FirstRow + + """ + Data object containing fields to be updated. + """ + data: Movie_Data! + ): Movie_KeyOutput @fdc_generated(from: "Movie", purpose: UPDATE_SINGLE) + """ + ✨ Update a single `Person` based on `id`, `key` or `first`, setting columns specified in `data`. Returns `null` if not found. + """ + person_update( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Person_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Person_FirstRow + + """ + Data object containing fields to be updated. + """ + data: Person_Data! + ): Person_KeyOutput @fdc_generated(from: "Person", purpose: UPDATE_SINGLE) + """ + ✨ Update a single `Thing` based on `id`, `key` or `first`, setting columns specified in `data`. Returns `null` if not found. + """ + thing_update( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Thing_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Thing_FirstRow + + """ + Data object containing fields to be updated. + """ + data: Thing_Data! + ): Thing_KeyOutput @fdc_generated(from: "Thing", purpose: UPDATE_SINGLE) + """ + ✨ Update a single `TimestampHolder` based on `id`, `key` or `first`, setting columns specified in `data`. Returns `null` if not found. + """ + timestampHolder_update( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: TimestampHolder_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: TimestampHolder_FirstRow + + """ + Data object containing fields to be updated. + """ + data: TimestampHolder_Data! + ): TimestampHolder_KeyOutput @fdc_generated(from: "TimestampHolder", purpose: UPDATE_SINGLE) + """ + ✨ Update `DirectedBy` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + directedBy_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: DirectedBy_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: DirectedBy_Data! + ): Int! @fdc_generated(from: "DirectedBy", purpose: UPDATE_MULTIPLE) + """ + ✨ Update `Movie` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + movie_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: Movie_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: Movie_Data! + ): Int! @fdc_generated(from: "Movie", purpose: UPDATE_MULTIPLE) + """ + ✨ Update `Person` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + person_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: Person_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: Person_Data! + ): Int! @fdc_generated(from: "Person", purpose: UPDATE_MULTIPLE) + """ + ✨ Update `Thing` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + thing_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: Thing_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: Thing_Data! + ): Int! @fdc_generated(from: "Thing", purpose: UPDATE_MULTIPLE) + """ + ✨ Update `TimestampHolder` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + timestampHolder_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: TimestampHolder_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: TimestampHolder_Data! + ): Int! @fdc_generated(from: "TimestampHolder", purpose: UPDATE_MULTIPLE) + """ + ✨ Delete a single `DirectedBy` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + directedBy_delete( + """ + The key used to identify the object. + """ + key: DirectedBy_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: DirectedBy_FirstRow + ): DirectedBy_KeyOutput @fdc_generated(from: "DirectedBy", purpose: DELETE_SINGLE) + """ + ✨ Delete a single `Movie` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + movie_delete( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Movie_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Movie_FirstRow + ): Movie_KeyOutput @fdc_generated(from: "Movie", purpose: DELETE_SINGLE) + """ + ✨ Delete a single `Person` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + person_delete( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Person_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Person_FirstRow + ): Person_KeyOutput @fdc_generated(from: "Person", purpose: DELETE_SINGLE) + """ + ✨ Delete a single `Thing` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + thing_delete( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Thing_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Thing_FirstRow + ): Thing_KeyOutput @fdc_generated(from: "Thing", purpose: DELETE_SINGLE) + """ + ✨ Delete a single `TimestampHolder` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + timestampHolder_delete( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: TimestampHolder_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: TimestampHolder_FirstRow + ): TimestampHolder_KeyOutput @fdc_generated(from: "TimestampHolder", purpose: DELETE_SINGLE) + """ + ✨ Delete `DirectedBy` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + directedBy_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: DirectedBy_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "DirectedBy", purpose: DELETE_MULTIPLE) + """ + ✨ Delete `Movie` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + movie_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: Movie_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "Movie", purpose: DELETE_MULTIPLE) + """ + ✨ Delete `Person` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + person_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: Person_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "Person", purpose: DELETE_MULTIPLE) + """ + ✨ Delete `Thing` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + thing_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: Thing_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "Thing", purpose: DELETE_MULTIPLE) + """ + ✨ Delete `TimestampHolder` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + timestampHolder_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: TimestampHolder_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "TimestampHolder", purpose: DELETE_MULTIPLE) +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql new file mode 100644 index 000000000000..1adc73e26313 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql @@ -0,0 +1,212 @@ +extend type Query { + """ + ✨ Look up a single `DirectedBy` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + directedBy( + """ + The key used to identify the object. + """ + key: DirectedBy_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: DirectedBy_FirstRow + ): DirectedBy @fdc_generated(from: "DirectedBy", purpose: QUERY_SINGLE) + """ + ✨ Look up a single `Movie` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + movie( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Movie_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Movie_FirstRow + ): Movie @fdc_generated(from: "Movie", purpose: QUERY_SINGLE) + """ + ✨ Look up a single `Person` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + person( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Person_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Person_FirstRow + ): Person @fdc_generated(from: "Person", purpose: QUERY_SINGLE) + """ + ✨ Look up a single `Thing` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + thing( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Thing_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Thing_FirstRow + ): Thing @fdc_generated(from: "Thing", purpose: QUERY_SINGLE) + """ + ✨ Look up a single `TimestampHolder` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + timestampHolder( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: TimestampHolder_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: TimestampHolder_FirstRow + ): TimestampHolder @fdc_generated(from: "TimestampHolder", purpose: QUERY_SINGLE) + """ + ✨ List `DirectedBy` objects in the table, optionally filtered by `where` conditions. + """ + directedBies( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [DirectedBy!]! @fdc_generated(from: "DirectedBy", purpose: QUERY_MULTIPLE) + """ + ✨ List `Movie` objects in the table, optionally filtered by `where` conditions. + """ + movies( + """ + Filter condition to narrow down the query results. + """ + where: Movie_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [Movie_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [Movie!]! @fdc_generated(from: "Movie", purpose: QUERY_MULTIPLE) + """ + ✨ List `Person` objects in the table, optionally filtered by `where` conditions. + """ + people( + """ + Filter condition to narrow down the query results. + """ + where: Person_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [Person_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [Person!]! @fdc_generated(from: "Person", purpose: QUERY_MULTIPLE) + """ + ✨ List `Thing` objects in the table, optionally filtered by `where` conditions. + """ + things( + """ + Filter condition to narrow down the query results. + """ + where: Thing_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [Thing_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [Thing!]! @fdc_generated(from: "Thing", purpose: QUERY_MULTIPLE) + """ + ✨ List `TimestampHolder` objects in the table, optionally filtered by `where` conditions. + """ + timestampHolders( + """ + Filter condition to narrow down the query results. + """ + where: TimestampHolder_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [TimestampHolder_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [TimestampHolder!]! @fdc_generated(from: "TimestampHolder", purpose: QUERY_MULTIPLE) +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/relation.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/relation.gql new file mode 100644 index 000000000000..7e46ed3716e0 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/relation.gql @@ -0,0 +1,100 @@ +extend type Movie { + """ + ✨ List `DirectedBy` objects in a one-to-many relationship (where `DirectedBy`.`movie` is this object). + """ + directedBies_on_movie( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [DirectedBy!]! @fdc_generated(from: "DirectedBy.movie", purpose: QUERY_MULTIPLE_ONE_TO_MANY) + """ + ✨ List `Person` objects using `DirectedBy` as the join table (a `DirectedBy` object exists where its `movie` is this and its `directedby` is that). + """ + people_via_DirectedBy( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [Person!]! @fdc_generated(from: "DirectedBy", purpose: QUERY_MULTIPLE_MANY_TO_MANY) +} +extend type Person { + """ + ✨ List `DirectedBy` objects in a one-to-many relationship (where `DirectedBy`.`directedby` is this object). + """ + directedBies_on_directedby( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [DirectedBy!]! @fdc_generated(from: "DirectedBy.directedby", purpose: QUERY_MULTIPLE_ONE_TO_MANY) + """ + ✨ List `Movie` objects using `DirectedBy` as the join table (a `DirectedBy` object exists where its `directedby` is this and its `movie` is that). + """ + movies_via_DirectedBy( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + ): [Movie!]! @fdc_generated(from: "DirectedBy", purpose: QUERY_MULTIPLE_MANY_TO_MANY) +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/prelude.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/prelude.gql new file mode 100644 index 000000000000..d6867cc45ed4 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/prelude.gql @@ -0,0 +1,1865 @@ +"AccessLevel specifies coarse access policies for common situations." +enum AccessLevel { + """ + This operation is accessible to anyone, with or without authentication. + Equivalent to: @auth(expr: "true") + """ + PUBLIC + + """ + This operation can be executed only with a valid Firebase Auth ID token. + **Note:** This access level allows anonymous and unverified accounts, + which may present security and abuse risks. + Equivalent to: `@auth(expr: "auth.uid != nil")` + """ + USER_ANON + + """ + This operation is restricted to non-anonymous Firebase Auth accounts. + Equivalent to: `@auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")` + """ + USER + + """ + This operation is restricted to Firebase Auth accounts with verified email addresses. + Equivalent to: `@auth(expr: "auth.uid != nil && auth.token.email_verified")` + """ + USER_EMAIL_VERIFIED + + """ + This operation cannot be executed by anyone. The operation can only be performed + by using the Admin SDK from a privileged environment. + Equivalent to: `@auth(expr: "false")` + """ + NO_ACCESS +} + +""" +The `@auth` directive defines the authentication policy for a query or mutation. + +It must be added to any operation that you wish to be accessible from a client +application. If not specified, the operation defaults to `@auth(level: NO_ACCESS)`. + +Refer to [Data Connect Auth Guide](https://firebase.google.com/docs/data-connect/authorization-and-security) for the best practices. +""" +directive @auth( + """ + The minimal level of access required to perform this operation. + Exactly one of `level` and `expr` should be specified. + """ + level: AccessLevel @fdc_oneOf(required: true) + """ + A CEL expression that grants access to this operation if the expression + evaluates to `true`. + Exactly one of `level` and `expr` should be specified. + """ + expr: Boolean_Expr @fdc_oneOf(required: true) +) on QUERY | MUTATION + + +""" +Require that this mutation always run in a DB transaction. + +Mutations with `@transaction` are guaranteed to either fully succeed or fully +fail. If any of the fields within the transaction fails, the entire transaction +is rolled back. From a client standpoint, any failure behaves as if the entire +request had failed with a request error and execution had not begun. + +Mutations without `@transaction` would execute each root field one after +another in sequence. It surfaces any errors as partial [field errors](https://spec.graphql.org/October2021/#sec-Errors.Field-errors), +but not impacts the subsequent executions. + +The `@transaction` directive cannot be added to queries for now. +Currently, queries cannot fail partially, the response data is not guaranteed +to be a consistent snapshot. +""" +directive @transaction on MUTATION + +"Query filter criteria for `String` scalar fields." +input String_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: String @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. Currently only `auth.uid` is supported as an expression. + """ + eq_expr: String_Expr @fdc_oneOf(group: "eq") + "Match if field is not equal to provided value." + ne: String @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. Currently only `auth.uid` is supported as an expression. + """ + ne_expr: String_Expr @fdc_oneOf(group: "ne") + "Match if field value is among the provided list of values." + in: [String!] + "Match if field value is not among the provided list of values." + nin: [String!] + "Match if field value is greater than the provided value." + gt: String + "Match if field value is greater than or equal to the provided value." + ge: String + "Match if field value is less than the provided value." + lt: String + "Match if field value is less than or equal to the provided value." + le: String + """ + Match if field value contains the provided value as a substring. Equivalent + to `LIKE '%value%'` + """ + contains: String + """ + Match if field value starts with the provided value. Equivalent to + `LIKE 'value%'` + """ + startsWith: String + """ + Match if field value ends with the provided value. Equivalent to + `LIKE '%value'` + """ + endsWith: String + """ + Match if field value matches the provided pattern. See `String_Pattern` for + more details. + """ + pattern: String_Pattern +} + +""" +The pattern match condition on a string. Specify either like or regex. +https://www.postgresql.org/docs/current/functions-matching.html +""" +input String_Pattern { + "Match using the provided `LIKE` expression." + like: String + "Match using the provided POSIX regular expression." + regex: String + "When true, ignore case when matching." + ignoreCase: Boolean + "When true, invert the match result. Equivalent to `NOT LIKE` or `!~`." + invert: Boolean +} + +"Query filter criteris for `[String!]` scalar fields." +input String_ListFilter { + "Match if list field contains the provided value as a member." + includes: String + "Match if list field does not contain the provided value as a member." + excludes: String + "Match if list field contains all of the provided values as members." + includesAll: [String!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [String!] +} + +"Query filter criteria for `UUID` scalar fields." +input UUID_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: UUID + "Match if field is not equal to provided value." + ne: UUID + "Match if field value is among the provided list of values." + in: [UUID!] + "Match if field value is not among the provided list of values." + nin: [UUID!] +} + +"Query filter criteris for `[UUID!]` scalar fields." +input UUID_ListFilter { + "Match if list field contains the provided value as a member." + includes: UUID + "Match if list field does not contain the provided value as a member." + excludes: UUID + "Match if list field contains all of the provided values as members." + includesAll: [UUID!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [UUID!] +} + +"Query filter criteria for `Int` scalar fields." +input Int_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Int + "Match if field is not equal to provided value." + ne: Int + "Match if field value is among the provided list of values." + in: [Int!] + "Match if field value is not among the provided list of values." + nin: [Int!] + "Match if field value is greater than the provided value." + gt: Int + "Match if field value is greater than or equal to the provided value." + ge: Int + "Match if field value is less than the provided value." + lt: Int + "Match if field value is less than or equal to the provided value." + le: Int +} + +"Query filter criteris for `[Int!]` scalar fields." +input Int_ListFilter { + "Match if list field contains the provided value as a member." + includes: Int + "Match if list field does not contain the provided value as a member." + excludes: Int + "Match if list field contains all of the provided values as members." + includesAll: [Int!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Int!] +} + +"Query filter criteria for `Int64` scalar fields." +input Int64_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Int64 + "Match if field is not equal to provided value." + ne: Int64 + "Match if field value is among the provided list of values." + in: [Int64!] + "Match if field value is not among the provided list of values." + nin: [Int64!] + "Match if field value is greater than the provided value." + gt: Int64 + "Match if field value is greater than or equal to the provided value." + ge: Int64 + "Match if field value is less than the provided value." + lt: Int64 + "Match if field value is less than or equal to the provided value." + le: Int64 +} + +"Query filter criteria for `[Int64!]` scalar fields." +input Int64_ListFilter { + "Match if list field contains the provided value as a member." + includes: Int64 + "Match if list field does not contain the provided value as a member." + excludes: Int64 + "Match if list field contains all of the provided values as members." + includesAll: [Int64!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Int64!] +} + +"Query filter criteria for `Float` scalar fields." +input Float_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Float + "Match if field is not equal to provided value." + ne: Float + "Match if field value is among the provided list of values." + in: [Float!] + "Match if field value is not among the provided list of values." + nin: [Float!] + "Match if field value is greater than the provided value." + gt: Float + "Match if field value is greater than or equal to the provided value." + ge: Float + "Match if field value is less than the provided value." + lt: Float + "Match if field value is less than or equal to the provided value." + le: Float +} + +"Query filter criteria for `[Float!]` scalar fields." +input Float_ListFilter { + "Match if list field contains the provided value as a member." + includes: Float + "Match if list field does not contain the provided value as a member." + excludes: Float + "Match if list field contains all of the provided values as members." + includesAll: [Float!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Float!] +} + +"Query filter criteria for `Boolean` scalar fields." +input Boolean_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Boolean + "Match if field is not equal to provided value." + ne: Boolean + "Match if field value is among the provided list of values." + in: [Boolean!] + "Match if field value is not among the provided list of values." + nin: [Boolean!] +} + +"Query filter criteria for `[Boolean!]` scalar fields." +input Boolean_ListFilter { + "Match if list field contains the provided value as a member." + includes: Boolean + "Match if list field does not contain the provided value as a member." + excludes: Boolean + "Match if list field contains all of the provided values as members." + includesAll: [Boolean!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Boolean!] +} + +"Query filter criteria for `Any` scalar fields." +input Any_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Any + "Match if field is not equal to provided value." + ne: Any + "Match if field value is among the provided list of values." + in: [Any!] + "Match if field value is not among the provided list of values." + nin: [Any!] +} + +"Query filter criteria for `[Any!]` scalar fields." +input Any_ListFilter { + "Match if list field contains the provided value as a member." + includes: Any + "Match if list field does not contain the provided value as a member." + excludes: Any + "Match if list field contains all of the provided values as members." + includesAll: [Any!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Any!] +} + +""" +(Internal) A string that uniquely identifies a type, field, and so on. + +The most common usage in FDC is `SomeType` or `SomeType.someField`. See the +linked page in the @specifiedBy directive for the GraphQL RFC with more details. +""" +scalar SchemaCoordinate + @specifiedBy(url: "https://github.com/graphql/graphql-wg/blob/6d02705dea034fb65ebc6799632adb7bd550d0aa/rfcs/SchemaCoordinates.md") + @fdc_forbiddenAsFieldType + @fdc_forbiddenAsVariableType + +"(Internal) The purpose of a generated type or field." +enum GeneratedPurpose { + # Implicit fields added to the table types as columns. + IMPLICIT_KEY_FIELD + IMPLICIT_REF_FIELD + + # Relational non-column fields extended to table types. + QUERY_MULTIPLE_ONE_TO_MANY + QUERY_MULTIPLE_MANY_TO_MANY + + # Top-level Query fields. + QUERY_SINGLE + QUERY_MULTIPLE + QUERY_MULTIPLE_BY_SIMILARITY + + # Top-level Mutation fields. + INSERT_SINGLE + INSERT_MULTIPLE + UPSERT_SINGLE + UPSERT_MULTIPLE + UPDATE_SINGLE + UPDATE_MULTIPLE + DELETE_SINGLE + DELETE_MULTIPLE +} + +"(Internal) Added to definitions generated by FDC." +directive @fdc_generated( + "The source type or field that causes this definition to be generated." + from: SchemaCoordinate! + "The reason why this definition is generated, such as the intended use case." + purpose: GeneratedPurpose! +) on + | SCALAR + | OBJECT + | FIELD_DEFINITION + | ARGUMENT_DEFINITION + | INTERFACE + | UNION + | ENUM + | ENUM_VALUE + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION + +type _Service { + "Full Service Definition Language of the Frebase Data Connect Schema, including normalized schema, predefined and generated types." + sdl: String! + "Orignal Schema Sources in the service." + schema: String! + "Generated documentation from the schema of the Firebase Data Connect Service." + docs: [_Doc!]! +} + +type _Doc { + "Name of the Doc Page." + page: String! + "The markdown content of the doc page." + markdown: String! +} + +"(Internal) Added to things that may be removed from FDC and will soon be no longer usable in schema or operations." +directive @fdc_deprecated(reason: String = "No longer supported") on + | SCHEMA + | SCALAR + | OBJECT + | FIELD_DEFINITION + | ARGUMENT_DEFINITION + | INTERFACE + | UNION + | ENUM + | ENUM_VALUE + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION + +"(Internal) Added to scalars representing quoted CEL expressions." +directive @fdc_celExpression( + "The expected CEL type that the expression should evaluate to." + returnType: String +) on SCALAR + +"(Internal) Added to scalars representing quoted SQL expressions." +directive @fdc_sqlExpression( + "The expected SQL type that the expression should evaluate to." + dataType: String +) on SCALAR + +"(Internal) Added to types that may not be used as variables." +directive @fdc_forbiddenAsVariableType on SCALAR | OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT + +"(Internal) Added to types that may not be used as fields in schema." +directive @fdc_forbiddenAsFieldType on SCALAR | OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT + +"Provides a frequently used example for this type / field / argument." +directive @fdc_example( + "A GraphQL literal value (verbatim) whose type matches the target." + value: Any + "A human-readable text description of what `value` means in this context." + description: String +) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | INPUT_OBJECT | INPUT_FIELD_DEFINITION + +"(Internal) Marks this field / argument as conflicting with others in the same group." +directive @fdc_oneOf( + "The group name where fields / arguments conflict with each other." + group: String! = "" + "If true, exactly one field / argument in the group must be specified." + required: Boolean! = false +) repeatable on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION + +type Mutation { + # This is just a dummy field so that Mutation is always non-empty. + _firebase: Void @fdc_deprecated(reason: "dummy field -- does nothing useful") +} + +""" +`UUID` is a string of hexadecimal digits representing an RFC4122-compliant UUID. + +UUIDs are always output as 32 lowercase hexadecimal digits without delimiters or +curly braces. +Inputs in the following formats are also accepted (case insensitive): + +- `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` +- `urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` +- `{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}` + +In the PostgreSQL table, it's stored as [`uuid`](https://www.postgresql.org/docs/current/datatype-uuid.html). +""" +scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") + +""" +`Int64` is a scalar that represents a 64-bit signed integer. + +In the PostgreSQL table, it's stored as [`bigint`](https://www.postgresql.org/docs/current/datatype-numeric.html). + +On the wire, it's encoded as string because 64-bit integer exceeds the range of JSON number. +""" +scalar Int64 + +""" +The `Any` scalar represents any valid [JSON value](https://www.json.org/json-en.html). +It can be an object, array, string, number, or boolean. + +Caution: JSON doesn't distinguish Int and Float. + +In the PostgreSQL table, it's stored as [`jsonb`](https://www.postgresql.org/docs/current/datatype-json.html). +""" +scalar Any @specifiedBy(url: "https://www.json.org/json-en.html") + +""" +The `Void` scalar type represents the absence of any value. It is typically used +in operations where no value is expected in return. +""" +scalar Void + +""" +The `True` scalar type only accepts the boolean value `true`. + +An optional field/argument typed as `True` may either be set +to `true` or omitted (not provided at all). The values `false` or `null` are not +accepted. +""" +scalar True + @fdc_forbiddenAsFieldType + @fdc_forbiddenAsVariableType + @fdc_example(value: true, description: "The only allowed value.") + +""" +A Common Expression Language (CEL) expression that returns a boolean at runtime. + +This expression can reference the `auth` variable, which is null when Firebase +Auth is not used. When Firebase Auth is used, the following fields are available: + + - `auth.uid`: The current user ID. + - `auth.token`: A map containing all token fields (e.g., claims). + +""" +scalar Boolean_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "bool") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "auth != null", description: "Allow only if a Firebase Auth user is present.") + +""" +A Common Expression Language (CEL) expression that returns a string at runtime. + +**Limitation**: Currently, only a limited set of expressions are supported. +""" +scalar String_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "string") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "auth.uid", description: "The ID of the currently logged in user in Firebase Auth. (Errors if not logged in.)") + @fdc_example(value: "uuidV4()", description: "Generates a new random UUID (version 4) string, formatted as 32 lower-case hex digits without delimiters.") + +""" +A Common Expression Language (CEL) expression that returns a UUID string at runtime. + +**Limitation**: Currently, only a limited set of expressions are supported. +""" +scalar UUID_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "string") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "uuidV4()", description: "Generates a new random UUID (version 4) every time.") + +""" +A Common Expression Language (CEL) expression whose return type is unspecified. + +**Limitation**: Only a limited set of expressions are currently supported for each +type. +""" +scalar Any_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "auth.uid", description: "The ID of the currently logged in user in Firebase Auth. (Errors if not logged in.)") + @fdc_example(value: "uuidV4()", description: "Generates a new random UUID version 4 (formatted as 32 lower-case hex digits without delimiters if result type is String).") + @fdc_example(value: "request.time", description: "The timestamp when the request is received (with microseconds precision).") + +""" +A PostgreSQL value expression whose return type is unspecified. +""" +scalar Any_SQL + @specifiedBy(url: "https://www.postgresql.org/docs/current/sql-expressions.html") + @fdc_sqlExpression + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + +""" +Defines a relational database table. + +In this example, we defined one table with a field named `myField`. + +```graphql +type TableName @table { + myField: String +} +``` +Data Connect adds an implicit `id` primary key column. So the above schema is equivalent to: + +```graphql +type TableName @table(key: "id") { + id: String @default(expr: "uuidV4()") + myField: String +} +``` + +Data Connect generates the following SQL table and CRUD operations to use it. + +```sql +CREATE TABLE "public"."table_name" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "my_field" text NULL, + PRIMARY KEY ("id") +) +``` + + * You can lookup a row: `query ($id: UUID!) { tableName(id: $id) { myField } } ` + * You can find rows using: `query tableNames(limit: 20) { myField }` + * You can insert a row: `mutation { tableName_insert(data: {myField: "foo"}) }` + * You can update a row: `mutation ($id: UUID!) { tableName_update(id: $id, data: {myField: "bar"}) }` + * You can delete a row: `mutation ($id: UUID!) { tableName_delete(id: $id) }` + +##### Customizations + +- `@table(singular)` and `@table(plural)` can customize the singular and plural name. +- `@table(name)` can customize the Postgres table name. +- `@table(key)` can customize the primary key field name and type. + +For example, the `User` table often has a `uid` as its primary key. + +```graphql +type User @table(key: "uid") { + uid: String! + name: String +} +``` + + * You can securely lookup a row: `query { user(key: {uid_expr: "auth.uid"}) { name } } ` + * You can securely insert a row: `mutation { user_insert(data: {uid_expr: "auth.uid" name: "Fred"}) }` + * You can securely update a row: `mutation { user_update(key: {uid_expr: "auth.uid"}, data: {name: "New Name"}) }` + * You can securely delete a row: `mutation { user_delete(key: {uid_expr: "auth.uid"}) }` + +`@table` type can be configured further with: + + - Custom SQL data types for columns. See `@col`. + - Add SQL indexes. See `@index`. + - Add SQL unique constraints. See `@unique`. + - Add foreign key constraints to define relations. See `@ref`. + +""" +directive @table( + """ + Configures the SQL database table name. Defaults to snake_case like `table_name`. + """ + name: String + """ + Configures the singular name. Defaults to the camelCase like `tableName`. + """ + singular: String + """ + Configures the plural name. Defaults to infer based on English plural pattern like `tableNames`. + """ + plural: String + """ + Defines the primary key of the table. Defaults to a single field named `id`. + If not present already, Data Connect adds an implicit field `id: UUID! @default(expr: "uuidV4()")`. + """ + key: [String!] +) on OBJECT + +""" +Defines a relational database Raw SQLview. + +Data Connect generates GraphQL queries with WHERE and ORDER BY clauses. +However, not all SQL features has native GraphQL equivalent. + +You can write **an arbitrary SQL SELECT statement**. Data Connect +would map Graphql fields on `@view` type to columns in your SELECT statement. + +* Scalar GQL fields (camelCase) should match a SQL column (snake_case) + in the SQL SELECT statement. +* Reference GQL field can point to another `@table` type. Similar to foreign key + defined with `@ref` on a `@table` type, a `@view` type establishes a relation + when `@ref(fields)` match `@ref(references)` on the target table. + +In this example, you can use `@view(sql)` to define an aggregation view on existing +table. + +```graphql +type User @table { + name: String + score: Int +} +type UserAggregation @view(sql: ''' + SELECT + COUNT(*) as count, + SUM(score) as sum, + AVG(score) as average, + PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score) AS median, + (SELECT id FROM "user" LIMIT 1) as example_id + FROM "user" +''') { + count: Int + sum: Int + average: Float + median: Float + example: User + exampleId: UUID +} +``` + +###### Example: Query Raw SQL View + +```graphql +query { + userAggregations { + count sum average median + exampleId example { id } + } +} +``` + +##### One-to-One View + +An one-to-one companion `@view` can be handy if you want to argument a `@table` +with additional implied content. + +```graphql +type Restaurant @table { + name: String! +} +type Review @table { + restaurant: Restaurant! + rating: Int! +} +type RestaurantStats @view(sql: ''' + SELECT + restaurant_id, + COUNT(*) AS review_count, + AVG(rating) AS average_rating + FROM review + GROUP BY restaurant_id +''') { + restaurant: Restaurant @unique + reviewCount: Int + averageRating: Float +} +``` + +In this example, `@unique` convey the assumption that each `Restaurant` should +have only one `RestaurantStats` object. + +###### Example: Query One-to-One View + +```graphql +query ListRestaurants { + restaurants { + name + stats: restaurantStats_on_restaurant { + reviewCount + averageRating + } + } +} +``` + +###### Example: Filter based on One-to-One View + +```graphql +query BestRestaurants($minAvgRating: Float, $minReviewCount: Int) { + restaurants(where: { + restaurantStats_on_restaurant: { + averageRating: {ge: $minAvgRating} + reviewCount: {ge: $minReviewCount} + } + }) { name } +} +``` + +##### Customizations + +- One of `@view(sql)` or `@view(name)` should be defined. + `@view(name)` can refer to a persisted SQL view in the Postgres schema. +- `@view(singular)` and `@view(plural)` can customize the singular and plural name. + +`@view` type can be configured further: + + - `@unique` lets you define one-to-one relation. + - `@col` lets you customize SQL column mapping. For example, `@col(name: "column_in_select")`. + +##### Limitations + +Raw SQL view doesn't have a primary key, so it doesn't support lookup. Other +`@table` or `@view` cannot have `@ref` to a view either. + +View cannot be mutated. You can perform CRUD operations on the underlying +table to alter its content. + +**Important: Data Connect doesn't parse and validate SQL** + +- If the SQL view is invalid or undefined, related requests may fail. +- If the SQL view return incompatible types. Firebase Data Connect may surface + errors. +- If a field doesn't have a corresponding column in the SQL SELECT statement, + it will always be `null`. +- There is no way to ensure VIEW to TABLE `@ref` constraint. +- All fields must be nullable in case they aren't found in the SELECT statement + or in the referenced table. + +**Important: You should always test `@view`!** + +""" +directive @view( + """ + The SQL view name. If neither `name` nor `sql` are provided, defaults to the + snake_case of the singular type name. + `name` and `sql` cannot be specified at the same time. + """ + name: String @fdc_oneOf + """ + SQL `SELECT` statement used as the basis for this type. + SQL SELECT columns should use snake_case. GraphQL fields should use camelCase. + `name` and `sql` cannot be specified at the same time. + """ + sql: String @fdc_oneOf + """ + Configures the singular name. Defaults to the camelCase like `viewName`. + """ + singular: String + """ + Configures the plural name. Defaults to infer based on English plural pattern like `viewNames`. + """ + plural: String +) on OBJECT + +""" +Customizes a field that represents a SQL database table column. + +Data Connect maps scalar Fields on `@table` type to a SQL column of +corresponding data type. + +- scalar `UUID`: [`uuid`](https://www.postgresql.org/docs/current/datatype-uuid.html). +- scalar `String`: [`text`](https://www.postgresql.org/docs/current/datatype-character.html) +- scalar `Int`: [`int`](https://www.postgresql.org/docs/current/datatype-numeric.html) +- scalar `Int64`: [`bigint`](https://www.postgresql.org/docs/current/datatype-numeric.html) +- scalar `Float`: [`double precision`](https://www.postgresql.org/docs/current/datatype-numeric.html) +- scalar `Boolean`: [`boolean`](https://www.postgresql.org/docs/current/datatype-boolean.html) +- scalar `Date`: [`date`](https://www.postgresql.org/docs/current/datatype-datetime.html) +- scalar `Timestamp`: [`timestamptz`](https://www.postgresql.org/docs/current/datatype-datetime.html) +- scalar `Any`: [`jsonb`](https://www.postgresql.org/docs/current/datatype-json.html) +- scalar `Vector`: [`pgvector`](https://github.com/pgvector/pgvector) + +Array scalar fields are mapped to [Postgres arrays](https://www.postgresql.org/docs/current/arrays.html). + +###### Example: Serial Primary Key + +For example, you can define auto-increment primary key. + +```graphql +type Post @table { + id: Int! @col(name: "post_id", dataType: "serial") +} +``` + +Data Connect converts it to the following SQL table schema. + +```sql +CREATE TABLE "public"."post" ( + "post_id" serial NOT NULL, + PRIMARY KEY ("id") +) +``` + +###### Example: Vector + +```graphql +type Post @table { + content: String! @col(name: "post_content") + contentEmbedding: Vector! @col(size:768) +} +``` + +""" +directive @col( + """ + The SQL database column name. Defaults to snake_case of the field name. + """ + name: String + """ + Configures the custom SQL data type. + + Each GraphQL type can map to multiple SQL data types. + Refer to [Postgres supported data types](https://www.postgresql.org/docs/current/datatype.html). + """ + dataType: String + """ + Required on `Vector` columns. It specifies the length of the Vector. + `textembedding-gecko@003` model generates `Vector` of `@col(size:768)`. + """ + size: Int +) on FIELD_DEFINITION + + +""" +Defines a foreign key reference to another table. + +For example, we can define a many-to-one relation. + +```graphql +type ManyTable @table { + refField: OneTable! +} +type OneTable @table { + someField: String! +} +``` +Data Connect adds implicit foreign key column and relation query field. So the +above schema is equivalent to the following schema. + +```graphql +type ManyTable @table { + id: UUID! @default(expr: "uuidV4()") + refField: OneTable! @ref(fields: "refFieldId", references: "id") + refFieldId: UUID! +} +type OneTable @table { + id: UUID! @default(expr: "uuidV4()") + someField: UUID! + manyTables_on_refField: [ManyTable!]! +} +``` +Data Connect generates the necessary foreign key constraint. + +```graphql +CREATE TABLE "public"."many_table" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "ref_field_id" uuid NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "many_table_ref_field_id_fkey" FOREIGN KEY ("ref_field_id") REFERENCES "public"."one_table" ("id") ON DELETE CASCADE +) +``` + +###### Example: Traverse the Reference Field + +```graphql +query ($id: UUID!) { + manyTable(id: $id) { + refField { id } + } +} +``` + +###### Example: Reverse Traverse the Reference field + +```graphql +query ($id: UUID!) { + oneTable(id: $id) { + manyTables_on_refField { id } + } +} +``` + +##### Optional Many-to-One Relation + +An optional foreign key reference will be set to null if the referenced row is deleted. + +In this example, if a `User` is deleted, the `assignee` and `reporter` +references will be set to null. + +```graphql +type Bug @table { + title: String! + assignee: User + reproter: User +} +``` + +##### Required Many-to-One Relation + +A required foreign key reference will cascade delete if the referenced row is +deleted. + +In this example, if a `Post` is deleted, associated comments will also be +deleted. + +```graphql +type Comment @table { + post: Post! + content: String! +} +``` + +##### Many To Many Relation + +You can define a many-to-many relation with a join table. + +```graphql +type Membership @table(key: ["group", "user"]) { + group: Group! + user: User! + role: String! @default(value: "member") +} + +type Group @table { name: String! } +type User @table { name: String! } +``` + +When Data Connect sees a table with two reference field as its primary key, it +knows this is a join table, so expands the many-to-many query field. + +```graphql +type Group @table { + name: String! + users_via_Membership: [User!]! + memberships_on_group: [Membership!]! +} +type User @table { + name: String! + groups_via_Membership: [Group!]! + memberships_on_user: [Membership!]! +} +``` + +###### Example: Transerse the Many-To-Many Relation + +```graphql +query ($id: UUID!) { + group(id: $id) { + users: users_via_Membership { + name + } + } +} +``` + +###### Example: Transerse to the Join Table + +```graphql +query ($id: UUID!) { + group(id: $id) { + memberships: memberships_on_group { + user { name } + role + } + } +} +``` + +##### One To One Relation + +You can even define a one-to-one relation with the help of `@unique` or `@table(key)`. + +```graphql +type User @table { + name: String +} +type Account @table { + user: User! @unique +} +# Alternatively, use primary key constraint. +type Account @table(key: "user") { + user: User! +} +``` + +###### Example: Transerse the Reference Field + +```graphql +query ($id: UUID!) { + account(id: $id) { + user { id } + } +} +``` + +###### Example: Reverse Tranverse the Reference field + +```graphql +query ($id: UUID!) { + user(id: $id) { + account_on_user { id } + } +} +``` + +##### Customizations + +- `@ref(constraintName)` can customize the SQL foreign key constraint name (`table_name_ref_field_fkey` above). +- `@ref(fields)` can customize the foreign key field names. +- `@ref(references)` can customize the constraint to reference other columns. + By default, `@ref(references)` is the primary key of the `@ref` table. + Other fields with `@unique` may also be referred in the foreign key constraint. + +""" +directive @ref( + "The SQL database foreign key constraint name. Defaults to {table_name}_{field_name}_fkey." + constraintName: String + """ + Foreign key fields. Defaults to {tableName}{PrimaryIdName}. + """ + fields: [String!] + "The fields that the foreign key references in the other table. Defaults to its primary key." + references: [String!] +) on FIELD_DEFINITION + +"Defines the direction of an orderby query" +enum OrderDirection { +"Results are ordered in ascending order." + ASC +"Results are ordered in descending order." + DESC +} + +""" +Specifies the default value for a column field. + +For example: + +```graphql +type User @table(key: "uid") { + uid: String! @default(expr: "auth.uid") + number: Int! @col(dataType: "serial") + createdAt: Date! @default(expr: "request.time") + role: String! @default(value: "Member") + credit: Int! @default(value: 100) +} +``` + +The supported arguments vary based on the field type. +""" +directive @default( + "A constant value validated against the field's GraphQL type during compilation." + value: Any @fdc_oneOf(required: true) + "A CEL expression whose return value must match the field's data type." + expr: Any_Expr @fdc_oneOf(required: true) + """ + A raw SQL expression, whose SQL data type must match the underlying column. + + The value is any variable-free expression (in particular, cross-references to + other columns in the current table are not allowed). Subqueries are not allowed either. + See [PostgreSQL defaults](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-DEFAULT) + for more details. + """ + sql: Any_SQL @fdc_oneOf(required: true) +) on FIELD_DEFINITION + +""" +Defines a database index to optimize query performance. + +```graphql +type User @table @index(fields: ["name", "phoneNumber"], order: [ASC, DESC]) { + name: String @index + phoneNumber: Int64 @index + tags: [String] @index # GIN Index +} +``` + +##### Single Field Index + +You can put `@index` on a `@col` field to create a SQL index. + +`@index(order)` matters little for single field indexes, as they can be scanned +in both directions. + +##### Composite Index + +You can put `@index(fields: [...])` on `@table` type to define composite indexes. + +`@index(order: [...])` can customize the index order to satisfy particular +filter and order requirement. + +""" +directive @index( + """ + Configure the SQL database index id. + + If not overridden, Data Connect generates the index name: + - `table_name_first_field_second_field_aa_idx` + - `table_name_only_field_name_idx` + """ + name: String + """ + Only allowed and required when used on a `@table` type. + Specifies the fields to create the index on. + """ + fields: [String!] + """ + Only allowed for `BTREE` `@index` on `@table` type. + Specifies the order for each indexed column. Defaults to all `ASC`. + """ + order: [IndexFieldOrder!] + """ + Customize the index type. + + For most index, it defaults to `BTREE`. + For array fields, only allowed `IndexType` is `GIN`. + For `Vector` fields, defaults to `HNSW`, may configure to `IVFFLAT`. + """ + type: IndexType + """ + Only allowed when used on vector field. + Defines the vector similarity method. Defaults to `INNER_PRODUCT`. + """ + vector_method: VectorSimilarityMethod +) repeatable on FIELD_DEFINITION | OBJECT + +"Specifies the sorting order for database indexes." +enum IndexFieldOrder { + "Sorts the field in ascending order (from lowest to highest)." + ASC + "Sorts the field in descending order (from highest to lowest)." + DESC +} + +"Defines the type of index to be used in the database." +enum IndexType { + "A general-purpose index type commonly used for sorting and searching." + BTREE + "Generalized Inverted Index, optimized for indexing composite values such as arrays." + GIN + "Hierarchical Navigable Small World graph, used for nearest-neighbor searches on vector fields." + HNSW + "Inverted File Index, optimized for approximate nearest-neighbor searches in vector databases." + IVFFLAT +} + +""" +Defines unique constraints on `@table`. + +For example, + +```graphql +type User @table { + phoneNumber: Int64 @unique +} +type UserProfile @table { + user: User! @unique + address: String @unique +} +``` + +- `@unique` on a `@col` field adds a single-column unique constraint. +- `@unique` on a `@table` type adds a composite unique constraint. +- `@unique` on a `@ref` defines a one-to-one relation. It adds unique constraint + on `@ref(fields)`. + +`@unique` ensures those fields can uniquely identify a row, so other `@table` +type may define `@ref(references)` to refer to fields that has a unique constraint. + +""" +directive @unique( + """ + Configures the SQL database unique constraint name. + + If not overridden, Data Connect generates the unique constraint name: + - `table_name_first_field_second_field_uidx` + - `table_name_only_field_name_uidx` + """ + indexName: String + """ + Only allowed and required when used on OBJECT, + this specifies the fields to create a unique constraint on. + """ + fields: [String!] +) repeatable on FIELD_DEFINITION | OBJECT + +""" +Date is a string in the YYYY-MM-DD format representing a local-only date. + +See the description for Timestamp for range and limitations. + +As a FDC-specific extension, inputs that includes time portions (as specified by +the Timestamp scalar) are accepted but only the date portion is used. In other +words, only the part before "T" is used and the rest discarded. This effectively +truncates it to the local date in the specified time-zone. + +Outputs will always be in the canonical YYYY-MM-DD format. + +In the PostgreSQL table, it's stored as [`date`](https://www.postgresql.org/docs/current/datatype-datetime.html). +""" +scalar Date @specifiedBy(url: "https://scalars.graphql.org/andimarek/local-date.html") + +""" +Timestamp is a RFC 3339 string that represents an exact point in time. + +The serialization format follows https://scalars.graphql.org/andimarek/date-time +except the "Non-optional exact milliseconds" Section. As a FDC-specific +extension, inputs and outputs may contain 0, 3, 6, or 9 fractional digits. + +Specifically, output precision varies by server-side factors such as data source +support and clients must not rely on an exact number of digits. Clients may +truncate extra digits as fit, with the caveat that there may be information loss +if the truncated value is subsequently sent back to the server. + +FDC only supports year 1583 to 9999 (inclusive) and uses the ISO-8601 calendar +system for all date-time calculations. Notably, the expanded year representation +(+/-YYYYY) is rejected and Year 1582 and before may either be rejected or cause +undefined behavior. + +In the PostgreSQL table, it's stored as [`timestamptz`](https://www.postgresql.org/docs/current/datatype-datetime.html). +""" +scalar Timestamp @specifiedBy(url: "https://scalars.graphql.org/andimarek/date-time") + +""" +A Common Expression Language (CEL) expression that returns a Timestamp at runtime. + +Limitation: Right now, only a few expressions are supported. +""" +scalar Timestamp_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "google.protobuf.Timestamp") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "request.time", description: "The timestamp when the request is received (with microseconds precision).") + +""" +A Common Expression Language (CEL) expression that returns a Timestamp at runtime, +which is then truncated to UTC date only. The time-of-day parts are discarded. + +Limitation: Right now, only a few expressions are supported. +""" +scalar Date_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "google.protobuf.Timestamp") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "request.time", description: "The UTC date on which the request is received.") + +"Conditions on a `Date` value." +input Date_Filter { + "Match if the field `IS NULL`." + isNull: Boolean + "Match if the field is exactly equal to the provided value." + eq: Date @fdc_oneOf(group: "eq") + "Match if the field equals the provided CEL expression." + eq_expr: Date_Expr @fdc_oneOf(group: "eq") + "Match if the field equals the provided relative date." + eq_date: Date_Relative @fdc_oneOf(group: "eq") + "Match if the field is not equal to the provided value." + ne: Date @fdc_oneOf(group: "ne") + "Match if the field is not equal to the provided CEL expression." + ne_expr: Date_Expr @fdc_oneOf(group: "ne") + "Match if the field is not equal to the provided relative date." + ne_date: Date_Relative @fdc_oneOf(group: "ne") + "Match if the field value is among the provided list of values." + in: [Date!] + "Match if the field value is not among the provided list of values." + nin: [Date!] + "Match if the field value is greater than the provided value." + gt: Date @fdc_oneOf(group: "gt") + "Match if the field value is greater than the provided CEL expression." + gt_expr: Date_Expr @fdc_oneOf(group: "gt") + "Match if the field value is greater than the provided relative date." + gt_date: Date_Relative @fdc_oneOf(group: "gt") + "Match if the field value is greater than or equal to the provided value." + ge: Date @fdc_oneOf(group: "ge") + "Match if the field value is greater than or equal to the provided CEL expression." + ge_expr: Date_Expr @fdc_oneOf(group: "ge") + "Match if the field value is greater than or equal to the provided relative date." + ge_date: Date_Relative @fdc_oneOf(group: "ge") + "Match if the field value is less than the provided value." + lt: Date @fdc_oneOf(group: "lt") + "Match if the field value is less than the provided CEL expression." + lt_expr: Date_Expr @fdc_oneOf(group: "lt") + "Match if the field value is less than the provided relative date." + lt_date: Date_Relative @fdc_oneOf(group: "lt") + "Match if the field value is less than or equal to the provided value." + le: Date @fdc_oneOf(group: "le") + "Match if the field value is less than or equal to the provided CEL expression." + le_expr: Date_Expr @fdc_oneOf(group: "le") + "Match if the field value is less than or equal to the provided relative date." + le_date: Date_Relative @fdc_oneOf(group: "le") +} + +"Conditions on a`Date` list." +input Date_ListFilter { + "Match if the list contains the provided date." + includes: Date @fdc_oneOf(group: "includes") + "Match if the list contains the provided date CEL expression." + includes_expr: Date_Expr @fdc_oneOf(group: "includes") + "Match if the list contains the provided relative date." + includes_date: Date_Relative @fdc_oneOf(group: "includes") + "Match if the list does not contain the provided date." + excludes: Date @fdc_oneOf(group: "excludes") + "Match if the list does not contain the provided date CEL expression." + excludes_expr: Date_Expr @fdc_oneOf(group: "excludes") + "Match if the list does not contain the provided relative date." + excludes_date: Date_Relative @fdc_oneOf(group: "excludes") + "Match if the list contains all the provided dates." + includesAll: [Date!] + "Match if the list contains none of the provided dates." + excludesAll: [Date!] +} + +"Conditions on a `Timestamp` value." +input Timestamp_Filter { + "Match if the field `IS NULL`." + isNull: Boolean + "Match if the field is exactly equal to the provided value." + eq: Timestamp @fdc_oneOf(group: "eq") + "Match if the field equals the provided CEL expression." + eq_expr: Timestamp_Expr @fdc_oneOf(group: "eq") + "Match if the field equals the provided relative time." + eq_time: Timestamp_Relative @fdc_oneOf(group: "eq") + "Match if the field is not equal to the provided value." + ne: Timestamp @fdc_oneOf(group: "ne") + "Match if the field is not equal to the provided CEL expression." + ne_expr: Timestamp_Expr @fdc_oneOf(group: "ne") + "Match if the field is not equal to the provided relative time." + ne_time: Timestamp_Relative @fdc_oneOf(group: "ne") + "Match if the field value is among the provided list of values." + in: [Timestamp!] + "Match if the field value is not among the provided list of values." + nin: [Timestamp!] + "Match if the field value is greater than the provided value." + gt: Timestamp @fdc_oneOf(group: "gt") + "Match if the field value is greater than the provided CEL expression." + gt_expr: Timestamp_Expr @fdc_oneOf(group: "gt") + "Match if the field value is greater than the provided relative time." + gt_time: Timestamp_Relative @fdc_oneOf(group: "gt") + "Match if the field value is greater than or equal to the provided value." + ge: Timestamp @fdc_oneOf(group: "ge") + "Match if the field value is greater than or equal to the provided CEL expression." + ge_expr: Timestamp_Expr @fdc_oneOf(group: "ge") + "Match if the field value is greater than or equal to the provided relative time." + ge_time: Timestamp_Relative @fdc_oneOf(group: "ge") + "Match if the field value is less than the provided value." + lt: Timestamp @fdc_oneOf(group: "lt") + "Match if the field value is less than the provided CEL expression." + lt_expr: Timestamp_Expr @fdc_oneOf(group: "lt") + "Match if the field value is less than the provided relative time." + lt_time: Timestamp_Relative @fdc_oneOf(group: "lt") + "Match if the field value is less than or equal to the provided value." + le: Timestamp @fdc_oneOf(group: "le") + "Match if the field value is less than or equal to the provided CEL expression." + le_expr: Timestamp_Expr @fdc_oneOf(group: "le") + "Match if the field value is less than or equal to the provided relative time." + le_time: Timestamp_Relative @fdc_oneOf(group: "le") +} + +"Conditions on a `Timestamp` list." +input Timestamp_ListFilter { + "Match if the list contains the provided timestamp." + includes: Timestamp @fdc_oneOf(group: "includes") + "Match if the list contains the provided timestamp CEL expression." + includes_expr: Timestamp_Expr @fdc_oneOf(group: "includes") + "Match if the list contains the provided relative timestamp." + includes_time: Timestamp_Relative @fdc_oneOf(group: "includes") + "Match if the list does not contain the provided timestamp." + excludes: Timestamp @fdc_oneOf(group: "excludes") + "Match if the list does not contain the provided timestamp CEL expression." + excludes_expr: Timestamp_Expr @fdc_oneOf(group: "excludes") + "Match if the list does not contain the provided relative timestamp." + excludes_time: Timestamp_Relative @fdc_oneOf(group: "excludes") + "Match if the list contains all the provided timestamps." + includesAll: [Timestamp!] + "Match if the list contains none of the provided timestamps." + excludesAll: [Timestamp!] +} + +"Update input of a `Date` value." +input Date_Update { + "Set the field to the provided date." + set: Date @fdc_oneOf(group: "set") + "Set the field to the provided date CEL expression." + set_expr: Date_Expr @fdc_oneOf(group: "set") + "Set the field to the provided relative date." + set_date: Date_Relative @fdc_oneOf(group: "set") +} + +"Update input of a `Date` list value." +input Date_ListUpdate { + "Replace the current list with the provided list of `Date` values." + set: [Date!] + "Append the provided `Date` values to the existing list." + append: [Date!] + "Prepend the provided `Date` values to the existing list." + prepend: [Date!] + "Remove the date value at the specified index." + delete: Int + "The index of the list to perform updates." + i: Int + "Update the date value at the specified index." + update: Date +} + +"Update input of a `Timestamp` value." +input Timestamp_Update { + "Set the field to the provided timestamp." + set: Timestamp @fdc_oneOf(group: "set") + "Set the field to the provided timestamp CEL expression." + set_expr: Timestamp_Expr @fdc_oneOf(group: "set") + "Set the field to the provided relative timestamp." + set_time: Timestamp_Relative @fdc_oneOf(group: "set") +} + +"Update input of an `Timestamp` list value." +input Timestamp_ListUpdate { + "Replace the current list with the provided list of `Timestamp` values." + set: [Timestamp!] + "Append the provided `Timestamp` values to the existing list." + append: [Timestamp!] + "Prepend the provided `Timestamp` values to the existing list." + prepend: [Timestamp!] + "Remove the timestamp value at the specified index." + delete: Int + "The index of the list to perform updates." + i: Int + "Update the timestamp value at the specified index." + update: Timestamp +} + + +"A runtime-calculated `Timestamp` value relative to `now` or `at`." +input Timestamp_Relative @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "Match for the current time." + now: True @fdc_oneOf(group: "from", required: true) + "A specific timestamp for matching." + at: Timestamp @fdc_oneOf(group: "from", required: true) + "Add the provided duration to the base timestamp." + add: Timestamp_Duration + "Subtract the provided duration from the base timestamp." + sub: Timestamp_Duration + "Truncate the timestamp to the provided interval." + truncateTo: Timestamp_Interval +} + +input Timestamp_Duration @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "The number of milliseconds for the duration." + milliseconds: Int! = 0 + "The number of seconds for the duration." + seconds: Int! = 0 + "The number of minutes for the duration." + minutes: Int! = 0 + "The number of hours for the duration." + hours: Int! = 0 + "The number of days for the duration." + days: Int! = 0 + "The number of weeks for the duration." + weeks: Int! = 0 + "The number of months for the duration." + months: Int! = 0 + "The number of years for the duration." + years: Int! = 0 +} + +enum Timestamp_Interval @fdc_forbiddenAsFieldType { + "Represents a time interval of one second." + SECOND + "Represents a time interval of one minute." + MINUTE + "Represents a time interval of one hour." + HOUR + "Represents a time interval of one day." + DAY + "Represents a time interval of one week." + WEEK + "Represents a time interval of one month." + MONTH + "Represents a time interval of one year." + YEAR +} + +"A runtime-calculated Date value relative to `today` or `on`." +input Date_Relative @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "Match for today’s date." + today: True @fdc_oneOf(group: "from", required: true) + "A specific date for matching." + on: Date @fdc_oneOf(group: "from", required: true) + "Add the provided duration to the base date." + add: Date_Duration + "Subtract the provided duration from the base date." + sub: Date_Duration + "Truncate the date to the provided interval." + truncateTo: Date_Interval +} + +input Date_Duration @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "The number of days for the duration." + days: Int! = 0 + "The number of weeks for the duration." + weeks: Int! = 0 + "The number of months for the duration." + months: Int! = 0 + "The number of years for the duration." + years: Int! = 0 +} + +enum Date_Interval @fdc_forbiddenAsFieldType { + "Represents a time interval of one week." + WEEK + "Represents a time interval of one month." + MONTH + "Represents a time interval of one year." + YEAR +} + +"Update input of a `String` value." +input String_Update { + "Set the field to a provided value." + set: String @fdc_oneOf(group: "set") + "Set the field to a provided server value expression." + set_expr: String_Expr @fdc_oneOf(group: "set") +} + +"Update input of a `String` list value." +input String_ListUpdate { + "Set the list with the provided values." + set: [String!] + "Append the provided values to the existing list." + append: [String!] + "Prepend the provided values to the existing list." + prepend: [String!] +} + +"Update input of a `UUID` value." +input UUID_Update { + "Set the field to a provided UUID." + set: UUID @fdc_oneOf(group: "set") + "Set the field to a provided UUID expression." + set_expr: UUID_Expr @fdc_oneOf(group: "set") +} + +"Update input of an `ID` list value." +input UUID_ListUpdate { + "Set the list with the provided list of UUIDs." + set: [UUID!] + "Append the provided UUIDs to the existing list." + append: [UUID!] + "Prepend the provided UUIDs to the existing list." + prepend: [UUID!] +} + +"Update input of an `Int` value." +input Int_Update { + "Set the field to a provided value." + set: Int + "Increment the field by a provided value." + inc: Int + "Decrement the field by a provided value." + dec: Int +} + +"Update input of an `Int` list value." +input Int_ListUpdate { + "Set the list with the provided values." + set: [Int!] + "Append the provided list of values to the existing list." + append: [Int!] + "Prepend the provided list of values to the existing list." + prepend: [Int!] +} + +"Update input of an `Int64` value." +input Int64_Update { + "Set the field to a provided value." + set: Int64 + "Increment the field by a provided value." + inc: Int64 + "Decrement the field by a provided value." + dec: Int64 +} + +"Update input of an `Int64` list value." +input Int64_ListUpdate { + "Replace the list with the provided values." + set: [Int64!] + "Append the provided list of values to the existing list." + append: [Int64!] + "Prepend the provided list of values to the existing list." + prepend: [Int64!] +} + +"Update input of a `Float` value." +input Float_Update { + "Set the field to a provided value." + set: Float + "Increment the field by a provided value." + inc: Float + "Decrement the field by a provided value." + dec: Float +} + +"Update input of a `Float` list value." +input Float_ListUpdate { + "Set the list with the provided values." + set: [Float!] + "Append the provided list of values to the existing list." + append: [Float!] + "Prepend the provided list of values to the existing list." + prepend: [Float!] +} + +"Update input of a `Boolean` value." +input Boolean_Update { + "Set the field to a provided value." + set: Boolean +} + +"Update input of a `Boolean` list value." +input Boolean_ListUpdate { + "Set the list with the provided values." + set: [Boolean!] + "Append the provided list of values to the existing list." + append: [Boolean!] + "Prepend the provided list of values to the existing list." + prepend: [Boolean!] +} + +"Update input of an `Any` value." +input Any_Update { + "Set the field to a provided value." + set: Any +} + +"Update input of an `Any` list value." +input Any_ListUpdate { + "Set the list with the provided values." + set: [Any!] + "Append the provided list of values to the existing list." + append: [Any!] + "Prepend the provided list of values to the existing list." + prepend: [Any!] +} + +type Query { + """ + _service provides customized introspection on Firebase Data Connect Sevice. + """ + _service: _Service! +} + +""" +Vector is an array of single-precision floating-point numbers, serialized +as a JSON array. All elements must be finite (no NaN, Infinity or -Infinity). + +Example: [1.1, 2, 3.3] + +In the PostgreSQL table, it's stored as [`pgvector`](https://github.com/pgvector/pgvector). +""" +scalar Vector + +""" +Defines the similarity function to use when comparing vectors in queries. + +Defaults to `INNER_PRODUCT`. + +View [all vector functions](https://github.com/pgvector/pgvector?tab=readme-ov-file#vector-functions). +""" +enum VectorSimilarityMethod { + "Measures the Euclidean (L2) distance between two vectors." + L2 + "Measures the cosine similarity between two vectors." + COSINE + "Measures the inner product(dot product) between two vectors." + INNER_PRODUCT +} + +"Conditions on a Vector value." +input Vector_Filter { + "Match if the field is exactly equal to the provided vector." + eq: Vector + "Match if the field is not equal to the provided vector." + ne: Vector + "Match if the field value is among the provided list of vectors." + in: [Vector!] + "Match if the field value is not among the provided list of vectors." + nin: [Vector!] + "Match if the field is `NULL`." + isNull: Boolean +} + +input Vector_ListFilter { + "Match if the list includes the supplied vector." + includes: Vector + "Match if the list does not include the supplied vector." + excludes: Vector + "Match if the list contains all the provided vectors." + includesAll: [Vector!] + "Match if the list contains none of the provided vectors." + excludesAll: [Vector!] +} + +"Update input of a Vector value." +input Vector_Update { + "Set the field to the provided vector value." + set: Vector @fdc_oneOf(group: "set") + "Set the field to the vector embedding result from a text input." + set_embed: Vector_Embed @fdc_oneOf(group: "set") +} + + +"Update input of a Vector list value." +input Vector_ListUpdate { + "Replace the current list with the provided list of Vector values." + set: [Vector] + "Append the provided Vector values to the existing list." + append: [Vector] + "Prepend the provided Vector values to the existing list." + prepend: [Vector] + "Delete the vector at the specified index." + delete: Int + "The index of the vector to be updated." + i: Int + "Update the vector at the specified index." + update: Vector +} + +""" +Create a vector embedding of text using the given model on Vertex AI. + +Cloud SQL for Postgresql natively integrates with [Vertex AI Text embeddings API](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api) +to effectively generate text embeddings. + +If you uses [`Vector`](scalar.md#Vector) in your schema, Firebase Data Connect automatically installs +[`pgvector`](https://github.com/pgvector/pgvector) and [`google_ml_integration`](https://cloud.google.com/sql/docs/postgres/integrate-cloud-sql-with-vertex-ai) +Postgres extensions in your Cloud SQL database. + +Given a Post table with a `Vector` embedding field. + +```graphql +type Post @table { + content: String! + contentEmbedding: Vector @col(size:768) +} +``` + +###### Example: Insert embedding + +```graphql +mutation CreatePost($content: String!) { + post_insert(data: { + content: $content, + contentEmbedding_embed: {model: "textembedding-gecko@003", text: $content}, + }) +} +``` + +###### Example: Vector similarity Search + +```graphql +query SearchPost($query: String!) { + posts_contentEmbedding_similarity(compare_embed: {model: "textembedding-gecko@003", text: $query}) { + id + content + } +} +``` +""" +input Vector_Embed @fdc_forbiddenAsVariableType { + """ + The model to use for vector embedding. + Recommend the latest stable model: `textembedding-gecko@003`. + """ + model: Vector_Embed_Model! + "The text to generate the vector embedding from." + text: String! +} + +""" +The Vertex AI model version that is required in input `Vector_Embed`. + +It is recommended to use the latest stable model version: `textembedding-gecko@003`. + +View all supported [Vertex AI Text embeddings APIs](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api). +""" +scalar Vector_Embed_Model + @specifiedBy(url: "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "textembedding-gecko@003", description: "A stable version of the textembedding-gecko model") + @fdc_example(value: "textembedding-gecko@001", description: "An older version of the textembedding-gecko model") + @fdc_example(value: "text-embedding-004", description: "Annother textembedding model") + diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/queries.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/queries.gql index 3c1ce8191a13..2bce5f89e109 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/queries.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/queries.gql @@ -22,27 +22,43 @@ query ListMoviesByPartialTitle($input: String!) @auth(level: PUBLIC) { query ListPersons @auth(level: USER) { people { - id + id name } } query ListThing($data: Any) @auth(level: USER) { - things(where: { - title: { - eq: $data - } - }) { + things(where: { title: { eq: $data } }) { title } } query ListTimestamps @auth(level: USER) { timestampHolders { - timestamp, + timestamp date } } +query ListMoviesByGenre($genre: String! = "Action") @auth(level: PUBLIC) { + mostPopular: movies( + where: { genre: { eq: $genre } } + orderBy: { rating: DESC } + limit: 10 + ) { + id + title + rating + } + mostRecent: movies( + where: { genre: { eq: $genre } } + orderBy: { releaseYear: DESC } + ) { + id + title + rating + } +} + # List subset of fields for users # query ListUsers @auth(level: PUBLIC) { # users { diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart index 568f499f37dc..71307831d987 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart @@ -30,40 +30,39 @@ void runGenerationTest() { testWidgets('should have generated correct MutationRef', (WidgetTester tester) async { - final ref = MoviesConnector.instance.createMovie - .ref( + final ref = MoviesConnector.instance + .createMovie( genre: 'Action', title: 'The Matrix', releaseYear: 1999, ) .rating(4.5); expect(ref, isNotNull); - expect(ref.build().execute, isNotNull); + expect(ref.ref().execute, isNotNull); }); testWidgets('should have generated correct QueryRef', (WidgetTester tester) async { - final ref = MoviesConnector.instance.listMovies.ref(); + final ref = MoviesConnector.instance.listMovies(); expect(ref, isNotNull); - expect(ref.build().execute, isNotNull); + expect(ref.ref().execute, isNotNull); }); testWidgets('should have generated correct MutationRef using name', (WidgetTester tester) async { - final ref = - MoviesConnector.instance.addPerson.ref().name('Keanu Reeves'); + final ref = MoviesConnector.instance.addPerson().name('Keanu Reeves'); expect(ref, isNotNull); - expect(ref.build().execute, isNotNull); + expect(ref.ref().execute, isNotNull); }); testWidgets('should have generated correct MutationRef using nested id', (WidgetTester tester) async { - final ref = MoviesConnector.instance.addDirectorToMovie - .ref() + final ref = MoviesConnector.instance + .addDirectorToMovie() .movieId('movieId') .personId(AddDirectorToMovieVariablesPersonId(id: 'personId')); expect(ref, isNotNull); - expect(ref.build().execute, isNotNull); + expect(ref.ref().execute, isNotNull); }); }, ); diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/listen_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/listen_e2e.dart index 2d4145994600..c8cfffaa9431 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/listen_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/listen_e2e.dart @@ -21,7 +21,7 @@ void runListenTests() { testWidgets('should be able to listen to the list of movies', (WidgetTester tester) async { final initialValue = - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); expect(initialValue.data.movies.length, 0, reason: 'Initial movie list should be empty'); @@ -29,9 +29,9 @@ void runListenTests() { final Completer hasBeenListened = Completer(); int count = 0; - final listener = MoviesConnector.instance.listMovies + final listener = MoviesConnector.instance + .listMovies() .ref() - .build() .subscribe() .listen((value) { final movies = value.data.movies; @@ -54,17 +54,17 @@ void runListenTests() { await isReady.future; // Create the movie - await MoviesConnector.instance.createMovie - .ref( + await MoviesConnector.instance + .createMovie( genre: 'Action', title: 'The Matrix', releaseYear: 1999, ) .rating(4.5) - .build() + .ref() .execute(); - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); // Wait for the listener to receive the movie update final bool hasListenerReceived = await hasBeenListened.future; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index c262e4035a6c..4ef24e41b467 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -7,14 +7,10 @@ import 'package:firebase_data_connect_example/generated/movies.dart'; import 'package:flutter_test/flutter_test.dart'; Future deleteAllMovies() async { - final value = - await MoviesConnector.instance.listMovies.ref().build().execute(); + final value = await MoviesConnector.instance.listMovies().ref().execute(); final result = value.data; for (var movie in result.movies) { - await MoviesConnector.instance.deleteMovie - .ref(id: movie.id) - .build() - .execute(); + await MoviesConnector.instance.deleteMovie(id: movie.id).ref().execute(); } } @@ -28,78 +24,76 @@ void runQueryTests() { testWidgets('can query', (WidgetTester tester) async { final value = - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); final result = value.data; expect(result.movies.length, 0); }); testWidgets('can add a movie', (WidgetTester tester) async { - MutationRef ref = MoviesConnector.instance.createMovie - .ref( + MutationRef ref = MoviesConnector.instance + .createMovie( genre: 'Action', title: 'The Matrix', releaseYear: 1999, ) .rating(4.5) - .build(); + .ref(); await ref.execute(); final value = - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); final result = value.data; expect(result.movies.length, 1); expect(result.movies[0].title, 'The Matrix'); }); testWidgets('can add a director to a movie', (WidgetTester tester) async { - MutationRef ref = MoviesConnector.instance.addPerson - .ref() - .name('Keanu Reeves') - .build(); + MutationRef ref = + MoviesConnector.instance.addPerson().name('Keanu Reeves').ref(); await ref.execute(); final personId = - (await MoviesConnector.instance.listPersons.ref().build().execute()) + (await MoviesConnector.instance.listPersons().ref().execute()) .data .people[0] .id; final value = - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); final result = value.data; expect(result.movies.length, 0); - ref = MoviesConnector.instance.createMovie - .ref( + ref = MoviesConnector.instance + .createMovie( genre: 'Action', title: 'The Matrix', releaseYear: 1999, ) .rating(4.5) - .build(); + .ref(); await ref.execute(); final value2 = - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); final result2 = value2.data; expect(result2.movies.length, 1); final movieId = result2.movies[0].id; - ref = MoviesConnector.instance.addDirectorToMovie - .ref() + ref = MoviesConnector.instance + .addDirectorToMovie() .movieId(movieId) .personId(AddDirectorToMovieVariablesPersonId(id: personId)) - .build(); + .ref(); await ref.execute(); final value3 = - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); final result3 = value3.data; expect(result3.movies.length, 1); expect(result3.movies[0].directed_by.length, 1); @@ -107,30 +101,30 @@ void runQueryTests() { }); testWidgets('can delete a movie', (WidgetTester tester) async { - MutationRef ref = MoviesConnector.instance.createMovie - .ref( + MutationRef ref = MoviesConnector.instance + .createMovie( genre: 'Action', title: 'The Matrix', releaseYear: 1999, ) .rating(4.5) - .build(); + .ref(); await ref.execute(); final value = - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); final result = value.data; expect(result.movies.length, 1); final movieId = result.movies[0].id; - ref = MoviesConnector.instance.deleteMovie.ref(id: movieId).build(); + ref = MoviesConnector.instance.deleteMovie(id: movieId).ref(); await ref.execute(); final value2 = - await MoviesConnector.instance.listMovies.ref().build().execute(); + await MoviesConnector.instance.listMovies().ref().execute(); final result2 = value2.data; expect(result2.movies.length, 0); }); diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart index 22cadd25caf6..a47e74d62b5b 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart @@ -15,7 +15,12 @@ class AddDateAndTimestampVariablesBuilder { (dynamic json) => AddDateAndTimestampData.fromJson(jsonDecode(json)); Serializer varsSerializer = (AddDateAndTimestampVariables vars) => jsonEncode(vars.toJson()); - MutationRef build() { + Future> + execute() { + return this.ref().execute(); + } + + MutationRef ref() { AddDateAndTimestampVariables vars = AddDateAndTimestampVariables( date: date, timestamp: timestamp, @@ -26,23 +31,6 @@ class AddDateAndTimestampVariablesBuilder { } } -class AddDateAndTimestamp { - String name = "addDateAndTimestamp"; - AddDateAndTimestamp({required this.dataConnect}); - AddDateAndTimestampVariablesBuilder ref({ - required DateTime date, - required Timestamp timestamp, - }) { - return AddDateAndTimestampVariablesBuilder( - dataConnect, - date: date, - timestamp: timestamp, - ); - } - - FirebaseDataConnect dataConnect; -} - class AddDateAndTimestampTimestampHolderInsert { String id; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_director_to_movie.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_director_to_movie.dart index f0170c7bc70a..62879fd75d0f 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_director_to_movie.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_director_to_movie.dart @@ -24,7 +24,12 @@ class AddDirectorToMovieVariablesBuilder { (dynamic json) => AddDirectorToMovieData.fromJson(jsonDecode(json)); Serializer varsSerializer = (AddDirectorToMovieVariables vars) => jsonEncode(vars.toJson()); - MutationRef build() { + Future> + execute() { + return this.ref().execute(); + } + + MutationRef ref() { AddDirectorToMovieVariables vars = AddDirectorToMovieVariables( personId: _personId, movieId: _movieId, @@ -35,18 +40,6 @@ class AddDirectorToMovieVariablesBuilder { } } -class AddDirectorToMovie { - String name = "addDirectorToMovie"; - AddDirectorToMovie({required this.dataConnect}); - AddDirectorToMovieVariablesBuilder ref() { - return AddDirectorToMovieVariablesBuilder( - dataConnect, - ); - } - - FirebaseDataConnect dataConnect; -} - class AddDirectorToMovieDirectedByInsert { String directedbyId; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_person.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_person.dart index f31befd81d12..e5773420278c 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_person.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_person.dart @@ -16,7 +16,11 @@ class AddPersonVariablesBuilder { (dynamic json) => AddPersonData.fromJson(jsonDecode(json)); Serializer varsSerializer = (AddPersonVariables vars) => jsonEncode(vars.toJson()); - MutationRef build() { + Future> execute() { + return this.ref().execute(); + } + + MutationRef ref() { AddPersonVariables vars = AddPersonVariables( name: _name, ); @@ -26,18 +30,6 @@ class AddPersonVariablesBuilder { } } -class AddPerson { - String name = "addPerson"; - AddPerson({required this.dataConnect}); - AddPersonVariablesBuilder ref() { - return AddPersonVariablesBuilder( - dataConnect, - ); - } - - FirebaseDataConnect dataConnect; -} - class AddPersonPersonInsert { String id; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart index 6fd98d0adc88..9a5f4266026e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart @@ -13,7 +13,11 @@ class AddTimestampVariablesBuilder { (dynamic json) => AddTimestampData.fromJson(jsonDecode(json)); Serializer varsSerializer = (AddTimestampVariables vars) => jsonEncode(vars.toJson()); - MutationRef build() { + Future> execute() { + return this.ref().execute(); + } + + MutationRef ref() { AddTimestampVariables vars = AddTimestampVariables( timestamp: timestamp, ); @@ -23,21 +27,6 @@ class AddTimestampVariablesBuilder { } } -class AddTimestamp { - String name = "addTimestamp"; - AddTimestamp({required this.dataConnect}); - AddTimestampVariablesBuilder ref({ - required Timestamp timestamp, - }) { - return AddTimestampVariablesBuilder( - dataConnect, - timestamp: timestamp, - ); - } - - FirebaseDataConnect dataConnect; -} - class AddTimestampTimestampHolderInsert { String id; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/create_movie.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/create_movie.dart index a685dc465dc1..3a3dbed47186 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/create_movie.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/create_movie.dart @@ -29,7 +29,11 @@ class CreateMovieVariablesBuilder { (dynamic json) => CreateMovieData.fromJson(jsonDecode(json)); Serializer varsSerializer = (CreateMovieVariables vars) => jsonEncode(vars.toJson()); - MutationRef build() { + Future> execute() { + return this.ref().execute(); + } + + MutationRef ref() { CreateMovieVariables vars = CreateMovieVariables( title: title, releaseYear: releaseYear, @@ -43,25 +47,6 @@ class CreateMovieVariablesBuilder { } } -class CreateMovie { - String name = "createMovie"; - CreateMovie({required this.dataConnect}); - CreateMovieVariablesBuilder ref({ - required String title, - required int releaseYear, - required String genre, - }) { - return CreateMovieVariablesBuilder( - dataConnect, - title: title, - releaseYear: releaseYear, - genre: genre, - ); - } - - FirebaseDataConnect dataConnect; -} - class CreateMovieMovieInsert { String id; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_movie.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_movie.dart index 326b0b9f9cdb..1b3edbe71cef 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_movie.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_movie.dart @@ -13,7 +13,11 @@ class DeleteMovieVariablesBuilder { (dynamic json) => DeleteMovieData.fromJson(jsonDecode(json)); Serializer varsSerializer = (DeleteMovieVariables vars) => jsonEncode(vars.toJson()); - MutationRef build() { + Future> execute() { + return this.ref().execute(); + } + + MutationRef ref() { DeleteMovieVariables vars = DeleteMovieVariables( id: id, ); @@ -23,21 +27,6 @@ class DeleteMovieVariablesBuilder { } } -class DeleteMovie { - String name = "deleteMovie"; - DeleteMovie({required this.dataConnect}); - DeleteMovieVariablesBuilder ref({ - required String id, - }) { - return DeleteMovieVariablesBuilder( - dataConnect, - id: id, - ); - } - - FirebaseDataConnect dataConnect; -} - class DeleteMovieMovieDelete { String id; @@ -61,7 +50,9 @@ class DeleteMovieData { DeleteMovieMovieDelete? movie_delete; DeleteMovieData.fromJson(dynamic json) - : movie_delete = DeleteMovieMovieDelete.fromJson(json['movie_delete']) {} + : movie_delete = json['movie_delete'] == null + ? null + : DeleteMovieMovieDelete.fromJson(json['movie_delete']) {} Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart index 6d8371f0d472..170559b9a690 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart @@ -9,22 +9,14 @@ class ListMoviesVariablesBuilder { Deserializer dataDeserializer = (dynamic json) => ListMoviesData.fromJson(jsonDecode(json)); - QueryRef build() { - return _dataConnect.query( - "ListMovies", dataDeserializer, emptySerializer, null); + Future> execute() { + return this.ref().execute(); } -} -class ListMovies { - String name = "ListMovies"; - ListMovies({required this.dataConnect}); - ListMoviesVariablesBuilder ref() { - return ListMoviesVariablesBuilder( - dataConnect, - ); + QueryRef ref() { + return _dataConnect.query( + "ListMovies", dataDeserializer, emptySerializer, null); } - - FirebaseDataConnect dataConnect; } class ListMoviesMovies { @@ -42,7 +34,9 @@ class ListMoviesMovies { directed_by = (json['directed_by'] as List) .map((e) => ListMoviesMoviesDirectedBy.fromJson(e)) .toList(), - rating = nativeFromJson(json['rating']) {} + rating = json['rating'] == null + ? null + : nativeFromJson(json['rating']) {} Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_genre.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_genre.dart new file mode 100644 index 000000000000..69d0bd8664f4 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_genre.dart @@ -0,0 +1,155 @@ +part of movies; + +class ListMoviesByGenreVariablesBuilder { + Optional _genre = Optional.optional(nativeFromJson, nativeToJson); + + FirebaseDataConnect _dataConnect; + ListMoviesByGenreVariablesBuilder genre(String t) { + this._genre.value = t; + return this; + } + + ListMoviesByGenreVariablesBuilder( + this._dataConnect, + ); + Deserializer dataDeserializer = + (dynamic json) => ListMoviesByGenreData.fromJson(jsonDecode(json)); + Serializer varsSerializer = + (ListMoviesByGenreVariables vars) => jsonEncode(vars.toJson()); + Future> + execute() { + return this.ref().execute(); + } + + QueryRef ref() { + ListMoviesByGenreVariables vars = ListMoviesByGenreVariables( + genre: _genre, + ); + + return _dataConnect.query( + "ListMoviesByGenre", dataDeserializer, varsSerializer, vars); + } +} + +class ListMoviesByGenreMostPopular { + String id; + + String title; + + double? rating; + + ListMoviesByGenreMostPopular.fromJson(dynamic json) + : id = nativeFromJson(json['id']), + title = nativeFromJson(json['title']), + rating = json['rating'] == null + ? null + : nativeFromJson(json['rating']) {} + + Map toJson() { + Map json = {}; + + json['id'] = nativeToJson(id); + + json['title'] = nativeToJson(title); + + if (rating != null) { + json['rating'] = nativeToJson(rating); + } + + return json; + } + + ListMoviesByGenreMostPopular({ + required this.id, + required this.title, + this.rating, + }); +} + +class ListMoviesByGenreMostRecent { + String id; + + String title; + + double? rating; + + ListMoviesByGenreMostRecent.fromJson(dynamic json) + : id = nativeFromJson(json['id']), + title = nativeFromJson(json['title']), + rating = json['rating'] == null + ? null + : nativeFromJson(json['rating']) {} + + Map toJson() { + Map json = {}; + + json['id'] = nativeToJson(id); + + json['title'] = nativeToJson(title); + + if (rating != null) { + json['rating'] = nativeToJson(rating); + } + + return json; + } + + ListMoviesByGenreMostRecent({ + required this.id, + required this.title, + this.rating, + }); +} + +class ListMoviesByGenreData { + List mostPopular; + + List mostRecent; + + ListMoviesByGenreData.fromJson(dynamic json) + : mostPopular = (json['mostPopular'] as List) + .map((e) => ListMoviesByGenreMostPopular.fromJson(e)) + .toList(), + mostRecent = (json['mostRecent'] as List) + .map((e) => ListMoviesByGenreMostRecent.fromJson(e)) + .toList() {} + + Map toJson() { + Map json = {}; + + json['mostPopular'] = mostPopular.map((e) => e.toJson()).toList(); + + json['mostRecent'] = mostRecent.map((e) => e.toJson()).toList(); + + return json; + } + + ListMoviesByGenreData({ + required this.mostPopular, + required this.mostRecent, + }); +} + +class ListMoviesByGenreVariables { + late Optional genre; + + ListMoviesByGenreVariables.fromJson(Map json) { + genre = Optional.optional(nativeFromJson, nativeToJson); + genre.value = + json['genre'] == null ? null : nativeFromJson(json['genre']); + } + + Map toJson() { + Map json = {}; + + if (genre.state == OptionalState.set) { + json['genre'] = genre.toJson(); + } + + return json; + } + + ListMoviesByGenreVariables({ + required this.genre, + }); +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_partial_title.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_partial_title.dart index 38eea667b30f..0aeb24a0cad4 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_partial_title.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_partial_title.dart @@ -13,8 +13,14 @@ class ListMoviesByPartialTitleVariablesBuilder { (dynamic json) => ListMoviesByPartialTitleData.fromJson(jsonDecode(json)); Serializer varsSerializer = (ListMoviesByPartialTitleVariables vars) => jsonEncode(vars.toJson()); + Future< + QueryResult> execute() { + return this.ref().execute(); + } + QueryRef - build() { + ref() { ListMoviesByPartialTitleVariables vars = ListMoviesByPartialTitleVariables( input: input, ); @@ -24,21 +30,6 @@ class ListMoviesByPartialTitleVariablesBuilder { } } -class ListMoviesByPartialTitle { - String name = "ListMoviesByPartialTitle"; - ListMoviesByPartialTitle({required this.dataConnect}); - ListMoviesByPartialTitleVariablesBuilder ref({ - required String input, - }) { - return ListMoviesByPartialTitleVariablesBuilder( - dataConnect, - input: input, - ); - } - - FirebaseDataConnect dataConnect; -} - class ListMoviesByPartialTitleMovies { String id; @@ -52,7 +43,9 @@ class ListMoviesByPartialTitleMovies { : id = nativeFromJson(json['id']), title = nativeFromJson(json['title']), genre = nativeFromJson(json['genre']), - rating = nativeFromJson(json['rating']) {} + rating = json['rating'] == null + ? null + : nativeFromJson(json['rating']) {} Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_persons.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_persons.dart index cf2d27f2693f..1cbdee8139f8 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_persons.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_persons.dart @@ -9,22 +9,14 @@ class ListPersonsVariablesBuilder { Deserializer dataDeserializer = (dynamic json) => ListPersonsData.fromJson(jsonDecode(json)); - QueryRef build() { - return _dataConnect.query( - "ListPersons", dataDeserializer, emptySerializer, null); + Future> execute() { + return this.ref().execute(); } -} -class ListPersons { - String name = "ListPersons"; - ListPersons({required this.dataConnect}); - ListPersonsVariablesBuilder ref() { - return ListPersonsVariablesBuilder( - dataConnect, - ); + QueryRef ref() { + return _dataConnect.query( + "ListPersons", dataDeserializer, emptySerializer, null); } - - FirebaseDataConnect dataConnect; } class ListPersonsPeople { diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_thing.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_thing.dart index 471682f13c20..50ed195d3723 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_thing.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_thing.dart @@ -17,7 +17,11 @@ class ListThingVariablesBuilder { (dynamic json) => ListThingData.fromJson(jsonDecode(json)); Serializer varsSerializer = (ListThingVariables vars) => jsonEncode(vars.toJson()); - QueryRef build() { + Future> execute() { + return this.ref().execute(); + } + + QueryRef ref() { ListThingVariables vars = ListThingVariables( data: _data, ); @@ -27,18 +31,6 @@ class ListThingVariablesBuilder { } } -class ListThing { - String name = "ListThing"; - ListThing({required this.dataConnect}); - ListThingVariablesBuilder ref() { - return ListThingVariablesBuilder( - dataConnect, - ); - } - - FirebaseDataConnect dataConnect; -} - class ListThingThings { AnyValue title; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_timestamps.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_timestamps.dart index c9efff6081d7..fb3c9e956b53 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_timestamps.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_timestamps.dart @@ -9,22 +9,14 @@ class ListTimestampsVariablesBuilder { Deserializer dataDeserializer = (dynamic json) => ListTimestampsData.fromJson(jsonDecode(json)); - QueryRef build() { - return _dataConnect.query( - "ListTimestamps", dataDeserializer, emptySerializer, null); + Future> execute() { + return this.ref().execute(); } -} -class ListTimestamps { - String name = "ListTimestamps"; - ListTimestamps({required this.dataConnect}); - ListTimestampsVariablesBuilder ref() { - return ListTimestampsVariablesBuilder( - dataConnect, - ); + QueryRef ref() { + return _dataConnect.query( + "ListTimestamps", dataDeserializer, emptySerializer, null); } - - FirebaseDataConnect dataConnect; } class ListTimestampsTimestampHolders { @@ -34,7 +26,9 @@ class ListTimestampsTimestampHolders { ListTimestampsTimestampHolders.fromJson(dynamic json) : timestamp = Timestamp.fromJson(json['timestamp']), - date = nativeFromJson(json['date']) {} + date = json['date'] == null + ? null + : nativeFromJson(json['date']) {} Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart index c85f61434f19..ff72630f4b5c 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart @@ -31,61 +31,118 @@ part 'list_thing.dart'; part 'list_timestamps.dart'; +part 'list_movies_by_genre.dart'; + class MoviesConnector { - AddPerson get addPerson { - return AddPerson(dataConnect: dataConnect); + AddPersonVariablesBuilder addPerson() { + return AddPersonVariablesBuilder( + dataConnect, + ); + } + + AddDirectorToMovieVariablesBuilder addDirectorToMovie() { + return AddDirectorToMovieVariablesBuilder( + dataConnect, + ); } - AddDirectorToMovie get addDirectorToMovie { - return AddDirectorToMovie(dataConnect: dataConnect); + AddTimestampVariablesBuilder addTimestamp({ + required Timestamp timestamp, + }) { + return AddTimestampVariablesBuilder( + dataConnect, + timestamp: timestamp, + ); } - AddTimestamp get addTimestamp { - return AddTimestamp(dataConnect: dataConnect); + AddDateAndTimestampVariablesBuilder addDateAndTimestamp({ + required DateTime date, + required Timestamp timestamp, + }) { + return AddDateAndTimestampVariablesBuilder( + dataConnect, + date: date, + timestamp: timestamp, + ); } - AddDateAndTimestamp get addDateAndTimestamp { - return AddDateAndTimestamp(dataConnect: dataConnect); + SeedMoviesVariablesBuilder seedMovies() { + return SeedMoviesVariablesBuilder( + dataConnect, + ); } - SeedMovies get seedMovies { - return SeedMovies(dataConnect: dataConnect); + CreateMovieVariablesBuilder createMovie({ + required String title, + required int releaseYear, + required String genre, + }) { + return CreateMovieVariablesBuilder( + dataConnect, + title: title, + releaseYear: releaseYear, + genre: genre, + ); } - CreateMovie get createMovie { - return CreateMovie(dataConnect: dataConnect); + DeleteMovieVariablesBuilder deleteMovie({ + required String id, + }) { + return DeleteMovieVariablesBuilder( + dataConnect, + id: id, + ); } - DeleteMovie get deleteMovie { - return DeleteMovie(dataConnect: dataConnect); + ThingVariablesBuilder thing() { + return ThingVariablesBuilder( + dataConnect, + ); } - Thing get thing { - return Thing(dataConnect: dataConnect); + SeedDataVariablesBuilder seedData() { + return SeedDataVariablesBuilder( + dataConnect, + ); } - SeedData get seedData { - return SeedData(dataConnect: dataConnect); + ListMoviesVariablesBuilder listMovies() { + return ListMoviesVariablesBuilder( + dataConnect, + ); } - ListMovies get listMovies { - return ListMovies(dataConnect: dataConnect); + ListMoviesByPartialTitleVariablesBuilder listMoviesByPartialTitle({ + required String input, + }) { + return ListMoviesByPartialTitleVariablesBuilder( + dataConnect, + input: input, + ); } - ListMoviesByPartialTitle get listMoviesByPartialTitle { - return ListMoviesByPartialTitle(dataConnect: dataConnect); + ListPersonsVariablesBuilder listPersons() { + return ListPersonsVariablesBuilder( + dataConnect, + ); } - ListPersons get listPersons { - return ListPersons(dataConnect: dataConnect); + ListThingVariablesBuilder listThing() { + return ListThingVariablesBuilder( + dataConnect, + ); } - ListThing get listThing { - return ListThing(dataConnect: dataConnect); + ListTimestampsVariablesBuilder listTimestamps() { + return ListTimestampsVariablesBuilder( + dataConnect, + ); } - ListTimestamps get listTimestamps { - return ListTimestamps(dataConnect: dataConnect); + ListMoviesByGenreVariablesBuilder listMoviesByGenre() { + return ListMoviesByGenreVariablesBuilder( + dataConnect, + ); } static ConnectorConfig connectorConfig = ConnectorConfig( diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_data.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_data.dart index 28757f8f0dcf..1e90fc1136bf 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_data.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_data.dart @@ -9,22 +9,14 @@ class SeedDataVariablesBuilder { Deserializer dataDeserializer = (dynamic json) => SeedDataData.fromJson(jsonDecode(json)); - MutationRef build() { - return _dataConnect.mutation( - "seedData", dataDeserializer, emptySerializer, null); + Future> execute() { + return this.ref().execute(); } -} -class SeedData { - String name = "seedData"; - SeedData({required this.dataConnect}); - SeedDataVariablesBuilder ref() { - return SeedDataVariablesBuilder( - dataConnect, - ); + MutationRef ref() { + return _dataConnect.mutation( + "seedData", dataDeserializer, emptySerializer, null); } - - FirebaseDataConnect dataConnect; } class SeedDataTheMatrix { diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_movies.dart index 8fbdb3c19254..833930f98388 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_movies.dart @@ -9,22 +9,14 @@ class SeedMoviesVariablesBuilder { Deserializer dataDeserializer = (dynamic json) => SeedMoviesData.fromJson(jsonDecode(json)); - MutationRef build() { - return _dataConnect.mutation( - "seedMovies", dataDeserializer, emptySerializer, null); + Future> execute() { + return this.ref().execute(); } -} -class SeedMovies { - String name = "seedMovies"; - SeedMovies({required this.dataConnect}); - SeedMoviesVariablesBuilder ref() { - return SeedMoviesVariablesBuilder( - dataConnect, - ); + MutationRef ref() { + return _dataConnect.mutation( + "seedMovies", dataDeserializer, emptySerializer, null); } - - FirebaseDataConnect dataConnect; } class SeedMoviesTheMatrix { diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/thing.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/thing.dart index 164eaef843e1..b9b6f3570f43 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/thing.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/thing.dart @@ -17,7 +17,11 @@ class ThingVariablesBuilder { (dynamic json) => ThingData.fromJson(jsonDecode(json)); Serializer varsSerializer = (ThingVariables vars) => jsonEncode(vars.toJson()); - MutationRef build() { + Future> execute() { + return this.ref().execute(); + } + + MutationRef ref() { ThingVariables vars = ThingVariables( title: _title, ); @@ -27,18 +31,6 @@ class ThingVariablesBuilder { } } -class Thing { - String name = "thing"; - Thing({required this.dataConnect}); - ThingVariablesBuilder ref() { - return ThingVariablesBuilder( - dataConnect, - ); - } - - FirebaseDataConnect dataConnect; -} - class ThingThingInsert { String id; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/main.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/main.dart index 5673469f0535..1a198383110d 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/main.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/main.dart @@ -101,7 +101,7 @@ class _DataConnectWidgetState extends State { double _rating = 0; Future triggerReload() async { - QueryRef ref = MoviesConnector.instance.listMovies.ref().build(); + QueryRef ref = MoviesConnector.instance.listMovies().ref(); ref.execute(); } @@ -110,7 +110,7 @@ class _DataConnectWidgetState extends State { super.initState(); QueryRef ref = - MoviesConnector.instance.listMovies.ref().build(); + MoviesConnector.instance.listMovies().ref(); ref.subscribe().listen((event) { setState(() { @@ -185,14 +185,14 @@ class _DataConnectWidgetState extends State { return; } - MutationRef ref = MoviesConnector.instance.createMovie - .ref( + MutationRef ref = MoviesConnector.instance + .createMovie( title: title, releaseYear: _releaseYearDate.year, genre: genre, ) .rating(_rating) - .build(); + .ref(); try { await ref.execute(); triggerReload(); From 8cf4c72ac51a5e653d2f47f86044a0cf8d57f770 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 12:29:45 +0200 Subject: [PATCH 04/18] add e2e tests --- .../.dataconnect/schema/main/implicit.gql | 6 -- .../.dataconnect/schema/main/input.gql | 52 +++++------- .../.dataconnect/schema/main/mutation.gql | 10 --- .../.dataconnect/schema/main/query.gql | 5 -- .../dataconnect/connector/mutations.gql | 21 +++-- .../example/dataconnect/schema/schema.gql | 2 +- .../example/integration_test/query_e2e.dart | 83 +++++++++++++++++++ .../lib/generated/add_date_and_timestamp.dart | 8 +- .../example/lib/generated/add_timestamp.dart | 8 +- .../lib/generated/delete_all_timestamps.dart | 41 +++++++++ .../example/lib/generated/movies.dart | 8 ++ 11 files changed, 171 insertions(+), 73 deletions(-) create mode 100644 packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_all_timestamps.dart diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql index f3cc340c8b71..d7fbe1ff8d51 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql @@ -26,9 +26,3 @@ extend type Thing { """ id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "Thing", purpose: IMPLICIT_KEY_FIELD) } -extend type TimestampHolder { - """ - ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. - """ - id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "TimestampHolder", purpose: IMPLICIT_KEY_FIELD) -} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql index cd0123266442..483808bee877 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql @@ -525,13 +525,17 @@ input Thing_Order { """ input TimestampHolder_Data { """ - ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` """ - id: UUID + timestamp: Timestamp """ - ✨ `_expr` server value variant of `id` (✨ Generated from Field `TimestampHolder`.`id` of type `UUID!`) + ✨ `_expr` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) """ - id_expr: UUID_Expr + timestamp_expr: Timestamp_Expr + """ + ✨ `_time` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) + """ + timestamp_time: Timestamp_Relative """ ✨ Generated from Field `TimestampHolder`.`date` of type `Date` """ @@ -544,18 +548,6 @@ input TimestampHolder_Data { ✨ `_expr` server value variant of `date` (✨ Generated from Field `TimestampHolder`.`date` of type `Date`) """ date_expr: Date_Expr - """ - ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` - """ - timestamp: Timestamp - """ - ✨ `_expr` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) - """ - timestamp_expr: Timestamp_Expr - """ - ✨ `_time` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) - """ - timestamp_time: Timestamp_Relative } """ ✨ Generated filter input type for table 'TimestampHolder'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. @@ -574,17 +566,13 @@ input TimestampHolder_Filter { """ _or: [TimestampHolder_Filter!] """ - ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` """ - id: UUID_Filter + timestamp: Timestamp_Filter """ ✨ Generated from Field `TimestampHolder`.`date` of type `Date` """ date: Date_Filter - """ - ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` - """ - timestamp: Timestamp_Filter } """ ✨ Generated first-row input type for table 'TimestampHolder'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. @@ -604,13 +592,17 @@ input TimestampHolder_FirstRow { """ input TimestampHolder_Key { """ - ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` """ - id: UUID + timestamp: Timestamp """ - ✨ `_expr` server value variant of `id` (✨ Generated from Field `TimestampHolder`.`id` of type `UUID!`) + ✨ `_expr` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) """ - id_expr: UUID_Expr + timestamp_expr: Timestamp_Expr + """ + ✨ `_time` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) + """ + timestamp_time: Timestamp_Relative } """ ✨ Generated list filter input type for table 'TimestampHolder'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. @@ -630,15 +622,11 @@ input TimestampHolder_ListFilter { """ input TimestampHolder_Order { """ - ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` """ - id: OrderDirection + timestamp: OrderDirection """ ✨ Generated from Field `TimestampHolder`.`date` of type `Date` """ date: OrderDirection - """ - ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` - """ - timestamp: OrderDirection } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql index 4628c9b8401c..947c23afa779 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql @@ -229,11 +229,6 @@ extend type Mutation { ✨ Update a single `TimestampHolder` based on `id`, `key` or `first`, setting columns specified in `data`. Returns `null` if not found. """ timestampHolder_update( - """ - The unique ID of the object. - """ - id: UUID - """ The key used to identify the object. """ @@ -419,11 +414,6 @@ extend type Mutation { ✨ Delete a single `TimestampHolder` based on `id`, `key` or `first` and return its key (or `null` if not found). """ timestampHolder_delete( - """ - The unique ID of the object. - """ - id: UUID - """ The key used to identify the object. """ diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql index 1adc73e26313..fefe75242191 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql @@ -74,11 +74,6 @@ extend type Query { ✨ Look up a single `TimestampHolder` based on `id`, `key` or `first` and return selected fields (or `null` if not found). """ timestampHolder( - """ - The unique ID of the object. - """ - id: UUID - """ The key used to identify the object. """ diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/mutations.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/mutations.gql index 1e4194f7999b..879ed8c7e404 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/mutations.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/mutations.gql @@ -7,15 +7,16 @@ mutation addDirectorToMovie($personId: Person_Key, $movieId: UUID) directedBy_insert(data: { directedby: $personId, movieId: $movieId }) } mutation addTimestamp($timestamp: Timestamp!) @auth(level: PUBLIC) { - timestampHolder_insert(data: { - timestamp: $timestamp - }) + timestampHolder_insert(data: { timestamp: $timestamp }) } -mutation addDateAndTimestamp($date: Date!, $timestamp: Timestamp!) @auth(level: PUBLIC) { - timestampHolder_insert(data: { - date: $date, - timestamp: $timestamp - }) + +mutation addDateAndTimestamp($date: Date!, $timestamp: Timestamp!) +@auth(level: PUBLIC) { + timestampHolder_insert(data: { date: $date, timestamp: $timestamp }) +} + +mutation deleteAllTimestamps @auth(level: PUBLIC) { + timestampHolder_deleteMany(all: true) } mutation seedMovies @auth(level: PUBLIC) { @@ -63,9 +64,7 @@ mutation deleteMovie($id: UUID!) @auth(level: PUBLIC) { } mutation thing($title: Any! = "ABC") @auth(level: PUBLIC) { - thing_insert(data: { - title: $title - }) + thing_insert(data: { title: $title }) } mutation seedData @auth(level: PUBLIC) { diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/schema/schema.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/schema/schema.gql index 5dac9add4885..2a51ef78d160 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/schema/schema.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/schema/schema.gql @@ -15,7 +15,7 @@ type Movie @table { type Thing @table { title: Any! } -type TimestampHolder @table { +type TimestampHolder @table(key: ["timestamp"]) { timestamp: Timestamp! date: Date } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index 4ef24e41b467..132306490f40 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -14,6 +14,10 @@ Future deleteAllMovies() async { } } +Future deleteAllTimestamps() async { + await MoviesConnector.instance.deleteAllTimestamps().ref().execute(); +} + void runQueryTests() { group( '$FirebaseDataConnect.instance query', @@ -128,6 +132,85 @@ void runQueryTests() { final result2 = value2.data; expect(result2.movies.length, 0); }); + + testWidgets('should be able to ignore optionnal values', + (WidgetTester tester) async { + MutationRef ref = MoviesConnector.instance + .createMovie( + genre: 'Action', + title: 'The Matrix', + releaseYear: 1999, + ) + .ref(); + + await ref.execute(); + + MutationRef ref2 = MoviesConnector.instance + .createMovie( + genre: 'SF', + title: 'Star Wars', + releaseYear: 1977, + ) + .ref(); + await ref2.execute(); + + final value = + await MoviesConnector.instance.listMoviesByGenre().ref().execute(); + final result = value.data; + expect(result.mostPopular.length, 1); + expect(result.mostPopular[0].title, 'The Matrix'); + + final value2 = await MoviesConnector.instance + .listMoviesByGenre() + .genre('SF') + .ref() + .execute(); + + final result2 = value2.data; + expect(result2.mostPopular.length, 1); + expect(result2.mostPopular[0].title, 'Star Wars'); + }); }, ); + + group('timestamp into query', () { + testWidgets('should be able to add a timestamp', + (WidgetTester tester) async { + await deleteAllTimestamps(); + final timestamp = Timestamp.fromJson('1970-01-11T00:00:00Z'); + MutationRef ref = + MoviesConnector.instance.addTimestamp(timestamp: timestamp).ref(); + + await ref.execute(); + + final value = + await MoviesConnector.instance.listTimestamps().ref().execute(); + final result = value.data; + expect(result.timestampHolders.length, 1); + expect(result.timestampHolders[0].timestamp.seconds, timestamp.seconds); + expect(result.timestampHolders[0].timestamp.nanoseconds, + timestamp.nanoseconds); + }); + + testWidgets('should be able to add a date and a timestamp', + (WidgetTester tester) async { + await deleteAllTimestamps(); + final timestamp = Timestamp.fromJson('1970-01-11T00:00:00Z'); + final date = DateTime.parse('1970-01-11T00:00:00Z'); + MutationRef ref = MoviesConnector.instance + .addDateAndTimestamp(date: date, timestamp: timestamp) + .ref(); + + await ref.execute(); + + final value = + await MoviesConnector.instance.listTimestamps().ref().execute(); + final result = value.data; + expect(result.timestampHolders.length, 1); + expect(result.timestampHolders[0].timestamp.seconds, timestamp.seconds); + expect(result.timestampHolders[0].timestamp.nanoseconds, + timestamp.nanoseconds); + expect(result.timestampHolders[0].date, date); + }); + }); } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart index a47e74d62b5b..53e79a0bb44e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart @@ -32,21 +32,21 @@ class AddDateAndTimestampVariablesBuilder { } class AddDateAndTimestampTimestampHolderInsert { - String id; + Timestamp timestamp; AddDateAndTimestampTimestampHolderInsert.fromJson(dynamic json) - : id = nativeFromJson(json['id']) {} + : timestamp = Timestamp.fromJson(json['timestamp']) {} Map toJson() { Map json = {}; - json['id'] = nativeToJson(id); + json['timestamp'] = timestamp.toJson(); return json; } AddDateAndTimestampTimestampHolderInsert({ - required this.id, + required this.timestamp, }); } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart index 9a5f4266026e..685c15c2e358 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart @@ -28,21 +28,21 @@ class AddTimestampVariablesBuilder { } class AddTimestampTimestampHolderInsert { - String id; + Timestamp timestamp; AddTimestampTimestampHolderInsert.fromJson(dynamic json) - : id = nativeFromJson(json['id']) {} + : timestamp = Timestamp.fromJson(json['timestamp']) {} Map toJson() { Map json = {}; - json['id'] = nativeToJson(id); + json['timestamp'] = timestamp.toJson(); return json; } AddTimestampTimestampHolderInsert({ - required this.id, + required this.timestamp, }); } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_all_timestamps.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_all_timestamps.dart new file mode 100644 index 000000000000..182ac579e9b1 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_all_timestamps.dart @@ -0,0 +1,41 @@ +part of movies; + +class DeleteAllTimestampsVariablesBuilder { + FirebaseDataConnect _dataConnect; + + DeleteAllTimestampsVariablesBuilder( + this._dataConnect, + ); + Deserializer dataDeserializer = + (dynamic json) => DeleteAllTimestampsData.fromJson(jsonDecode(json)); + + Future> execute() { + return this.ref().execute(); + } + + MutationRef ref() { + return _dataConnect.mutation( + "deleteAllTimestamps", dataDeserializer, emptySerializer, null); + } +} + +class DeleteAllTimestampsData { + int timestampHolder_deleteMany; + + DeleteAllTimestampsData.fromJson(dynamic json) + : timestampHolder_deleteMany = + nativeFromJson(json['timestampHolder_deleteMany']) {} + + Map toJson() { + Map json = {}; + + json['timestampHolder_deleteMany'] = + nativeToJson(timestampHolder_deleteMany); + + return json; + } + + DeleteAllTimestampsData({ + required this.timestampHolder_deleteMany, + }); +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart index ff72630f4b5c..e2151b9e2d89 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart @@ -11,6 +11,8 @@ part 'add_timestamp.dart'; part 'add_date_and_timestamp.dart'; +part 'delete_all_timestamps.dart'; + part 'seed_movies.dart'; part 'create_movie.dart'; @@ -66,6 +68,12 @@ class MoviesConnector { ); } + DeleteAllTimestampsVariablesBuilder deleteAllTimestamps() { + return DeleteAllTimestampsVariablesBuilder( + dataConnect, + ); + } + SeedMoviesVariablesBuilder seedMovies() { return SeedMoviesVariablesBuilder( dataConnect, From 9e61a55c79428d81f7838a70042be1008c7c5368 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 14:04:22 +0200 Subject: [PATCH 05/18] add licence --- .../test/src/core/ref_test.mocks.dart | 13 +++++++++++++ .../test/src/network/grpc_transport_test.dart | 13 +++++++++++++ .../test/src/network/grpc_transport_test.mocks.dart | 13 +++++++++++++ 3 files changed, 39 insertions(+) diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart index e70983d3d58f..e8452de50feb 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart @@ -1,3 +1,16 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // Mocks generated by Mockito 5.4.4 from annotations // in firebase_data_connect/test/src/core/ref_test.dart. // Do not manually edit this file. diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart index 631aa135fc39..5c9290116950 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart @@ -1,3 +1,16 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_data_connect/src/common/common_library.dart'; diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart index 48ec8af42b40..348b080db0db 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart @@ -1,3 +1,16 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // Mocks generated by Mockito 5.4.4 from annotations // in firebase_data_connect/test/src/network/grpc_transport_test.dart. // Do not manually edit this file. From 13536d0b3a443b27b1e14e2196ebed03fae718e8 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 14:06:32 +0200 Subject: [PATCH 06/18] test --- .../example/integration_test/query_e2e.dart | 4 ++-- .../firebase_data_connect/lib/src/network/grpc_transport.dart | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index 132306490f40..3ae34f4f3f69 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -195,8 +195,8 @@ void runQueryTests() { testWidgets('should be able to add a date and a timestamp', (WidgetTester tester) async { await deleteAllTimestamps(); - final timestamp = Timestamp.fromJson('1970-01-11T00:00:00Z'); - final date = DateTime.parse('1970-01-11T00:00:00Z'); + final timestamp = Timestamp.fromJson('1970-01-11T00:00:00'); + final date = DateTime.parse('1970-01-11T00:00:00'); MutationRef ref = MoviesConnector.instance .addDateAndTimestamp(date: date, timestamp: timestamp) .ref(); diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart index 09675e41f0c7..7956f9e1c1e2 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart @@ -116,7 +116,6 @@ class GRPCTransport implements DataConnectTransport { try { response = await stub.executeQuery(request, options: CallOptions(metadata: await getMetadata())); - print("coucou response $response"); return deserializer(jsonEncode(response.data.toProto3Json())); } on Exception catch (e) { throw DataConnectError(DataConnectErrorCode.other, From ac9ba60963fe80399980f9dcd6afb4b3081c1801 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 14:15:27 +0200 Subject: [PATCH 07/18] test --- .../example/integration_test/query_e2e.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index 3ae34f4f3f69..132306490f40 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -195,8 +195,8 @@ void runQueryTests() { testWidgets('should be able to add a date and a timestamp', (WidgetTester tester) async { await deleteAllTimestamps(); - final timestamp = Timestamp.fromJson('1970-01-11T00:00:00'); - final date = DateTime.parse('1970-01-11T00:00:00'); + final timestamp = Timestamp.fromJson('1970-01-11T00:00:00Z'); + final date = DateTime.parse('1970-01-11T00:00:00Z'); MutationRef ref = MoviesConnector.instance .addDateAndTimestamp(date: date, timestamp: timestamp) .ref(); From 7eded02fb8398765af8532b015670d93f25e611f Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 14:15:46 +0200 Subject: [PATCH 08/18] more --- .../example/integration_test/query_e2e.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index 132306490f40..e70c4b0a3894 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -196,7 +196,7 @@ void runQueryTests() { (WidgetTester tester) async { await deleteAllTimestamps(); final timestamp = Timestamp.fromJson('1970-01-11T00:00:00Z'); - final date = DateTime.parse('1970-01-11T00:00:00Z'); + final date = timestamp.toDateTime(); MutationRef ref = MoviesConnector.instance .addDateAndTimestamp(date: date, timestamp: timestamp) .ref(); From f6de0b2c041864fd1c584491ca9f5c2c92b5524a Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 14:29:21 +0200 Subject: [PATCH 09/18] tests --- .../example/integration_test/query_e2e.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index e70c4b0a3894..1d3f502e0b7e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -196,7 +196,7 @@ void runQueryTests() { (WidgetTester tester) async { await deleteAllTimestamps(); final timestamp = Timestamp.fromJson('1970-01-11T00:00:00Z'); - final date = timestamp.toDateTime(); + final date = DateTime(1970, 1, 11); MutationRef ref = MoviesConnector.instance .addDateAndTimestamp(date: date, timestamp: timestamp) .ref(); From e50922bf5cd5fab10b9bb93918e5741433d8c273 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 9 Oct 2024 14:43:08 +0200 Subject: [PATCH 10/18] more tests --- .../example/dataconnect/connector/queries.gql | 2 ++ .../integration_test/generation_e2e.dart | 16 ++++++++++++++ .../example/integration_test/query_e2e.dart | 21 +++++++++++++++++++ .../example/lib/generated/list_movies.dart | 16 ++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/queries.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/queries.gql index 2bce5f89e109..d0769d53a329 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/queries.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/queries.gql @@ -3,6 +3,8 @@ query ListMovies @auth(level: USER) { movies { id title + genre + releaseYear directed_by: people_via_DirectedBy { name } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart index 71307831d987..5bf78070cb8d 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart @@ -64,6 +64,22 @@ void runGenerationTest() { expect(ref, isNotNull); expect(ref.ref().execute, isNotNull); }); + + testWidgets( + 'should populate only non-required fields with builder pattern', + (WidgetTester tester) async { + final movieBuilder = MoviesConnector.instance.createMovie( + genre: 'Action', + title: 'Inception', + releaseYear: 2010, + ); + + movieBuilder.rating(4.8); + + final mutationRef = movieBuilder.ref(); + expect(mutationRef, isNotNull); + expect(mutationRef.execute, isNotNull); + }); }, ); } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index 1d3f502e0b7e..a304fefc2c4b 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -212,5 +212,26 @@ void runQueryTests() { timestamp.nanoseconds); expect(result.timestampHolders[0].date, date); }); + + testWidgets('should only send fields set in the builder pattern', + (WidgetTester tester) async { + await deleteAllMovies(); + final movieBuilder = MoviesConnector.instance.createMovie( + genre: 'Action', // Required field + title: 'Inception', // Required field + releaseYear: 2010, // Required field + ); + + await movieBuilder.ref().execute(); + + final value = await MoviesConnector.instance.listMovies().ref().execute(); + + final result = value.data; + expect(result.movies.length, 1); + expect(result.movies[0].title, 'Inception'); + expect(result.movies[0].genre, 'Action'); + expect(result.movies[0].releaseYear, 2010); + expect(result.movies[0].rating, null); + }); }); } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart index 170559b9a690..18ff51f45bcb 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart @@ -24,6 +24,10 @@ class ListMoviesMovies { String title; + String genre; + + int? releaseYear; + List directed_by; double? rating; @@ -31,6 +35,10 @@ class ListMoviesMovies { ListMoviesMovies.fromJson(dynamic json) : id = nativeFromJson(json['id']), title = nativeFromJson(json['title']), + genre = nativeFromJson(json['genre']), + releaseYear = json['releaseYear'] == null + ? null + : nativeFromJson(json['releaseYear']), directed_by = (json['directed_by'] as List) .map((e) => ListMoviesMoviesDirectedBy.fromJson(e)) .toList(), @@ -45,6 +53,12 @@ class ListMoviesMovies { json['title'] = nativeToJson(title); + json['genre'] = nativeToJson(genre); + + if (releaseYear != null) { + json['releaseYear'] = nativeToJson(releaseYear); + } + json['directed_by'] = directed_by.map((e) => e.toJson()).toList(); if (rating != null) { @@ -57,6 +71,8 @@ class ListMoviesMovies { ListMoviesMovies({ required this.id, required this.title, + required this.genre, + this.releaseYear, required this.directed_by, this.rating, }); From aa997f8b05002d4d59ce924403afcd5b041735b1 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Tue, 29 Oct 2024 14:55:34 +0100 Subject: [PATCH 11/18] Update packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart Co-authored-by: Maneesh Tewani --- .../example/integration_test/query_e2e.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index a304fefc2c4b..daff17830029 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -133,7 +133,7 @@ void runQueryTests() { expect(result2.movies.length, 0); }); - testWidgets('should be able to ignore optionnal values', + testWidgets('should be able to ignore optional values', (WidgetTester tester) async { MutationRef ref = MoviesConnector.instance .createMovie( From 7c545e6903746301de8bfaa6109cd3ecf3f94970 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Tue, 29 Oct 2024 14:56:36 +0100 Subject: [PATCH 12/18] remove useless mock --- .../firebase_data_connect/test/src/core/ref_test.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart index cf3dfa3cf051..a90c5b291028 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart @@ -108,9 +108,6 @@ void main() { final mockStreamController = MockStreamController(); final completer = Completer(); - when(mockTransport.invokeQuery('testQuery', any, any, null)) - .thenThrow(Exception('Test Error')); - final streamController = StreamController.broadcast(); queryManager.trackedQueries['testQuery'] = {'': streamController}; From b905c9773b4b74eda32979a760b185bb6e0bc2a6 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Tue, 29 Oct 2024 15:01:16 +0100 Subject: [PATCH 13/18] format --- .../example/lib/generated/movies.dart | 195 ++++++++++-------- 1 file changed, 112 insertions(+), 83 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart index b7ab684c1a47..e2151b9e2d89 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart @@ -1,4 +1,5 @@ library movies; + import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'dart:convert'; @@ -34,94 +35,123 @@ part 'list_timestamps.dart'; part 'list_movies_by_genre.dart'; +class MoviesConnector { + AddPersonVariablesBuilder addPerson() { + return AddPersonVariablesBuilder( + dataConnect, + ); + } + AddDirectorToMovieVariablesBuilder addDirectorToMovie() { + return AddDirectorToMovieVariablesBuilder( + dataConnect, + ); + } + AddTimestampVariablesBuilder addTimestamp({ + required Timestamp timestamp, + }) { + return AddTimestampVariablesBuilder( + dataConnect, + timestamp: timestamp, + ); + } + AddDateAndTimestampVariablesBuilder addDateAndTimestamp({ + required DateTime date, + required Timestamp timestamp, + }) { + return AddDateAndTimestampVariablesBuilder( + dataConnect, + date: date, + timestamp: timestamp, + ); + } + DeleteAllTimestampsVariablesBuilder deleteAllTimestamps() { + return DeleteAllTimestampsVariablesBuilder( + dataConnect, + ); + } + SeedMoviesVariablesBuilder seedMovies() { + return SeedMoviesVariablesBuilder( + dataConnect, + ); + } -class MoviesConnector { - - - AddPersonVariablesBuilder addPerson () { - return AddPersonVariablesBuilder(dataConnect, ); - } - - - AddDirectorToMovieVariablesBuilder addDirectorToMovie () { - return AddDirectorToMovieVariablesBuilder(dataConnect, ); - } - - - AddTimestampVariablesBuilder addTimestamp ({required Timestamp timestamp,}) { - return AddTimestampVariablesBuilder(dataConnect, timestamp: timestamp,); - } - - - AddDateAndTimestampVariablesBuilder addDateAndTimestamp ({required DateTime date,required Timestamp timestamp,}) { - return AddDateAndTimestampVariablesBuilder(dataConnect, date: date,timestamp: timestamp,); - } - - - DeleteAllTimestampsVariablesBuilder deleteAllTimestamps () { - return DeleteAllTimestampsVariablesBuilder(dataConnect, ); - } - - - SeedMoviesVariablesBuilder seedMovies () { - return SeedMoviesVariablesBuilder(dataConnect, ); - } - - - CreateMovieVariablesBuilder createMovie ({required String title,required int releaseYear,required String genre,}) { - return CreateMovieVariablesBuilder(dataConnect, title: title,releaseYear: releaseYear,genre: genre,); - } - - - DeleteMovieVariablesBuilder deleteMovie ({required String id,}) { - return DeleteMovieVariablesBuilder(dataConnect, id: id,); - } - - - ThingVariablesBuilder thing () { - return ThingVariablesBuilder(dataConnect, ); - } - - - SeedDataVariablesBuilder seedData () { - return SeedDataVariablesBuilder(dataConnect, ); - } - - - ListMoviesVariablesBuilder listMovies () { - return ListMoviesVariablesBuilder(dataConnect, ); - } - - - ListMoviesByPartialTitleVariablesBuilder listMoviesByPartialTitle ({required String input,}) { - return ListMoviesByPartialTitleVariablesBuilder(dataConnect, input: input,); - } - - - ListPersonsVariablesBuilder listPersons () { - return ListPersonsVariablesBuilder(dataConnect, ); - } - - - ListThingVariablesBuilder listThing () { - return ListThingVariablesBuilder(dataConnect, ); - } - - - ListTimestampsVariablesBuilder listTimestamps () { - return ListTimestampsVariablesBuilder(dataConnect, ); - } - - - ListMoviesByGenreVariablesBuilder listMoviesByGenre () { - return ListMoviesByGenreVariablesBuilder(dataConnect, ); - } - + CreateMovieVariablesBuilder createMovie({ + required String title, + required int releaseYear, + required String genre, + }) { + return CreateMovieVariablesBuilder( + dataConnect, + title: title, + releaseYear: releaseYear, + genre: genre, + ); + } + + DeleteMovieVariablesBuilder deleteMovie({ + required String id, + }) { + return DeleteMovieVariablesBuilder( + dataConnect, + id: id, + ); + } + + ThingVariablesBuilder thing() { + return ThingVariablesBuilder( + dataConnect, + ); + } + + SeedDataVariablesBuilder seedData() { + return SeedDataVariablesBuilder( + dataConnect, + ); + } + + ListMoviesVariablesBuilder listMovies() { + return ListMoviesVariablesBuilder( + dataConnect, + ); + } + + ListMoviesByPartialTitleVariablesBuilder listMoviesByPartialTitle({ + required String input, + }) { + return ListMoviesByPartialTitleVariablesBuilder( + dataConnect, + input: input, + ); + } + + ListPersonsVariablesBuilder listPersons() { + return ListPersonsVariablesBuilder( + dataConnect, + ); + } + + ListThingVariablesBuilder listThing() { + return ListThingVariablesBuilder( + dataConnect, + ); + } + + ListTimestampsVariablesBuilder listTimestamps() { + return ListTimestampsVariablesBuilder( + dataConnect, + ); + } + + ListMoviesByGenreVariablesBuilder listMoviesByGenre() { + return ListMoviesByGenreVariablesBuilder( + dataConnect, + ); + } static ConnectorConfig connectorConfig = ConnectorConfig( 'us-west2', @@ -139,4 +169,3 @@ class MoviesConnector { FirebaseDataConnect dataConnect; } - From 6be43ead890e7c0d4925cad32256aeb2b38b402e Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Tue, 29 Oct 2024 15:01:42 +0100 Subject: [PATCH 14/18] more --- .../example/integration_test/generation_e2e.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart index e3efd29a2044..d30191556e7e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/generation_e2e.dart @@ -79,7 +79,6 @@ void runGenerationTest() { final mutationRef = movieBuilder.ref(); expect(mutationRef, isNotNull); expect(mutationRef.execute, isNotNull); - expect(ref.execute, isNotNull); }); }, ); From d5141615d7356850beccbccd7f25d1697b472035 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Tue, 29 Oct 2024 15:08:07 +0100 Subject: [PATCH 15/18] mocks updated --- .../test/src/core/ref_test.dart | 3 -- .../test/src/core/ref_test.mocks.dart | 47 +++++++------------ .../src/firebase_data_connect_test.mocks.dart | 14 ------ .../test/src/network/grpc_transport_test.dart | 1 - .../network/grpc_transport_test.mocks.dart | 13 ----- .../network/rest_transport_test.mocks.dart | 14 ------ 6 files changed, 16 insertions(+), 76 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart index d35796a6110f..7f1aca52e9b4 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart @@ -28,9 +28,6 @@ import 'package:mockito/mockito.dart'; import '../network/rest_transport_test.mocks.dart'; -// Mock classes -class MockDataConnectTransport extends Mock implements DataConnectTransport {} - class MockFirebaseDataConnect extends Mock implements FirebaseDataConnect {} class DCMockUser extends Mock implements User { diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart index e8452de50feb..4983386e4ce6 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.mocks.dart @@ -1,16 +1,3 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. // Mocks generated by Mockito 5.4.4 from annotations // in firebase_data_connect/test/src/core/ref_test.dart. // Do not manually edit this file. @@ -18,11 +5,10 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; -import 'package:firebase_app_check/firebase_app_check.dart' as _i5; -import 'package:firebase_auth/firebase_auth.dart' as _i4; +import 'package:firebase_app_check/firebase_app_check.dart' as _i4; import 'package:firebase_data_connect/src/common/common_library.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i6; +import 'package:mockito/src/dummies.dart' as _i5; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -116,16 +102,7 @@ class MockDataConnectTransport extends _i1.Mock ); @override - set auth(_i4.FirebaseAuth? _auth) => super.noSuchMethod( - Invocation.setter( - #auth, - _auth, - ), - returnValueForMissingStub: null, - ); - - @override - set appCheck(_i5.FirebaseAppCheck? _appCheck) => super.noSuchMethod( + set appCheck(_i4.FirebaseAppCheck? _appCheck) => super.noSuchMethod( Invocation.setter( #appCheck, _appCheck, @@ -151,7 +128,7 @@ class MockDataConnectTransport extends _i1.Mock @override String get appId => (super.noSuchMethod( Invocation.getter(#appId), - returnValue: _i6.dummyValue( + returnValue: _i5.dummyValue( this, Invocation.getter(#appId), ), @@ -172,6 +149,7 @@ class MockDataConnectTransport extends _i1.Mock _i2.Deserializer? deserializer, _i2.Serializer? serializer, Variables? vars, + String? token, ) => (super.noSuchMethod( Invocation.method( @@ -181,10 +159,11 @@ class MockDataConnectTransport extends _i1.Mock deserializer, serializer, vars, + token, ], ), - returnValue: _i6.ifNotNull( - _i6.dummyValueOrNull( + returnValue: _i5.ifNotNull( + _i5.dummyValueOrNull( this, Invocation.method( #invokeQuery, @@ -193,6 +172,7 @@ class MockDataConnectTransport extends _i1.Mock deserializer, serializer, vars, + token, ], ), ), @@ -207,6 +187,7 @@ class MockDataConnectTransport extends _i1.Mock deserializer, serializer, vars, + token, ], ), ), @@ -218,6 +199,7 @@ class MockDataConnectTransport extends _i1.Mock _i2.Deserializer? deserializer, _i2.Serializer? serializer, Variables? vars, + String? token, ) => (super.noSuchMethod( Invocation.method( @@ -227,10 +209,11 @@ class MockDataConnectTransport extends _i1.Mock deserializer, serializer, vars, + token, ], ), - returnValue: _i6.ifNotNull( - _i6.dummyValueOrNull( + returnValue: _i5.ifNotNull( + _i5.dummyValueOrNull( this, Invocation.method( #invokeMutation, @@ -239,6 +222,7 @@ class MockDataConnectTransport extends _i1.Mock deserializer, serializer, vars, + token, ], ), ), @@ -253,6 +237,7 @@ class MockDataConnectTransport extends _i1.Mock deserializer, serializer, vars, + token, ], ), ), diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart index 228dc815abe0..ef96fe7c1e26 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart @@ -1,17 +1,3 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - // Mocks generated by Mockito 5.4.4 from annotations // in firebase_data_connect/test/src/firebase_data_connect_test.dart. // Do not manually edit this file. diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart index 5c9290116950..81fda9f1cd9a 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart @@ -57,7 +57,6 @@ void main() { ), 'testAppId', CallerSDKType.core, - mockAuth, mockAppCheck, ); diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart index 348b080db0db..48ec8af42b40 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.mocks.dart @@ -1,16 +1,3 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. // Mocks generated by Mockito 5.4.4 from annotations // in firebase_data_connect/test/src/network/grpc_transport_test.dart. // Do not manually edit this file. diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart index cb633caecf6f..244aad50e971 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart @@ -1,17 +1,3 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - // Mocks generated by Mockito 5.4.4 from annotations // in firebase_data_connect/test/src/network/rest_transport_test.dart. // Do not manually edit this file. From ef8d65ea0d562a16c9e2ac4ed1dc48cb8c3d5604 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Tue, 29 Oct 2024 15:13:05 +0100 Subject: [PATCH 16/18] tests --- .../test/src/core/ref_test.dart | 3 ++- .../test/src/network/grpc_transport_test.dart | 12 ++++++------ .../test/src/network/rest_transport_test.dart | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart index 7f1aca52e9b4..2e4417c72439 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart @@ -27,6 +27,7 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import '../network/rest_transport_test.mocks.dart'; +import 'ref_test.mocks.dart'; class MockFirebaseDataConnect extends Mock implements FirebaseDataConnect {} @@ -217,7 +218,7 @@ void main() { final queryManager = QueryManager(mockDataConnect); // Simulate server throwing an exception - when(mockTransport.invokeQuery(any, any, any, any)) + when(mockTransport.invokeQuery(any, any, any, any, null)) .thenThrow(Exception('Server Error')); final queryRef = QueryRef( diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart index 81fda9f1cd9a..452a58a0380e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/grpc_transport_test.dart @@ -82,7 +82,6 @@ void main() { ), 'testAppId', CallerSDKType.core, - mockAuth, mockAppCheck, ); @@ -100,7 +99,8 @@ void main() { final deserializer = (String data) => 'Deserialized Data'; expect( - () => transport.invokeQuery('testQuery', deserializer, null, null), + () => + transport.invokeQuery('testQuery', deserializer, null, null, null), throwsA(isA()), ); }); @@ -113,8 +113,8 @@ void main() { final deserializer = (String data) => 'Deserialized Data'; expect( - () => - transport.invokeMutation('testMutation', deserializer, null, null), + () => transport.invokeMutation( + 'testMutation', deserializer, null, null, null), throwsA(isA()), ); }); @@ -124,7 +124,7 @@ void main() { when(mockUser.getIdToken()).thenAnswer((_) async => 'authToken123'); when(mockAppCheck.getToken()).thenAnswer((_) async => 'appCheckToken123'); - final metadata = await transport.getMetadata(); + final metadata = await transport.getMetadata('authToken123'); expect(metadata['x-firebase-auth-token'], 'authToken123'); expect(metadata['X-Firebase-AppCheck'], 'appCheckToken123'); @@ -136,7 +136,7 @@ void main() { when(mockUser.getIdToken()).thenThrow(Exception('Auth error')); when(mockAppCheck.getToken()).thenThrow(Exception('AppCheck error')); - final metadata = await transport.getMetadata(); + final metadata = await transport.getMetadata(null); expect(metadata.containsKey('x-firebase-auth-token'), isFalse); expect(metadata.containsKey('X-Firebase-AppCheck'), isFalse); diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart index a3d0f92dd0ea..780d42884f04 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart @@ -218,8 +218,8 @@ void main() { final deserializer = (String data) => 'Deserialized Data'; - await transport.invokeOperation( - 'testQuery', deserializer, null, null, 'executeQuery'); + await transport.invokeOperation('testQuery', 'testEndpoint', deserializer, + null, null, 'executeQuery'); verify(mockHttpClient.post( any, From 8d0e1b82c185abfaef0b8c8199ef8e561e4b749f Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Tue, 29 Oct 2024 15:14:55 +0100 Subject: [PATCH 17/18] fix licence --- melos.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/melos.yaml b/melos.yaml index 78ba09d39ad5..5906dec3ef80 100644 --- a/melos.yaml +++ b/melos.yaml @@ -266,6 +266,7 @@ scripts: --ignore "**/*.yaml" \ --ignore "**/*.xml" \ --ignore "**/*.g.dart" \ + --ignore "**/*.mocks.dart" \ --ignore "**/*.sh" \ --ignore "**/*.html" \ --ignore "**/*.js" \ From 0b729a9e9715dfea546265322be897972b3a879d Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Tue, 29 Oct 2024 15:17:19 +0100 Subject: [PATCH 18/18] licence --- melos.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/melos.yaml b/melos.yaml index 5906dec3ef80..f7cc3c94b6e9 100644 --- a/melos.yaml +++ b/melos.yaml @@ -303,6 +303,7 @@ scripts: --ignore "**/*.yaml" \ --ignore "**/*.xml" \ --ignore "**/*.g.dart" \ + --ignore "**/*.mocks.dart" \ --ignore "**/*.sh" \ --ignore "**/*.html" \ --ignore "**/*.js" \