[linting_tool] Implement exporting profiles (#869)

pull/871/head
Abdullah Deshmukh 4 years ago committed by GitHub
parent a46327ef80
commit 9986fe2f2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,6 +11,8 @@ import 'package:linting_tool/routes.dart' as routes;
import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;
final client = http.Client();
class LintingTool extends StatefulWidget {
const LintingTool({Key? key}) : super(key: key);
@ -26,10 +28,10 @@ class _LintingToolState extends State<LintingTool> {
return MultiProvider(
providers: [
ChangeNotifierProvider<RuleStore>(
create: (context) => RuleStore(http.Client()),
create: (context) => RuleStore(client),
),
ChangeNotifierProvider<ProfilesStore>(
create: (context) => ProfilesStore(),
create: (context) => ProfilesStore(client),
),
],
child: MaterialApp(

@ -2,16 +2,26 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:json2yaml/json2yaml.dart';
import 'package:linting_tool/model/profile.dart';
import 'package:linting_tool/model/rule.dart';
import 'package:linting_tool/repository/hive_service.dart';
import 'package:linting_tool/repository/repository.dart';
import 'package:http/http.dart' as http;
import 'package:file_selector/file_selector.dart' as file_selector;
import 'package:yaml/yaml.dart';
const _boxName = 'rules_profile';
class ProfilesStore extends ChangeNotifier {
ProfilesStore() {
late final Repository repository;
ProfilesStore(http.Client httpClient) {
repository = Repository(httpClient);
fetchSavedProfiles();
}
bool _isLoading = true;
@ -66,4 +76,84 @@ class ProfilesStore extends ChangeNotifier {
await fetchSavedProfiles();
});
}
Future<bool> exportProfileFile(
RulesProfile profile, {
RulesStyle rulesStyle = RulesStyle.booleanMap,
}) async {
_isLoading = true;
notifyListeners();
var resultSaved = false;
try {
var templateFileData = await repository.getTemplateFile();
String newYamlFile =
_prepareYamlFile(profile, templateFileData, rulesStyle);
resultSaved = await _saveFileToDisk(newYamlFile);
} on SocketException catch (e) {
log(e.toString());
_error = 'Check internet connection.';
resultSaved = false;
} on Exception catch (e) {
log(e.toString());
}
_isLoading = false;
notifyListeners();
return resultSaved;
}
Future<bool> _saveFileToDisk(String newYamlFile) async {
const name = 'analysis_options.yaml';
/// Get file path using file picker.
var savePath = await file_selector.getSavePath(
suggestedName: name,
);
final data = Uint8List.fromList(newYamlFile.codeUnits);
final file = file_selector.XFile.fromData(data, name: name);
/// Save file to disk if path was provided.
if (savePath != null) {
file.saveTo(savePath);
return true;
}
var errorMessage = 'File path not found.';
_error = errorMessage;
throw Exception(errorMessage);
}
String _prepareYamlFile(
RulesProfile profile, YamlMap templateFile, RulesStyle rulesStyle) {
var rules = profile.rules.map((e) => e.name).toList();
var rulesData =
json.decode(json.encode(templateFile)) as Map<String, dynamic>;
/// Add rules to existing template according to formatting style.
if (rulesStyle == RulesStyle.booleanMap) {
var rulesMap = Map.fromEntries(
rules.map(
(e) => MapEntry(e, true),
),
);
rulesData.update('linter', (dynamic value) => {'rules': rulesMap});
} else {
rulesData.update('linter', (dynamic value) => {'rules': rules});
}
return json2yaml(rulesData, yamlStyle: YamlStyle.pubspecYaml);
}
}
/// Formatting style for rules.
enum RulesStyle {
list,
booleanMap,
}

@ -45,7 +45,7 @@ class SavedLintsPage extends StatelessWidget {
),
itemCount: profilesStore.savedProfiles.length,
cacheExtent: 5,
itemBuilder: (context, index) {
itemBuilder: (itemBuilderContext, index) {
var profile = profilesStore.savedProfiles[index];
return ListTile(
title: Text(
@ -74,10 +74,21 @@ class SavedLintsPage extends StatelessWidget {
),
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert),
onSelected: (value) {
onSelected: (value) async {
switch (value) {
case 'Export file':
// TODO(abd99): Implement exporting files.
// TODO(abd99): Add option to select formatting style.
var saved = await profilesStore
.exportProfileFile(profile);
if (!saved) {
_showSnackBar(
context,
profilesStore.error ?? 'Failed to save file.',
);
}
break;
case 'Delete':
profilesStore.deleteProfile(profile);
@ -123,4 +134,12 @@ class SavedLintsPage extends StatelessWidget {
},
);
}
void _showSnackBar(BuildContext context, String data) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(data),
),
);
}
}

@ -5,6 +5,7 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:linting_tool/model/rule.dart';
import 'package:yaml/yaml.dart';
class APIProvider {
final _baseURL = 'https://dart-lang.github.io/linter';
@ -12,7 +13,7 @@ class APIProvider {
APIProvider(this.httpClient);
Future<List<Rule>> getRulesList() async {
http.Response response =
final response =
await httpClient.get(Uri.parse('$_baseURL//lints/machine/rules.json'));
if (response.statusCode == 200) {
@ -26,4 +27,14 @@ class APIProvider {
throw Exception('Failed to load rules');
}
}
Future<YamlMap> getTemplateFile() async {
final response = await httpClient.get(Uri.parse(
'https://raw.githubusercontent.com/flutter/flutter/master/packages/flutter_tools/templates/app_shared/analysis_options.yaml.tmpl'));
if (response.statusCode == 200) {
return loadYaml(response.body) as YamlMap;
} else {
throw Exception('Failed to load template file');
}
}
}

@ -5,6 +5,7 @@
import 'package:linting_tool/model/rule.dart';
import 'package:linting_tool/repository/api_provider.dart';
import 'package:http/http.dart' as http;
import 'package:yaml/yaml.dart';
class Repository {
late final APIProvider _apiProvider;
@ -14,4 +15,6 @@ class Repository {
}
Future<List<Rule>> getRulesList() => _apiProvider.getRulesList();
Future<YamlMap> getTemplateFile() => _apiProvider.getTemplateFile();
}

@ -4,6 +4,10 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
}

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
)
set(PLUGIN_BUNDLED_LIBRARIES)

@ -5,8 +5,10 @@
import FlutterMacOS
import Foundation
import file_selector_macos
import path_provider_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}

@ -1,19 +1,25 @@
PODS:
- file_selector_macos (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- path_provider_macos (0.0.1):
- FlutterMacOS
DEPENDENCIES:
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
EXTERNAL SOURCES:
file_selector_macos:
:path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
FlutterMacOS:
:path: Flutter/ephemeral
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
SPEC CHECKSUMS:
file_selector_macos: ff6dc948d4ddd34e8602a1f60b7d0b4cc6051a47
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b

@ -10,5 +10,7 @@
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>

@ -8,5 +8,7 @@
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>

@ -155,6 +155,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
cross_file:
dependency: transitive
description:
name: cross_file
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1+4"
crypto:
dependency: transitive
description:
@ -204,6 +211,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.2"
file_selector:
dependency: "direct main"
description:
name: file_selector
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.2"
file_selector_linux:
dependency: "direct main"
description:
name: file_selector_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+1"
file_selector_macos:
dependency: "direct main"
description:
name: file_selector_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+1"
file_selector_platform_interface:
dependency: transitive
description:
name: file_selector_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
file_selector_web:
dependency: "direct main"
description:
name: file_selector_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.1+1"
file_selector_windows:
dependency: "direct main"
description:
name: file_selector_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+1"
fixnum:
dependency: transitive
description:
@ -235,6 +284,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
@ -319,6 +373,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
json2yaml:
dependency: "direct main"
description:
name: json2yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
json_annotation:
dependency: "direct main"
description:
@ -626,7 +687,7 @@ packages:
source: hosted
version: "0.2.0"
yaml:
dependency: transitive
dependency: "direct main"
description:
name: yaml
url: "https://pub.dartlang.org"
@ -634,4 +695,4 @@ packages:
version: "3.1.0"
sdks:
dart: ">=2.13.0 <3.0.0"
flutter: ">=1.20.0"
flutter: ">=2.0.0"

@ -12,14 +12,21 @@ dependencies:
adaptive_breakpoints: ^0.0.4
cupertino_icons: ^1.0.2
equatable: ^2.0.3
file_selector: ^0.8.2
file_selector_linux: ^0.0.2+1
file_selector_macos: ^0.0.4+1
file_selector_web: ^0.8.1+1
file_selector_windows: ^0.0.2+1
flutter_markdown: ^0.6.2
google_fonts: ^2.1.0
hive: ^2.0.4
hive_flutter: ^1.1.0
http: ^0.13.3
json2yaml: ^2.0.0
json_annotation: ^4.0.1
mockito: ^5.0.13
provider: ^5.0.0
yaml: ^3.1.0
dev_dependencies:
flutter_test:

@ -38,7 +38,7 @@ class _TestApp extends StatelessWidget {
create: (context) => RuleStore(_mockClient),
),
ChangeNotifierProvider<ProfilesStore>(
create: (context) => ProfilesStore(),
create: (context) => ProfilesStore(_mockClient),
),
],
child: MaterialApp(

@ -4,6 +4,9 @@
#include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorPlugin"));
}

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
)
set(PLUGIN_BUNDLED_LIBRARIES)

Loading…
Cancel
Save