Skip to content

Commit

Permalink
Merge pull request #164 from humhub/119-enhance-find-manifest
Browse files Browse the repository at this point in the history
119 Enhance find manifest procedure and add tests.
  • Loading branch information
luke- committed Mar 11, 2024
2 parents e24b6cc + 892ec08 commit 8087329
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 112 deletions.
1 change: 1 addition & 0 deletions .github/workflows/version-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ jobs:
versionName: ${{ env.version }}
- run: flutter clean
- run: flutter pub get
- run: flutter test
- run: flutter analyze --no-fatal-warnings --no-fatal-infos
- run: flutter build apk --release --split-per-abi --build-name="${{ env.version }}" --build-number=${{ env.version_code }}
- run: flutter build appbundle --release --build-name="${{ env.version }}" --build-number=${{ env.version_code }}
Expand Down
10 changes: 6 additions & 4 deletions lib/models/manifest.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ class Manifest {
'theme_color': themeColor,
};

static Future<Manifest> Function(Dio dio) get(String url, {bool isUriPretty = true}) => (dio) async {
Response<dynamic> res = !isUriPretty
? await dio.get('$url/index.php?r=web%2Fpwa-manifest%2Findex')
: await dio.get('$url/manifest.json');
static Future<Manifest> Function(Dio dio) get(String url) => (dio) async {
Response<dynamic> res = await dio.get(url);
return Manifest.fromJson(res.data);
};

static String defineUrl(String url, {bool isUriPretty = true}) {
return !isUriPretty ? '$url/index.php?r=web%2Fpwa-manifest%2Findex' : '$url/manifest.json';
}
}
2 changes: 1 addition & 1 deletion lib/pages/web_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class WebViewAppState extends ConsumerState<WebViewApp> {
}
String? payloadFromPush = InitFromPush.usePayload();
if (payloadFromPush != null) url = payloadFromPush;
return URLRequest(url: Uri.parse(url ?? manifest.baseUrl), headers: ref.read(humHubProvider).customHeaders);
return URLRequest(url: Uri.parse(url ?? manifest.startUrl), headers: ref.read(humHubProvider).customHeaders);
}

_onLoadStop(InAppWebViewController controller, Uri? url) {
Expand Down
8 changes: 0 additions & 8 deletions lib/util/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,3 @@ extension FutureAsyncValueX<T> on Future<AsyncValue<T>> {
(asyncValue) => asyncValue.asData?.value,
);
}

extension PrettyUri on Uri {
bool isUriPretty() {
RegExp regex = RegExp(r'index\.php.*[?&]r=');
String path = Uri.decodeComponent(toString());
return !regex.hasMatch(path);
}
}
65 changes: 43 additions & 22 deletions lib/util/opener_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart';
import 'package:humhub/models/hum_hub.dart';
import 'package:humhub/models/manifest.dart';
import 'package:humhub/util/extensions.dart';
import 'package:humhub/util/providers.dart';
import 'package:http/http.dart' as http;
import 'package:loggy/loggy.dart';
Expand All @@ -24,22 +23,25 @@ class OpenerController {

OpenerController({required this.ref, required this.helper});

findManifest(String url) async {
Uri uri = assumeUrl(url);
if (!uri.isUriPretty()) {
asyncData = await APIProvider.of(ref).request(Manifest.get(uri.origin, isUriPretty: false));
} else {
for (var i = uri.pathSegments.length - 1; i >= 0; i--) {
String urlIn = "${uri.origin}/${uri.pathSegments.getRange(0, i).join('/')}";
asyncData = await APIProvider.of(ref).request(Manifest.get(i != 0 ? urlIn : uri.origin));
if (!asyncData!.hasError) break;
}
if (uri.pathSegments.isEmpty) {
asyncData = await APIProvider.of(ref).request(Manifest.get(uri.origin));
}
/// Finds the `manifest.json` file associated with the given URL. If the URL does not
/// directly point to the `manifest.json` file, it traverses up the directory structure
/// to locate it. If not found, it assumes a default path format. This method makes
/// asynchronous requests to fetch the manifest data.
///
/// @param url The URL from which to start searching for the `manifest.json` file.
/// @return A Future that completes with no result once the `manifest.json` file is found
/// or the default path is assumed, or if an error occurs during the search process.
/// Additionally, it may trigger a check for the HumHub module view based on the start URL
/// obtained from the manifest data.
///
/// @throws Exception if an error occurs during the search process.
Future<bool> findManifest(String url) async {
List<String> possibleUrls = generatePossibleManifestsUrls(url);
for (var url in possibleUrls) {
asyncData = await APIProvider.of(ref).request(Manifest.get(url));
if (!asyncData!.hasError) break;
}
if (asyncData!.hasError) return;
await checkHumHubModuleView(asyncData!.value!.startUrl);
return asyncData!.hasError;
}

checkHumHubModuleView(String url) async {
Expand Down Expand Up @@ -67,8 +69,12 @@ class OpenerController {
}
// Get the manifest.json for given url.
await findManifest(helper.model[formUrlKey]!);
logDebug("Here");
if (asyncData!.hasValue) {
await checkHumHubModuleView(asyncData!.value!.startUrl);
}
// If manifest.json does not exist the url is incorrect.
// This is a temp. fix the validator expect sync. function this is some established workaround.
// This is a temp. fix the validator expect sync function this is established workaround.
// In the future we could define our own TextFormField that would also validate the API responses.
// But it this is not acceptable I can suggest simple popup or tempPopup.
if (asyncData!.hasError || !doesViewExist) {
Expand All @@ -92,11 +98,6 @@ class OpenerController {

bool get allOk => !(asyncData == null || asyncData!.hasError || !doesViewExist);

Uri assumeUrl(String url) {
if (url.startsWith("https://") || url.startsWith("http://")) return Uri.parse(url);
return Uri.parse("https://$url");
}

String? validateUrl(String? value) {
if (value == error404) return 'Your HumHub installation does not exist';
if (value == noConnection) return 'Please check your internet connection.';
Expand All @@ -105,4 +106,24 @@ class OpenerController {
}
return null;
}

static List<String> generatePossibleManifestsUrls(String url) {
List<String> urls = [];
Uri uri = assumeUrl(url);

for (var i = uri.pathSegments.length; i >= 0; i--) {
String urlIn = "${uri.origin}/${uri.pathSegments.getRange(0, i).join('/')}";
urls.add(Manifest.defineUrl(i != 0 ? urlIn : uri.origin));
}
for (var i = uri.pathSegments.length; i >= 0; i--) {
String urlIn = "${uri.origin}/${uri.pathSegments.getRange(0, i).join('/')}";
urls.add(Manifest.defineUrl(i != 0 ? urlIn : uri.origin, isUriPretty: false));
}
return urls;
}

static Uri assumeUrl(String url) {
if (url.startsWith("https://") || url.startsWith("http://")) return Uri.parse(url);
return Uri.parse("https://$url");
}
}
116 changes: 62 additions & 54 deletions test/opener_test.dart
Original file line number Diff line number Diff line change
@@ -1,65 +1,73 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:humhub/pages/opener.dart';
import 'package:mockito/mockito.dart';
import 'package:humhub/util/opener_controller.dart';

class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
}
}
void main() {
void testGroupOfURIs(Map<String, String> uriMap) {
List<String> failedExpectations = [];

class MockFlutterSecureStorage extends Mock implements FlutterSecureStorage {}
uriMap.forEach((key, value) {
List<String> generatedUrls = OpenerController.generatePossibleManifestsUrls(key);
if (!generatedUrls.contains(value)) {
failedExpectations.add(
"🐛 Opener URL $key generated:\n ${generatedUrls.toString()} list \n the expected value $value was not found");
}
});

Future<void> main() async {
setUp(() {
HttpOverrides.global = MyHttpOverrides();
});
if (failedExpectations.isNotEmpty) {
fail(failedExpectations.join("\n\n"));
}
}

testWidgets('Test opener URL parsing', (WidgetTester tester) async {
// Key value map of URLs with bool that represent the expected value
Map<String, bool> urlsAndValuesIn = {
"https://community.humhub.com": true,
"https://demo.cuzy.app/": true,
"https://sometestproject12345.humhub.com/": true,
"https://sometestproject12345.humhub.com/some/path": true,
"https://sometestproject123456.humhub.com/": false,
"https://sometestproject123456.humhubb.com": false,
"sometestproject12345.humhub.com": true,
"//demo.cuzy.app/": false,
};
group('Generate possible Manifests.json URLs and check if exists', () {
/// [key] represents the opener dialog input string
/// [value] represents the actual manifest.json location
Map<String, bool> urlsAndValuesOut = {};
Key openerKey = const Key('opener');
test('Check HumHub Community URLs', () {
Map<String, String> map = {
"https://community.humhub.com/": "https://community.humhub.com/manifest.json",
"https://community.humhub.com": "https://community.humhub.com/manifest.json",
"community.humhub.com/": "https://community.humhub.com/manifest.json",
"community.humhub.com": "https://community.humhub.com/manifest.json",
"community.humhub.com/some/more": "https://community.humhub.com/manifest.json",
"https://community.humhub.com/and/more": "https://community.humhub.com/manifest.json",
};
testGroupOfURIs(map);
});

for (var urlEntry in urlsAndValuesIn.entries) {
String url = urlEntry.key;
await tester.pumpWidget(
MaterialApp(
home: ProviderScope(
child: Scaffold(body: Opener(key: openerKey)),
),
),
);
final state = tester.state<OpenerState>(find.byKey(openerKey));
state.controlLer.helper.model[state.controlLer.formUrlKey] = url;
bool isBreaking = false;
test('Check sometestproject12345 URLs', () {
Map<String, String> map = {
"https://sometestproject12345.humhub.com/": "https://sometestproject12345.humhub.com/manifest.json",
"https://sometestproject12345.humhub.com": "https://sometestproject12345.humhub.com/manifest.json",
"sometestproject12345.humhub.com/": "https://sometestproject12345.humhub.com/manifest.json",
"sometestproject12345.humhub.com": "https://sometestproject12345.humhub.com/manifest.json",
"https://sometestproject12345.humhub.com/some/more": "https://sometestproject12345.humhub.com/manifest.json",
"https://sometestproject12345.humhub.com/manifest.json":
"https://sometestproject12345.humhub.com/manifest.json",
};
testGroupOfURIs(map);
});

await tester.runAsync(() async {
try {
await state.controlLer.findManifest(url);
} catch (er) {
isBreaking = true;
}
});
isBreaking ? urlsAndValuesOut[url] = !isBreaking : urlsAndValuesOut[url] = !state.controlLer.asyncData!.hasError;
}
test('Check some test.cuzy.app URLs', () {
Map<String, String> map = {
"https://test.cuzy.app/humhub": "https://test.cuzy.app/humhub/index.php?r=web%2Fpwa-manifest%2Findex",
"test.cuzy.app/humhub/": "https://test.cuzy.app/humhub/index.php?r=web%2Fpwa-manifest%2Findex",
"test.cuzy.app/humhub/some": "https://test.cuzy.app/humhub/index.php?r=web%2Fpwa-manifest%2Findex",
"test.cuzy.app/humhub": "https://test.cuzy.app/humhub/index.php?r=web%2Fpwa-manifest%2Findex",
"https://test.cuzy.app/humhub/some": "https://test.cuzy.app/humhub/index.php?r=web%2Fpwa-manifest%2Findex",
"https://test.cuzy.app/humhub/index.php?r=dashboard%2Fdashboard" : "https://test.cuzy.app/humhub/index.php?r=web%2Fpwa-manifest%2Findex"
};
testGroupOfURIs(map);
});

expect(urlsAndValuesOut, urlsAndValuesIn);
/// TEMPLATE: copy this and change it for your use case
/*test('Check some HumHum instance URLs', () {
/// [key] represents the opener dialog input string
/// [value] represents the actual manifest.json location
Map<String, String> map = {
"https://test.cuzy.app/humhub": "https://test.cuzy.app/humhub/index.php?r=web%2Fpwa-manifest%2Findex",
"test.cuzy.app/humhub": "https://test.cuzy.app/humhub/index.php?r=web%2Fpwa-manifest%2Findex",
};
testGroupOfURIs(map);
});*/
});
}
23 changes: 0 additions & 23 deletions test/pretty_urls_test.dart

This file was deleted.

0 comments on commit 8087329

Please sign in to comment.