Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offline support for Network page #8332

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fe11d5b
added initial implementation
hrajwade96 Sep 21, 2024
6c140b4
adding constants file
hrajwade96 Sep 29, 2024
0c1f414
updated NEXT_RELEASE_NOTES.md
hrajwade96 Sep 30, 2024
e7eeecb
Revert "updated NEXT_RELEASE_NOTES.md"
hrajwade96 Sep 30, 2024
c5ec6f6
used constants, added util for header size calc, added params
hrajwade96 Oct 2, 2024
131cde6
Merge branch 'master' into network_screen_offline_support
hrajwade96 Oct 2, 2024
2a9c4cb
param renamed, used _log
hrajwade96 Oct 2, 2024
4f473be
Merge remote-tracking branch 'origin/network_screen_offline_support' …
hrajwade96 Oct 2, 2024
ae58738
added copyright header
hrajwade96 Oct 2, 2024
2bfbe1c
setup changes implemented
hrajwade96 Oct 3, 2024
857cfbe
put class name in square brackets
hrajwade96 Oct 3, 2024
7150436
added isNullOrEmpty check
hrajwade96 Oct 3, 2024
da11a67
added exit offline cta, hiding other controls, removed json string
hrajwade96 Oct 4, 2024
941ec88
fix for request selection
hrajwade96 Oct 4, 2024
852b144
comments resolved
hrajwade96 Oct 8, 2024
433af38
removing getFullRequestData section
hrajwade96 Oct 8, 2024
4644b00
using updateOrAddAll for setting data
hrajwade96 Oct 8, 2024
6fd98d0
Wrapped with OfflineAwareControls, removed if-else.
hrajwade96 Oct 8, 2024
665ea0d
removed addAutoDisposeListener as we are using OfflineAwareControls
hrajwade96 Oct 8, 2024
b6df572
removed try-catch
hrajwade96 Oct 8, 2024
be63db4
code refactoring
hrajwade96 Oct 9, 2024
91505b5
added enum
hrajwade96 Oct 9, 2024
0715379
code refactoring, reverting changes on network service
hrajwade96 Oct 10, 2024
ade8b5f
using values from enum
hrajwade96 Oct 10, 2024
f21a4f6
delegating toJson using extension methods, code refactoring
hrajwade96 Oct 23, 2024
454ca92
socket data de-serialisation changes
hrajwade96 Oct 27, 2024
b7b9679
Merge branch 'master' into network_screen_offline_support
hrajwade96 Oct 27, 2024
ac726e8
used factory constructor, made enum private
hrajwade96 Nov 4, 2024
2ea3044
reorder params, used isEmpty
hrajwade96 Nov 4, 2024
e01bc3c
added tests, minor fixes
hrajwade96 Nov 17, 2024
e7b5c90
removed repetitive expect statements
hrajwade96 Nov 21, 2024
6ab0884
Merge remote-tracking branch 'upstream/master' into network_screen_of…
hrajwade96 Nov 22, 2024
4f31b92
minor fix
hrajwade96 Nov 22, 2024
04c8b41
added timelineMicrosOffset to offline data
hrajwade96 Nov 24, 2024
ab8c519
added timelineMicrosOffset to tests
hrajwade96 Nov 24, 2024
a97135a
Merge remote-tracking branch 'upstream/master' into network_screen_of…
hrajwade96 Dec 27, 2024
2a9cacf
release notes updated
hrajwade96 Dec 27, 2024
4298078
code reformatted
hrajwade96 Dec 27, 2024
a538a74
lint fixes - fixed import, removed unused import
hrajwade96 Jan 6, 2025
e05cd02
comment added for ignore
hrajwade96 Jan 6, 2025
ae52105
lint fix: removed redundant async
hrajwade96 Jan 6, 2025
9f6fd00
setting globals for test cases
hrajwade96 Jan 14, 2025
560d5a6
fixed visible screens test
hrajwade96 Jan 15, 2025
243dda4
added handing and tests for requests having error
hrajwade96 Jan 25, 2025
0b847a3
fixed requiresConnection value and import
hrajwade96 Jan 28, 2025
70f8a43
comments resolved, added disconnected network screen body
hrajwade96 Jan 28, 2025
0454095
Merge branch 'master' into network_screen_offline_support
hrajwade96 Jan 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 40 additions & 25 deletions packages/devtools_app/lib/src/shared/http/http_request_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -383,36 +383,51 @@ extension HttpProfileRequestExtension on HttpProfileRequest {

extension HttpProfileRequestDataExtension on HttpProfileRequestData {
Map<String, Object?> toJson() {
return {
HttpRequestDataKeys.headers.name: headers,
HttpRequestDataKeys.followRedirects.name: followRedirects,
HttpRequestDataKeys.maxRedirects.name: maxRedirects,
HttpRequestDataKeys.connectionInfo.name: connectionInfo,
HttpRequestDataKeys.contentLength.name: contentLength,
HttpRequestDataKeys.cookies.name: cookies,
HttpRequestDataKeys.persistentConnection.name: persistentConnection,
HttpRequestDataKeys.proxyDetails.name: proxyDetails,
};
final Map<String, Object?> jsonMap = {};
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
try {
jsonMap[HttpRequestDataKeys.headers.name] = headers ?? {};
jsonMap[HttpRequestDataKeys.followRedirects.name] = followRedirects;
jsonMap[HttpRequestDataKeys.maxRedirects.name] = maxRedirects;
jsonMap[HttpRequestDataKeys.connectionInfo.name] = connectionInfo;
jsonMap[HttpRequestDataKeys.contentLength.name] = contentLength;
jsonMap[HttpRequestDataKeys.cookies.name] = cookies ?? [];
jsonMap[HttpRequestDataKeys.persistentConnection.name] =
persistentConnection;
jsonMap[HttpRequestDataKeys.proxyDetails.name] = proxyDetails?.toJson();
} catch (e, st) {
_log.shout('Error serializing HttpProfileRequestData', e, st);
jsonMap[HttpRequestDataKeys.error.name] =
'Serialization failed due to: $e';
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
}
return jsonMap;
}
}

extension HttpProfileResponseDataExtension on HttpProfileResponseData {
Map<String, Object?> toJson() {
return {
HttpRequestDataKeys.startTime.name: startTime?.microsecondsSinceEpoch,
HttpRequestDataKeys.endTime.name: endTime?.microsecondsSinceEpoch,
HttpRequestDataKeys.headers.name: headers,
HttpRequestDataKeys.compressionState.name: compressionState,
HttpRequestDataKeys.connectionInfo.name: connectionInfo,
HttpRequestDataKeys.contentLength.name: contentLength,
HttpRequestDataKeys.cookies.name: cookies,
HttpRequestDataKeys.isRedirect.name: isRedirect,
HttpRequestDataKeys.persistentConnection.name: persistentConnection,
HttpRequestDataKeys.reasonPhrase.name: reasonPhrase,
HttpRequestDataKeys.redirects.name: redirects,
HttpRequestDataKeys.statusCode.name: statusCode,
HttpRequestDataKeys.error.name: error,
};
final Map<String, Object?> jsonMap = {};
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
try {
jsonMap[HttpRequestDataKeys.startTime.name] =
startTime?.microsecondsSinceEpoch;
jsonMap[HttpRequestDataKeys.endTime.name] =
endTime?.microsecondsSinceEpoch;
jsonMap[HttpRequestDataKeys.headers.name] = headers ?? {};
jsonMap[HttpRequestDataKeys.compressionState.name] = compressionState;
jsonMap[HttpRequestDataKeys.connectionInfo.name] = connectionInfo;
jsonMap[HttpRequestDataKeys.contentLength.name] = contentLength;
jsonMap[HttpRequestDataKeys.cookies.name] = cookies ?? [];
jsonMap[HttpRequestDataKeys.isRedirect.name] = isRedirect;
jsonMap[HttpRequestDataKeys.persistentConnection.name] =
persistentConnection;
jsonMap[HttpRequestDataKeys.reasonPhrase.name] = reasonPhrase;
jsonMap[HttpRequestDataKeys.redirects.name] = redirects;
jsonMap[HttpRequestDataKeys.statusCode.name] = statusCode;
} catch (e, st) {
_log.shout('Error serializing HttpProfileResponseData', e, st);
jsonMap[HttpRequestDataKeys.error.name] =
'Serialization failed due to: $e';
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
}
return jsonMap;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import 'dart:convert';
import 'dart:io';

import 'package:devtools_app/src/screens/network/network_model.dart';
import 'package:devtools_app/devtools_app.dart';
import 'package:devtools_app/src/screens/network/offline_network_data.dart';
import 'package:devtools_app/src/shared/http/constants.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:vm_service/vm_service.dart';

void main() {
late Map<String, Object?> jsonData;
Expand Down Expand Up @@ -174,5 +176,91 @@ void main() {
expect(restoredData.socketData.length, offlineData.socketData.length);
expect(restoredData.selectedRequestId, offlineData.selectedRequestId);
});

test('Handles serialization errors gracefully', () {
final requestData = HttpProfileRequestData.buildErrorRequest(
error: 'Simulated error',
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
);

final jsonData = requestData.toJson();

expect(jsonData[HttpRequestDataKeys.error.name], isNotNull);
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
expect(
jsonData[HttpRequestDataKeys.error.name],
contains('Serialization failed due to: HttpProfileRequestError'),
);
});

test('Handles successful serialization', () {
final requestData = HttpProfileRequestData.buildSuccessfulRequest(
headers: {'Content-Type': 'application/json'},
connectionInfo: {'host': 'example.com'},
contentLength: 1024,
cookies: ['sessionId=abc123'],
followRedirects: true,
maxRedirects: 5,
persistentConnection: true,
);

final jsonData = requestData.toJson();

expect(jsonData[HttpRequestDataKeys.headers.name], {
'Content-Type': 'application/json',
});

expect(jsonData[HttpRequestDataKeys.connectionInfo.name], {
'host': 'example.com',
});
expect(jsonData[HttpRequestDataKeys.contentLength.name], 1024);
expect(jsonData[HttpRequestDataKeys.cookies.name], ['sessionId=abc123']);
expect(jsonData[HttpRequestDataKeys.followRedirects.name], true);
expect(jsonData[HttpRequestDataKeys.maxRedirects.name], 5);
expect(jsonData[HttpRequestDataKeys.persistentConnection.name], true);
});

test('Includes proxy details when available', () {
final proxyDetails = HttpProfileProxyData(
host: 'proxy.example.com',
port: 8080,
username: 'user',
isDirect: false,
);

final requestData = HttpProfileRequestData.buildSuccessfulRequest(
headers: {'Accept': 'text/html'},
proxyDetails: proxyDetails,
cookies: [],
);

final jsonData = requestData.toJson();

expect(jsonData[HttpRequestDataKeys.proxyDetails.name], isNotNull);
expect(jsonData[HttpRequestDataKeys.proxyDetails.name], {
'host': 8080,
'username': 'user',
'isDirect': false,
});
expect(
jsonData[HttpRequestDataKeys.proxyDetails.name],
containsPair('username', 'user'),
);
expect(
jsonData[HttpRequestDataKeys.proxyDetails.name],
containsPair('isDirect', false),
);
});

test('Handles null and empty fields gracefully', () {
final requestData = HttpProfileRequestData.buildSuccessfulRequest(
cookies: [],
);

final jsonData = requestData.toJson();

expect(jsonData[HttpRequestDataKeys.headers.name], {});
expect(jsonData[HttpRequestDataKeys.connectionInfo.name], null);
expect(jsonData[HttpRequestDataKeys.cookies.name], []);
expect(jsonData[HttpRequestDataKeys.contentLength.name], null);
});
});
}
Loading