From 3aa12c24cf85709acb1791bdf35272f6e39cb414 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 28 Feb 2024 20:38:37 -0800 Subject: [PATCH] chore(repo): Update examples for 0.2.0 --- examples/gemini/celest/functions/gemini.dart | 55 ++++- examples/gemini/celest/lib/client.dart | 53 ++++- examples/gemini/celest/lib/exceptions.dart | 2 - examples/gemini/celest/lib/models.dart | 13 -- .../celest/lib/src/client/functions.dart | 95 +++++---- .../celest/lib/src/client/serializers.dart | 90 +++++++-- examples/gemini/celest/pubspec.lock | 26 ++- examples/gemini/celest/pubspec.yaml | 6 +- examples/gemini/celest/resources.dart | 6 +- examples/gemini/lib/main.dart | 71 +------ examples/gemini/pubspec.lock | 36 ++-- examples/gemini/pubspec.yaml | 2 +- examples/openai/celest/functions/open_ai.dart | 4 +- examples/openai/celest/lib/client.dart | 53 ++++- .../celest/lib/src/client/functions.dart | 79 ++++---- .../celest/lib/src/client/serializers.dart | 49 +++-- examples/openai/celest/pubspec.lock | 38 ++-- examples/openai/celest/pubspec.yaml | 4 +- examples/openai/celest/resources.dart | 6 +- .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- examples/openai/lib/main.dart | 3 +- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- examples/openai/pubspec.lock | 70 +++++-- examples/todo/celest/lib/client.dart | 55 +++-- .../todo/celest/lib/src/client/functions.dart | 191 +++++++----------- .../celest/lib/src/client/serializers.dart | 90 ++++----- examples/todo/celest/pubspec.lock | 34 ++-- examples/todo/celest/pubspec.yaml | 4 +- examples/todo/celest/resources.dart | 4 +- .../todo/ios/Flutter/AppFrameworkInfo.plist | 2 +- .../todo/ios/Runner.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- examples/todo/pubspec.lock | 18 +- .../example/celest/functions/greeting.dart | 17 +- .../celest/example/celest/lib/client.dart | 53 ++++- .../celest/example/celest/lib/exceptions.dart | 2 - .../lib/exceptions/bad_name_exception.dart | 11 + .../celest/example/celest/lib/models.dart | 2 - .../example/celest/lib/models/person.dart | 8 + .../celest/lib/src/client/functions.dart | 50 +++-- .../celest/lib/src/client/serializers.dart | 25 +++ packages/celest/example/celest/project.dart | 2 +- packages/celest/example/celest/pubspec.yaml | 6 +- packages/celest/example/celest/resources.dart | 4 +- .../celest/test/functions/greeting_test.dart | 10 +- .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- packages/celest/example/lib/main.dart | 5 +- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- 52 files changed, 789 insertions(+), 591 deletions(-) delete mode 100644 examples/gemini/celest/lib/exceptions.dart delete mode 100644 examples/gemini/celest/lib/models.dart delete mode 100644 packages/celest/example/celest/lib/exceptions.dart create mode 100644 packages/celest/example/celest/lib/exceptions/bad_name_exception.dart delete mode 100644 packages/celest/example/celest/lib/models.dart create mode 100644 packages/celest/example/celest/lib/models/person.dart create mode 100644 packages/celest/example/celest/lib/src/client/serializers.dart diff --git a/examples/gemini/celest/functions/gemini.dart b/examples/gemini/celest/functions/gemini.dart index 615e2971..f2892a3c 100644 --- a/examples/gemini/celest/functions/gemini.dart +++ b/examples/gemini/celest/functions/gemini.dart @@ -1,8 +1,9 @@ // Cloud functions are top-level Dart functions defined in the `functions/` // folder of your Celest project. +import 'dart:convert'; + import 'package:celest/celest.dart'; -import 'package:celest_backend/models.dart'; import 'package:google_generative_ai/google_generative_ai.dart'; import '../resources.dart'; @@ -24,8 +25,7 @@ const _availableModels = [ Future generateContent({ required String modelName, required String prompt, - ModelParameters parameters = const ModelParameters(), - @env.geminiApiKey required String apiKey, + @Env.geminiApiKey required String apiKey, }) async { if (!_availableModels.contains(modelName)) { throw BadRequestException('Invalid model: $modelName'); @@ -35,19 +35,52 @@ Future generateContent({ final request = [ Content.text(prompt), ]; - final response = await model.generateContent( - request, - generationConfig: GenerationConfig( - maxOutputTokens: parameters.maxTokens, - temperature: parameters.temperature, - ), - ); + print('Sending prompt: $prompt'); + + final response = await model.generateContent(request); + print('Got response: ${_prettyJson(response.toJson())}'); switch (response.text) { case final text?: - print('Gemini response: $text'); + print('Selected answer: $text'); return text; case _: throw InternalServerException('Failed to generate content'); } } + +extension on GenerateContentResponse { + Map toJson() => { + 'promptFeedback': { + 'blockReason': promptFeedback?.blockReason?.name, + 'blockReasonMessage': promptFeedback?.blockReasonMessage, + 'safetyRatings': [ + for (final rating + in promptFeedback?.safetyRatings ?? []) + { + 'category': rating.category.name, + 'probability': rating.probability.name, + }, + ], + }, + 'candidates': [ + for (final candidate in candidates) + { + 'content': candidate.content.toJson(), + 'safetyRatings': [ + for (final rating + in candidate.safetyRatings ?? []) + { + 'category': rating.category.name, + 'probability': rating.probability.name, + }, + ], + 'finishReason': candidate.finishReason?.name, + 'finishMessage': candidate.finishMessage, + }, + ], + }; +} + +String _prettyJson(Object? json) => + const JsonEncoder.withIndent(' ').convert(json); diff --git a/examples/gemini/celest/lib/client.dart b/examples/gemini/celest/lib/client.dart index f1d42e40..546780c3 100644 --- a/examples/gemini/celest/lib/client.dart +++ b/examples/gemini/celest/lib/client.dart @@ -2,29 +2,60 @@ // it can be checked into version control. // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import -library; +library; // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:io'; +import 'dart:io' as _$io; -import 'package:celest/celest.dart'; import 'package:celest_core/src/util/globals.dart'; -import 'package:http/http.dart' as http; +import 'package:http/http.dart' as _$http; import 'src/client/functions.dart'; import 'src/client/serializers.dart'; final Celest celest = Celest(); +enum CelestEnvironment { + local; + + Uri get baseUri => switch (this) { + local => kIsWeb || !_$io.Platform.isAndroid + ? Uri.parse('http://localhost:7781') + : Uri.parse('http://10.0.2.2:7781'), + }; +} + class Celest { - late http.Client httpClient = http.Client(); + var _initialized = false; + + late CelestEnvironment _currentEnvironment; + + late _$http.Client httpClient = _$http.Client(); + + late Uri _baseUri; + + final _functions = CelestFunctions(); + + T _checkInitialized(T Function() value) { + if (!_initialized) { + throw StateError( + 'Celest has not been initialized. Make sure to call `celest.init()` at the start of your `main` method.'); + } + return value(); + } + + CelestEnvironment get currentEnvironment => + _checkInitialized(() => _currentEnvironment); - late final Uri baseUri = kIsWeb || !Platform.isAndroid - ? Uri.parse('http://localhost:7777') - : Uri.parse('http://10.0.2.2:7777'); + Uri get baseUri => _checkInitialized(() => _baseUri); - final functions = CelestFunctions(); + CelestFunctions get functions => _checkInitialized(() => _functions); - void init() { - Serializers.instance.put(const ModelParametersSerializer()); + void init({CelestEnvironment environment = CelestEnvironment.local}) { + _currentEnvironment = environment; + _baseUri = environment.baseUri; + if (!_initialized) { + initSerializers(); + } + _initialized = true; } } diff --git a/examples/gemini/celest/lib/exceptions.dart b/examples/gemini/celest/lib/exceptions.dart deleted file mode 100644 index 1c7d9dde..00000000 --- a/examples/gemini/celest/lib/exceptions.dart +++ /dev/null @@ -1,2 +0,0 @@ -// By convention, any custom exception types thrown by an API must be defined -// in this file or exported from this file. diff --git a/examples/gemini/celest/lib/models.dart b/examples/gemini/celest/lib/models.dart deleted file mode 100644 index 8bd76650..00000000 --- a/examples/gemini/celest/lib/models.dart +++ /dev/null @@ -1,13 +0,0 @@ -// By convention, any custom types used within an API request/response must be -// defined in this file or exported from this file. - -final class ModelParameters { - const ModelParameters({ - double? temperature, - int? maxTokens, - }) : maxTokens = maxTokens ?? 100, - temperature = temperature ?? 1.0; - - final double temperature; - final int maxTokens; -} diff --git a/examples/gemini/celest/lib/src/client/functions.dart b/examples/gemini/celest/lib/src/client/functions.dart index 8eaa3a5e..2a117d0d 100644 --- a/examples/gemini/celest/lib/src/client/functions.dart +++ b/examples/gemini/celest/lib/src/client/functions.dart @@ -2,13 +2,15 @@ // it can be checked into version control. // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import -library; +library; // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:convert'; +import 'dart:convert' as _$convert; import 'package:celest/celest.dart'; -import 'package:celest_backend/models.dart'; import 'package:celest_core/src/exception/cloud_exception.dart'; +import 'package:celest_core/src/exception/serialization_exception.dart'; +import 'package:google_generative_ai/src/error.dart' as _$error; +import 'package:http/src/exception.dart' as _$exception; import '../../client.dart'; @@ -17,18 +19,10 @@ class CelestFunctions { } class CelestFunctionsGemini { - /// Returns a list of available models. - Future> availableModels() async { - final $response = await celest.httpClient.post( - celest.baseUri.resolve('/gemini/available-models'), - headers: const {'Content-Type': 'application/json; charset=utf-8'}, - ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return ($body['response'] as Iterable) - .map((el) => (el as String)) - .toList(); - } + Never _throwError({ + required int $statusCode, + required Map $body, + }) { final $error = ($body['error'] as Map); final $code = ($error['code'] as String); final $details = ($error['details'] as Map?); @@ -38,8 +32,25 @@ class CelestFunctionsGemini { case r'InternalServerException': throw Serializers.instance .deserialize($details); + case r'SerializationException': + throw Serializers.instance + .deserialize($details); + case r'GenerativeAIException': + throw Serializers.instance + .deserialize<_$error.GenerativeAIException>($details); + case r'InvalidApiKey': + throw Serializers.instance.deserialize<_$error.InvalidApiKey>($details); + case r'UnsupportedUserLocation': + throw Serializers.instance + .deserialize<_$error.UnsupportedUserLocation>($details); + case r'ServerException': + throw Serializers.instance + .deserialize<_$error.ServerException>($details); + case r'ClientException': + throw Serializers.instance + .deserialize<_$exception.ClientException>($details); case _: - switch ($response.statusCode) { + switch ($statusCode) { case 400: throw BadRequestException($code); case _: @@ -48,44 +59,48 @@ class CelestFunctionsGemini { } } + /// Returns a list of available models. + Future> availableModels() async { + final $response = await celest.httpClient.post( + celest.baseUri.resolve('/gemini/available-models'), + headers: const {'Content-Type': 'application/json; charset=utf-8'}, + ); + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); + } + return ($body['response'] as Iterable) + .map((el) => (el as String)) + .toList(); + } + /// Prompts the Gemini [modelName] with the given [prompt] and [parameters]. /// /// Returns the generated text. Future generateContent({ required String modelName, required String prompt, - ModelParameters parameters = const ModelParameters(), }) async { final $response = await celest.httpClient.post( celest.baseUri.resolve('/gemini/generate-content'), headers: const {'Content-Type': 'application/json; charset=utf-8'}, - body: jsonEncode({ + body: _$convert.jsonEncode({ r'modelName': modelName, r'prompt': prompt, - r'parameters': - Serializers.instance.serialize(parameters), }), ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return ($body['response'] as String); - } - final $error = ($body['error'] as Map); - final $code = ($error['code'] as String); - final $details = ($error['details'] as Map?); - switch ($code) { - case r'BadRequestException': - throw Serializers.instance.deserialize($details); - case r'InternalServerException': - throw Serializers.instance - .deserialize($details); - case _: - switch ($response.statusCode) { - case 400: - throw BadRequestException($code); - case _: - throw InternalServerException($code); - } + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); } + return ($body['response'] as String); } } diff --git a/examples/gemini/celest/lib/src/client/serializers.dart b/examples/gemini/celest/lib/src/client/serializers.dart index 958fd6f0..bc4b892d 100644 --- a/examples/gemini/celest/lib/src/client/serializers.dart +++ b/examples/gemini/celest/lib/src/client/serializers.dart @@ -1,23 +1,77 @@ // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:celest/celest.dart'; -import 'package:celest_backend/models.dart'; +import 'package:celest_core/src/exception/cloud_exception.dart'; +import 'package:celest_core/src/exception/serialization_exception.dart'; +import 'package:google_generative_ai/src/error.dart' as _$error; +import 'package:http/src/exception.dart' as _$exception; -final class ModelParametersSerializer extends Serializer { - const ModelParametersSerializer(); - - @override - ModelParameters deserialize(Object? value) { - final serialized = assertWireType?>(value); - return ModelParameters( - temperature: ((serialized?[r'temperature'] as num?)?.toDouble()) ?? null, - maxTokens: ((serialized?[r'maxTokens'] as num?)?.toInt()) ?? null, - ); - } - - @override - Map serialize(ModelParameters value) => { - r'temperature': value.temperature, - r'maxTokens': value.maxTokens, - }; +void initSerializers() { + Serializers.instance + .put(Serializer.define>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return BadRequestException(($serialized[r'message'] as String)); + }, + )); + Serializers.instance + .put(Serializer.define>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return InternalServerException(($serialized[r'message'] as String)); + }, + )); + Serializers.instance + .put(Serializer.define>( + serialize: ($value) => { + r'message': $value.message, + r'offset': $value.offset, + r'source': $value.source, + }, + deserialize: ($serialized) { + return SerializationException(($serialized[r'message'] as String)); + }, + )); + Serializers.instance.put( + Serializer.define<_$error.GenerativeAIException, Map>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return _$error.GenerativeAIException(($serialized[r'message'] as String)); + }, + )); + Serializers.instance + .put(Serializer.define<_$error.InvalidApiKey, Map>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return _$error.InvalidApiKey(($serialized[r'message'] as String)); + }, + )); + Serializers.instance + .put(Serializer.define<_$error.ServerException, Map>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return _$error.ServerException(($serialized[r'message'] as String)); + }, + )); + Serializers.instance.put( + Serializer.define<_$error.UnsupportedUserLocation, Map?>( + serialize: ($value) => {}, + deserialize: ($serialized) { + return _$error.UnsupportedUserLocation(); + }, + )); + Serializers.instance + .put(Serializer.define<_$exception.ClientException, Map>( + serialize: ($value) => { + r'message': $value.message, + r'uri': Serializers.instance.serialize($value.uri), + }, + deserialize: ($serialized) { + return _$exception.ClientException( + ($serialized[r'message'] as String), + Serializers.instance.deserialize($serialized[r'uri']), + ); + }, + )); } diff --git a/examples/gemini/celest/pubspec.lock b/examples/gemini/celest/pubspec.lock index e7427849..fb2f5741 100644 --- a/examples/gemini/celest/pubspec.lock +++ b/examples/gemini/celest/pubspec.lock @@ -44,19 +44,17 @@ packages: celest: dependency: "direct main" description: - name: celest - sha256: fa5f2cf62073d2e71a2ce4c559d703886c2a48062b08752040d3afcc4531b846 - url: "https://pub.dev" - source: hosted - version: "0.1.1" + path: "../../../packages/celest" + relative: true + source: path + version: "0.2.0" celest_core: dependency: "direct main" description: - name: celest_core - sha256: c5d6408d047816f2b81c63b75c8f270377768612ee47272a5782ed48af887e55 - url: "https://pub.dev" - source: hosted - version: "0.1.1" + path: "../../../packages/celest_core" + relative: true + source: path + version: "0.2.0" chunked_stream: dependency: transitive description: @@ -125,10 +123,10 @@ packages: dependency: "direct main" description: name: google_generative_ai - sha256: "2c8cb557c196487c5624da02a0914935e48874112a8201cb7b9379c79cd3bde7" + sha256: b2d3f7277a85e3e6be4c4392c59e73ea211b5b6c8bb21c24c71fd411a2d1822e url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.2" http: dependency: "direct main" description: @@ -389,10 +387,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + sha256: e7d5ecd604e499358c5fe35ee828c0298a320d54455e791e9dcf73486bc8d9f0 url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "14.1.0" watcher: dependency: transitive description: diff --git a/examples/gemini/celest/pubspec.yaml b/examples/gemini/celest/pubspec.yaml index b50e4886..c01c72d4 100644 --- a/examples/gemini/celest/pubspec.yaml +++ b/examples/gemini/celest/pubspec.yaml @@ -3,11 +3,11 @@ description: The Celest backend for celest_project. publish_to: none environment: - sdk: ^3.2.0 + sdk: ^3.3.0 dependencies: - celest: ^0.1.0 - celest_core: ^0.1.0 + celest: ^0.2.0 + celest_core: ^0.2.0 google_generative_ai: ^0.2.0 http: ">=0.13.0 <2.0.0" diff --git a/examples/gemini/celest/resources.dart b/examples/gemini/celest/resources.dart index 7a2357d1..90db0e06 100644 --- a/examples/gemini/celest/resources.dart +++ b/examples/gemini/celest/resources.dart @@ -6,11 +6,11 @@ library; import 'package:celest/celest.dart'; -abstract final class apis { +abstract final class Apis { static const gemini = CloudApi(name: r'gemini'); } -abstract final class functions { +abstract final class Functions { static const geminiAvailableModels = CloudFunction( api: r'gemini', functionName: r'availableModels', @@ -22,6 +22,6 @@ abstract final class functions { ); } -abstract final class env { +abstract final class Env { static const geminiApiKey = EnvironmentVariable(name: r'GEMINI_API_KEY'); } diff --git a/examples/gemini/lib/main.dart b/examples/gemini/lib/main.dart index ee1a273e..6985f87c 100644 --- a/examples/gemini/lib/main.dart +++ b/examples/gemini/lib/main.dart @@ -2,9 +2,7 @@ import 'dart:developer'; // Import the generated Celest client import 'package:celest_backend/client.dart'; -import 'package:celest_backend/models.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; void main() { // Initializes Celest in your Flutter app @@ -23,7 +21,6 @@ class _GeminiAppState extends State { // Controllers for the text fields. final _questionController = TextEditingController(); final _answerController = TextEditingController(); - final _maxTokensController = TextEditingController(); /// The selected model to use. /// @@ -31,9 +28,6 @@ class _GeminiAppState extends State { /// are loaded from the backend. late String _selectedModel; - /// The value of the temperature slider. - var _temperatureSliderValue = 1.0; - /// Whether [_availableModelsFuture] has completed and the available models /// have been loaded fromthe backend. var _loadedAvailableModels = false; @@ -46,12 +40,6 @@ class _GeminiAppState extends State { final response = await celest.functions.gemini.generateContent( prompt: _questionController.text, modelName: _selectedModel, - parameters: ModelParameters( - maxTokens: _maxTokensController.text.isNotEmpty - ? int.parse(_maxTokensController.text) - : null, - temperature: _temperatureSliderValue, - ), ); log(response); setState(() { @@ -60,7 +48,7 @@ class _GeminiAppState extends State { } on Exception catch (e) { log('Failed to generate content', error: e); setState(() { - _answerController.text = '${e.runtimeType}: $e'; + _answerController.text = '$e'; }); } } @@ -127,67 +115,12 @@ class _GeminiAppState extends State { _availableModelsDropdown(data), // Handle the error case - AsyncSnapshot(:final error?) => - Text('${error.runtimeType}: $error'), + AsyncSnapshot(:final error?) => Text('$error'), // If waiting, show a progress indicator _ => const CircularProgressIndicator(), }, ), - SizedBox( - width: double.infinity, - child: Wrap( - alignment: WrapAlignment.spaceEvenly, - children: [ - Tooltip( - message: - 'The maximum number of words for the AI to generate', - child: SizedBox( - width: 150, - child: TextField( - keyboardType: TextInputType.number, - // Allow only digits - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - decoration: const InputDecoration( - hintText: 'Max tokens', - ), - controller: _maxTokensController, - ), - ), - ), - Tooltip( - message: - 'The higher the temperature, the more random the text', - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Temperature', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox( - width: 150, - child: Slider( - value: _temperatureSliderValue, - min: 0, - max: 2, - divisions: 10, - label: _temperatureSliderValue.toString(), - onChanged: (double value) { - setState(() { - _temperatureSliderValue = value; - }); - }, - ), - ), - ], - ), - ), - ], - ), - ), const SizedBox(height: 20), TextField( maxLines: 10, diff --git a/examples/gemini/pubspec.lock b/examples/gemini/pubspec.lock index e152749f..7285476d 100644 --- a/examples/gemini/pubspec.lock +++ b/examples/gemini/pubspec.lock @@ -18,13 +18,12 @@ packages: source: hosted version: "2.1.1" celest: - dependency: transitive + dependency: "direct overridden" description: - name: celest - sha256: fa5f2cf62073d2e71a2ce4c559d703886c2a48062b08752040d3afcc4531b846 - url: "https://pub.dev" - source: hosted - version: "0.1.1" + path: "../../packages/celest" + relative: true + source: path + version: "0.2.0" celest_backend: dependency: "direct main" description: @@ -33,13 +32,12 @@ packages: source: path version: "0.0.0" celest_core: - dependency: transitive + dependency: "direct overridden" description: - name: celest_core - sha256: c5d6408d047816f2b81c63b75c8f270377768612ee47272a5782ed48af887e55 - url: "https://pub.dev" - source: hosted - version: "0.1.1" + path: "../../packages/celest_core" + relative: true + source: path + version: "0.2.0" characters: dependency: transitive description: @@ -110,18 +108,18 @@ packages: dependency: transitive description: name: google_generative_ai - sha256: "2c8cb557c196487c5624da02a0914935e48874112a8201cb7b9379c79cd3bde7" + sha256: b2d3f7277a85e3e6be4c4392c59e73ea211b5b6c8bb21c24c71fd411a2d1822e url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.2" http: dependency: transitive description: name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" http_methods: dependency: transitive description: @@ -299,9 +297,9 @@ packages: dependency: transitive description: name: web - sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" + sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad" url: "https://pub.dev" source: hosted - version: "0.4.2" + version: "0.5.0" sdks: - dart: ">=3.3.0-279.3.beta <4.0.0" + dart: ">=3.3.0 <4.0.0" diff --git a/examples/gemini/pubspec.yaml b/examples/gemini/pubspec.yaml index 0a9cb2b1..5aec0aa4 100644 --- a/examples/gemini/pubspec.yaml +++ b/examples/gemini/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=3.3.0-279.3.beta <4.0.0' + sdk: ^3.3.0 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions diff --git a/examples/openai/celest/functions/open_ai.dart b/examples/openai/celest/functions/open_ai.dart index bc725313..c69d6bee 100644 --- a/examples/openai/celest/functions/open_ai.dart +++ b/examples/openai/celest/functions/open_ai.dart @@ -26,7 +26,6 @@ Future> availableModels() async => _availableModels; /// client or Flutter app. const _availableModels = [ 'gpt-4', - 'gpt-4-turbo-preview', 'gpt-3.5-turbo', ]; @@ -37,7 +36,7 @@ Future openAIRequest({ required String model, required String prompt, ModelParameters parameters = const ModelParameters(), - @env.openAiToken required String openAiToken, + @Env.openAiToken required String openAiToken, }) async { final openAI = _createOpenAI(openAiToken); @@ -49,6 +48,7 @@ Future openAIRequest({ model: ChatModelFromValue(model: model), maxToken: parameters.maxTokens, temperature: parameters.temperature, + topP: null, ); final requestJson = _prettyJson(request.toJson()); diff --git a/examples/openai/celest/lib/client.dart b/examples/openai/celest/lib/client.dart index f1d42e40..546780c3 100644 --- a/examples/openai/celest/lib/client.dart +++ b/examples/openai/celest/lib/client.dart @@ -2,29 +2,60 @@ // it can be checked into version control. // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import -library; +library; // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:io'; +import 'dart:io' as _$io; -import 'package:celest/celest.dart'; import 'package:celest_core/src/util/globals.dart'; -import 'package:http/http.dart' as http; +import 'package:http/http.dart' as _$http; import 'src/client/functions.dart'; import 'src/client/serializers.dart'; final Celest celest = Celest(); +enum CelestEnvironment { + local; + + Uri get baseUri => switch (this) { + local => kIsWeb || !_$io.Platform.isAndroid + ? Uri.parse('http://localhost:7781') + : Uri.parse('http://10.0.2.2:7781'), + }; +} + class Celest { - late http.Client httpClient = http.Client(); + var _initialized = false; + + late CelestEnvironment _currentEnvironment; + + late _$http.Client httpClient = _$http.Client(); + + late Uri _baseUri; + + final _functions = CelestFunctions(); + + T _checkInitialized(T Function() value) { + if (!_initialized) { + throw StateError( + 'Celest has not been initialized. Make sure to call `celest.init()` at the start of your `main` method.'); + } + return value(); + } + + CelestEnvironment get currentEnvironment => + _checkInitialized(() => _currentEnvironment); - late final Uri baseUri = kIsWeb || !Platform.isAndroid - ? Uri.parse('http://localhost:7777') - : Uri.parse('http://10.0.2.2:7777'); + Uri get baseUri => _checkInitialized(() => _baseUri); - final functions = CelestFunctions(); + CelestFunctions get functions => _checkInitialized(() => _functions); - void init() { - Serializers.instance.put(const ModelParametersSerializer()); + void init({CelestEnvironment environment = CelestEnvironment.local}) { + _currentEnvironment = environment; + _baseUri = environment.baseUri; + if (!_initialized) { + initSerializers(); + } + _initialized = true; } } diff --git a/examples/openai/celest/lib/src/client/functions.dart b/examples/openai/celest/lib/src/client/functions.dart index a8d5b9a9..bf2f5649 100644 --- a/examples/openai/celest/lib/src/client/functions.dart +++ b/examples/openai/celest/lib/src/client/functions.dart @@ -2,12 +2,12 @@ // it can be checked into version control. // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import -library; +library; // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:convert'; +import 'dart:convert' as _$convert; import 'package:celest/celest.dart'; -import 'package:celest_backend/models.dart'; +import 'package:celest_backend/models.dart' as _$models; import 'package:celest_core/src/exception/cloud_exception.dart'; import '../../client.dart'; @@ -17,18 +17,10 @@ class CelestFunctions { } class CelestFunctionsOpenAi { - /// Returns a list of available models. - Future> availableModels() async { - final $response = await celest.httpClient.post( - celest.baseUri.resolve('/open-ai/available-models'), - headers: const {'Content-Type': 'application/json; charset=utf-8'}, - ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return ($body['response'] as Iterable) - .map((el) => (el as String)) - .toList(); - } + Never _throwError({ + required int $statusCode, + required Map $body, + }) { final $error = ($body['error'] as Map); final $code = ($error['code'] as String); final $details = ($error['details'] as Map?); @@ -39,7 +31,7 @@ class CelestFunctionsOpenAi { throw Serializers.instance .deserialize($details); case _: - switch ($response.statusCode) { + switch ($statusCode) { case 400: throw BadRequestException($code); case _: @@ -48,44 +40,51 @@ class CelestFunctionsOpenAi { } } + /// Returns a list of available models. + Future> availableModels() async { + final $response = await celest.httpClient.post( + celest.baseUri.resolve('/open-ai/available-models'), + headers: const {'Content-Type': 'application/json; charset=utf-8'}, + ); + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); + } + return ($body['response'] as Iterable) + .map((el) => (el as String)) + .toList(); + } + /// Prompts the GPT [model] with the given [prompt] and [parameters]. /// /// Returns the generated text. Future openAiRequest({ required String model, required String prompt, - ModelParameters parameters = const ModelParameters(), + _$models.ModelParameters parameters = const _$models.ModelParameters(), }) async { final $response = await celest.httpClient.post( celest.baseUri.resolve('/open-ai/open-ai-request'), headers: const {'Content-Type': 'application/json; charset=utf-8'}, - body: jsonEncode({ + body: _$convert.jsonEncode({ r'model': model, r'prompt': prompt, - r'parameters': - Serializers.instance.serialize(parameters), + r'parameters': Serializers.instance + .serialize<_$models.ModelParameters>(parameters), }), ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return ($body['response'] as String); - } - final $error = ($body['error'] as Map); - final $code = ($error['code'] as String); - final $details = ($error['details'] as Map?); - switch ($code) { - case r'BadRequestException': - throw Serializers.instance.deserialize($details); - case r'InternalServerException': - throw Serializers.instance - .deserialize($details); - case _: - switch ($response.statusCode) { - case 400: - throw BadRequestException($code); - case _: - throw InternalServerException($code); - } + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); } + return ($body['response'] as String); } } diff --git a/examples/openai/celest/lib/src/client/serializers.dart b/examples/openai/celest/lib/src/client/serializers.dart index 958fd6f0..9f4a2275 100644 --- a/examples/openai/celest/lib/src/client/serializers.dart +++ b/examples/openai/celest/lib/src/client/serializers.dart @@ -1,23 +1,36 @@ // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:celest/celest.dart'; -import 'package:celest_backend/models.dart'; +import 'package:celest_backend/models.dart' as _$models; +import 'package:celest_core/src/exception/cloud_exception.dart'; -final class ModelParametersSerializer extends Serializer { - const ModelParametersSerializer(); - - @override - ModelParameters deserialize(Object? value) { - final serialized = assertWireType?>(value); - return ModelParameters( - temperature: ((serialized?[r'temperature'] as num?)?.toDouble()) ?? null, - maxTokens: ((serialized?[r'maxTokens'] as num?)?.toInt()) ?? null, - ); - } - - @override - Map serialize(ModelParameters value) => { - r'temperature': value.temperature, - r'maxTokens': value.maxTokens, - }; +void initSerializers() { + Serializers.instance + .put(Serializer.define<_$models.ModelParameters, Map?>( + serialize: ($value) => { + r'temperature': $value.temperature, + r'maxTokens': $value.maxTokens, + }, + deserialize: ($serialized) { + return _$models.ModelParameters( + temperature: ($serialized?[r'temperature'] as num?)?.toDouble(), + maxTokens: ($serialized?[r'maxTokens'] as num?)?.toInt(), + ); + }, + )); + Serializers.instance + .put(Serializer.define>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return BadRequestException(($serialized[r'message'] as String)); + }, + )); + Serializers.instance + .put(Serializer.define>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return InternalServerException(($serialized[r'message'] as String)); + }, + )); } diff --git a/examples/openai/celest/pubspec.lock b/examples/openai/celest/pubspec.lock index 98d39703..9156f2af 100644 --- a/examples/openai/celest/pubspec.lock +++ b/examples/openai/celest/pubspec.lock @@ -45,18 +45,18 @@ packages: dependency: "direct main" description: name: celest - sha256: fa5f2cf62073d2e71a2ce4c559d703886c2a48062b08752040d3afcc4531b846 + sha256: aa0d6bd43ed45f52d2f1a88b5dc00bf88439ab21f1d727c68938f54ccf3ba445 url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.2.0" celest_core: dependency: "direct main" description: name: celest_core - sha256: c5d6408d047816f2b81c63b75c8f270377768612ee47272a5782ed48af887e55 + sha256: b9da8eb41f3b0ea5f76733dd74fede23f5430a57d8c8138963b28e99319bc0d0 url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.2.0" chat_gpt_sdk: dependency: "direct main" description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: dio - sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" + sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.4.1" file: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: "direct main" description: name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" http_methods: dependency: transitive description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: js - sha256: "4186c61b32f99e60f011f7160e32c89a758ae9b1d0c6d28e2c02ef0382300e2b" + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.1" lints: dependency: "direct dev" description: @@ -213,10 +213,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -397,10 +397,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + sha256: e7d5ecd604e499358c5fe35ee828c0298a320d54455e791e9dcf73486bc8d9f0 url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "14.1.0" watcher: dependency: transitive description: @@ -413,18 +413,18 @@ packages: dependency: transitive description: name: web - sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" + sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad" url: "https://pub.dev" source: hosted - version: "0.4.2" + version: "0.5.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" + sha256: "1d8e795e2a8b3730c41b8a98a2dff2e0fb57ae6f0764a1c46ec5915387d257b2" url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.4" webkit_inspection_protocol: dependency: transitive description: @@ -442,4 +442,4 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.3 <4.0.0" + dart: ">=3.3.0 <4.0.0" diff --git a/examples/openai/celest/pubspec.yaml b/examples/openai/celest/pubspec.yaml index 42a27e40..740aa8bf 100644 --- a/examples/openai/celest/pubspec.yaml +++ b/examples/openai/celest/pubspec.yaml @@ -6,8 +6,8 @@ environment: sdk: ^3.3.0 dependencies: - celest: ^0.1.0 - celest_core: ^0.1.0 + celest: ^0.2.0 + celest_core: ^0.2.0 chat_gpt_sdk: ^2.2.8 http: ">=0.13.0 <2.0.0" diff --git a/examples/openai/celest/resources.dart b/examples/openai/celest/resources.dart index ab92674c..8a77c198 100644 --- a/examples/openai/celest/resources.dart +++ b/examples/openai/celest/resources.dart @@ -6,11 +6,11 @@ library; import 'package:celest/celest.dart'; -abstract final class apis { +abstract final class Apis { static const openAi = CloudApi(name: r'open_ai'); } -abstract final class functions { +abstract final class Functions { static const openAiAvailableModels = CloudFunction( api: r'open_ai', functionName: r'availableModels', @@ -22,6 +22,6 @@ abstract final class functions { ); } -abstract final class env { +abstract final class Env { static const openAiToken = EnvironmentVariable(name: r'OPEN_AI_TOKEN'); } diff --git a/examples/openai/ios/Runner.xcodeproj/project.pbxproj b/examples/openai/ios/Runner.xcodeproj/project.pbxproj index adaabd3f..9aee34ad 100644 --- a/examples/openai/ios/Runner.xcodeproj/project.pbxproj +++ b/examples/openai/ios/Runner.xcodeproj/project.pbxproj @@ -169,7 +169,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { diff --git a/examples/openai/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/openai/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a09..8e3ca5df 100644 --- a/examples/openai/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/examples/openai/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ { /// Whether [_availableModelsFuture] has completed and the available models /// have been loaded fromthe backend. var _loadedAvailableModels = false; - final _availableModelsFuture = celest.functions.openAi.availableModels(); + Future> get _availableModelsFuture => + celest.functions.openAi.availableModels(); /// Sends the prompt request to the backend and updates the UI with the /// response. diff --git a/examples/openai/macos/Runner.xcodeproj/project.pbxproj b/examples/openai/macos/Runner.xcodeproj/project.pbxproj index b5035f35..e2b51a76 100644 --- a/examples/openai/macos/Runner.xcodeproj/project.pbxproj +++ b/examples/openai/macos/Runner.xcodeproj/project.pbxproj @@ -227,7 +227,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { diff --git a/examples/openai/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/openai/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 7b4e89d7..654b8e8c 100644 --- a/examples/openai/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/examples/openai/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ =3.2.5 <4.0.0" + dart: ">=3.3.0 <4.0.0" diff --git a/examples/todo/celest/lib/client.dart b/examples/todo/celest/lib/client.dart index 7f77957d..546780c3 100644 --- a/examples/todo/celest/lib/client.dart +++ b/examples/todo/celest/lib/client.dart @@ -2,31 +2,60 @@ // it can be checked into version control. // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import -library; +library; // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:io'; +import 'dart:io' as _$io; -import 'package:celest/celest.dart'; import 'package:celest_core/src/util/globals.dart'; -import 'package:http/http.dart' as http; +import 'package:http/http.dart' as _$http; import 'src/client/functions.dart'; import 'src/client/serializers.dart'; final Celest celest = Celest(); +enum CelestEnvironment { + local; + + Uri get baseUri => switch (this) { + local => kIsWeb || !_$io.Platform.isAndroid + ? Uri.parse('http://localhost:7781') + : Uri.parse('http://10.0.2.2:7781'), + }; +} + class Celest { - late http.Client httpClient = http.Client(); + var _initialized = false; + + late CelestEnvironment _currentEnvironment; + + late _$http.Client httpClient = _$http.Client(); + + late Uri _baseUri; + + final _functions = CelestFunctions(); + + T _checkInitialized(T Function() value) { + if (!_initialized) { + throw StateError( + 'Celest has not been initialized. Make sure to call `celest.init()` at the start of your `main` method.'); + } + return value(); + } + + CelestEnvironment get currentEnvironment => + _checkInitialized(() => _currentEnvironment); - late final Uri baseUri = kIsWeb || !Platform.isAndroid - ? Uri.parse('http://localhost:7777') - : Uri.parse('http://10.0.2.2:7777'); + Uri get baseUri => _checkInitialized(() => _baseUri); - final functions = CelestFunctions(); + CelestFunctions get functions => _checkInitialized(() => _functions); - void init() { - Serializers.instance.put(const ImportanceSerializer()); - Serializers.instance.put(const TaskSerializer()); - Serializers.instance.put(const ServerExceptionSerializer()); + void init({CelestEnvironment environment = CelestEnvironment.local}) { + _currentEnvironment = environment; + _baseUri = environment.baseUri; + if (!_initialized) { + initSerializers(); + } + _initialized = true; } } diff --git a/examples/todo/celest/lib/src/client/functions.dart b/examples/todo/celest/lib/src/client/functions.dart index fcd6f4e8..d2d2abfb 100644 --- a/examples/todo/celest/lib/src/client/functions.dart +++ b/examples/todo/celest/lib/src/client/functions.dart @@ -2,14 +2,13 @@ // it can be checked into version control. // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import -library; +library; // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:convert'; +import 'dart:convert' as _$convert; import 'package:celest/celest.dart'; -import 'package:celest_backend/exceptions.dart'; -import 'package:celest_backend/models.dart'; -import 'package:celest_core/src/exception/cloud_exception.dart'; +import 'package:celest_backend/exceptions.dart' as _$exceptions; +import 'package:celest_backend/models.dart' as _$models; import '../../client.dart'; @@ -18,33 +17,19 @@ class CelestFunctions { } class CelestFunctionsTasks { - Future> listAllTasks() async { - final $response = await celest.httpClient.post( - celest.baseUri.resolve('/tasks/list-all-tasks'), - headers: const {'Content-Type': 'application/json; charset=utf-8'}, - ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return ($body['response'] as Map).map(( - key, - value, - ) => - MapEntry( - key, - Serializers.instance.deserialize(value), - )); - } + Never _throwError({ + required int $statusCode, + required Map $body, + }) { final $error = ($body['error'] as Map); final $code = ($error['code'] as String); final $details = ($error['details'] as Map?); switch ($code) { - case r'BadRequestException': - throw Serializers.instance.deserialize($details); - case r'InternalServerException': + case r'ServerException': throw Serializers.instance - .deserialize($details); + .deserialize<_$exceptions.ServerException>($details); case _: - switch ($response.statusCode) { + switch ($statusCode) { case 400: throw BadRequestException($code); case _: @@ -53,131 +38,101 @@ class CelestFunctionsTasks { } } + Future> listAllTasks() async { + final $response = await celest.httpClient.post( + celest.baseUri.resolve('/tasks/list-all-tasks'), + headers: const {'Content-Type': 'application/json; charset=utf-8'}, + ); + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); + } + return ($body['response'] as Map).map(( + key, + value, + ) => + MapEntry( + key, + Serializers.instance.deserialize<_$models.Task>(value), + )); + } + Future addTask({ required String title, - required Importance importance, + required _$models.Importance importance, }) async { final $response = await celest.httpClient.post( celest.baseUri.resolve('/tasks/add-task'), headers: const {'Content-Type': 'application/json; charset=utf-8'}, - body: jsonEncode({ + body: _$convert.jsonEncode({ r'title': title, - r'importance': Serializers.instance.serialize(importance), + r'importance': + Serializers.instance.serialize<_$models.Importance>(importance), }), ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return; - } - final $error = ($body['error'] as Map); - final $code = ($error['code'] as String); - final $details = ($error['details'] as Map?); - switch ($code) { - case r'ServerException': - throw Serializers.instance.deserialize($details); - case r'BadRequestException': - throw Serializers.instance.deserialize($details); - case r'InternalServerException': - throw Serializers.instance - .deserialize($details); - case _: - switch ($response.statusCode) { - case 400: - throw BadRequestException($code); - case _: - throw InternalServerException($code); - } + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); } + return; } Future deleteTask({required String id}) async { final $response = await celest.httpClient.post( celest.baseUri.resolve('/tasks/delete-task'), headers: const {'Content-Type': 'application/json; charset=utf-8'}, - body: jsonEncode({r'id': id}), + body: _$convert.jsonEncode({r'id': id}), ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return; - } - final $error = ($body['error'] as Map); - final $code = ($error['code'] as String); - final $details = ($error['details'] as Map?); - switch ($code) { - case r'BadRequestException': - throw Serializers.instance.deserialize($details); - case r'InternalServerException': - throw Serializers.instance - .deserialize($details); - case _: - switch ($response.statusCode) { - case 400: - throw BadRequestException($code); - case _: - throw InternalServerException($code); - } + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); } + return; } Future markAsCompleted({required String id}) async { final $response = await celest.httpClient.post( celest.baseUri.resolve('/tasks/mark-as-completed'), headers: const {'Content-Type': 'application/json; charset=utf-8'}, - body: jsonEncode({r'id': id}), + body: _$convert.jsonEncode({r'id': id}), ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return; - } - final $error = ($body['error'] as Map); - final $code = ($error['code'] as String); - final $details = ($error['details'] as Map?); - switch ($code) { - case r'ServerException': - throw Serializers.instance.deserialize($details); - case r'BadRequestException': - throw Serializers.instance.deserialize($details); - case r'InternalServerException': - throw Serializers.instance - .deserialize($details); - case _: - switch ($response.statusCode) { - case 400: - throw BadRequestException($code); - case _: - throw InternalServerException($code); - } + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); } + return; } Future markAsIncomplete({required String id}) async { final $response = await celest.httpClient.post( celest.baseUri.resolve('/tasks/mark-as-incomplete'), headers: const {'Content-Type': 'application/json; charset=utf-8'}, - body: jsonEncode({r'id': id}), + body: _$convert.jsonEncode({r'id': id}), ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return; - } - final $error = ($body['error'] as Map); - final $code = ($error['code'] as String); - final $details = ($error['details'] as Map?); - switch ($code) { - case r'ServerException': - throw Serializers.instance.deserialize($details); - case r'BadRequestException': - throw Serializers.instance.deserialize($details); - case r'InternalServerException': - throw Serializers.instance - .deserialize($details); - case _: - switch ($response.statusCode) { - case 400: - throw BadRequestException($code); - case _: - throw InternalServerException($code); - } + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); } + return; } } diff --git a/examples/todo/celest/lib/src/client/serializers.dart b/examples/todo/celest/lib/src/client/serializers.dart index 128b87b8..645be481 100644 --- a/examples/todo/celest/lib/src/client/serializers.dart +++ b/examples/todo/celest/lib/src/client/serializers.dart @@ -1,57 +1,41 @@ // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:celest/celest.dart'; -import 'package:celest_backend/exceptions.dart'; -import 'package:celest_backend/models.dart'; - -final class ImportanceSerializer extends Serializer { - const ImportanceSerializer(); - - @override - Importance deserialize(Object? value) { - final serialized = assertWireType(value); - return Importance.values.byName(serialized); - } - - @override - String serialize(Importance value) => value.name; -} - -final class TaskSerializer extends Serializer { - const TaskSerializer(); - - @override - Task deserialize(Object? value) { - final serialized = assertWireType>(value); - return Task( - id: (serialized[r'id'] as String), - title: (serialized[r'title'] as String), - importance: Serializers.instance - .deserialize(serialized[r'importance']), - isCompleted: ((serialized[r'isCompleted'] as bool?)) ?? false, - ); - } - - @override - Map serialize(Task value) => { - r'id': value.id, - r'title': value.title, - r'importance': - Serializers.instance.serialize(value.importance), - r'isCompleted': value.isCompleted, - }; -} - -final class ServerExceptionSerializer extends Serializer { - const ServerExceptionSerializer(); - - @override - ServerException deserialize(Object? value) { - final serialized = assertWireType>(value); - return ServerException((serialized[r'message'] as String)); - } - - @override - Map serialize(ServerException value) => - {r'message': value.message}; +import 'package:celest_backend/exceptions.dart' as _$exceptions; +import 'package:celest_backend/models.dart' as _$models; + +void initSerializers() { + Serializers.instance.put( + Serializer.define<_$exceptions.ServerException, Map>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return _$exceptions.ServerException(($serialized[r'message'] as String)); + }, + )); + Serializers.instance.put(Serializer.define<_$models.Importance, String>( + serialize: ($value) => $value.name, + deserialize: ($serialized) { + return _$models.Importance.values.byName($serialized); + }, + )); + Serializers.instance + .put(Serializer.define<_$models.Task, Map>( + serialize: ($value) => { + r'id': $value.id, + r'title': $value.title, + r'importance': Serializers.instance + .serialize<_$models.Importance>($value.importance), + r'isCompleted': $value.isCompleted, + }, + deserialize: ($serialized) { + return _$models.Task( + id: ($serialized[r'id'] as String), + title: ($serialized[r'title'] as String), + importance: Serializers.instance + .deserialize<_$models.Importance>($serialized[r'importance']), + isCompleted: (($serialized[r'isCompleted'] as bool?)) ?? false, + ); + }, + )); } diff --git a/examples/todo/celest/pubspec.lock b/examples/todo/celest/pubspec.lock index 170ce673..d63a84c0 100644 --- a/examples/todo/celest/pubspec.lock +++ b/examples/todo/celest/pubspec.lock @@ -45,18 +45,18 @@ packages: dependency: "direct main" description: name: celest - sha256: fa5f2cf62073d2e71a2ce4c559d703886c2a48062b08752040d3afcc4531b846 + sha256: aa0d6bd43ed45f52d2f1a88b5dc00bf88439ab21f1d727c68938f54ccf3ba445 url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.2.0" celest_core: dependency: "direct main" description: name: celest_core - sha256: c5d6408d047816f2b81c63b75c8f270377768612ee47272a5782ed48af887e55 + sha256: b9da8eb41f3b0ea5f76733dd74fede23f5430a57d8c8138963b28e99319bc0d0 url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.2.0" chunked_stream: dependency: transitive description: @@ -133,10 +133,10 @@ packages: dependency: "direct main" description: name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" http_methods: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: js - sha256: "4186c61b32f99e60f011f7160e32c89a758ae9b1d0c6d28e2c02ef0382300e2b" + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.1" lints: dependency: "direct dev" description: @@ -205,10 +205,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -405,10 +405,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + sha256: e7d5ecd604e499358c5fe35ee828c0298a320d54455e791e9dcf73486bc8d9f0 url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "14.1.0" watcher: dependency: transitive description: @@ -421,18 +421,18 @@ packages: dependency: transitive description: name: web - sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" + sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad" url: "https://pub.dev" source: hosted - version: "0.4.2" + version: "0.5.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" + sha256: "1d8e795e2a8b3730c41b8a98a2dff2e0fb57ae6f0764a1c46ec5915387d257b2" url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.4" webkit_inspection_protocol: dependency: transitive description: @@ -450,4 +450,4 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0 <4.0.0" + dart: ">=3.3.0 <4.0.0" diff --git a/examples/todo/celest/pubspec.yaml b/examples/todo/celest/pubspec.yaml index 33fe89cd..a2f16401 100644 --- a/examples/todo/celest/pubspec.yaml +++ b/examples/todo/celest/pubspec.yaml @@ -6,8 +6,8 @@ environment: sdk: ^3.3.0 dependencies: - celest: ^0.1.0 - celest_core: ^0.1.0 + celest: ^0.2.0 + celest_core: ^0.2.0 http: ">=0.13.0 <2.0.0" uuid: ^4.3.3 diff --git a/examples/todo/celest/resources.dart b/examples/todo/celest/resources.dart index 4e9a6a94..842003ec 100644 --- a/examples/todo/celest/resources.dart +++ b/examples/todo/celest/resources.dart @@ -6,11 +6,11 @@ library; import 'package:celest/celest.dart'; -abstract final class apis { +abstract final class Apis { static const tasks = CloudApi(name: r'tasks'); } -abstract final class functions { +abstract final class Functions { static const tasksAddTask = CloudFunction( api: r'tasks', functionName: r'addTask', diff --git a/examples/todo/ios/Flutter/AppFrameworkInfo.plist b/examples/todo/ios/Flutter/AppFrameworkInfo.plist index 9625e105..7c569640 100644 --- a/examples/todo/ios/Flutter/AppFrameworkInfo.plist +++ b/examples/todo/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/examples/todo/ios/Runner.xcodeproj/project.pbxproj b/examples/todo/ios/Runner.xcodeproj/project.pbxproj index 7fb33734..ab68198e 100644 --- a/examples/todo/ios/Runner.xcodeproj/project.pbxproj +++ b/examples/todo/ios/Runner.xcodeproj/project.pbxproj @@ -169,7 +169,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -345,7 +345,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -472,7 +472,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -521,7 +521,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/examples/todo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/todo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a09..8e3ca5df 100644 --- a/examples/todo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/examples/todo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ =3.2.3 <4.0.0" + dart: ">=3.3.0 <4.0.0" diff --git a/packages/celest/example/celest/functions/greeting.dart b/packages/celest/example/celest/functions/greeting.dart index 9da487b8..b5692dfb 100644 --- a/packages/celest/example/celest/functions/greeting.dart +++ b/packages/celest/example/celest/functions/greeting.dart @@ -1,10 +1,19 @@ // Cloud functions are top-level Dart functions defined in the `functions/` // folder of your Celest project. -/// Says hello to a person called [name]. -Future sayHello(String name) async { +import 'package:celest_backend/exceptions/bad_name_exception.dart'; +import 'package:celest_backend/models/person.dart'; + +/// Says hello to a [person]. +Future sayHello({required Person person}) async { + if (person.name.isEmpty) { + // Throw a custom exception defined in the `lib/exceptions/` and catch + // it on the frontend. + throw BadNameException('Name cannot be empty'); + } + // Logging is handled automatically when you print to the console. - print('Saying hello to $name'); + print('Saying hello to ${person.name}'); - return 'Hello, $name!'; + return 'Hello, ${person.name}!'; } diff --git a/packages/celest/example/celest/lib/client.dart b/packages/celest/example/celest/lib/client.dart index aced9430..546780c3 100644 --- a/packages/celest/example/celest/lib/client.dart +++ b/packages/celest/example/celest/lib/client.dart @@ -2,25 +2,60 @@ // it can be checked into version control. // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import -library; +library; // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:io'; +import 'dart:io' as _$io; import 'package:celest_core/src/util/globals.dart'; -import 'package:http/http.dart' as http; +import 'package:http/http.dart' as _$http; import 'src/client/functions.dart'; +import 'src/client/serializers.dart'; final Celest celest = Celest(); +enum CelestEnvironment { + local; + + Uri get baseUri => switch (this) { + local => kIsWeb || !_$io.Platform.isAndroid + ? Uri.parse('http://localhost:7781') + : Uri.parse('http://10.0.2.2:7781'), + }; +} + class Celest { - late http.Client httpClient = http.Client(); + var _initialized = false; + + late CelestEnvironment _currentEnvironment; + + late _$http.Client httpClient = _$http.Client(); + + late Uri _baseUri; + + final _functions = CelestFunctions(); + + T _checkInitialized(T Function() value) { + if (!_initialized) { + throw StateError( + 'Celest has not been initialized. Make sure to call `celest.init()` at the start of your `main` method.'); + } + return value(); + } + + CelestEnvironment get currentEnvironment => + _checkInitialized(() => _currentEnvironment); - late final Uri baseUri = kIsWeb || !Platform.isAndroid - ? Uri.parse('http://localhost:7777') - : Uri.parse('http://10.0.2.2:7777'); + Uri get baseUri => _checkInitialized(() => _baseUri); - final functions = CelestFunctions(); + CelestFunctions get functions => _checkInitialized(() => _functions); - void init() {} + void init({CelestEnvironment environment = CelestEnvironment.local}) { + _currentEnvironment = environment; + _baseUri = environment.baseUri; + if (!_initialized) { + initSerializers(); + } + _initialized = true; + } } diff --git a/packages/celest/example/celest/lib/exceptions.dart b/packages/celest/example/celest/lib/exceptions.dart deleted file mode 100644 index 1c7d9dde..00000000 --- a/packages/celest/example/celest/lib/exceptions.dart +++ /dev/null @@ -1,2 +0,0 @@ -// By convention, any custom exception types thrown by an API must be defined -// in this file or exported from this file. diff --git a/packages/celest/example/celest/lib/exceptions/bad_name_exception.dart b/packages/celest/example/celest/lib/exceptions/bad_name_exception.dart new file mode 100644 index 00000000..28c67ecb --- /dev/null +++ b/packages/celest/example/celest/lib/exceptions/bad_name_exception.dart @@ -0,0 +1,11 @@ +// By convention, any custom exception types thrown by an API must be defined +// in this file or exported from this file. + +class BadNameException implements Exception { + const BadNameException(this.message); + + final String message; + + @override + String toString() => 'BadNameException: $message'; +} diff --git a/packages/celest/example/celest/lib/models.dart b/packages/celest/example/celest/lib/models.dart deleted file mode 100644 index 9d381311..00000000 --- a/packages/celest/example/celest/lib/models.dart +++ /dev/null @@ -1,2 +0,0 @@ -// By convention, any custom types used within an API request/response must be -// defined in this file or exported from this file. diff --git a/packages/celest/example/celest/lib/models/person.dart b/packages/celest/example/celest/lib/models/person.dart new file mode 100644 index 00000000..b2b60309 --- /dev/null +++ b/packages/celest/example/celest/lib/models/person.dart @@ -0,0 +1,8 @@ +// By convention, any custom types used within an API request/response must be +// defined in the `models/` folder. + +class Person { + const Person({required this.name}); + + final String name; +} diff --git a/packages/celest/example/celest/lib/src/client/functions.dart b/packages/celest/example/celest/lib/src/client/functions.dart index 02705f66..97ac8f14 100644 --- a/packages/celest/example/celest/lib/src/client/functions.dart +++ b/packages/celest/example/celest/lib/src/client/functions.dart @@ -2,12 +2,14 @@ // it can be checked into version control. // ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import -library; +library; // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:convert'; +import 'dart:convert' as _$convert; import 'package:celest/celest.dart'; -import 'package:celest_core/src/exception/cloud_exception.dart'; +import 'package:celest_backend/exceptions/bad_name_exception.dart' + as _$bad_name_exception; +import 'package:celest_backend/models/person.dart' as _$person; import '../../client.dart'; @@ -16,28 +18,19 @@ class CelestFunctions { } class CelestFunctionsGreeting { - /// Says hello to a person called [name]. - Future sayHello(String name) async { - final $response = await celest.httpClient.post( - celest.baseUri.resolve('/greeting/say-hello'), - headers: const {'Content-Type': 'application/json; charset=utf-8'}, - body: jsonEncode({r'name': name}), - ); - final $body = (jsonDecode($response.body) as Map); - if ($response.statusCode == 200) { - return ($body['response'] as String); - } + Never _throwError({ + required int $statusCode, + required Map $body, + }) { final $error = ($body['error'] as Map); final $code = ($error['code'] as String); final $details = ($error['details'] as Map?); switch ($code) { - case r'BadRequestException': - throw Serializers.instance.deserialize($details); - case r'InternalServerException': + case r'BadNameException': throw Serializers.instance - .deserialize($details); + .deserialize<_$bad_name_exception.BadNameException>($details); case _: - switch ($response.statusCode) { + switch ($statusCode) { case 400: throw BadRequestException($code); case _: @@ -45,4 +38,23 @@ class CelestFunctionsGreeting { } } } + + /// Says hello to a [person]. + Future sayHello({required _$person.Person person}) async { + final $response = await celest.httpClient.post( + celest.baseUri.resolve('/greeting/say-hello'), + headers: const {'Content-Type': 'application/json; charset=utf-8'}, + body: _$convert.jsonEncode( + {r'person': Serializers.instance.serialize<_$person.Person>(person)}), + ); + final $body = + (_$convert.jsonDecode($response.body) as Map); + if ($response.statusCode != 200) { + _throwError( + $statusCode: $response.statusCode, + $body: $body, + ); + } + return ($body['response'] as String); + } } diff --git a/packages/celest/example/celest/lib/src/client/serializers.dart b/packages/celest/example/celest/lib/src/client/serializers.dart new file mode 100644 index 00000000..381836e8 --- /dev/null +++ b/packages/celest/example/celest/lib/src/client/serializers.dart @@ -0,0 +1,25 @@ +// ignore_for_file: type=lint, unused_local_variable, unnecessary_cast, unnecessary_import + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:celest/celest.dart'; +import 'package:celest_backend/exceptions/bad_name_exception.dart' + as _$bad_name_exception; +import 'package:celest_backend/models/person.dart' as _$person; + +void initSerializers() { + Serializers.instance.put(Serializer.define< + _$bad_name_exception.BadNameException, Map>( + serialize: ($value) => {r'message': $value.message}, + deserialize: ($serialized) { + return _$bad_name_exception.BadNameException( + ($serialized[r'message'] as String)); + }, + )); + Serializers.instance + .put(Serializer.define<_$person.Person, Map>( + serialize: ($value) => {r'name': $value.name}, + deserialize: ($serialized) { + return _$person.Person(name: ($serialized[r'name'] as String)); + }, + )); +} diff --git a/packages/celest/example/celest/project.dart b/packages/celest/example/celest/project.dart index c85f1904..5f6c5a24 100644 --- a/packages/celest/example/celest/project.dart +++ b/packages/celest/example/celest/project.dart @@ -1,5 +1,5 @@ import 'package:celest/celest.dart'; const project = Project( - name: 'example_app', + name: 'example', ); diff --git a/packages/celest/example/celest/pubspec.yaml b/packages/celest/example/celest/pubspec.yaml index a26ba48e..d6693e1d 100644 --- a/packages/celest/example/celest/pubspec.yaml +++ b/packages/celest/example/celest/pubspec.yaml @@ -1,13 +1,13 @@ name: celest_backend -description: The Celest backend for example_app. +description: The Celest backend for example. publish_to: none environment: sdk: ^3.3.0 dependencies: - celest: ^0.1.0 - celest_core: ^0.1.0 + celest: ^0.2.0 + celest_core: ^0.2.0 http: ">=0.13.0 <2.0.0" dev_dependencies: diff --git a/packages/celest/example/celest/resources.dart b/packages/celest/example/celest/resources.dart index 4cb26244..2925f5ca 100644 --- a/packages/celest/example/celest/resources.dart +++ b/packages/celest/example/celest/resources.dart @@ -6,11 +6,11 @@ library; import 'package:celest/celest.dart'; -abstract final class apis { +abstract final class Apis { static const greeting = CloudApi(name: r'greeting'); } -abstract final class functions { +abstract final class Functions { static const greetingSayHello = CloudFunction( api: r'greeting', functionName: r'sayHello', diff --git a/packages/celest/example/celest/test/functions/greeting_test.dart b/packages/celest/example/celest/test/functions/greeting_test.dart index 1068709e..dc9ec6df 100644 --- a/packages/celest/example/celest/test/functions/greeting_test.dart +++ b/packages/celest/example/celest/test/functions/greeting_test.dart @@ -1,3 +1,5 @@ +import 'package:celest_backend/exceptions/bad_name_exception.dart'; +import 'package:celest_backend/models/person.dart'; import 'package:test/test.dart'; import '../../functions/greeting.dart'; @@ -5,7 +7,13 @@ import '../../functions/greeting.dart'; void main() { group('greeting', () { test('sayHello', () async { - expect(await sayHello('Celest'), 'Hello, Celest!'); + expect(await sayHello(person: Person(name: 'Celest')), 'Hello, Celest!'); + }); + test('sayHello (empty name)', () async { + expect( + sayHello(person: Person(name: '')), + throwsA(isA()), + ); }); }); } diff --git a/packages/celest/example/ios/Runner.xcodeproj/project.pbxproj b/packages/celest/example/ios/Runner.xcodeproj/project.pbxproj index 9c89ada2..4363bd5a 100644 --- a/packages/celest/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/celest/example/ios/Runner.xcodeproj/project.pbxproj @@ -169,7 +169,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { diff --git a/packages/celest/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/celest/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a09..8e3ca5df 100644 --- a/packages/celest/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/celest/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ switch (snapshot) { AsyncSnapshot(:final data?) => Text(data), AsyncSnapshot(:final error?) => diff --git a/packages/celest/example/macos/Runner.xcodeproj/project.pbxproj b/packages/celest/example/macos/Runner.xcodeproj/project.pbxproj index b07ddf06..8b005e22 100644 --- a/packages/celest/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/celest/example/macos/Runner.xcodeproj/project.pbxproj @@ -227,7 +227,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { diff --git a/packages/celest/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/celest/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e93dc924..1242ce6d 100644 --- a/packages/celest/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/celest/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@