Skip to content

Commit

Permalink
Add SQL and MQL tabular data query (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
njooma authored Nov 15, 2023
1 parent 2e941d7 commit b661f4a
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 13 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ setup:
format:
dart format --line-length=140 --set-exit-if-changed $$(find . -name "*.dart" -not -path "./lib/src/gen/*" -not -path "**.mocks.dart" -not -path "**/.dart_tool/*")

build_mocks:
dart run build_runner build

test:
flutter test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80D4294CF70F00263BE5 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
19 changes: 19 additions & 0 deletions lib/src/app/data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:async/async.dart';
import 'package:collection/collection.dart';
import 'package:fixnum/fixnum.dart';
import 'package:viam_sdk/src/gen/google/protobuf/any.pb.dart';
import 'package:viam_sdk/src/utils.dart';

import '../gen/app/data/v1/data.pbgrpc.dart';
import '../gen/app/datasync/v1/data_sync.pbgrpc.dart' hide CaptureInterval;
Expand Down Expand Up @@ -113,6 +114,24 @@ class DataClient {
return response.data;
}

/// Obtain unified tabular data and metadata, queried with SQL.
Future<List<Map<String, dynamic>>> tabularDataBySql(String organizationId, String query) async {
final request = TabularDataBySQLRequest()
..organizationId = organizationId
..sqlQuery = query;
final response = await _dataClient.tabularDataBySQL(request);
return response.data.map((e) => e.toMap()).toList();
}

/// Obtain unified tabular data and metadata, queried with MQL.
Future<List<Map<String, dynamic>>> tabularDataByMql(String organizationId, String query) async {
final request = TabularDataByMQLRequest()
..organizationId = organizationId
..mqlQuery = query;
final response = await _dataClient.tabularDataByMQL(request);
return response.data.map((e) => e.toMap()).toList();
}

/// Upload an image to Viam's Data Manager
///
/// If no name is provided, the current timestamp will be used as the filename.
Expand Down
2 changes: 1 addition & 1 deletion lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extension ValueUtils on Value {
dynamic toPrimitive() {
if (hasBoolValue()) return boolValue;
if (hasListValue()) return listValue.values.map((e) => e.toPrimitive());
if (hasNullValue()) return nullValue;
if (hasNullValue()) return null;
if (hasNumberValue()) return numberValue;
if (hasStringValue()) return stringValue;
if (hasStructValue()) return structValue.fields.map((key, value) => MapEntry(key, value.toPrimitive()));
Expand Down
8 changes: 4 additions & 4 deletions lib/src/viam_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import './viam_sdk_impl.dart';
/// An object to interact with the Viam app
abstract class Viam {
/// Create an authenticated Viam instance with the provided [accessToken]
static Viam withAccessToken(String accessToken) {
return ViamImpl.withAccessToken(accessToken);
static Viam withAccessToken(String accessToken, {String serviceHost = 'app.viam.com', int servicePort = 443}) {
return ViamImpl.withAccessToken(accessToken, serviceHost: serviceHost, servicePort: servicePort);
}

static Future<Viam> withApiKey(String apiKeyId, String apiKey) async {
return ViamImpl.withApiKey(apiKeyId, apiKey);
static Future<Viam> withApiKey(String apiKeyId, String apiKey, {String serviceHost = 'app.viam.com'}) async {
return ViamImpl.withApiKey(apiKeyId, apiKey, serviceHost: serviceHost);
}

/// A client to communicate with Viam's app service
Expand Down
8 changes: 5 additions & 3 deletions lib/src/viam_sdk_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ class ViamImpl implements Viam {
_dataClient = DataClient(DataServiceClient(_clientChannelBase), DataSyncServiceClient(_clientChannelBase));
}

ViamImpl.withAccessToken(String accessToken) : _clientChannelBase = AuthenticatedChannel('app.viam.com', 443, accessToken, false) {
ViamImpl.withAccessToken(String accessToken, {String serviceHost = 'app.viam.com', int servicePort = 443})
: _clientChannelBase = AuthenticatedChannel(serviceHost, servicePort, accessToken, servicePort == 443 ? false : true) {
_appClient = AppClient(AppServiceClient(_clientChannelBase));
_dataClient = DataClient(DataServiceClient(_clientChannelBase), DataSyncServiceClient(_clientChannelBase));
}

static Future<ViamImpl> withApiKey(String apiKeyId, String apiKey) async {
static Future<ViamImpl> withApiKey(String apiKeyId, String apiKey, {String serviceHost = 'app.viam.com'}) async {
final channel = await dial(
'app.viam.com',
serviceHost,
DialOptions()
..authEntity = apiKeyId
..credentials = Credentials.apiKey(apiKey)
..attemptMdns = false
..webRtcOptions = (DialWebRtcOptions()..disable = true),
() => '');
return ViamImpl._withChannel(channel);
Expand Down
39 changes: 37 additions & 2 deletions test/unit_test/app/data_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:viam_sdk/protos/app/data.dart';
import 'package:viam_sdk/protos/app/data_sync.dart' hide CaptureInterval;
import 'package:viam_sdk/src/app/data.dart';
import 'package:viam_sdk/src/media/image.dart';
import 'package:viam_sdk/src/utils.dart';

import '../mocks/mock_response_future.dart';
import '../mocks/service_clients_mocks.mocks.dart';
Expand Down Expand Up @@ -111,7 +112,41 @@ void main() {
when(serviceClient.binaryDataByIDs(any)).thenAnswer((_) => MockResponseFuture.value(BinaryDataByIDsResponse()..data.addAll(data)));

final response = await dataClient.binaryDataByIds(ids);
expect(response, data);
expect(response, equals(data));
});

test('tabularDataBySql', () async {
final List<Map<String, dynamic>> data = [
{
'key1': 1,
'key2': '2',
'key3': [1, 2, 3],
'key4': {'key4sub1': 1}
},
];

when(serviceClient.tabularDataBySQL(any))
.thenAnswer((_) => MockResponseFuture.value(TabularDataBySQLResponse()..data.addAll(data.map((e) => e.toStruct()))));

final response = await dataClient.tabularDataBySql('some_org_id', 'some_query');
expect(response, equals(data));
});

test('tabularDataByMql', () async {
final List<Map<String, dynamic>> data = [
{
'key1': 1,
'key2': '2',
'key3': [1, 2, 3],
'key4': {'key4sub1': 1}
},
];

when(serviceClient.tabularDataByMQL(any))
.thenAnswer((_) => MockResponseFuture.value(TabularDataByMQLResponse()..data.addAll(data.map((e) => e.toStruct()))));

final response = await dataClient.tabularDataByMql('some_org_id', 'some_query');
expect(response, equals(data));
});
});

Expand Down Expand Up @@ -141,7 +176,7 @@ void main() {
expect(syncServiceClient.metadata, expected);

await dataClient.uploadFile('/dev/null', 'partId', fileName: 'otherName');
expect(syncServiceClient.metadata?.fileName, 'otherName');
expect(syncServiceClient.metadata?.fileName, equals('otherName'));
});
});
});
Expand Down
60 changes: 60 additions & 0 deletions test/unit_test/mocks/service_clients_mocks.mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2825,6 +2825,66 @@ class MockDataServiceClient extends _i1.Mock implements _i12.DataServiceClient {
),
) as _i4.ResponseFuture<_i13.TabularDataByFilterResponse>);

@override
_i4.ResponseFuture<_i13.TabularDataBySQLResponse> tabularDataBySQL(
_i13.TabularDataBySQLRequest? request, {
_i3.CallOptions? options,
}) =>
(super.noSuchMethod(
Invocation.method(
#tabularDataBySQL,
[request],
{#options: options},
),
returnValue: _FakeResponseFuture_2<_i13.TabularDataBySQLResponse>(
this,
Invocation.method(
#tabularDataBySQL,
[request],
{#options: options},
),
),
returnValueForMissingStub:
_FakeResponseFuture_2<_i13.TabularDataBySQLResponse>(
this,
Invocation.method(
#tabularDataBySQL,
[request],
{#options: options},
),
),
) as _i4.ResponseFuture<_i13.TabularDataBySQLResponse>);

@override
_i4.ResponseFuture<_i13.TabularDataByMQLResponse> tabularDataByMQL(
_i13.TabularDataByMQLRequest? request, {
_i3.CallOptions? options,
}) =>
(super.noSuchMethod(
Invocation.method(
#tabularDataByMQL,
[request],
{#options: options},
),
returnValue: _FakeResponseFuture_2<_i13.TabularDataByMQLResponse>(
this,
Invocation.method(
#tabularDataByMQL,
[request],
{#options: options},
),
),
returnValueForMissingStub:
_FakeResponseFuture_2<_i13.TabularDataByMQLResponse>(
this,
Invocation.method(
#tabularDataByMQL,
[request],
{#options: options},
),
),
) as _i4.ResponseFuture<_i13.TabularDataByMQLResponse>);

@override
_i4.ResponseFuture<_i13.BinaryDataByFilterResponse> binaryDataByFilter(
_i13.BinaryDataByFilterRequest? request, {
Expand Down
2 changes: 1 addition & 1 deletion test/unit_test/utils/utils_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void main() {
group('ValueUtils toPrimitive', () {
test('null', () {
final nullValue = Value()..nullValue = NullValue.NULL_VALUE;
expect(nullValue.toPrimitive(), NullValue.NULL_VALUE);
expect(nullValue.toPrimitive(), null);
});

test('num', () {
Expand Down

0 comments on commit b661f4a

Please sign in to comment.