Skip to content

Commit

Permalink
feat: finalize license status api
Browse files Browse the repository at this point in the history
  • Loading branch information
mcquenji committed Nov 26, 2024
1 parent 9dae401 commit 8998f4a
Show file tree
Hide file tree
Showing 44 changed files with 10,970 additions and 6,002 deletions.
94 changes: 94 additions & 0 deletions bin/doc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import 'dart:io';
import 'package:echidna_server/echidna_server.dart';
import 'package:shelf_modular/shelf_modular.dart';
import 'dart:mirrors';

Check warning on line 4 in bin/doc.dart

View workflow job for this annotation

GitHub Actions / Lint Check

Place 'dart:' imports before other imports.. See https://dart.dev/tools/diagnostic-messages#directives_ordering
import 'package:analyzer/dart/analysis/utilities.dart';

Check warning on line 5 in bin/doc.dart

View workflow job for this annotation

GitHub Actions / Lint Check

Sort directive sections alphabetically.. See https://dart.dev/tools/diagnostic-messages#directives_ordering
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';

class PrismaStub implements PrismaClient {
@override
dynamic noSuchMethod(Invocation invocation) {
return super.noSuchMethod(invocation);
}
}

class ReflectedRouteManager extends RouteManager {
final List<ModularRoute> _routes = [];

List<ModularRoute> get routes => List.unmodifiable(_routes);

@override
void add(ModularRoute route) {
_routes.add(route);
}
}

void main() {
final server = ServerModule(PrismaStub());

final manager = ReflectedRouteManager();

server.routes(manager);

for (final route in manager.routes) {
inspectRoute(route);
}
}

void inspectRoute(ModularRoute route, {String path = ''}) {
final module = route.module;
if (module != null) {
final manager = ReflectedRouteManager();

module.routes(manager);

for (final route in manager.routes) {
inspectRoute(route, path: '$path/${route.name}');
}

return;
}

if (route is Route) {
final handlerMirror = reflect(route.handler);
final handlerType = handlerMirror.type;
final location = handlerType.location;

print(handlerType.originalDeclaration.metadata.map((e) => e.type));

Check warning on line 58 in bin/doc.dart

View workflow job for this annotation

GitHub Actions / Lint Check

Don't invoke 'print' in production code.. See https://dart.dev/tools/diagnostic-messages#avoid_print

// print('$path/${route.name} -> ${handlerType.simpleName}');

if (location != null) {
final filePath = location.sourceUri;

Check warning on line 63 in bin/doc.dart

View workflow job for this annotation

GitHub Actions / Lint Check

The value of the local variable 'filePath' isn't used.. See https://dart.dev/tools/diagnostic-messages#unused_local_variable
// print('Handler defined in: $filePath');
// parseAndAnalyzeFile(filePath);
}
}

// print("$path/${route.name}");
for (final child in route.children) {
inspectRoute(child, path: '$path/${route.name}');
}
}

void parseAndAnalyzeFile(String filePath) {
try {
final fileContent = File(filePath).readAsStringSync();
final parseResult = parseString(content: fileContent);

final CompilationUnit unit = parseResult.unit;

Check warning on line 80 in bin/doc.dart

View workflow job for this annotation

GitHub Actions / Lint Check

Unnecessary type annotation on a local variable.. See https://dart.dev/tools/diagnostic-messages#omit_local_variable_types
final visitor = _ASTPrinter();
unit.visitChildren(visitor);
} catch (e) {
print('Error reading or parsing file $filePath: $e');

Check warning on line 84 in bin/doc.dart

View workflow job for this annotation

GitHub Actions / Lint Check

Don't invoke 'print' in production code.. See https://dart.dev/tools/diagnostic-messages#avoid_print
}
}

class _ASTPrinter extends GeneralizingAstVisitor<void> {
@override
void visitNode(AstNode node) {
print('${node.runtimeType}: ${node.toString()}');

Check warning on line 91 in bin/doc.dart

View workflow job for this annotation

GitHub Actions / Lint Check

Don't invoke 'print' in production code.. See https://dart.dev/tools/diagnostic-messages#avoid_print

Check warning on line 91 in bin/doc.dart

View workflow job for this annotation

GitHub Actions / Lint Check

The expression has no effect and can be removed.. See https://dart.dev/tools/diagnostic-messages#noop_primitive_operations
super.visitNode(node);
}
}
1 change: 1 addition & 0 deletions lib/echidna_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export 'package:orm/orm.dart';

export 'modules/admin/admin.dart';
export 'modules/auth/auth.dart';
export 'modules/client/client.dart';
export 'modules/server/server.dart';
export 'orm/client.dart';
export 'orm/model.dart';
Expand Down
9 changes: 6 additions & 3 deletions lib/modules/admin/admin.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:echidna_server/echidna_server.dart';
import 'package:echidna_server/modules/admin/infra/services/hash_license_key_generator_service.dart';
import 'package:mcquenji_core/mcquenji_core.dart';
import 'package:shelf_modular/shelf_modular.dart';
import 'package:uuid/uuid.dart';

export 'domain/domain.dart';
export 'presentation/presentation.dart';
Expand All @@ -16,7 +16,9 @@ class AdminModule extends Module {

@override
void binds(Injector i) {
i.add<LicenseKeyGeneratorService>(HashLicenseKeyGeneratorService.new);
// [Uuid.new] takes an optional argument but we don't need to pass it.
// ignore: unnecessary_lambdas
i.addLazySingleton<Uuid>(() => const Uuid());
}

@override
Expand All @@ -29,6 +31,7 @@ class AdminModule extends Module {
..resource(LicensesResource(), name: '/licenses')
..resource(ProductsResource(), name: '/products')
..resource(FeaturesResource(), name: '/features')
..resource(PaymentsResource(), name: '/payments');
..resource(PaymentsResource(), name: '/payments')
..resource(ClientKeyResource(), name: '/client-keys');
}
}

This file was deleted.

2 changes: 1 addition & 1 deletion lib/modules/admin/domain/services/services.dart
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export 'license_key_generator_service.dart';

This file was deleted.

2 changes: 1 addition & 1 deletion lib/modules/admin/infra/services/services.dart
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export 'hash_license_key_generator_service.dart';

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import 'package:collection/collection.dart';
import 'package:echidna_server/echidna_server.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_modular/shelf_modular.dart';
import 'package:uuid/uuid.dart';

/// Creates a new client key for a product.
Future<Response> createClientKeyHandler(Request request, Injector i, ModularArguments args) async {
final productId = int.tryParse(args.params['product_id'] as String? ?? '');
final customerId = int.tryParse(args.params['customer_id'] as String? ?? '');

final prisma = i.get<PrismaClient>();

if (productId == null) {
request.log('No product ID provided, aborting.');

return Response.badRequest(body: 'product_id is required.');
}

if (customerId == null) {
request.log('No customer ID provided, aborting.');

return Response.badRequest(body: 'customer_id is required.');
}

final product = await prisma.product.findUnique(where: ProductWhereUniqueInput(id: productId));

if (product == null) {
request.log('Not Found: Product with ID $productId does not exist.');
return Response.notFound(null);
}

final customer = await prisma.customer.findUnique(where: CustomerWhereUniqueInput(id: customerId));

if (customer == null) {
request.log('Not Found: Customer with ID $customerId does not exist.');
return Response.notFound(null);
}

final clientKeys = await prisma.clientKey.findMany(
where: ClientKeyWhereInput(
productId: PrismaUnion.$2(productId),
customerId: PrismaUnion.$2(customerId),
),
);

if (clientKeys.isNotEmpty) {
final clientKey = clientKeys.firstWhereOrNull((k) => k.revoked == false);

if (clientKey != null) {
request.log('Found existing client key for product with ID $productId, returning it.');
return clientKey.toResponse();
}

request.log('No active client key found for product with ID $productId, creating a new one.');
}

try {
final uuid = i.get<Uuid>();

final key = uuid.v4();

final clientKey = await prisma.clientKey.create(
data: PrismaUnion.$1(
ClientKeyCreateInput(
key: key,
product: ProductCreateNestedOneWithoutClientKeysInput(
connect: ProductWhereUniqueInput(id: productId),
),
customer: CustomerCreateNestedOneWithoutClientKeysInput(
connect: CustomerWhereUniqueInput(id: customerId),
),
),
),
);

request.log('Created new client key for product with ID $productId.');

return clientKey.toResponse();
} catch (e, s) {
request.log('Error creating client key for product with ID $productId', e, s);
return Response.internalServerError();
}
}
39 changes: 27 additions & 12 deletions lib/modules/admin/presentation/handlers/create_license_handler.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import 'package:echidna_server/echidna_server.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_modular/shelf_modular.dart';
import 'package:uuid/uuid.dart';

/// Adds a new License to the DB with the given ID.
Future<Response> createLicenseHandler(Request request, Injector i, ModularArguments args) async {
final prisma = i.get<PrismaClient>();

final licenseGenerator = i.get<LicenseKeyGeneratorService>();

final customerId = args.data['customerId'] as int?;
final productId = args.data['productId'] as int?;
final userId = args.data['userId'] as String?;

if (customerId == null || productId == null || userId == null) {
if (userId == null) {
request.log('Creating customer wide license');
}

if (customerId == null || productId == null) {
request.log('Bad Request: Customer ID, Product ID and User ID ar not given');
return Response.badRequest(body: 'Customer ID, Product ID and User ID are required.');
}
Expand All @@ -27,17 +30,24 @@ Future<Response> createLicenseHandler(Request request, Injector i, ModularArgume
return Response.badRequest(body: 'Product with ID $productId does not exist.');
}

final key = await licenseGenerator.generateLicenseKey(
productId,
customerId,
userId,
);
final uuid = i.get<Uuid>();

if (await prisma.license.findUnique(where: LicenseWhereUniqueInput(licenseKey: key)) != null) {
if (await prisma.license.findUnique(
where: LicenseWhereUniqueInput(
userId: PrismaUnion.$2(
userId != null ? PrismaUnion.$1(userId) : const PrismaUnion.$2(PrismaNull()),
),
customerId: PrismaUnion.$2(customerId),
productId: PrismaUnion.$2(productId),
),
) !=
null) {
request.log('Bad Request: License already exists');
return Response.badRequest(body: 'License already exists.');
}

final key = uuid.v4();

final license = License(
licenseKey: key,
customerId: customerId,
Expand All @@ -50,7 +60,7 @@ Future<Response> createLicenseHandler(Request request, Injector i, ModularArgume
data: PrismaUnion.$1(
LicenseCreateInput(
licenseKey: key,
userId: PrismaUnion.$1(userId),
userId: userId != null ? PrismaUnion.$1(userId) : const PrismaUnion.$2(PrismaNull()),
customer: CustomerCreateNestedOneWithoutLicensesInput(
connect: CustomerWhereUniqueInput(id: customerId),
),
Expand All @@ -62,11 +72,16 @@ Future<Response> createLicenseHandler(Request request, Injector i, ModularArgume
),
isolationLevel: TransactionIsolationLevel.serializable,
);
request.log('License Created for user with id $userId');

if (userId != null) {
request.log('License created for user with id $userId');
} else {
request.log('Customer wide license for customer with id $customerId');
}

return license.toResponse();
} on Exception catch (e, s) {
request.log('Failed to add license', e, s);
request.log('Failed to create license', e, s);
return Response.internalServerError();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:echidna_server/echidna_server.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_modular/shelf_modular.dart';

/// Returns a list of all client keys.
Future<Response> getClientKeysHandler(Request request, Injector i, ModularArguments args) async {
final prisma = i.get<PrismaClient>();

final clientKeys = await prisma.clientKey.findMany();

request.log('Found ${clientKeys.length} client keys');

return clientKeys.toResponse();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'dart:convert';

import 'package:echidna_server/echidna_server.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_modular/shelf_modular.dart';

/// Gets the status of a license by its id.
Future<Response> getLicenseStatusHandler(Request request, Injector i, ModularArguments args) async {
final datasource = i.get<LicenseStatusDatasource>();

final id = args.params['id'];

if (id == null) {
request.log('Bad Request: No id given');
return Response.badRequest(body: 'ID required');
}

final licenseStatus = await datasource.getLicenseStatus(id);

return Response.ok(jsonEncode(licenseStatus));
}
4 changes: 4 additions & 0 deletions lib/modules/admin/presentation/handlers/handlers.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
export 'create_client_key_handler.dart';
export 'create_customer_handler.dart';
export 'create_feature_handler.dart';
export 'create_license_handler.dart';
export 'create_product_handler.dart';
export 'delete_customer_handler.dart';
export 'delete_feature_handler.dart';
export 'delete_product_handler.dart';
export 'get_client_keys_handler.dart';
export 'get_customers_handler.dart';
export 'get_features_handler.dart';
export 'get_license_payments_handler.dart';
export 'get_licenses_handler.dart';
export 'get_payments_handler.dart';
export 'get_products_handler.dart';
export 'get_license_status_handler.dart';

Check warning on line 16 in lib/modules/admin/presentation/handlers/handlers.dart

View workflow job for this annotation

GitHub Actions / Lint Check

Sort directive sections alphabetically.. See https://dart.dev/tools/diagnostic-messages#directives_ordering
export 'revoke_client_key_handler.dart';
export 'revoke_license_handler.dart';
export 'update_customer_handler.dart';
export 'update_feature_handler.dart';
Expand Down
Loading

0 comments on commit 8998f4a

Please sign in to comment.