Idgen dart

This commit is contained in:
2024-10-19 14:31:02 -05:00
parent 12a701e1c5
commit 3745b39726
12 changed files with 676 additions and 24 deletions

View File

@ -0,0 +1,40 @@
/// Response class for IDs generated by IDGen.
class IDGenResponse {
final String id;
IDGenResponse({required this.id});
factory IDGenResponse.fromJson(Map<String, dynamic> json) {
return IDGenResponse(
id: json['id'],
);
}
}
/// Response class for keypairs generated by IDGen.
class IDKeypairResponse implements IDGenResponse {
@override
final String id;
final String privateID;
IDKeypairResponse({required this.id, required this.privateID});
factory IDKeypairResponse.fromJson(Map<String, dynamic> json) {
return IDKeypairResponse(
id: json['id'],
privateID: json['privateID'],
);
}
}
/// Custom exception for IDGen, thrown when an error occurs when generating an ID.
class IDGenException implements Exception {
final String message;
IDGenException(this.message);
@override
String toString() {
return message;
}
}

83
lib/src/idgen_http.dart Normal file
View File

@ -0,0 +1,83 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:teamhydra_idgen/src/idgen_dart_base.dart';
class IDGenHTTPWorker {
final Dio _dio = Dio();
final String _baseURL = 'https://id.hydra.workers.dev';
final String username;
final String token;
IDGenHTTPWorker({required this.username, required this.token});
Future<IDGenResponse> generate(
String type, Map<String, dynamic>? data) async {
try {
final response = await _dio.post(_baseURL,
data: {
'username': username,
'token': token,
'type': type,
...?data,
},
options: Options(contentType: Headers.jsonContentType));
return IDGenResponse.fromJson(response.data);
} on DioException catch (e) {
Map<String, dynamic>? errorData;
// Do we have a response?
if (e.response != null) {
// Try decoding the response
try {
errorData = jsonDecode(e.response!.data);
} catch (_) {
// Do nothing
}
}
if (errorData != null && errorData['error'] != null) {
throw IDGenException(
'Server rejected ID generation: ${errorData['error']}');
} else if (errorData != null) {
throw IDGenException('Server rejected ID generation: $errorData');
} else {
throw IDGenException(
'An error occurred during generation ($type): ${e.message}');
}
} catch (e) {
throw IDGenException(
'An unknown error occurred during generation ($type): $e');
}
}
Future<IDKeypairResponse> generateKeypair() async {
try {
final response = await _dio.post(_baseURL,
data: {
'username': username,
'token': token,
'type': 'keypair',
},
options: Options(contentType: Headers.jsonContentType));
return IDKeypairResponse.fromJson(response.data);
} on DioException catch (e) {
final errorData = e.response?.data;
if (errorData != null && errorData['error'] != null) {
throw IDGenException(
'Server rejected ID generation: ${errorData['error']}');
} else if (errorData != null) {
throw IDGenException('Server rejected ID generation: $errorData');
} else {
throw IDGenException(
'An error occurred during generation (keypair): ${e.message}');
}
} catch (e) {
throw IDGenException(
'An unknown error occurred during generation (keypair): $e');
}
}
}

145
lib/src/idgen_main.dart Normal file
View File

@ -0,0 +1,145 @@
import 'package:teamhydra_idgen/src/idgen_http.dart';
import 'package:teamhydra_idgen/teamhydra_idgen.dart';
/// The main class to use when generating an ID
///
/// All returned IDs are in the form of a [IDGenResponse] object which contains the ID.
/// If an error occurs, an [IDGenException] is thrown.
class IDGen {
final String username;
final String token;
final IDGenHTTPWorker _worker;
/// Create a new IDGen class to generate IDs
///
/// [username] and [token] are required to authenticate with the IDGen API.
IDGen({required this.username, required this.token})
: _worker = IDGenHTTPWorker(username: username, token: token);
/// Generate a new UUID V4
///
/// Returns a [IDGenResponse] object containing the generated ID.
/// Throws an [IDGenException] if an error occurs.
Future<IDGenResponse> generateUUIDV4() async {
return await _worker.generate('uuid', null);
}
/// Generate a new nanoID
///
/// Returns a [IDGenResponse] object containing the generated ID.
/// Optionally, you can pass a [size] parameter to specify the length of the nanoID, default is 10.
/// You can also pass a [alphabet] parameter to specify the characters used in the nanoID, default is provided by the API.
///
/// Throws an [IDGenException] if an error occurs.
///
/// Throws an [ArgumentError] if the size is not between 1 and 256 or the alphabet is empty.
Future<IDGenResponse> generateNanoID({int? size, String? alphabet}) async {
// Ensure length is between 1 and 256 (if specified)
if (size != null && (size < 1 || size > 256)) {
throw ArgumentError(
'Cannot generate a nanoID with a length of $size, must be between 1 and 256');
}
// Ensure alphabet is not empty (if specified)
if (alphabet != null && alphabet.isEmpty) {
throw ArgumentError('Cannot generate a nanoID with an empty alphabet');
}
// Ensure alphabet is at least 3 characters long and not longer than 256
if (alphabet != null && (alphabet.length < 3 || alphabet.length > 256)) {
throw ArgumentError(
'Cannot generate a nanoID with an alphabet of length ${alphabet.length}, must be between 3 and 256');
}
return await _worker.generate('nanoid', {
if (size != null) 'length': size else 'length': 10,
if (alphabet != null) 'alphabet': alphabet,
});
}
/// Generate a 2FA code pair
///
/// Returns a [IDGenResponse] object containing the generated ID.
///
/// Optionally, you can pass a [length] parameter to specify the length of the 2FA code, default is 6.
///
/// Throws an [IDGenException] if an error occurs.
///
/// Throws an [ArgumentError] if the length is not between 1 and 256.
Future<IDGenResponse> generate2FACode({int? length}) async {
// Ensure length is between 1 and 256 (if specified)
if (length != null && (length < 1 || length > 256)) {
throw ArgumentError(
'Cannot generate a 2FA code with a length of $length, must be between 1 and 256');
}
return await _worker.generate('2fa', {
if (length != null) 'length': length,
});
}
/// Generate a license key
///
/// Returns a [IDGenResponse] object containing the generated ID.
/// Keys are generated with a 25 character length, resulting in a 5-5-5-5-5 format.
///
/// Throws an [IDGenException] if an error occurs.
Future<IDGenResponse> generateLicenseKey() async {
return await _worker.generate('license', null);
}
/// Generate word based string
///
/// Returns a [IDGenResponse] object containing the generated ID.
/// Optionally, you can pass a [length] parameter to specify the length of the word based string, default is 5.
/// You can also pass a [separator] parameter to specify the separator used in the word based string, options are 'slug', 'title' and 'formal'. Default is 'slug'.
///
/// Slug: lowercase words separated by hyphens
///
/// Title: Title case words separated by spaces
///
/// Formal: Title case words with no spaces or separators
///
/// Throws an [IDGenException] if an error occurs.
///
/// Throws an [ArgumentError] if the length is not between 1 and 16 or the separator is invalid.
Future<IDGenResponse> generateWordBasedString(
{int? length, String? separator}) async {
// Ensure length is between 1 and 256 (if specified)
if (length != null && (length < 1 || length > 16)) {
throw ArgumentError(
'Cannot generate a word based string with a length of $length, must be between 1 and 16');
}
// Ensure separator is valid (if specified)
if (separator != null &&
separator != 'slug' &&
separator != 'title' &&
separator != 'formal') {
throw ArgumentError(
'Cannot generate a word based string with an invalid separator');
}
return await _worker.generate('word', {
if (length != null) 'length': length else 'length': 5,
if (separator != null) 'style': separator else 'style': 'slug',
});
}
/// Generate a snowflake ID
///
/// Returns a [IDKeypairResponse] object containing
///
/// Throws an [IDGenException] if an error occurs.
Future<IDGenResponse> generateSnowflakeID() async {
return await _worker.generate('snowflake', null);
}
/// Generate a keypair
///
/// Returns a [IDKeypairResponse] object containing the generated ID and secret key.
/// Throws an [IDGenException] if an error occurs.
Future<IDKeypairResponse> generateKeypair() async {
return await _worker.generateKeypair();
}
}

5
lib/teamhydra_idgen.dart Normal file
View File

@ -0,0 +1,5 @@
/// Team Hydra ID Generator Library - Generate unique IDs for your projects, using the IDGen API.
library;
export 'src/idgen_dart_base.dart';
export 'src/idgen_main.dart';