mirror of
https://github.com/foss42/apidash.git
synced 2025-09-18 23:22:58 +08:00
Added Kotlin OkHttp Tests and refactoring.
This commit is contained in:
@ -8,21 +8,16 @@ import '../providers/settings_providers.dart';
|
|||||||
import 'dart/pkg_http.dart';
|
import 'dart/pkg_http.dart';
|
||||||
|
|
||||||
class Codegen {
|
class Codegen {
|
||||||
const Codegen({required this.codegenLanguage});
|
|
||||||
final CodegenLanguage codegenLanguage;
|
|
||||||
String? getCode(
|
String? getCode(
|
||||||
WidgetRef ref,
|
CodegenLanguage codegenLanguage,
|
||||||
RequestModel requestModel,
|
RequestModel requestModel,
|
||||||
String defaultUriScheme,
|
String defaultUriScheme,
|
||||||
) {
|
) {
|
||||||
final activeRequestModel = ref.watch(activeRequestModelProvider);
|
|
||||||
final defaultUriScheme =
|
|
||||||
ref.watch(settingsProvider.select((value) => value.defaultUriScheme));
|
|
||||||
switch (codegenLanguage) {
|
switch (codegenLanguage) {
|
||||||
case CodegenLanguage.dartHttp:
|
case CodegenLanguage.dartHttp:
|
||||||
return DartHttpCodeGen().getCode(activeRequestModel!, defaultUriScheme);
|
return DartHttpCodeGen().getCode(requestModel, defaultUriScheme);
|
||||||
case CodegenLanguage.kotlinOkHttp:
|
case CodegenLanguage.kotlinOkHttp:
|
||||||
return KotlinOkHttpCodeGen().getCode(activeRequestModel!);
|
return KotlinOkHttpCodeGen().getCode(requestModel);
|
||||||
default:
|
default:
|
||||||
throw ArgumentError('Invalid codegenLanguage');
|
throw ArgumentError('Invalid codegenLanguage');
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,8 @@ val body = "${requestModel.requestBody}".toRequestBody(mediaType)\n""";
|
|||||||
requestModel.method != HTTPVerb.head &&
|
requestModel.method != HTTPVerb.head &&
|
||||||
requestModel.method != HTTPVerb.delete) {
|
requestModel.method != HTTPVerb.delete) {
|
||||||
result = """$result .${requestModel.method.name}(body)\n""";
|
result = """$result .${requestModel.method.name}(body)\n""";
|
||||||
|
} else if (requestModel.method == HTTPVerb.head) {
|
||||||
|
result = """$result .${requestModel.method.name}()\n""";
|
||||||
}
|
}
|
||||||
if (requestModel.method == HTTPVerb.delete) {
|
if (requestModel.method == HTTPVerb.delete) {
|
||||||
result = """$result .method("DELETE", body)\n""";
|
result = """$result .method("DELETE", body)\n""";
|
||||||
|
@ -229,8 +229,8 @@ const kDefaultHttpMethod = HTTPVerb.get;
|
|||||||
const kDefaultContentType = ContentType.json;
|
const kDefaultContentType = ContentType.json;
|
||||||
|
|
||||||
enum CodegenLanguage {
|
enum CodegenLanguage {
|
||||||
dartHttp("Dart - http"),
|
dartHttp("Dart (http)"),
|
||||||
kotlinOkHttp("Kotlin - OkHttp");
|
kotlinOkHttp("Kotlin (OkHttp)");
|
||||||
|
|
||||||
const CodegenLanguage(this.label);
|
const CodegenLanguage(this.label);
|
||||||
final String label;
|
final String label;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:apidash/utils/convert_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:apidash/providers/providers.dart';
|
import 'package:apidash/providers/providers.dart';
|
||||||
@ -13,6 +14,8 @@ class CodePane extends ConsumerStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CodePaneState extends ConsumerState<CodePane> {
|
class _CodePaneState extends ConsumerState<CodePane> {
|
||||||
|
final Codegen codegen = Codegen();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -22,11 +25,13 @@ class _CodePaneState extends ConsumerState<CodePane> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final CodegenLanguage codegenLanguage =
|
final CodegenLanguage codegenLanguage =
|
||||||
ref.watch(codegenLanguageStateProvider);
|
ref.watch(codegenLanguageStateProvider);
|
||||||
final Codegen codegen = Codegen(codegenLanguage: codegenLanguage);
|
|
||||||
final activeRequestModel = ref.watch(activeRequestModelProvider);
|
final activeRequestModel = ref.watch(activeRequestModelProvider);
|
||||||
final defaultUriScheme =
|
final defaultUriScheme =
|
||||||
ref.watch(settingsProvider.select((value) => value.defaultUriScheme));
|
ref.watch(settingsProvider.select((value) => value.defaultUriScheme));
|
||||||
final code = codegen.getCode(ref, activeRequestModel!, defaultUriScheme);
|
|
||||||
|
final code =
|
||||||
|
codegen.getCode(codegenLanguage, activeRequestModel!, defaultUriScheme);
|
||||||
if (code == null) {
|
if (code == null) {
|
||||||
return const ErrorMessage(
|
return const ErrorMessage(
|
||||||
message: "An error was encountered while generating code. $kRaiseIssue",
|
message: "An error was encountered while generating code. $kRaiseIssue",
|
||||||
@ -37,3 +42,82 @@ class _CodePaneState extends ConsumerState<CodePane> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ViewCodePane extends ConsumerStatefulWidget {
|
||||||
|
const ViewCodePane({
|
||||||
|
super.key,
|
||||||
|
required this.code,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String code;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<ConsumerStatefulWidget> createState() => _ViewCodePaneState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ViewCodePaneState extends ConsumerState<ViewCodePane> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var codeTheme = Theme.of(context).brightness == Brightness.light
|
||||||
|
? kLightCodeTheme
|
||||||
|
: kDarkCodeTheme;
|
||||||
|
final textContainerdecoration = BoxDecoration(
|
||||||
|
color: Color.alphaBlend(
|
||||||
|
(Theme.of(context).brightness == Brightness.dark
|
||||||
|
? Theme.of(context).colorScheme.onPrimaryContainer
|
||||||
|
: Theme.of(context).colorScheme.primaryContainer)
|
||||||
|
.withOpacity(kForegroundOpacity),
|
||||||
|
Theme.of(context).colorScheme.surface),
|
||||||
|
border: Border.all(color: Theme.of(context).colorScheme.surfaceVariant),
|
||||||
|
borderRadius: kBorderRadius8,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: kP10,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: kHeaderHeight,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"Code",
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DropdownButtonCodegenLanguage(
|
||||||
|
codegenLanguage: ref.watch(codegenLanguageStateProvider),
|
||||||
|
onChanged: (CodegenLanguage? value) {
|
||||||
|
ref
|
||||||
|
.read(codegenLanguageStateProvider.notifier)
|
||||||
|
.update((state) => value!);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
CopyButton(toCopy: widget.code),
|
||||||
|
SaveInDownloadsButton(
|
||||||
|
content: stringToBytes(widget.code),
|
||||||
|
mimeType: "application/vnd.dart",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
kVSpacer10,
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
width: double.maxFinite,
|
||||||
|
padding: kP8,
|
||||||
|
decoration: textContainerdecoration,
|
||||||
|
child: CodeGenPreviewer(
|
||||||
|
code: widget.code,
|
||||||
|
theme: codeTheme,
|
||||||
|
language: 'dart',
|
||||||
|
textStyle: kCodeStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
import 'package:apidash/widgets/dropdowns.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:highlighter/highlighter.dart' show highlight;
|
import 'package:highlighter/highlighter.dart' show highlight;
|
||||||
import 'package:apidash/consts.dart';
|
|
||||||
import 'package:apidash/utils/utils.dart';
|
|
||||||
import '../providers/ui_providers.dart';
|
|
||||||
import 'code_previewer.dart' show convert;
|
import 'code_previewer.dart' show convert;
|
||||||
import 'buttons.dart';
|
|
||||||
|
|
||||||
class CodeGenPreviewer extends StatefulWidget {
|
class CodeGenPreviewer extends StatefulWidget {
|
||||||
const CodeGenPreviewer({
|
const CodeGenPreviewer({
|
||||||
@ -103,98 +97,3 @@ List<TextSpan> generateSpans(
|
|||||||
var spans = convert(parsed.nodes!, theme);
|
var spans = convert(parsed.nodes!, theme);
|
||||||
return spans;
|
return spans;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewCodePane extends StatefulWidget {
|
|
||||||
const ViewCodePane({
|
|
||||||
super.key,
|
|
||||||
required this.code,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String code;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ViewCodePane> createState() => _ViewCodePaneState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ViewCodePaneState extends State<ViewCodePane> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var codeTheme = Theme.of(context).brightness == Brightness.light
|
|
||||||
? kLightCodeTheme
|
|
||||||
: kDarkCodeTheme;
|
|
||||||
final textContainerdecoration = BoxDecoration(
|
|
||||||
color: Color.alphaBlend(
|
|
||||||
(Theme.of(context).brightness == Brightness.dark
|
|
||||||
? Theme.of(context).colorScheme.onPrimaryContainer
|
|
||||||
: Theme.of(context).colorScheme.primaryContainer)
|
|
||||||
.withOpacity(kForegroundOpacity),
|
|
||||||
Theme.of(context).colorScheme.surface),
|
|
||||||
border: Border.all(color: Theme.of(context).colorScheme.surfaceVariant),
|
|
||||||
borderRadius: kBorderRadius8,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: kP10,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
height: kHeaderHeight,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"Code",
|
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const DropdownButtonCodeCodegenLanguage(),
|
|
||||||
CopyButton(toCopy: widget.code),
|
|
||||||
SaveInDownloadsButton(
|
|
||||||
content: stringToBytes(widget.code),
|
|
||||||
mimeType: "application/vnd.dart",
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
kVSpacer10,
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
width: double.maxFinite,
|
|
||||||
padding: kP8,
|
|
||||||
decoration: textContainerdecoration,
|
|
||||||
child: CodeGenPreviewer(
|
|
||||||
code: widget.code,
|
|
||||||
theme: codeTheme,
|
|
||||||
language: 'dart',
|
|
||||||
textStyle: kCodeStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DropdownButtonCodeCodegenLanguage extends ConsumerStatefulWidget {
|
|
||||||
const DropdownButtonCodeCodegenLanguage({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState createState() => _DropdownButtonCodeCodegenLanguageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DropdownButtonCodeCodegenLanguageState
|
|
||||||
extends ConsumerState<DropdownButtonCodeCodegenLanguage> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final requestCodeLanguage = ref.watch(codegenLanguageStateProvider);
|
|
||||||
return DropdownButtonCodegenLanguage(
|
|
||||||
codegenLanguage: requestCodeLanguage,
|
|
||||||
onChanged: (CodegenLanguage? value) {
|
|
||||||
ref.read(codegenLanguageStateProvider.notifier).state = value!;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
125
test/codegen/kotlin_okhttp_codegen_test.dart
Normal file
125
test/codegen/kotlin_okhttp_codegen_test.dart
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import 'package:apidash/codegen/kotlin/pkg_okhttp.dart';
|
||||||
|
import 'package:apidash/models/request_model.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('KotlinOkHttpCodeGen', () {
|
||||||
|
final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen();
|
||||||
|
|
||||||
|
test('getCode returns valid code for GET request', () {
|
||||||
|
const requestModel = RequestModel(
|
||||||
|
url: 'https://jsonplaceholder.typicode.com/todos/1',
|
||||||
|
method: HTTPVerb.get,
|
||||||
|
id: '',
|
||||||
|
);
|
||||||
|
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
val client = OkHttpClient()
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://jsonplaceholder.typicode.com/todos/1")
|
||||||
|
.build()
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
println(response.body!!.string())
|
||||||
|
""";
|
||||||
|
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getCode returns valid code for POST request', () {
|
||||||
|
const requestModel = RequestModel(
|
||||||
|
url: 'https://jsonplaceholder.typicode.com/posts',
|
||||||
|
method: HTTPVerb.post,
|
||||||
|
requestBody: '{"title": "foo","body": "bar","userId": 1}',
|
||||||
|
requestBodyContentType: ContentType.json,
|
||||||
|
id: '1',
|
||||||
|
);
|
||||||
|
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
val client = OkHttpClient()
|
||||||
|
val mediaType = "application/json".toMediaType()
|
||||||
|
val body = "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}".toRequestBody(mediaType)
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://jsonplaceholder.typicode.com/posts")
|
||||||
|
.post(body)
|
||||||
|
.build()
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
println(response.body!!.string())
|
||||||
|
""";
|
||||||
|
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getCode returns valid code for DELETE request', () {
|
||||||
|
const requestModel = RequestModel(
|
||||||
|
url: 'https://jsonplaceholder.typicode.com/posts/1',
|
||||||
|
method: HTTPVerb.delete,
|
||||||
|
requestBody: '{"title": "foo","body": "bar","userId": 1}',
|
||||||
|
requestBodyContentType: ContentType.json,
|
||||||
|
id: '1',
|
||||||
|
);
|
||||||
|
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
val client = OkHttpClient()
|
||||||
|
val mediaType = "application/json".toMediaType()
|
||||||
|
val body = "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}".toRequestBody(mediaType)
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://jsonplaceholder.typicode.com/posts/1")
|
||||||
|
.method("DELETE", body)
|
||||||
|
.build()
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
println(response.body!!.string())
|
||||||
|
""";
|
||||||
|
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getCode returns valid code for HEAD request', () {
|
||||||
|
const requestModel = RequestModel(
|
||||||
|
url: 'https://jsonplaceholder.typicode.com/posts/1',
|
||||||
|
method: HTTPVerb.head,
|
||||||
|
id: '1',
|
||||||
|
);
|
||||||
|
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
val client = OkHttpClient()
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://jsonplaceholder.typicode.com/posts/1")
|
||||||
|
.head()
|
||||||
|
.build()
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
println(response.body!!.string())
|
||||||
|
""";
|
||||||
|
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
// This is a basic Flutter widget test.
|
|
||||||
//
|
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
|
||||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
|
||||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
|
||||||
// tree, read text, and verify that the values of widget properties are correct.
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
import 'package:apidash/main.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
|
||||||
// Build our app and trigger a frame.
|
|
||||||
await tester.pumpWidget(const MyApp());
|
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
|
||||||
expect(find.text('0'), findsOneWidget);
|
|
||||||
expect(find.text('1'), findsNothing);
|
|
||||||
|
|
||||||
// Tap the '+' icon and trigger a frame.
|
|
||||||
await tester.tap(find.byIcon(Icons.add));
|
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
// Verify that our counter has incremented.
|
|
||||||
expect(find.text('0'), findsNothing);
|
|
||||||
expect(find.text('1'), findsOneWidget);
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:apidash/screens/home_page/editor_pane/details_card/code_pane.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:apidash/widgets/codegen_previewer.dart';
|
import 'package:apidash/widgets/codegen_previewer.dart';
|
||||||
|
Reference in New Issue
Block a user