diff --git a/.gitignore b/.gitignore index 62c8935..68729d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.idea/ \ No newline at end of file +.idea/ +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 544fe13..cd2ea97 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,14 @@ The project uses: - [Amplify Datastore](https://pub.dev/packages/amplify_datastore) for saving data and real time updates - [bloc](https://pub.dev/packages/bloc) for state management. +## Requirements πŸš€ + +Before you move forward, be sure to have +- your AWS Account created +- Amplify CLI is installed. + +If you do not know how to do it, you can check the [official documentation](https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter/) or this [blog post](https://medium.com/p/ef748798fdbf) for detailed guide. + ## How to use πŸš€ You can either fill in the variables on your CLI: @@ -25,12 +33,6 @@ let CLI guide you through the process mason make amplify_starter ``` ------ - -> This project creates the base project for you. You are still expected to configure AWS Amplify afterwards. -> -> Once the project is created go to the [README.md](https://github.com/salihgueler/amplify_starter/blob/main/__brick__/%7B%7Bproject_name%7D%7D/README.md) file of the generated project and follow the steps described there. - ## Variables ✨ | Variable | Description | Default | Type | diff --git a/__brick__/{{project_name}}/README.md b/__brick__/{{project_name}}/README.md index cf3c7e9..e26b971 100644 --- a/__brick__/{{project_name}}/README.md +++ b/__brick__/{{project_name}}/README.md @@ -11,130 +11,7 @@ The project uses: - ## Getting Started -> Before you move forward, be sure to have your AWS Account created and Amplify CLI is installed. -> -> If you do not know how to do it, you can check the [official documentation](https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter/) or this [blog post](https://medium.com/p/ef748798fdbf) for detailed guide. - -Once the project is created, open your terminal on the generated project path. Afterwards write the following command: -```shell -amplify init -``` - -This will initialize AWS Amplify for your application. - -```shell -msalihg@ AWS TODO % amplify init -Note: It is recommended to run this command from the root of your app directory -? Enter a name for the project AWSTODO -The following configuration will be applied: - -Project information -| Name: AWSTODO -| Environment: dev -| Default editor: Visual Studio Code -| App type: flutter -| Configuration file location: ./lib/ - -? Initialize the project with the above configuration? Yes -Using default provider awscloudformation -? Select the authentication method you want to use: AWS profile - -For more information on AWS Profiles, see: -https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html - -? Please choose the profile you want to use default -Adding backend environment dev to AWS Amplify app: -β Ό Initializing project in the cloud... -... -βœ… Initialized your environment successfully -``` - -At above, you have chosen the default configuration for your project, it is more than enough to start with. But, you can always have your own version of the settings as well. - -Once you see the success message, you are ready to add libraries. First, add Amplify Authentication library: - -```shell -amplify add auth -``` - -This will take you to select couple of options: -``` -msalihg@ AWS TODO % amplify add auth -Using service: Cognito, provided by: awscloudformation - -The current configured provider is Amazon Cognito. - -Do you want to use the default authentication and security configuration? Default configuration -Warning: you will not be able to edit these selections. -How do you want users to be able to sign in? Username -Do you want to configure advanced settings? No, I am done. -βœ… Successfully added auth resource locally -``` - -Just select the default ones and username as a sign in method. That is enough for Authentication. - -Next step is adding the Amplify DataStore. - -```shell -amplify add api -``` - -This is a tricky one. You need to select GraphQL if you would like to work with real time updates. - -```shell -msalihg@ AWS TODO % amplify add api -? Select from one of the below mentioned services: GraphQL -? Here is the GraphQL API that we will create. Select a setting to edit or continue -? Choose the default authorization type for the API Amazon Cognito User Pool -Use a Cognito user pool configured as a part of this project. -? Configure additional auth types? No -? Here is the GraphQL API that we will create. Select a setting to edit or continue Conflict detection (required for DataStore): Disabled -? Enable conflict detection? Yes -? Select the default resolution strategy Auto Merge -? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue -? Choose a schema template: Single object with fields (e.g., β€œTodo” with ID, name, description) -``` - -You can pick any option from above **but for the sake of the starter project, do the selections as seen like above.** - -Now open the `schema.graphql` file from `amplify/backend/api/<{{project_name}}>/schema.graphql` path. Remove everything and paste the following there: - -```graphql -type Todo @model @auth(rules: [{ allow: owner }]) { - id: ID! - name: String! - description: String - isComplete: Boolean! -} -``` - -This will create a Todo object that will be available only for the owner to read/create/update. - -Amplify has a tool to help you generate models out of the GraphQL reference. - -```shell -amplify codegen models -``` - -Once you create the models, now you are set to push your changes to cloud. But before write `amplify status` command. It should show you the state of your application and your setup for AWS Amplify. - -```shell -msalihg@ {{project_name}} % amplify status - - Current Environment: dev - -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Category β”‚ Resource name β”‚ Operation β”‚ Provider plugin β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ Auth β”‚ {{project_name}} β”‚ Create β”‚ awscloudformation β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ Api β”‚ {{project_name}} β”‚ Create β”‚ awscloudformation β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - -GraphQL transformer version: 2 -``` - -You can see that, two categories have been created for your project and now ready to be pushed. +The project is structured and configured for your usage. Only thing you have to do is to change your changes to the cloud __IF__ you have not done it yet. ```shell amplify push @@ -142,10 +19,4 @@ amplify push With this command, you are pushing your changes to the cloud. These changes will take a couple of minutes so in the meantime enjoy your coffee. -Once it is done, get the Flutter packages as follows: - -```shell -flutter pub get -``` - -and run your application! \ No newline at end of file +Once it is done, go ahead and run your application! \ No newline at end of file diff --git a/__brick__/{{project_name}}/add_api_request.json b/__brick__/{{project_name}}/add_api_request.json new file mode 100644 index 0000000..48f1bed --- /dev/null +++ b/__brick__/{{project_name}}/add_api_request.json @@ -0,0 +1 @@ +{"version":1,"serviceConfiguration":{"serviceName":"AppSync","apiName":"apiGraphQl{{project_name.pascalCase()}}","transformSchema":"type Todo @model @auth(rules: [{ allow: owner }]) {\n id: ID!\n name: String!\n description: String\n isComplete: Boolean!\n}","defaultAuthType":{"mode":"AMAZON_COGNITO_USER_POOLS"},"conflictResolution":{"defaultResolutionStrategy":{"type":"AUTOMERGE"}}}} \ No newline at end of file diff --git a/__brick__/{{project_name}}/add_auth_request.json b/__brick__/{{project_name}}/add_auth_request.json new file mode 100644 index 0000000..2ddfd11 --- /dev/null +++ b/__brick__/{{project_name}}/add_auth_request.json @@ -0,0 +1 @@ +{"version": 1,"resourceName": "{{project_name.pascalCase()}}","serviceConfiguration": {"serviceName": "Cognito","userPoolConfiguration": {"signinMethod": "USERNAME","requiredSignupAttributes": ["EMAIL"], "autoVerifiedAttributes": [{"type": "EMAIL"}]},"includeIdentityPool": true}} \ No newline at end of file diff --git a/__brick__/{{project_name}}/lib/main.dart b/__brick__/{{project_name}}/lib/main.dart index 0cbdf56..4590454 100644 --- a/__brick__/{{project_name}}/lib/main.dart +++ b/__brick__/{{project_name}}/lib/main.dart @@ -4,13 +4,13 @@ import 'package:amplify_authenticator/amplify_authenticator.dart'; import 'package:amplify_datastore/amplify_datastore.dart'; import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:{{project_name.snakeCase()}}/models/ModelProvider.dart'; +import 'package:{{project_name.snakeCase()}}/todos/todos_list/ui/todos_page.dart'; import 'package:{{project_name.snakeCase()}}/profile/domain/remote_user_repository.dart'; import 'package:{{project_name.snakeCase()}}/profile/domain/user_repository.dart'; import 'package:{{project_name.snakeCase()}}/todos/domain/remote_todos_repository.dart'; import 'package:{{project_name.snakeCase()}}/todos/domain/todos_repository.dart'; -import 'package:{{project_name.snakeCase()}}/todos/todos_list/ui/todos_page.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'amplifyconfiguration.dart'; diff --git a/__brick__/{{project_name}}/pubspec.yaml b/__brick__/{{project_name}}/pubspec.yaml index f29d191..e47d266 100644 --- a/__brick__/{{project_name}}/pubspec.yaml +++ b/__brick__/{{project_name}}/pubspec.yaml @@ -31,11 +31,11 @@ dependencies: sdk: flutter # Amplify Libraries - amplify_flutter: ^0.6.1 - amplify_datastore: ^0.6.1 - amplify_authenticator: ^0.2.1 - amplify_auth_cognito: ^0.6.1 - amplify_api: ^0.6.1 + amplify_flutter: ^0.6.5 + amplify_datastore: ^0.6.5 + amplify_authenticator: ^0.2.2 + amplify_auth_cognito: ^0.6.5 + amplify_api: ^0.6.5 # Bloc libraries equatable: ^2.0.3 diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..41ffce1 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,3 @@ +analyzer: + exclude: + - __brick__/** diff --git a/hooks/.gitignore b/hooks/.gitignore new file mode 100644 index 0000000..c8b1c01 --- /dev/null +++ b/hooks/.gitignore @@ -0,0 +1,4 @@ +.dart_tool +.packages +pubspec.lock +coverage diff --git a/hooks/post_gen.dart b/hooks/post_gen.dart new file mode 100644 index 0000000..3d2464b --- /dev/null +++ b/hooks/post_gen.dart @@ -0,0 +1,190 @@ +import 'dart:io'; + +import 'package:mason/mason.dart'; + +Future run(HookContext context) async { + await _runFlutterPubGet(context); + + await _runAmplifyInit(context); + + await _runAmplifyAddAuth(context); + + await _runAmplifyAddApi(context); + + await _runAmplifyCodegen(context); + + stdout.writeln("Do you want to push your changes to the cloud now?"); + stdout.write( + "This might take couple of minutes (between 5-10 minutes) and you can do it any time by running 'amplify push': (Y/n)", + ); + final input = stdin.readLineSync() ?? ''; + + _runAmplifyPush(context, input); +} + +Future _runFlutterPubGet(HookContext context) async { + final flutterPubGetProgress = context.logger.progress( + 'running "flutter pub get"', + ); + final result = await Process.start( + 'flutter', + ['pub', 'get'], + workingDirectory: '{{project_name}}', + ); + + final exitCode = await result.exitCode; + + if (exitCode == 0) { + flutterPubGetProgress.complete('Flutter pub get successfully finished.'); + } else { + final errorBytes = await result.stderr.first; + final error = systemEncoding.decode(errorBytes); + flutterPubGetProgress.complete( + 'Flutter pub get had an error $error.', + ); + exit(exitCode); + } +} + +Future _runAmplifyInit(HookContext context) async { + final amplifyInitProgress = context.logger.progress( + 'running "amplify init --yes"', + ); + + final result = await Process.start( + 'amplify', + ['init', '--yes'], + workingDirectory: '{{project_name}}', + ); + + final exitCode = await result.exitCode; + + if (exitCode == 0) { + amplifyInitProgress.complete('Project is initialized.'); + } else { + final errorBytes = await result.stderr.first; + final error = systemEncoding.decode(errorBytes); + amplifyInitProgress.complete('Project initialization failed. $error'); + exit(exitCode); + } +} + +Future _runAmplifyAddAuth(HookContext context) async { + final amplifyAddProgress = context.logger.progress( + 'running "amplify add auth --headless"', + ); + + final catAddAuthRequest = await Process.start( + 'cat', + ['add_auth_request.json'], + workingDirectory: '{{project_name}}', + ); + + final amplifyAddAuth = await Process.start( + 'amplify', + ['add', 'auth', '--headless'], + workingDirectory: '{{project_name}}', + ); + + await catAddAuthRequest.stdout.pipe(amplifyAddAuth.stdin); + + final exitCode = await amplifyAddAuth.exitCode; + + if (exitCode == 0) { + amplifyAddProgress.complete('Amplify Auth added.'); + } else { + final errorBytes = await amplifyAddAuth.stderr.first; + final error = systemEncoding.decode(errorBytes); + amplifyAddProgress.complete('Amplify Auth failed to be added. $error'); + exit(exitCode); + } +} + +Future _runAmplifyAddApi(HookContext context) async { + final amplifyApiProgress = context.logger.progress( + 'running "amplify add api --headless"', + ); + + final catAddApiRequest = await Process.start( + 'cat', + ['add_api_request.json'], + workingDirectory: '{{project_name}}', + ); + + final amplifyAddApi = await Process.start( + 'amplify', + ['add', 'api', '--headless'], + workingDirectory: '{{project_name}}', + ); + + await catAddApiRequest.stdout.pipe(amplifyAddApi.stdin); + + final exitCodeApi = await amplifyAddApi.exitCode; + + if (exitCodeApi == 0) { + amplifyApiProgress.complete('Amplify API added.'); + } else { + final errorBytes = await amplifyAddApi.stderr.first; + final error = systemEncoding.decode(errorBytes); + amplifyApiProgress.complete('Amplify API failed to be added. $error'); + exit(exitCodeApi); + } +} + +Future _runAmplifyCodegen(HookContext context) async { + final amplifyCodegenProgress = context.logger.progress( + 'running "amplify codegen models"', + ); + + final result = await Process.start( + 'amplify', + ['codegen', 'models'], + workingDirectory: '{{project_name}}', + ); + + final exitCode = await result.exitCode; + + if (exitCode == 0) { + amplifyCodegenProgress.complete( + 'Models are created.', + ); + } else { + final errorBytes = await result.stderr.first; + final error = systemEncoding.decode(errorBytes); + amplifyCodegenProgress.complete('Model creation failed. $error'); + exit(exitCode); + } +} + +Future _runAmplifyPush(HookContext context, String input) async { + if (input.isEmpty || input == 'Y' || input == 'y') { + stdout.writeln( + '\nRunning "amplify push", do not close the terminal until it is finished.', + ); + + final amplifyPushProgress = context.logger.progress( + 'running "amplify push"', + ); + + final result = Process.runSync( + 'amplify', + ['push', '--yes'], + workingDirectory: '{{project_name}}', + ); + + final exitCode = await result.exitCode; + + if (exitCode == 0) { + amplifyPushProgress.complete('Backend is pushed'); + } else { + final errorBytes = await result.stderr.first; + final error = systemEncoding.decode(errorBytes); + amplifyPushProgress.complete('Backend push is failed. $error'); + exit(exitCode); + } + } else { + stdout.writeln( + 'Do not forget to push your changes to the cloud by running "amplify push"', + ); + } +} diff --git a/hooks/pre_gen.dart b/hooks/pre_gen.dart new file mode 100644 index 0000000..8635ffa --- /dev/null +++ b/hooks/pre_gen.dart @@ -0,0 +1,10 @@ +import 'dart:io'; +import 'package:mason/mason.dart'; + +Future run(HookContext context) async { + final result = await Process.run('which', ['amplify']); + if (result.exitCode != 0) { + context.logger.err(result.stderr); + exit(result.exitCode); + } +} diff --git a/hooks/pubspec.lock b/hooks/pubspec.lock new file mode 100644 index 0000000..36b922e --- /dev/null +++ b/hooks/pubspec.lock @@ -0,0 +1,145 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.0" + mason: + dependency: "direct main" + description: + name: mason + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0-dev.31" + mason_logger: + dependency: transitive + description: + name: mason_logger + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + mustache_template: + dependency: transitive + description: + name: mustache_template + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + recase: + dependency: transitive + description: + name: recase + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.16.0 <3.0.0" diff --git a/hooks/pubspec.yaml b/hooks/pubspec.yaml new file mode 100644 index 0000000..fbbf6a4 --- /dev/null +++ b/hooks/pubspec.yaml @@ -0,0 +1,7 @@ +name: amplify_starter_hooks + +environment: + sdk: ">=2.12.0 <3.0.0" + +dependencies: + mason: ^0.1.0-dev.31 \ No newline at end of file