From 3d133a75d177908622a406e269acddbf412d19f3 Mon Sep 17 00:00:00 2001
From: Arun Prakash
Date: Sun, 8 Sep 2024 14:59:20 +0530
Subject: [PATCH 1/6] Refactor cache exceptions and add memory cache store
implementation | Docs update
---
CHANGELOG.md | 162 +++++-----
README.md | 50 +--
lib/src/authorization/authorization_base.dart | 103 +++++--
.../authorization/authorization_builder.dart | 55 +++-
.../authorization/methods/app_password.dart | 26 +-
lib/src/authorization/methods/basic_auth.dart | 46 ++-
lib/src/authorization/methods/basic_jwt.dart | 83 ++++-
lib/src/authorization/methods/useful_jwt.dart | 86 +++++-
lib/src/bootstrap_builder.dart | 43 ++-
lib/src/cache/cache_entry.dart | 24 ++
lib/src/cache/cache_manager_base.dart | 32 ++
.../exceptions/cache_exception_base.dart | 9 +
.../exceptions/cache_expired_exception.dart | 8 +
.../cache_not_exists_exception.dart | 8 +
lib/src/cache/stores/memory_cache_store.dart | 37 +++
lib/src/client_configuration.dart | 56 +++-
lib/src/constants.dart | 29 ++
lib/src/enums.dart | 241 +++++++++++++--
lib/src/interface/application_passwords.dart | 43 ++-
lib/src/interface/category.dart | 35 ++-
lib/src/interface/comments.dart | 38 ++-
lib/src/interface/me.dart | 30 +-
lib/src/interface/media.dart | 35 ++-
lib/src/interface/page.dart | 37 ++-
lib/src/interface/posts.dart | 37 ++-
lib/src/interface/search.dart | 28 +-
lib/src/interface/tags.dart | 26 +-
lib/src/interface/users.dart | 38 ++-
lib/src/interface_key.dart | 42 +++
lib/src/middleware/delegated_middleware.dart | 24 ++
.../models/middleware_raw_response.dart | 46 +++
.../middleware/wordpress_middleware_base.dart | 65 +++-
lib/src/operations/create.dart | 22 +-
lib/src/operations/custom.dart | 24 +-
lib/src/operations/delete.dart | 25 +-
lib/src/operations/list.dart | 24 +-
lib/src/operations/retrieve.dart | 18 +-
lib/src/operations/update.dart | 19 +-
lib/src/parallel_wordpress/exports.dart | 1 +
.../extensions/parallel_result_exts.dart | 46 +++
.../parallel_wordpress/parallel_result.dart | 32 +-
.../parallel_wordpress.dart | 285 +++++++++++++++++-
lib/src/parallel_wordpress/typedefs.dart | 13 +-
lib/src/request_executor_base.dart | 73 ++++-
lib/src/requests/wordpress_request.dart | 42 ++-
.../application_password_response.dart | 63 ++++
lib/src/responses/category_response.dart | 35 +++
lib/src/responses/comment_response.dart | 43 +++
lib/src/responses/media_response.dart | 61 ++++
lib/src/responses/page_response.dart | 57 ++++
lib/src/responses/post_response.dart | 53 ++++
lib/src/responses/properties/author_meta.dart | 40 +++
lib/src/responses/properties/avatar_urls.dart | 34 +++
lib/src/responses/properties/content.dart | 34 +++
.../properties/extra_capabilities.dart | 27 ++
lib/src/responses/properties/image_meta.dart | 40 +++
.../responses/properties/link_container.dart | 48 +++
lib/src/responses/properties/links.dart | 49 +++
.../responses/properties/media_details.dart | 36 +++
.../properties/media_size_value.dart | 25 ++
lib/src/responses/search_response.dart | 28 ++
lib/src/responses/tag_response.dart | 28 ++
lib/src/responses/user_response.dart | 46 +++
.../wordpress_discovery_response.dart | 37 +++
lib/src/responses/wordpress_raw_response.dart | 131 ++++++--
lib/src/responses/wordpress_response.dart | 94 +++++-
.../utilities/codable_map/codable_map.dart | 40 +++
lib/src/utilities/codable_map/type_key.dart | 18 ++
.../utilities/extensions/map_extensions.dart | 22 ++
.../parallel_result_extensions.dart | 4 +-
lib/src/utilities/helpers.dart | 183 +++++++----
lib/src/utilities/request_url.dart | 51 +++-
lib/src/utilities/self_representive_base.dart | 23 +-
lib/src/utilities/wordpress_events.dart | 29 +-
lib/src/wordpress_client_base.dart | 262 +++++++++++-----
test/wordpress_client_test.dart | 2 +-
76 files changed, 3448 insertions(+), 441 deletions(-)
create mode 100644 lib/src/cache/cache_entry.dart
create mode 100644 lib/src/cache/cache_manager_base.dart
create mode 100644 lib/src/cache/exceptions/cache_exception_base.dart
create mode 100644 lib/src/cache/exceptions/cache_expired_exception.dart
create mode 100644 lib/src/cache/exceptions/cache_not_exists_exception.dart
create mode 100644 lib/src/cache/stores/memory_cache_store.dart
create mode 100644 lib/src/parallel_wordpress/extensions/parallel_result_exts.dart
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 051a2f3..d03b55e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,106 +1,106 @@
## 8.5.3
-- ๐ฉน Fix invalid base url on web
+- Fix invalid base url on web
## 8.5.2
-- ๐ฉน Fix versions
+- Fix versions
## 8.5.1
-- ๐ฉน Fix dio sendTimeout exception
+- Fix dio sendTimeout exception
## 8.5.0
-- ๐ Added support for initialize the client without a base url.
+- Added support for initializing the client without a base url.
- Use the `WordpressClient.generic` constructor to initialize the client without a base url.
- Use the `reconfigure` method to set the base url after initializing the client.
- Failure to set the base url will throw an exception when making requests.
-- ๐ Added `isAuthenticated` method to check if the current instance has a valid authentication.
+- Added `isAuthenticated` method to check if the current instance has a valid authentication.
- Optionally, pass an instance of `IAuthorization` to check if the client is authenticated with the given authorization.
- ๐ฅ Deprecated `reconfigureClient` method in favour of `reconfigure` method.
## 8.4.10
-- ๐ฉน Fix bug on clearing middleware list
+- Fix bug on clearing middleware list
## 8.4.9
-- ๐ฆ Downgrade meta package to match flutter meta version
+- Downgrade meta package to match flutter meta version
## 8.4.8
-- ๐ฉน Bug fixes
+- Bug fixes
- ๐ฅ Deprecated `WordpressClient.initialize(...)` ctor and `initialize()` method
-- ๐ Added `WordpressClient.fromDioInstance(...)` constructor
+- Added `WordpressClient.fromDioInstance(...)` constructor
## 8.4.7
-- ๐ฉน Bug fixes
-- ๐ Fix validations for entered `baseUrl`; Supporting sites with custom REST Api paths
+- Bug fixes
+- Fix validations for entered `baseUrl`; Supporting sites with custom REST Api paths
## 8.4.6
-- ๐ฉน Bug fixes
-- ๐ Added validations for entered `baseUrl`
+- Bug fixes
+- Added validations for entered `baseUrl`
- ๐ฅ Renamed `executeGuarded` to `guardAsync` and added `guard` method
## 8.4.5
-- ๐ฉน Bug fixes
+- Bug fixes
## 8.4.4
-- ๐ฉน Bug fixes
+- Bug fixes
## 8.4.3
-- ๐ New static method to check if a site is built using WordPress
+- New static method to check if a site is built using WordPress
## 8.4.2
-- ๐ฉน Bug fixes
-- ๐ Added static methods to validate base URL and discover a website
+- Bug fixes
+- Added static methods to validate base URL and discover a website
## 8.4.1
-- ๐ Introduce `ParallelWordpress` class to generate and execute parallel requests
-- ๐ Bug fixes
+- Introduce `ParallelWordpress` class to generate and execute parallel requests
+- Bug fixes
## 8.4.0
-- ๐ Added support for Middlewares
+- Added support for Middlewares
- ๐ฅ Removed dependency on `synchronised` package
## 8.3.10
-- ๐ฉน Bug fixes
+- Bug fixes
## 8.3.9
-- ๐ฉน Bug fixes
-- ๐ Iterate over the raw response of the endpoint using [] operator
+- Bug fixes
+- Iterate over the raw response of the endpoint using [] operator
## 8.3.8
-- ๐ Renamed retrive -> retrieve. Fix typo
+- Renamed retrive -> retrieve. Fix typo
## 8.3.7
-- ๐ Support for raw requests
-- ๐ Bug fixes
+- Support for raw requests
+- Bug fixes
## 8.3.6
-- ๐ฉน Bug fixes on enum parsing
+- Bug fixes on enum parsing
## 8.3.5
-- ๐ฉน Bug fixes on comment list request
+- Bug fixes on comment list request
## 8.3.4
-- ๐ Media response model null exception when parsing if media details is empty
+- Media response model null exception when parsing if media details is empty
## 8.3.3
@@ -120,73 +120,73 @@
## 8.3.0
-- ๐ Supports Application Password endpoint
+- Supports Application Password endpoint
- Packages update
## 8.2.2
-- ๐ Bug fixes
+- Bug fixes
## 8.2.1
-- ๐ Fixed exporting WordPressDiscovery class
+- Fixed exporting WordPressDiscovery class
## 8.2.0
-- ๐ Added support for Pages endpoint
+- Added support for Pages endpoint
## 8.1.0
-- ๐ Added ability to fetch and cache the discovery endpoint of WordPress REST API
+- Added ability to fetch and cache the discovery endpoint of WordPress REST API
## 8.0.11
-- โ Added `extra` property to all request classes
-- โ Added `addAllIfNotNull(...)` extension method
+- Added `extra` property to all request classes
+- Added `addAllIfNotNull(...)` extension method
## 8.0.10
-- ๐ `featured_media_src_url` key now decodes as expected
-- โ Added `decodeByMultiKeys` method
+- `featured_media_src_url` key now decodes as expected
+- Added `decodeByMultiKeys` method
## 8.0.9
-- โ Added App Password support
+- Added App Password support
## 8.0.8
-- ๐ง Integrated new lint rules and code refactors
+- Integrated new lint rules and code refactors
## 8.0.7
-- ๐ Bug fixes
-- โ Introduced `RequestErrorType` for failure responses
-- โ Introduced `mapGuarded(...)` and `executeGuarded(...)` methods
-- ๐ง Usual refactors and improvements
+- Bug fixes
+- Introduced `RequestErrorType` for failure responses
+- Introduced `mapGuarded(...)` and `executeGuarded(...)` methods
+- Usual refactors and improvements
## 8.0.6
-- ๐ Docs update
+- Docs update
## 8.0.5
-- ๐ฉน Bug fixes and improvements
+- Bug fixes and improvements
## 8.0.4
-- ๐ฉน Bug fixes and improvements
+- Bug fixes and improvements
## 8.0.3
-- ๐ค Export response extensions
+- Export response extensions
## 8.0.2
-- ๐ฝ Downgrade collection version
+- Downgrade collection version
## 8.0.1
-- ๐ Docs update
+- Docs update
## 8.0.0
@@ -198,114 +198,114 @@
## 6.3.1
-- ๐ Implemented search endpoint
+- Implemented search endpoint
## 6.3.0
-- ๐ Major changes in the API
+- Major changes in the API
## 6.2.1-pre
-- ๐งช Misc changes
+- Misc changes
## 6.2.0-pre
-- ๐ง Refactoring, Request Synchronization, and Debug Mode
+- Refactoring, Request Synchronization, and Debug Mode
## 6.1.7-pre to 6.1.9-pre
-- ๐ง Refactoring & Bug fixes
+- Refactoring & Bug fixes
## 6.1.6-pre
-- ๐ Support 3xx series responses (Cached Response)
+- Support 3xx series responses (Cached Response)
## 6.1.5-pre
-- โ Added Post extension for Media and Author
+- Added Post extension for Media and Author
## 6.1.3-pre & 6.1.4-pre
-- ๐ฉน Bug fixes
+- Bug fixes
## 6.1.2-pre
-- ๐ Version fix
+- Version fix
## 6.1.1-pre
-- ๐๏ธ Removed test package
+- Removed test package
## 6.1.0-pre
-- ๐ Entire API changed
-- ๐ Fluency maintained using Dart's cascading operator
-- โก Performance and memory consumption improvements
+- Entire API changed
+- Fluency maintained using Dart's cascading operator
+- Performance and memory consumption improvements
## 5.4.3
-- ๐ Total pages parsing fix
+- Total pages parsing fix
## 5.4.2
-- ๐ฆ Packages fix
+- Packages fix
## 5.4.1
-- ๐ Null safety fix
+- Null safety fix
## 5.4.0
-- ๐ฆ Packages update
+- Packages update
## 5.3.1
-- ๐ Response structure fix
+- Response structure fix
## 5.3.0
-- ๐ฉน Bug fix
+- Bug fix
## 5.2.9
-- ๐ฉน Bug Fix
+- Bug Fix
## 5.2.8
-- ๐ Revert author meta & featured image removal
+- Revert author meta & featured image removal
## 5.2.7
-- ๐ฉน Bug fix
+- Bug fix
## 5.2.6
-- โ๏ธ Experimental Request Caching system
+- Experimental Request Caching system
## 5.2.5
-- ๐ BREAKING CHANGE: Remove Author Meta & Featured Image URL Fields from Post response
+- ๐ฅ BREAKING CHANGE: Remove Author Meta & Featured Image URL Fields from Post response
## 5.2.4
-- ๐๏ธ Remove unused package
+- Remove unused package
## 5.2.3
-- ๐ BREAKING CHANGE: Request API Change
+- ๐ฅ BREAKING CHANGE: Request API Change
## 5.1.1
-- ๐ Formatting changes
+- Formatting changes
## 5.1.0
-- ๐ BREAKING CHANGE: Authorization API Change
+- ๐ฅ BREAKING CHANGE: Authorization API Change
## 5.0.4
-- ๐ Fixed Authorization Bugs
+- Fixed Authorization Bugs
## 4.0.0
-- ๐ Initial version, created by Stagehand
+- Initial version, created by Stagehand
diff --git a/README.md b/README.md
index 24276f3..5824aef 100644
--- a/README.md
+++ b/README.md
@@ -2,20 +2,25 @@
WordPress Client
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
- A powerful and easy-to-use WordPress REST API client for Dart & Flutter.
-
-
+ A powerful and easy-to-use WordPress REST API client for Dart & Flutter.
+
-## โจ Features
+## ๐ Features
- ๐ฆ API discovery support.
- โฒ๏ธ Measures request completion time.
@@ -32,19 +37,21 @@
If you find any functionality which you require is missing from the package and you are not able to work it out using built in options like raw requests etc, then please share the functionality in details as a comment here: https://github.com/ArunPrakashG/wordpress_client/discussions/55
-## ๐ How to Use
+## ๐ฆ Installation
-### **1. Setup**
-
-Add `wordpress_client` in your `pubspec.yaml`:
+Add `wordpress_client` to your `pubspec.yaml`:
```dart
dependencies:
- wordpress_client: ^8.4.8
+ wordpress_client: ^8.5.3
```
> ๐ก Ensure you get the [latest version here](https://pub.dev/packages/wordpress_client).
+Then run `flutter pub get` to install the package.
+
+## ๐ง Usage
+
Import the package where you need:
```dart
@@ -153,23 +160,22 @@ By Useful Team, this is another implementation using JWT for authentication purp
Learn how to implement [Custom Requests here](https://github.com/ArunPrakashG/wordpress_client/wiki/Using-Custom-Requests).
-## ๐ฃ Feedback
+## ๐ค Feedback & Contributing
- ๐ For bugs or feature requests, use the [issue tracker][tracker].
- ๐ก Contributions are always appreciated. PRs are welcome!
-## ๐ License
+## ๐ License
-Licensed under [MIT](https://github.com/ArunPrakashG/wordpress_client/blob/master/LICENSE).
-
-[tracker]: https://github.com/ArunPrakashG/wordpress_client/issues
+This project is [MIT](https://github.com/ArunPrakashG/wordpress_client/blob/master/LICENSE) licensed.
---
-
-Support Me:
+ If you find this package helpful, consider supporting the development:
[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/arunprakashg)
+
+[tracker]: https://github.com/ArunPrakashG/wordpress_client/issues
diff --git a/lib/src/authorization/authorization_base.dart b/lib/src/authorization/authorization_base.dart
index eb6a0fa..277825b 100644
--- a/lib/src/authorization/authorization_base.dart
+++ b/lib/src/authorization/authorization_base.dart
@@ -4,13 +4,38 @@ import 'package:meta/meta.dart';
import '../../wordpress_client.dart';
import '../utilities/helpers.dart';
-/// Base class for all authorization types.
+/// Base class for all authorization types in the WordPress client.
///
-/// To implement a custom authorization system, You _must_ extend this class.
+/// To implement a custom authorization system, you must extend this class.
///
-/// Note that, there is no storage system internally to store and retrive
+/// Note: There is no built-in storage system to store and retrieve credentials.
+/// You need to implement your own storage mechanism if required.
+///
+/// Example of a custom authorization implementation:
+/// ```dart
+/// class CustomAuth extends IAuthorization {
+/// CustomAuth({required String userName, required String password})
+/// : super(userName: userName, password: password);
+///
+/// @override
+/// String get scheme => 'Custom';
+///
+/// @override
+/// Future authorize() async {
+/// // Implement your custom authorization logic here
+/// return true;
+/// }
+///
+/// // Implement other required methods...
+/// }
+/// ```
abstract base class IAuthorization {
/// Creates a new instance of [IAuthorization] with the given username and password.
+ ///
+ /// [userName]: The username for authentication.
+ /// [password]: The password for authentication.
+ /// [headerKey]: The HTTP header key to use for authorization (default is 'Authorization').
+ /// [events]: Optional [WordpressEvents] to listen to during the authorization process.
IAuthorization({
required this.userName,
required this.password,
@@ -18,35 +43,38 @@ abstract base class IAuthorization {
this.events,
});
- /// The base url of the wordpress site.
+ /// The base URL of the WordPress site.
late final Uri baseUrl;
- /// The username
+ /// The username for authentication.
final String userName;
- /// The password
+ /// The password for authentication.
final String password;
- /// The header key to use for authorization.
+ /// The HTTP header key to use for authorization.
final String headerKey;
- /// The events to listen to.
+ /// Optional events to listen to during the authorization process.
WordpressEvents? events;
- /// Gets if this authorization instance has valid authentication nounce. (token/encryptedToken)
+ /// Indicates if this authorization instance has a valid authentication nonce (token/encryptedToken).
bool get isValidAuth;
- /// Gets if this is an invalid or default authorization instance without username or password fields.
+ /// Indicates if this is an invalid or default authorization instance without username or password fields.
bool get isDefault => isNullOrEmpty(userName) || isNullOrEmpty(password);
- /// Gets the authorization scheme.
+ /// Gets the authorization scheme (e.g., 'Bearer' for JWT, 'Basic' for Basic Auth).
String get scheme;
- /// Helps to initialize authorization instance with internal requesting client passed as a parameter.
+ /// Initializes the authorization instance with the internal requesting client.
///
- /// This function is called only if there is no valid nounce available i.e., when isAuthenticated() returns false.
+ /// This method is called only if there is no valid nonce available (i.e., when [isAuthenticated] returns false).
+ /// [authorize] and [validate] methods will not be called before calling [initialize].
///
- /// `authorize()` / `validate()` functions will not be called before calling `initialize()` function.
+ /// [baseUrl]: The base URL of the WordPress site.
+ ///
+ /// Returns a [Future] indicating whether initialization was successful.
@mustCallSuper
Future initialize({
required Uri baseUrl,
@@ -56,39 +84,50 @@ abstract base class IAuthorization {
}
/// Provides this instance of [IAuthorization] with the Dio client instance for requests.
+ ///
+ /// [client]: The Dio client instance to use for making HTTP requests.
void clientFactoryProvider(Dio client);
- /// Called to validate token. (such as in JWT auth)
- ///
- /// As of right now, this function is not called outside of this instance. This can change in the future if there is a requirement to validate the nounce from the core client itself.
- /// Therefore, be sure to implement this with valid logic for the validation process.
+ /// Validates the authentication token (e.g., JWT token).
///
- /// Example 1: JWT authentication token can be validated through an endpoint, you can implement that validation logic inside this.
+ /// This method is not called outside of this instance by default, but it may be used in the future.
+ /// Implement this method with valid logic for the validation process.
///
- /// Example 2: Basic Auth does not require any validation, therefore you can simply return true or if still require some custom logic, you can implement that as well!
+ /// Example for JWT:
+ /// ```dart
+ /// @override
+ /// Future validate() async {
+ /// try {
+ /// final response = await _client.post('/validate-token', data: {'token': _token});
+ /// return response.statusCode == 200;
+ /// } catch (e) {
+ /// return false;
+ /// }
+ /// }
+ /// ```
Future validate();
- /// Called to check if this instance has a valid authentication nounce and generateAuthUrl() won't return null.
+ /// Checks if this instance has a valid authentication nonce and [generateAuthUrl] won't return null.
///
- /// This function will be called before init() function, therefore if you are using client instance passed through init() then there will be NullReferenceException.
+ /// This method is called before [initialize], so if you need to use the client instance,
+ /// you should implement custom logic to handle potential null references.
///
- /// If you require HTTP requests in this method, then you need to implement custom logic.
+ /// Returns a [Future] indicating whether the instance is authenticated.
Future isAuthenticated();
- /// Called to authorize a request if the request requires authentication.
+ /// Authorizes a request if authentication is required.
///
- /// Returning true means the request should be authorized, false means authorization failed.
+ /// Returns a [Future] indicating whether authorization was successful (true) or failed (false).
Future authorize();
- /// After `authorize()` is called, to get the authorization header string, (ie, '{scheme} {token}') the client calls this method to generate the raw string.
- ///
- /// The returning string formate must always be like
- ///
- /// {scheme} {token}
+ /// Generates the authorization header string after [authorize] is called.
///
- /// - Example 1: In case of JWT, `Bearer {jwt_token}`
+ /// The returned string format must always be: "{scheme} {token}"
///
- /// - Example 2: In case of Basic Auth, `Basic {Base64UsernamePassword}`
+ /// Examples:
+ /// - JWT: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
+ /// - Basic Auth: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
///
+ /// Returns a [Future] containing the authorization header string, or null if not authorized.
Future generateAuthUrl();
}
diff --git a/lib/src/authorization/authorization_builder.dart b/lib/src/authorization/authorization_builder.dart
index d7982d6..26c2f39 100644
--- a/lib/src/authorization/authorization_builder.dart
+++ b/lib/src/authorization/authorization_builder.dart
@@ -8,33 +8,86 @@ import 'methods/basic_jwt.dart';
import 'methods/useful_jwt.dart';
/// Creates a new instance of [AuthorizationBuilder].
+///
+/// This class uses the builder pattern to construct an authorization instance.
+/// Use the various 'with' methods to set the required parameters, then call
+/// [build] to create the authorization object.
+///
+/// Example usage:
+/// ```dart
+/// final auth = AuthorizationBuilder()
+/// .withUserName('myusername')
+/// .withPassword('mypassword')
+/// .withType(AuthorizationType.useful_jwt)
+/// .build();
+/// ```
final class AuthorizationBuilder {
String _userName = '';
String _password = '';
AuthorizationType? _type;
WordpressEvents? _events;
+ /// Sets the username for the authorization.
+ ///
+ /// Example:
+ /// ```dart
+ /// builder.withUserName('myusername');
+ /// ```
AuthorizationBuilder withUserName(String userName) {
_userName = userName;
return this;
}
+ /// Sets the password for the authorization.
+ ///
+ /// Example:
+ /// ```dart
+ /// builder.withPassword('mypassword');
+ /// ```
AuthorizationBuilder withPassword(String password) {
_password = password;
return this;
}
+ /// Sets the authorization type.
+ ///
+ /// If not set, it defaults to [AuthorizationType.useful_jwt].
+ ///
+ /// Example:
+ /// ```dart
+ /// builder.withType(AuthorizationType.basic_jwt);
+ /// ```
AuthorizationBuilder withType(AuthorizationType type) {
_type = type;
return this;
}
+ /// Sets the WordPress events for the authorization.
+ ///
+ /// This is optional and can be used to handle specific WordPress events.
+ ///
+ /// Example:
+ /// ```dart
+ /// builder.withEvents(myWordPressEvents);
+ /// ```
AuthorizationBuilder withEvents(WordpressEvents events) {
_events = events;
return this;
}
- /// Builds the authorization instance.
+ /// Builds and returns the authorization instance based on the set parameters.
+ ///
+ /// If no type is specified, it defaults to [AuthorizationType.useful_jwt].
+ ///
+ /// Example:
+ /// ```dart
+ /// final auth = builder.build();
+ /// ```
+ ///
+ /// Returns an instance of [IAuthorization] which can be one of:
+ /// - [BasicJwtAuth]
+ /// - [UsefulJwtAuth]
+ /// - [AppPasswordAuth]
IAuthorization build() {
_type ??= AuthorizationType.useful_jwt;
diff --git a/lib/src/authorization/methods/app_password.dart b/lib/src/authorization/methods/app_password.dart
index 5ecb1c6..554f753 100644
--- a/lib/src/authorization/methods/app_password.dart
+++ b/lib/src/authorization/methods/app_password.dart
@@ -3,8 +3,24 @@ import 'package:dio/dio.dart';
import '../../../wordpress_client.dart';
import '../../utilities/helpers.dart';
-/// Authentication using Application Passwords which are supported on all Wordpress installations version 5.6 or higher.
+/// Authentication using Application Passwords, supported on WordPress installations version 5.6 or higher.
+///
+/// This class provides a way to authenticate with WordPress using Application Passwords,
+/// which is a secure method for third-party applications to access WordPress sites.
+///
+/// Example usage:
+/// ```dart
+/// final auth = AppPasswordAuth(
+/// userName: 'your_username',
+/// password: 'your_app_password',
+/// );
+///
final class AppPasswordAuth extends IAuthorization {
+ /// Creates an instance of [AppPasswordAuth].
+ ///
+ /// [userName]: The WordPress username.
+ /// [password]: The application password generated for this user in WordPress.
+ /// [events]: Optional [WordpressEvents] to listen to during the authorization process.
AppPasswordAuth({
required super.userName,
required super.password,
@@ -13,16 +29,19 @@ final class AppPasswordAuth extends IAuthorization {
@override
Future authorize() async {
+ // Application Passwords don't require an explicit authorization step
return true;
}
@override
Future generateAuthUrl() async {
+ // Generates the Basic Auth header value
return '$scheme ${base64Encode('$userName:$password')}';
}
@override
Future isAuthenticated() async {
+ // Application Passwords are always considered authenticated if provided
return true;
}
@@ -32,11 +51,14 @@ final class AppPasswordAuth extends IAuthorization {
@override
Future validate() async {
+ // Application Passwords don't require validation
return true;
}
@override
- void clientFactoryProvider(Dio client) {}
+ void clientFactoryProvider(Dio client) {
+ // No additional configuration needed for Dio client
+ }
@override
String get scheme => 'Basic';
diff --git a/lib/src/authorization/methods/basic_auth.dart b/lib/src/authorization/methods/basic_auth.dart
index 05fdade..00966a0 100644
--- a/lib/src/authorization/methods/basic_auth.dart
+++ b/lib/src/authorization/methods/basic_auth.dart
@@ -7,11 +7,31 @@ import '../authorization_base.dart';
/// The most basic authentication system using username and password.
///
-/// Implemented on basis of https://github.com/WP-API/Basic-Auth wordpress plugin.
+/// This class implements Basic Authentication for WordPress API, based on
+/// the https://github.com/WP-API/Basic-Auth WordPress plugin.
///
-/// Make sure to only use this method for testing purposes as this isn't secure.
+/// WARNING: This method should only be used for testing purposes as it is not secure
+/// for production environments.
+///
+/// Example usage:
+/// ```dart
+/// final auth = BasicAuth(userName: 'myuser', password: 'mypassword');
+/// final isAuthorized = await auth.authorize();
+/// if (isAuthorized) {
+/// final authHeader = await auth.generateAuthUrl();
+/// // Use authHeader in your API requests
+/// }
+/// ```
+///
+/// @deprecated Use AppPasswordAuth instead for more secure authentication.
@Deprecated('Use AppPasswordAuth instead')
final class BasicAuth extends IAuthorization {
+ /// Creates a new BasicAuth instance.
+ ///
+ /// [userName] and [password] are required parameters.
+ /// [events] is an optional parameter for authentication events.
+ ///
+ /// @deprecated Use AppPasswordAuth instead for more secure authentication.
@Deprecated('Use AppPasswordAuth instead')
BasicAuth({
required super.userName,
@@ -19,32 +39,54 @@ final class BasicAuth extends IAuthorization {
super.events,
});
+ /// Authorizes the user. Always returns true for BasicAuth.
+ ///
+ /// This method is part of the IAuthorization interface but doesn't perform
+ /// any actual authorization for BasicAuth.
@override
Future authorize() async {
return true;
}
+ /// Generates the authorization header value.
+ ///
+ /// Returns a Future containing the Basic Auth header value.
+ /// The returned string is in the format: "Basic base64EncodedCredentials"
@override
Future generateAuthUrl() async {
return '$scheme ${base64Encode('$userName:$password')}';
}
+ /// Checks if the user is authenticated. Always returns true for BasicAuth.
+ ///
+ /// This method is part of the IAuthorization interface but doesn't perform
+ /// any actual authentication check for BasicAuth.
@override
Future isAuthenticated() async {
return true;
}
+ /// Indicates whether the authentication is valid. Always true for BasicAuth.
@override
bool get isValidAuth => true;
+ /// Validates the authentication. Always returns true for BasicAuth.
+ ///
+ /// This method is part of the IAuthorization interface but doesn't perform
+ /// any actual validation for BasicAuth.
@override
Future validate() async {
return true;
}
+ /// Provides a way to modify the Dio client. Not used in BasicAuth.
+ ///
+ /// This method is part of the IAuthorization interface but doesn't perform
+ /// any modifications to the Dio client for BasicAuth.
@override
void clientFactoryProvider(Dio client) {}
+ /// The authentication scheme used. Returns 'Basic' for BasicAuth.
@override
String get scheme => 'Basic';
}
diff --git a/lib/src/authorization/methods/basic_jwt.dart b/lib/src/authorization/methods/basic_jwt.dart
index fcd709d..8cff06b 100644
--- a/lib/src/authorization/methods/basic_jwt.dart
+++ b/lib/src/authorization/methods/basic_jwt.dart
@@ -8,14 +8,38 @@ import '../../utilities/helpers.dart';
import '../authorization_base.dart';
import 'useful_jwt.dart';
-/// Most widely used authentication system, which is most easy to integrate and secure (when compared with basic auth)
+/// BasicJwtAuth implements JWT (JSON Web Token) authentication for WordPress APIs.
///
-/// Implemented on basis of https://github.com/Tmeister/wp-api-jwt-auth wordpress plugin.
+/// This class is based on the WordPress plugin https://github.com/Tmeister/wp-api-jwt-auth.
///
-/// ### NOTE
+/// ### Usage Example:
///
-/// This plugin isn't in active development and may contain lots of bugs/issues. It is recommended to use [UsefulJwtAuth] instead.
+/// ```dart
+/// final auth = BasicJwtAuth(
+/// userName: 'your_username',
+/// password: 'your_password',
+/// );
+///
+/// // Authorize the user
+/// bool isAuthorized = await auth.authorize();
+///
+/// if (isAuthorized) {
+/// print('Successfully authorized!');
+/// // Use the auth object for subsequent API calls
+/// } else {
+/// print('Authorization failed.');
+/// }
+/// ```
+///
+/// ### Important Note:
+///
+/// This implementation relies on a WordPress plugin that is not actively maintained.
+/// For a more robust and up-to-date JWT authentication, consider using [UsefulJwtAuth] instead.
final class BasicJwtAuth extends IAuthorization {
+ /// Creates a new instance of BasicJwtAuth.
+ ///
+ /// [userName] and [password] are required for authentication.
+ /// [events] is optional and can be used to handle authentication events.
BasicJwtAuth({
required super.userName,
required super.password,
@@ -27,17 +51,33 @@ final class BasicJwtAuth extends IAuthorization {
bool _hasValidatedOnce = false;
Dio? _client;
+ /// Number of days until the token expires.
static const int DAYS_UNTIL_TOKEN_EXPIRY = 3;
+ /// Checks if the current authentication is valid.
@override
bool get isValidAuth => !isNullOrEmpty(_encryptedAccessToken);
+ /// Checks if the current authentication has expired.
bool get _isAuthExpiried {
return _lastAuthorizedTime != null &&
DateTime.now().difference(_lastAuthorizedTime!).inHours >
(DAYS_UNTIL_TOKEN_EXPIRY * 24);
}
+ /// Authorizes the user with the provided credentials.
+ ///
+ /// Returns `true` if authorization is successful, `false` otherwise.
+ ///
+ /// Example:
+ /// ```dart
+ /// bool success = await auth.authorize();
+ /// if (success) {
+ /// print('Authorization successful');
+ /// } else {
+ /// print('Authorization failed');
+ /// }
+ /// ```
@override
Future authorize() async {
if (isValidAuth) {
@@ -86,6 +126,15 @@ final class BasicJwtAuth extends IAuthorization {
return _hasValidatedOnce = !isNullOrEmpty(_encryptedAccessToken);
}
+ /// Checks if the user is currently authenticated.
+ ///
+ /// Returns `true` if the user is authenticated, `false` otherwise.
+ ///
+ /// Example:
+ /// ```dart
+ /// bool isAuth = await auth.isAuthenticated();
+ /// print(isAuth ? 'User is authenticated' : 'User is not authenticated');
+ /// ```
@override
Future isAuthenticated() async {
if (_hasValidatedOnce && !isNullOrEmpty(_encryptedAccessToken)) {
@@ -95,6 +144,15 @@ final class BasicJwtAuth extends IAuthorization {
return false;
}
+ /// Validates the current authentication token.
+ ///
+ /// Returns `true` if the token is valid, `false` otherwise.
+ ///
+ /// Example:
+ /// ```dart
+ /// bool isValid = await auth.validate();
+ /// print(isValid ? 'Token is valid' : 'Token is invalid');
+ /// ```
@override
Future validate() async {
if (_client == null || isNullOrEmpty(_encryptedAccessToken)) {
@@ -122,6 +180,19 @@ final class BasicJwtAuth extends IAuthorization {
(response.data['code'] as String) == 'jwt_auth_valid_token';
}
+ /// Generates an authentication URL with the current token.
+ ///
+ /// Returns the authentication URL as a string, or `null` if not authenticated.
+ ///
+ /// Example:
+ /// ```dart
+ /// String? authUrl = await auth.generateAuthUrl();
+ /// if (authUrl != null) {
+ /// print('Auth URL: $authUrl');
+ /// } else {
+ /// print('Not authenticated');
+ /// }
+ /// ```
@override
Future generateAuthUrl() async {
if (!await isAuthenticated()) {
@@ -131,11 +202,15 @@ final class BasicJwtAuth extends IAuthorization {
return '$scheme $_encryptedAccessToken';
}
+ /// Sets the Dio client for making HTTP requests.
+ ///
+ /// This method is called internally to set up the HTTP client.
@override
void clientFactoryProvider(Dio client) {
_client = client;
}
+ /// The authentication scheme used for this method (Bearer).
@override
String get scheme => 'Bearer';
}
diff --git a/lib/src/authorization/methods/useful_jwt.dart b/lib/src/authorization/methods/useful_jwt.dart
index 12456fc..5e7c0e3 100644
--- a/lib/src/authorization/methods/useful_jwt.dart
+++ b/lib/src/authorization/methods/useful_jwt.dart
@@ -7,12 +7,42 @@ import 'package:dio/dio.dart';
import '../../enums.dart';
import '../../utilities/helpers.dart';
import '../authorization_base.dart';
-import 'basic_jwt.dart';
-/// Similar to [BasicJwtAuth], this plugin is in active development and has much more features than the previous one. It is recommended to use this plugin instead of the previous one.
+/// UsefulJwtAuth implements JWT (JSON Web Token) authentication for WordPress APIs.
///
-/// Implemented on basis of https://github.com/usefulteam/jwt-auth wordpress plugin.
+/// This class is based on the WordPress plugin https://github.com/usefulteam/jwt-auth,
+/// which is actively maintained and offers more features compared to the BasicJwtAuth.
+///
+/// ### Usage Example:
+///
+/// ```dart
+/// final auth = UsefulJwtAuth(
+/// userName: 'your_username',
+/// password: 'your_password',
+/// );
+///
+/// // Authorize the user
+/// bool isAuthorized = await auth.authorize();
+///
+/// if (isAuthorized) {
+/// print('Successfully authorized!');
+/// // Use the auth object for subsequent API calls
+/// } else {
+/// print('Authorization failed.');
+/// }
+///
+/// // Generate auth URL for API requests
+/// String? authHeader = await auth.generateAuthUrl();
+/// if (authHeader != null) {
+/// // Use authHeader in your API requests
+/// print('Auth header: $authHeader');
+/// }
+/// ```
final class UsefulJwtAuth extends IAuthorization {
+ /// Creates a new instance of UsefulJwtAuth.
+ ///
+ /// [userName] and [password] are required for authentication.
+ /// [events] is optional and can be used to handle authentication events.
UsefulJwtAuth({
required super.userName,
required super.password,
@@ -24,16 +54,32 @@ final class UsefulJwtAuth extends IAuthorization {
bool _hasValidatedOnce = false;
Dio? _client;
+ /// Number of days until the token expires.
static const int DAYS_UNTILS_TOKEN_EXPIRY = 3;
+ /// Checks if the current authentication is valid.
@override
bool get isValidAuth => !isNullOrEmpty(_encryptedAccessToken);
+ /// Checks if the current authentication has expired.
bool get _isAuthExpiried =>
_lastAuthorizedTime != null &&
DateTime.now().difference(_lastAuthorizedTime!).inHours >
(DAYS_UNTILS_TOKEN_EXPIRY * 24);
+ /// Authorizes the user with the provided credentials.
+ ///
+ /// Returns `true` if authorization is successful, `false` otherwise.
+ ///
+ /// Example:
+ /// ```dart
+ /// bool success = await auth.authorize();
+ /// if (success) {
+ /// print('Authorization successful');
+ /// } else {
+ /// print('Authorization failed');
+ /// }
+ /// ```
@override
Future authorize() async {
if (isValidAuth) {
@@ -79,6 +125,15 @@ final class UsefulJwtAuth extends IAuthorization {
return _hasValidatedOnce = !isNullOrEmpty(_encryptedAccessToken);
}
+ /// Checks if the user is currently authenticated.
+ ///
+ /// Returns `true` if the user is authenticated, `false` otherwise.
+ ///
+ /// Example:
+ /// ```dart
+ /// bool isAuth = await auth.isAuthenticated();
+ /// print(isAuth ? 'User is authenticated' : 'User is not authenticated');
+ /// ```
@override
Future isAuthenticated() async {
if (_hasValidatedOnce && !isNullOrEmpty(_encryptedAccessToken)) {
@@ -88,6 +143,15 @@ final class UsefulJwtAuth extends IAuthorization {
return false;
}
+ /// Validates the current authentication token.
+ ///
+ /// Returns `true` if the token is valid, `false` otherwise.
+ ///
+ /// Example:
+ /// ```dart
+ /// bool isValid = await auth.validate();
+ /// print(isValid ? 'Token is valid' : 'Token is invalid');
+ /// ```
@override
Future validate() async {
if (isNullOrEmpty(_encryptedAccessToken)) {
@@ -114,6 +178,18 @@ final class UsefulJwtAuth extends IAuthorization {
(response.data['code'] as String) == 'jwt_auth_valid_token';
}
+ /// Generates an authentication URL with the current token.
+ ///
+ /// Returns the authentication header as a string, or `null` if not authenticated.
+ ///
+ /// Example:
+ /// ```dart
+ /// String? authHeader = await auth.generateAuthUrl();
+ /// if (authHeader != null) {
+ /// print('Auth header: $authHeader');
+ /// // Use authHeader in your API requests
+ /// }
+ /// ```
@override
Future generateAuthUrl() async {
if (!await isAuthenticated()) {
@@ -123,11 +199,15 @@ final class UsefulJwtAuth extends IAuthorization {
return '$scheme $_encryptedAccessToken';
}
+ /// Configures the Dio client for this authentication method.
+ ///
+ /// This method is called internally by the WordPress client.
@override
void clientFactoryProvider(Dio client) {
_client = client;
}
+ /// The authentication scheme used. Returns 'Bearer' for UsefulJwtAuth.
@override
String get scheme => 'Bearer';
}
diff --git a/lib/src/bootstrap_builder.dart b/lib/src/bootstrap_builder.dart
index 3d951c0..927b93d 100644
--- a/lib/src/bootstrap_builder.dart
+++ b/lib/src/bootstrap_builder.dart
@@ -1,4 +1,4 @@
-// ignore_for_file: avoid_positional_boolean_parameters, avoid_returning_this
+// ignore_for_file: avoid_positional_boolean_parameters
import 'package:dio/dio.dart';
@@ -9,9 +9,17 @@ import 'constants.dart';
import 'middleware/wordpress_middleware_base.dart';
import 'utilities/typedefs.dart';
+/// A builder class for creating a [BootstrapConfiguration] with a fluent API.
+///
+/// This class allows for easy configuration of various WordPress client settings
+/// through method chaining.
class BootstrapBuilder {
+ /// Creates a new [BootstrapBuilder] instance.
BootstrapBuilder();
+ /// Creates a [BootstrapBuilder] instance from an existing [BootstrapConfiguration].
+ ///
+ /// This constructor initializes the builder with the values from the provided configuration.
BootstrapBuilder.fromConfiguration(BootstrapConfiguration config) {
_debugMode = config.enableDebugMode;
_statisticsDelegate = config.statisticsDelegate;
@@ -25,8 +33,7 @@ class BootstrapBuilder {
_followRedirects = config.shouldFollowRedirects;
_middlewares = config.middlewares;
}
-
- Duration _defaultRequestTimeout = DEFAULT_REQUEST_TIMEOUT; // 60 seconds
+ Duration _defaultRequestTimeout = DEFAULT_REQUEST_TIMEOUT;
bool Function(dynamic)? _responsePreprocessorDelegate;
IAuthorization? _defaultAuthorization;
String? _defaultUserAgent;
@@ -38,52 +45,61 @@ class BootstrapBuilder {
bool _debugMode = false;
List? _middlewares;
- /// Attaches [LogInterceptor] to the [Dio] instance.
+ /// Enables or disables debug mode.
+ ///
+ /// When enabled, this attaches a [LogInterceptor] to the [Dio] instance.
BootstrapBuilder withDebugMode(bool value) {
_debugMode = value;
return this;
}
+ /// Adds a single middleware to the configuration.
BootstrapBuilder withMiddleware(IWordpressMiddleware middleware) {
_middlewares ??= [];
_middlewares!.add(middleware);
return this;
}
+ /// Adds multiple middlewares to the configuration.
BootstrapBuilder withMiddlewares(Iterable middlewares) {
_middlewares ??= [];
_middlewares!.addAll(middlewares);
return this;
}
+ /// Adds a Dio interceptor to the configuration.
BootstrapBuilder withDioInterceptor(Interceptor interceptor) {
_interceptors ??= [];
_interceptors!.add(interceptor);
return this;
}
+ /// Sets the statistics delegate for collecting request statistics.
BootstrapBuilder withStatisticDelegate(StatisticsCallback? delegate) {
_statisticsDelegate = delegate;
return this;
}
+ /// Sets the default request timeout.
BootstrapBuilder withRequestTimeout(Duration timeout) {
_defaultRequestTimeout = timeout;
return this;
}
+ /// Sets a response preprocessor function.
BootstrapBuilder withResponsePreprocessor(
- bool Function(dynamic) responsePreprocessor,
- ) {
- _responsePreprocessorDelegate = responsePreprocessor;
+ bool Function(dynamic) preprocessor) {
+ _responsePreprocessorDelegate = preprocessor;
return this;
}
+ /// Sets the default authorization for requests.
BootstrapBuilder withDefaultAuthorization(IAuthorization authorization) {
_defaultAuthorization = authorization;
return this;
}
+ /// Sets the default authorization using a builder function.
BootstrapBuilder withDefaultAuthorizationBuilder(
IAuthorization Function(AuthorizationBuilder) builder,
) {
@@ -91,26 +107,31 @@ class BootstrapBuilder {
return this;
}
+ /// Sets the default User-Agent header for requests.
BootstrapBuilder withDefaultUserAgent(String userAgent) {
_defaultUserAgent = userAgent;
return this;
}
+ /// Sets default headers for all requests.
BootstrapBuilder withDefaultHeaders(Map headers) {
_defaultHeaders = headers;
return this;
}
- BootstrapBuilder withFollowRedirects(bool followRedirects) {
- _followRedirects = followRedirects;
+ /// Configures whether to follow redirects automatically.
+ BootstrapBuilder withFollowRedirects(bool follow) {
+ _followRedirects = follow;
return this;
}
- BootstrapBuilder withDefaultMaxRedirects(int defaultMaxRedirects) {
- _defaultMaxRedirects = defaultMaxRedirects;
+ /// Sets the maximum number of redirects to follow.
+ BootstrapBuilder withMaxRedirects(int maxRedirects) {
+ _defaultMaxRedirects = maxRedirects;
return this;
}
+ /// Builds and returns a [BootstrapConfiguration] instance with the configured settings.
BootstrapConfiguration build() {
return BootstrapConfiguration(
receiveTimeout: _defaultRequestTimeout,
diff --git a/lib/src/cache/cache_entry.dart b/lib/src/cache/cache_entry.dart
new file mode 100644
index 0000000..ff8d3c6
--- /dev/null
+++ b/lib/src/cache/cache_entry.dart
@@ -0,0 +1,24 @@
+import 'package:meta/meta.dart';
+
+/// Represents an entry in the cache with a value and optional expiry time.
+@immutable
+class CacheEntry {
+ /// Creates a new [CacheEntry] with the given [value] and optional [expiryTime].
+ ///
+ /// [value] The data to be stored in the cache.
+ /// [expiryTime] Optional. The time at which this entry should be considered expired.
+ const CacheEntry(this.value, this.expiryTime);
+
+ /// The data stored in this cache entry.
+ final T value;
+
+ /// The time at which this entry should be considered expired.
+ /// If null, the entry does not expire.
+ final DateTime? expiryTime;
+
+ /// Checks if the cache entry has expired.
+ ///
+ /// Returns true if [expiryTime] is set and has passed, false otherwise.
+ bool get isExpired =>
+ expiryTime != null && DateTime.now().isAfter(expiryTime!);
+}
diff --git a/lib/src/cache/cache_manager_base.dart b/lib/src/cache/cache_manager_base.dart
new file mode 100644
index 0000000..a466f01
--- /dev/null
+++ b/lib/src/cache/cache_manager_base.dart
@@ -0,0 +1,32 @@
+import 'dart:async';
+
+/// An abstract class defining the interface for a cache manager.
+///
+/// This interface provides methods for basic cache operations such as
+/// setting, getting, removing, and clearing cache entries.
+abstract class ICacheManager {
+ const ICacheManager();
+
+ /// Stores a value in the cache with the specified key.
+ ///
+ /// [key] The unique identifier for the cache entry.
+ /// [value] The value to be stored in the cache.
+ /// [expiry] Optional duration after which the cache entry should expire.
+ FutureOr set(String key, T value, {Duration? expiry});
+
+ /// Retrieves a value from the cache using the specified key.
+ ///
+ /// [key] The unique identifier for the cache entry.
+ /// [T] The expected type of the cached value.
+ ///
+ /// Returns a [FutureOr] that resolves to the cached value of type [T].
+ FutureOr get(String key);
+
+ /// Removes a specific entry from the cache.
+ ///
+ /// [key] The unique identifier of the cache entry to be removed.
+ FutureOr remove(String key);
+
+ /// Clears all entries from the cache.
+ FutureOr clear();
+}
diff --git a/lib/src/cache/exceptions/cache_exception_base.dart b/lib/src/cache/exceptions/cache_exception_base.dart
new file mode 100644
index 0000000..750f394
--- /dev/null
+++ b/lib/src/cache/exceptions/cache_exception_base.dart
@@ -0,0 +1,9 @@
+abstract base class CacheExceptionBase implements Exception {
+ const CacheExceptionBase({
+ required this.message,
+ this.cause,
+ });
+
+ final String message;
+ final Exception? cause;
+}
diff --git a/lib/src/cache/exceptions/cache_expired_exception.dart b/lib/src/cache/exceptions/cache_expired_exception.dart
new file mode 100644
index 0000000..8c59152
--- /dev/null
+++ b/lib/src/cache/exceptions/cache_expired_exception.dart
@@ -0,0 +1,8 @@
+import 'cache_exception_base.dart';
+
+final class CacheExpiredException extends CacheExceptionBase {
+ const CacheExpiredException({
+ super.cause,
+ super.message = 'Cache expired',
+ });
+}
diff --git a/lib/src/cache/exceptions/cache_not_exists_exception.dart b/lib/src/cache/exceptions/cache_not_exists_exception.dart
new file mode 100644
index 0000000..9311da8
--- /dev/null
+++ b/lib/src/cache/exceptions/cache_not_exists_exception.dart
@@ -0,0 +1,8 @@
+import 'cache_exception_base.dart';
+
+final class CacheNotExistsException extends CacheExceptionBase {
+ const CacheNotExistsException({
+ super.cause,
+ super.message = 'Cache with the specified key does not exist.',
+ });
+}
diff --git a/lib/src/cache/stores/memory_cache_store.dart b/lib/src/cache/stores/memory_cache_store.dart
new file mode 100644
index 0000000..a3a1247
--- /dev/null
+++ b/lib/src/cache/stores/memory_cache_store.dart
@@ -0,0 +1,37 @@
+import '../cache_entry.dart';
+import '../cache_manager_base.dart';
+
+class MemoryCacheStore implements ICacheManager {
+ MemoryCacheStore();
+
+ final Map> _cache = {};
+
+ @override
+ void set(String key, T value, {Duration? expiry}) {
+ final expiryTime = expiry != null ? DateTime.now().add(expiry) : null;
+ _cache[key] = CacheEntry(value, expiryTime);
+ }
+
+ @override
+ T? get(String key) {
+ final entry = _cache[key];
+
+ if (entry == null || entry.isExpired) {
+ _cache.remove(key);
+
+ return null;
+ }
+
+ return entry.value;
+ }
+
+ @override
+ void remove(String key) {
+ _cache.remove(key);
+ }
+
+ @override
+ void clear() {
+ _cache.clear();
+ }
+}
diff --git a/lib/src/client_configuration.dart b/lib/src/client_configuration.dart
index 34218a8..a2c1904 100644
--- a/lib/src/client_configuration.dart
+++ b/lib/src/client_configuration.dart
@@ -5,8 +5,13 @@ import 'package:meta/meta.dart';
import '../wordpress_client.dart';
import 'constants.dart';
+/// Configuration class for bootstrapping the WordPress client.
+///
+/// This class provides a fluent API for setting up various configuration options
+/// for the WordPress client, including timeouts, authorization, headers, and more.
@immutable
final class BootstrapConfiguration {
+ /// Creates a new instance of [BootstrapConfiguration] with default or specified values.
const BootstrapConfiguration({
this.receiveTimeout = DEFAULT_REQUEST_TIMEOUT,
this.connectTimeout = DEFAULT_CONNECT_TIMEOUT,
@@ -22,17 +27,40 @@ final class BootstrapConfiguration {
this.middlewares,
});
+ /// Enables or disables debug mode.
final bool enableDebugMode;
+
+ /// The timeout duration for receiving a response.
final Duration receiveTimeout;
+
+ /// The timeout duration for establishing a connection.
final Duration connectTimeout;
+
+ /// A function to preprocess the response before it's handled by the client.
final bool Function(dynamic)? responsePreprocessorDelegate;
+
+ /// The default authorization to use for requests.
final IAuthorization? defaultAuthorization;
+
+ /// The default User-Agent header to use for requests.
final String? defaultUserAgent;
+
+ /// Default headers to include in all requests.
final Map? defaultHeaders;
+
+ /// Whether to follow redirects automatically.
final bool shouldFollowRedirects;
+
+ /// The maximum number of redirects to follow.
final int maxRedirects;
+
+ /// A list of interceptors to use for requests.
final List? interceptors;
+
+ /// A callback for collecting statistics about requests.
final StatisticsCallback? statisticsDelegate;
+
+ /// A list of middlewares to apply to requests.
final List? middlewares;
@override
@@ -58,20 +86,23 @@ final class BootstrapConfiguration {
@override
int get hashCode {
- return enableDebugMode.hashCode ^
- receiveTimeout.hashCode ^
- responsePreprocessorDelegate.hashCode ^
- defaultAuthorization.hashCode ^
- defaultUserAgent.hashCode ^
- defaultHeaders.hashCode ^
- shouldFollowRedirects.hashCode ^
- maxRedirects.hashCode ^
- interceptors.hashCode ^
- middlewares.hashCode ^
- connectTimeout.hashCode ^
- statisticsDelegate.hashCode;
+ return Object.hash(
+ enableDebugMode,
+ receiveTimeout,
+ responsePreprocessorDelegate,
+ defaultAuthorization,
+ defaultUserAgent,
+ defaultHeaders,
+ shouldFollowRedirects,
+ maxRedirects,
+ interceptors,
+ middlewares,
+ connectTimeout,
+ statisticsDelegate,
+ );
}
+ /// Creates a copy of this configuration with the given fields replaced with new values.
BootstrapConfiguration copyWith({
bool? enableDebugMode,
Duration? receiveTimeout,
@@ -82,7 +113,6 @@ final class BootstrapConfiguration {
bool? shouldFollowRedirects,
int? maxRedirects,
List? interceptors,
- bool? synchronized,
StatisticsCallback? statisticsDelegate,
List? middlewares,
Duration? connectTimeout,
diff --git a/lib/src/constants.dart b/lib/src/constants.dart
index 4e7dd0f..9d701cf 100644
--- a/lib/src/constants.dart
+++ b/lib/src/constants.dart
@@ -1,3 +1,32 @@
+/// The default timeout duration for HTTP requests.
+///
+/// This constant defines the maximum amount of time allowed for an HTTP request
+/// to complete before it times out. If a request takes longer than this duration,
+/// it will be terminated, and an error will be thrown.
+///
+/// The value is set to 30 seconds, which is generally sufficient for most API
+/// calls, but can be adjusted if needed for specific use cases.
const Duration DEFAULT_REQUEST_TIMEOUT = Duration(seconds: 30);
+
+/// The default timeout duration for establishing a connection.
+///
+/// This constant specifies the maximum time allowed for the initial connection
+/// to be established with the server. If the connection cannot be made within
+/// this timeframe, the request will fail with a connection timeout error.
+///
+/// Like the request timeout, this is also set to 30 seconds, providing a
+/// balance between allowing enough time for connections in various network
+/// conditions and failing quickly in case of connectivity issues.
const Duration DEFAULT_CONNECT_TIMEOUT = Duration(seconds: 30);
+
+/// The header key used to identify local middleware.
+///
+/// This constant defines the HTTP header key that is used to indicate that
+/// a request has been processed by local middleware. It allows for tracking
+/// and managing the flow of requests through various middleware components
+/// in the WordPress client.
+///
+/// The value 'X-Local-Middleware' follows the convention of using 'X-' prefix
+/// for custom headers, making it clear that this is a non-standard header
+/// specific to this WordPress client implementation.
const String MIDDLEWARE_HEADER_KEY = 'X-Local-Middleware';
diff --git a/lib/src/enums.dart b/lib/src/enums.dart
index fd6ea85..9aac39e 100644
--- a/lib/src/enums.dart
+++ b/lib/src/enums.dart
@@ -1,163 +1,332 @@
// ignore_for_file: constant_identifier_names
+/// Represents various error types that can occur in the WordPress client.
enum ErrorType {
+ /// Interface does not exist
interfaceNotExist,
+
+ /// Interface already exists
interfaceAlreadyExist,
+
+ /// Request failed internally
requestFailedInternally,
+
+ /// Request failed
requestFailed,
+
+ /// Client is not ready
clientNotReady,
+
+ /// Authorization failed
authorizationFailed,
+
+ /// Bootstrap process failed
bootstrapFailed,
+
+ /// File doesn't exist
fileDoesntExist,
+
+ /// Interface does not exist
interfaceDoNotExist,
+
+ /// Interface exists
interfaceExist,
+
+ /// Interface is not initialized
interfaceNotInitialized,
+
+ /// Invalid interface
invalidInterface,
+
+ /// Discovery is pending
discoveryPending,
+
+ /// Discovery failed
discoveryFailed,
+
+ /// Null reference encountered
nullReference,
+
+ /// Request URI parsing failed
requestUriParsingFailed,
}
+/// Represents specific error types that can occur during a request.
enum RequestErrorType {
+ /// No error occurred
noError,
+
+ /// Unknown error
unknown,
+
+ /// Internal generic error
internalGenericError,
+
+ /// Authorization module not found
authorizationModuleNotFound,
+
+ /// Authorization failed with provided credentials
authorizationFailedWithProvidedCredentials,
+
+ /// Connection failed
connectionFailed,
+
+ /// Request was cancelled
requestCancelled,
+
+ /// Invalid status code received
invalidStatusCode,
+
+ /// Middleware aborted the request
middlewareAborted,
+
+ /// Middleware execution failed
middlewareExecutionFailed,
}
+/// Represents different types of search operations.
enum SearchType {
+ /// Search for posts
post,
+
+ /// Search for terms
term,
+
+ /// Search for post formats
postFormat,
}
+/// Represents the status of an item (e.g., a post or comment).
enum Status {
+ /// Item is open
open,
+
+ /// Item is closed
closed,
}
+/// Represents the status of a comment.
enum CommentStatus {
+ /// Comment is to be approved
approve,
+
+ /// Comment is approved
approved,
+
+ /// Comment is pending approval
pending,
}
-/// Different HTTP Methods which is supported by the client.
+/// Represents different HTTP methods supported by the client.
enum HttpMethod {
- /// Put Method
+ /// PUT method
put,
- /// Post Method
+ /// POST method
post,
- /// Get Method
+ /// GET method
get,
- /// Delete Method
+ /// DELETE method
delete,
- /// Update Method
+ /// UPDATE method
update,
- /// Head Method
+ /// HEAD method
head,
- /// Options Method
+ /// OPTIONS method
options,
- /// Patch Method
+ /// PATCH method
patch,
- /// Trace Method
+ /// TRACE method
trace,
}
+/// Represents the order of results (ascending or descending).
enum Order {
+ /// Ascending order
asc,
+
+ /// Descending order
desc,
}
+/// Represents different criteria for ordering results.
enum OrderBy {
+ /// Order by date
date,
+
+ /// Order by author
author,
+
+ /// Order by ID
id,
+
+ /// Order by included items
include,
+
+ /// Order by modification date
modified,
+
+ /// Order by parent
parent,
+
+ /// Order by relevance
relevance,
+
+ /// Order by slug
slug,
+
+ /// Order by included slugs
include_slugs,
+
+ /// Order by title
title,
+
+ /// Order by email
email,
+
+ /// Order by URL
url,
+
+ /// Order by name
name,
+
+ /// Order by registration date
registered_date,
+
+ /// Order by term group
term_group,
+
+ /// Order by description
description,
+
+ /// Order by count
count,
}
+/// Represents different contexts for a request.
enum RequestContext {
+ /// View context
view,
+
+ /// Embed context
embed,
+
+ /// Edit context
edit,
}
+/// Represents the status for media filtering.
enum MediaFilterStatus {
+ /// Inherit status from parent
inherit,
}
+/// Represents the relation between taxonomy terms.
enum TaxonomyRelation {
+ /// AND relation
and,
+
+ /// OR relation
or,
}
+/// Represents different statuses for content (e.g., posts).
enum ContentStatus {
+ /// Published content
publish,
+
+ /// Scheduled content
future,
+
+ /// Draft content
draft,
+
+ /// Pending content
pending,
+
+ /// Private content
private,
}
+/// Represents different post formats.
enum PostFormat {
+ /// Standard post format
standard,
+
+ /// Aside post format
aside,
+
+ /// Chat post format
chat,
+
+ /// Gallery post format
gallery,
+
+ /// Link post format
link,
+
+ /// Image post format
image,
+
+ /// Quote post format
quote,
+
+ /// Status post format
status,
+
+ /// Video post format
video,
+
+ /// Audio post format
audio,
}
+/// Represents different types of authorization.
enum AuthorizationType {
+ /// Basic JWT authorization
basic_jwt,
+
+ /// Useful JWT authorization
useful_jwt,
+
+ /// Basic authorization
basic,
}
+/// Represents different locales.
enum Locale {
+ /// English (United States)
en_US,
}
+/// Represents different types of media.
enum MediaType {
+ /// Image media type
image,
+
+ /// Video media type
video,
+
+ /// Text media type
text,
+
+ /// Application media type
application,
+
+ /// Audio media type
audio,
}
+/// Converts a string value to a [ContentStatus] enum.
+///
+/// If the value is null or doesn't match any enum value, returns [defaultValue].
ContentStatus getContentStatusFromValue(
String? value, {
ContentStatus defaultValue = ContentStatus.pending,
@@ -166,12 +335,15 @@ ContentStatus getContentStatusFromValue(
return defaultValue;
}
- return ContentStatus.values
- .where((element) => element.name.toLowerCase() == value.toLowerCase())
- .firstOrNull ??
- defaultValue;
+ return ContentStatus.values.firstWhere(
+ (element) => element.name.toLowerCase() == value.toLowerCase(),
+ orElse: () => defaultValue,
+ );
}
+/// Converts a string value to a [CommentStatus] enum.
+///
+/// If the value is null or doesn't match any enum value, returns [defaultValue].
CommentStatus getCommentStatusFromValue(
String? value, {
CommentStatus defaultValue = CommentStatus.pending,
@@ -180,12 +352,15 @@ CommentStatus getCommentStatusFromValue(
return defaultValue;
}
- return CommentStatus.values
- .where((element) => element.name.toLowerCase() == value.toLowerCase())
- .firstOrNull ??
- defaultValue;
+ return CommentStatus.values.firstWhere(
+ (element) => element.name.toLowerCase() == value.toLowerCase(),
+ orElse: () => defaultValue,
+ );
}
+/// Converts a string value to a [MediaFilterStatus] enum.
+///
+/// If the value is null or doesn't match any enum value, returns [defaultValue].
MediaFilterStatus getMediaFilterStatusFromValue(
String? value, {
MediaFilterStatus defaultValue = MediaFilterStatus.inherit,
@@ -194,12 +369,15 @@ MediaFilterStatus getMediaFilterStatusFromValue(
return defaultValue;
}
- return MediaFilterStatus.values
- .where((element) => element.name.toLowerCase() == value.toLowerCase())
- .firstOrNull ??
- defaultValue;
+ return MediaFilterStatus.values.firstWhere(
+ (element) => element.name.toLowerCase() == value.toLowerCase(),
+ orElse: () => defaultValue,
+ );
}
+/// Converts a string value to a [PostFormat] enum.
+///
+/// If the value is null or doesn't match any enum value, returns [defaultValue].
PostFormat getFormatFromValue(
String? value, {
PostFormat defaultValue = PostFormat.standard,
@@ -208,19 +386,22 @@ PostFormat getFormatFromValue(
return defaultValue;
}
- return PostFormat.values
- .where((e) => e.name.toLowerCase() == value.toLowerCase())
- .firstOrNull ??
- defaultValue;
+ return PostFormat.values.firstWhere(
+ (e) => e.name.toLowerCase() == value.toLowerCase(),
+ orElse: () => defaultValue,
+ );
}
+/// Converts a string value to a [Status] enum.
+///
+/// If the value is null, empty, or doesn't match any enum value, returns [defaultValue].
Status getStatusFromValue(String? value, {Status defaultValue = Status.open}) {
if (value == null || value.isEmpty) {
return defaultValue;
}
- return Status.values
- .where((element) => element.name.toLowerCase() == value.toLowerCase())
- .firstOrNull ??
- defaultValue;
+ return Status.values.firstWhere(
+ (element) => element.name.toLowerCase() == value.toLowerCase(),
+ orElse: () => defaultValue,
+ );
}
diff --git a/lib/src/interface/application_passwords.dart b/lib/src/interface/application_passwords.dart
index d821a3e..9dc1305 100644
--- a/lib/src/interface/application_passwords.dart
+++ b/lib/src/interface/application_passwords.dart
@@ -1,6 +1,47 @@
import '../../wordpress_client.dart';
-/// Represents the application password interface.
+/// Represents the application password interface for WordPress.
+///
+/// This interface provides methods to manage application passwords, including:
+/// - Creating new application passwords
+/// - Deleting existing application passwords
+/// - Listing all application passwords
+/// - Retrieving specific application passwords
+/// - Updating existing application passwords
+///
+/// Example usage:
+/// ```dart
+/// final wpClient = WordPressClient('https://your-wordpress-site.com');
+/// final appPasswords = wpClient.applicationPasswords;
+///
+/// // Create a new application password
+/// final newPassword = await appPasswords.create(CreateApplicationPasswordRequest(
+/// name: 'My App Password',
+/// user: 1, // User ID
+/// ));
+///
+/// // List all application passwords
+/// final passwords = await appPasswords.list(ListApplicationPasswordRequest());
+///
+/// // Retrieve a specific application password
+/// final password = await appPasswords.retrieve(RetriveApplicationPasswordRequest(
+/// id: newPassword.id,
+/// user: 1, // User ID
+/// ));
+///
+/// // Update an application password
+/// final updatedPassword = await appPasswords.update(UpdateApplicationPasswordRequest(
+/// id: newPassword.id,
+/// user: 1, // User ID
+/// name: 'Updated App Password',
+/// ));
+///
+/// // Delete an application password
+/// await appPasswords.delete(DeleteApplicationPasswordRequest(
+/// id: newPassword.id,
+/// user: 1, // User ID
+/// ));
+/// ```
final class ApplicationPasswordsInterface extends IRequestInterface
with
CreateOperation,
diff --git a/lib/src/interface/category.dart b/lib/src/interface/category.dart
index acbfc5c..df8cf15 100644
--- a/lib/src/interface/category.dart
+++ b/lib/src/interface/category.dart
@@ -1,6 +1,39 @@
import '../../wordpress_client.dart';
-/// Represents the category interface.
+/// Represents the category interface for interacting with WordPress categories.
+///
+/// This interface provides CRUD (Create, Read, Update, Delete) operations for categories.
+/// It extends [IRequestInterface] and mixes in various operations to handle different
+/// category-related tasks.
+///
+/// Example usage:
+/// ```dart
+/// final wordpress = WordPressClient('https://your-wordpress-site.com/wp-json');
+/// final categoryInterface = wordpress.categories;
+///
+/// // Create a new category
+/// final newCategory = await categoryInterface.create(
+/// CreateCategoryRequest(name: 'New Category'),
+/// );
+///
+/// // Retrieve a category
+/// final category = await categoryInterface.retrieve(
+/// RetrieveCategoryRequest(id: 123),
+/// );
+///
+/// // Update a category
+/// final updatedCategory = await categoryInterface.update(
+/// UpdateCategoryRequest(id: 123, name: 'Updated Category Name'),
+/// );
+///
+/// // Delete a category
+/// await categoryInterface.delete(DeleteCategoryRequest(id: 123));
+///
+/// // List categories
+/// final categories = await categoryInterface.list(
+/// ListCategoryRequest(perPage: 10, page: 1),
+/// );
+/// ```
final class CategoryInterface extends IRequestInterface
with
CreateOperation,
diff --git a/lib/src/interface/comments.dart b/lib/src/interface/comments.dart
index 479f2ba..d0933b8 100644
--- a/lib/src/interface/comments.dart
+++ b/lib/src/interface/comments.dart
@@ -1,6 +1,42 @@
import '../../wordpress_client.dart';
-/// Represents the comment interface.
+/// Represents the comment interface for interacting with WordPress comments.
+///
+/// This interface provides methods for creating, deleting, retrieving, updating,
+/// and listing comments in a WordPress site.
+///
+/// Example usage:
+///
+/// ```dart
+/// final wp = WordPressClient('https://your-wordpress-site.com/wp-json');
+/// final commentInterface = wp.comments;
+///
+/// // Create a new comment
+/// final newComment = await commentInterface.create(CreateCommentRequest(
+/// content: 'Great post!',
+/// post: 123,
+/// author: 'John Doe',
+/// authorEmail: 'john@example.com',
+/// ));
+///
+/// // Retrieve a comment
+/// final comment = await commentInterface.retrieve(RetrieveCommentRequest(id: 456));
+///
+/// // Update a comment
+/// final updatedComment = await commentInterface.update(UpdateCommentRequest(
+/// id: 456,
+/// content: 'Updated comment content',
+/// ));
+///
+/// // Delete a comment
+/// await commentInterface.delete(DeleteCommentRequest(id: 456));
+///
+/// // List comments
+/// final comments = await commentInterface.list(ListCommentRequest(
+/// post: 123,
+/// status: 'approved',
+/// ));
+/// ```
final class CommentInterface extends IRequestInterface
with
CreateOperation,
diff --git a/lib/src/interface/me.dart b/lib/src/interface/me.dart
index 09efdc3..efeb16e 100644
--- a/lib/src/interface/me.dart
+++ b/lib/src/interface/me.dart
@@ -1,6 +1,34 @@
import '../../wordpress_client.dart';
-/// Represents the current user interface.
+/// Represents the current user interface for interacting with the WordPress API.
+///
+/// This class provides operations to manage the current user's account, including:
+/// - Retrieving user information
+/// - Updating user details
+/// - Deleting the user account
+///
+/// Usage examples:
+///
+/// Retrieve current user information:
+/// ```dart
+/// final user = await interface.retrieve(RetrieveMeRequest());
+/// print(user.username);
+/// ```
+///
+/// Update user information:
+/// ```dart
+/// final updatedUser = await interface.update(UpdateMeRequest(
+/// firstName: 'John',
+/// lastName: 'Doe',
+/// ));
+/// print('Updated name: ${updatedUser.firstName} ${updatedUser.lastName}');
+/// ```
+///
+/// Delete user account:
+/// ```dart
+/// await interface.delete(DeleteMeRequest());
+/// print('User account deleted');
+/// ```
final class MeInterface extends IRequestInterface
with
DeleteOperation,
diff --git a/lib/src/interface/media.dart b/lib/src/interface/media.dart
index 6b2d62f..1929a95 100644
--- a/lib/src/interface/media.dart
+++ b/lib/src/interface/media.dart
@@ -1,6 +1,39 @@
import '../../wordpress_client.dart';
-/// Represents the media interface.
+/// Represents the media interface for interacting with WordPress media items.
+///
+/// This interface provides operations to manage media files such as images,
+/// videos, and documents in a WordPress site.
+///
+/// Example usage:
+/// ```dart
+/// final wp = WordPressClient('https://example.com/wp-json');
+/// final mediaInterface = wp.media;
+///
+/// // Create a new media item
+/// final newMedia = await mediaInterface.create(CreateMediaRequest(
+/// file: File('image.jpg'),
+/// title: 'My Image',
+/// ));
+///
+/// // Retrieve a media item
+/// final media = await mediaInterface.retrieve(RetrieveMediaRequest(id: 123));
+///
+/// // Update a media item
+/// final updatedMedia = await mediaInterface.update(UpdateMediaRequest(
+/// id: 123,
+/// title: 'Updated Image Title',
+/// ));
+///
+/// // Delete a media item
+/// await mediaInterface.delete(DeleteMediaRequest(id: 123));
+///
+/// // List media items
+/// final mediaList = await mediaInterface.list(ListMediaRequest(
+/// perPage: 10,
+/// page: 1,
+/// ));
+/// ```
final class MediaInterface extends IRequestInterface
with
CreateOperation,
diff --git a/lib/src/interface/page.dart b/lib/src/interface/page.dart
index 9321ad1..daf579b 100644
--- a/lib/src/interface/page.dart
+++ b/lib/src/interface/page.dart
@@ -1,6 +1,41 @@
import '../../wordpress_client.dart';
-/// Represents the page interface.
+/// Represents the interface for interacting with WordPress pages.
+///
+/// This class provides methods for creating, retrieving, updating, deleting,
+/// and listing pages in a WordPress site.
+///
+/// Example usage:
+///
+/// ```dart
+/// final wordpress = WordPressClient('https://your-site.com/wp-json');
+/// final pagesInterface = wordpress.pages;
+///
+/// // Create a new page
+/// final newPage = await pagesInterface.create(CreatePageRequest(
+/// title: 'My New Page',
+/// content: 'This is the content of my new page.',
+/// status: 'publish',
+/// ));
+///
+/// // Retrieve a page
+/// final page = await pagesInterface.retrieve(RetrievePageRequest(id: 123));
+///
+/// // Update a page
+/// final updatedPage = await pagesInterface.update(UpdatePageRequest(
+/// id: 123,
+/// title: 'Updated Page Title',
+/// ));
+///
+/// // Delete a page
+/// await pagesInterface.delete(DeletePageRequest(id: 123));
+///
+/// // List pages
+/// final pages = await pagesInterface.list(ListPageRequest(
+/// perPage: 10,
+/// page: 1,
+/// ));
+/// ```
final class PagesInterface extends IRequestInterface
with
CreateOperation,
diff --git a/lib/src/interface/posts.dart b/lib/src/interface/posts.dart
index 8e9355d..9bd3a10 100644
--- a/lib/src/interface/posts.dart
+++ b/lib/src/interface/posts.dart
@@ -1,6 +1,41 @@
import '../../wordpress_client.dart';
-/// Represents the post interface.
+/// Represents the interface for interacting with WordPress posts.
+///
+/// This class provides methods for creating, retrieving, updating, deleting,
+/// and listing posts in a WordPress site.
+///
+/// Example usage:
+///
+/// ```dart
+/// final wordpress = WordPressClient('https://your-wordpress-site.com');
+/// final postsInterface = wordpress.posts;
+///
+/// // Create a new post
+/// final newPost = await postsInterface.create(CreatePostRequest(
+/// title: 'My New Post',
+/// content: 'This is the content of my new post.',
+/// status: 'publish',
+/// ));
+///
+/// // Retrieve a post
+/// final post = await postsInterface.retrieve(RetrievePostRequest(id: 123));
+///
+/// // Update a post
+/// final updatedPost = await postsInterface.update(UpdatePostRequest(
+/// id: 123,
+/// title: 'Updated Post Title',
+/// ));
+///
+/// // Delete a post
+/// await postsInterface.delete(DeletePostRequest(id: 123));
+///
+/// // List posts
+/// final posts = await postsInterface.list(ListPostRequest(
+/// perPage: 10,
+/// page: 1,
+/// ));
+/// ```
final class PostsInterface extends IRequestInterface
with
CreateOperation,
diff --git a/lib/src/interface/search.dart b/lib/src/interface/search.dart
index f37d4c7..d7924ae 100644
--- a/lib/src/interface/search.dart
+++ b/lib/src/interface/search.dart
@@ -1,5 +1,31 @@
import '../library_exports.dart';
-/// Represents the search interface.
+/// Represents the search interface for interacting with WordPress search functionality.
+///
+/// This interface provides methods for searching content across a WordPress site.
+///
+/// Example usage:
+///
+/// ```dart
+/// final wordpress = WordPressClient('https://your-wordpress-site.com/wp-json');
+/// final searchInterface = wordpress.search;
+///
+/// // Perform a search
+/// final searchResults = await searchInterface.list(ListSearchRequest(
+/// search: 'example query',
+/// perPage: 10,
+/// page: 1,
+/// ));
+///
+/// // Process search results
+/// for (var result in searchResults) {
+/// print('Title: ${result.title}');
+/// print('Type: ${result.type}');
+/// print('URL: ${result.url}');
+/// }
+/// ```
+///
+/// The SearchInterface uses the ListOperation to perform searches, returning
+/// a list of Search objects that match the given criteria.
final class SearchInterface extends IRequestInterface
with ListOperation {}
diff --git a/lib/src/interface/tags.dart b/lib/src/interface/tags.dart
index 6860e94..f02ac1f 100644
--- a/lib/src/interface/tags.dart
+++ b/lib/src/interface/tags.dart
@@ -1,6 +1,30 @@
import '../../wordpress_client.dart';
-/// Represents the tag interface.
+/// Represents the tag interface for interacting with WordPress tags.
+///
+/// This interface provides CRUD (Create, Read, Update, Delete) operations for tags.
+/// It extends [IRequestInterface] and mixes in various operations to handle tag-related tasks.
+///
+/// Example usage:
+/// ```dart
+/// final wordpress = WordPressClient('https://your-wordpress-site.com/wp-json');
+/// final tagInterface = wordpress.tags;
+///
+/// // Create a new tag
+/// final newTag = await tagInterface.create(CreateTagRequest(name: 'New Tag'));
+///
+/// // Retrieve a tag
+/// final tag = await tagInterface.retrieve(RetrieveTagRequest(id: 123));
+///
+/// // Update a tag
+/// final updatedTag = await tagInterface.update(UpdateTagRequest(id: 123, name: 'Updated Tag'));
+///
+/// // Delete a tag
+/// await tagInterface.delete(DeleteTagRequest(id: 123));
+///
+/// // List tags
+/// final tags = await tagInterface.list(ListTagRequest());
+/// ```
final class TagInterface extends IRequestInterface
with
CreateOperation,
diff --git a/lib/src/interface/users.dart b/lib/src/interface/users.dart
index b4d74e6..6ea3911 100644
--- a/lib/src/interface/users.dart
+++ b/lib/src/interface/users.dart
@@ -1,6 +1,42 @@
import '../../wordpress_client.dart';
-/// Represents the user interface.
+/// Represents the user interface for managing WordPress users.
+///
+/// This class provides methods for creating, deleting, retrieving, updating,
+/// and listing users in a WordPress site.
+///
+/// Example usage:
+///
+/// ```dart
+/// final wordpress = WordPressClient('https://your-site.com');
+/// final usersInterface = wordpress.users;
+///
+/// // Create a new user
+/// final newUser = await usersInterface.create(CreateUserRequest(
+/// username: 'newuser',
+/// email: 'newuser@example.com',
+/// password: 'securepassword',
+/// ));
+///
+/// // Retrieve a user
+/// final user = await usersInterface.retrieve(RetrieveUserRequest(id: 1));
+///
+/// // Update a user
+/// final updatedUser = await usersInterface.update(UpdateUserRequest(
+/// id: 1,
+/// firstName: 'John',
+/// lastName: 'Doe',
+/// ));
+///
+/// // Delete a user
+/// await usersInterface.delete(DeleteUserRequest(id: 1));
+///
+/// // List users
+/// final users = await usersInterface.list(ListUserRequest(
+/// page: 1,
+/// perPage: 10,
+/// ));
+/// ```
final class UsersInterface extends IRequestInterface
with
CreateOperation,
diff --git a/lib/src/interface_key.dart b/lib/src/interface_key.dart
index fefebf9..19922d2 100644
--- a/lib/src/interface_key.dart
+++ b/lib/src/interface_key.dart
@@ -2,26 +2,68 @@ import 'package:meta/meta.dart';
import 'utilities/helpers.dart';
+/// A class representing a unique key for an interface of type T.
+///
+/// This class is used to create a unique identifier for interfaces,
+/// combining the type T with an optional string key.
+///
+/// Example:
+/// ```dart
+/// final userKey = InterfaceKey('primary');
+/// final postKey = InterfaceKey();
+/// ```
@immutable
final class InterfaceKey {
+ /// Creates an [InterfaceKey] with an optional string key.
+ ///
+ /// If no key is provided, an empty string is used as the default.
+ ///
+ /// Example:
+ /// ```dart
+ /// final key1 = InterfaceKey('custom');
+ /// final key2 = InterfaceKey(); // Uses default empty string
+ /// ```
const InterfaceKey([this._key = '']);
+ /// The type of the interface this key represents.
Type get _type => typeOf();
+
+ /// An optional string to further specify the key.
final String? _key;
+ /// Compares this [InterfaceKey] with another object for equality.
+ ///
+ /// Two [InterfaceKey]s are considered equal if they have the same hash code.
@override
bool operator ==(Object other) => hashCode == other.hashCode;
+ /// Generates a hash code for this [InterfaceKey].
+ ///
+ /// The hash code is a combination of the type's hash code and the optional key's hash code.
@override
int get hashCode {
return _type.hashCode ^ (_key?.hashCode ?? 0);
}
+ /// Returns a string representation of this [InterfaceKey].
+ ///
+ /// Example:
+ /// ```dart
+ /// final key = InterfaceKey('admin');
+ /// print(key.toString()); // Outputs: InterfaceKeyadmin
+ /// ```
@override
String toString() {
return 'InterfaceKey<$_type>$_key';
}
+ /// Returns a more detailed string representation for debugging purposes.
+ ///
+ /// Example:
+ /// ```dart
+ /// final key = InterfaceKey('admin');
+ /// print(key.toDebugString()); // Outputs: InterfaceKey(User, admin)
+ /// ```
String toDebugString() {
final tag = _key == null ? '' : ', $_key';
return 'InterfaceKey<$_type>($_type$tag)';
diff --git a/lib/src/middleware/delegated_middleware.dart b/lib/src/middleware/delegated_middleware.dart
index 52277a7..da873b3 100644
--- a/lib/src/middleware/delegated_middleware.dart
+++ b/lib/src/middleware/delegated_middleware.dart
@@ -2,21 +2,36 @@ import '../requests/wordpress_request.dart';
import '../responses/wordpress_raw_response.dart';
import 'middleware_exports.dart';
+/// A function type for modifying a WordPress request before it's sent.
typedef OnRequestDelegate = Future Function(
WordpressRequest request,
);
+/// A function type for processing a WordPress response after it's received.
typedef OnResponseDelegate = Future Function(
WordpressRawResponse response,
);
+/// A function type for initializing the middleware.
typedef InitializeDelegate = Future Function();
+
+/// A function type for cleaning up when the middleware is removed.
typedef OnRemovedDelegate = Future Function();
+
+/// A function type for custom execution logic in the middleware.
typedef OnExecuteDelegate = Future Function(
WordpressRequest request,
);
+/// A middleware that delegates its functionality to provided functions.
+///
+/// This allows for flexible and customizable middleware behavior without
+/// needing to create a new class for each variation.
final class DelegatedMiddleware extends IWordpressMiddleware {
+ /// Creates a new [DelegatedMiddleware] instance.
+ ///
+ /// [onRequestDelegate] and [onResponseDelegate] are required.
+ /// Other delegates are optional and will only be called if provided.
const DelegatedMiddleware({
required this.onRequestDelegate,
required this.onResponseDelegate,
@@ -25,10 +40,19 @@ final class DelegatedMiddleware extends IWordpressMiddleware {
this.onRemovedDelegate,
});
+ /// Called when the middleware is loaded.
final InitializeDelegate? initializeDelegate;
+
+ /// Called for each request passing through the middleware.
final OnRequestDelegate onRequestDelegate;
+
+ /// Called for each response passing through the middleware.
final OnResponseDelegate onResponseDelegate;
+
+ /// Called when the middleware is removed.
final OnRemovedDelegate? onRemovedDelegate;
+
+ /// Called for custom execution logic, if provided.
final OnExecuteDelegate? onExecuteDelegate;
@override
diff --git a/lib/src/middleware/models/middleware_raw_response.dart b/lib/src/middleware/models/middleware_raw_response.dart
index 9fc5f7e..958292c 100644
--- a/lib/src/middleware/models/middleware_raw_response.dart
+++ b/lib/src/middleware/models/middleware_raw_response.dart
@@ -1,4 +1,22 @@
+/// Represents a raw response from a middleware operation.
+///
+/// This class encapsulates various components of an HTTP response, including
+/// status code, headers, body, and additional metadata.
final class MiddlewareRawResponse {
+ /// Creates a new [MiddlewareRawResponse] instance.
+ ///
+ /// [statusCode] and [body] are required parameters, while [message], [headers],
+ /// and [extra] are optional.
+ ///
+ /// Example:
+ /// ```dart
+ /// final response = MiddlewareRawResponse(
+ /// statusCode: 200,
+ /// body: {'data': 'example'},
+ /// headers: {'Content-Type': 'application/json'},
+ /// message: 'Success',
+ /// );
+ /// ```
const MiddlewareRawResponse({
required this.statusCode,
required this.body,
@@ -7,16 +25,44 @@ final class MiddlewareRawResponse {
this.extra,
});
+ /// Creates a default instance of [MiddlewareRawResponse] with a status code of -99
+ /// and a null body.
+ ///
+ /// This can be used as a placeholder or for initialization purposes.
+ ///
+ /// Example:
+ /// ```dart
+ /// final defaultResponse = MiddlewareRawResponse.defaultInstance();
+ /// print(defaultResponse.statusCode); // Outputs: -99
+ /// ```
factory MiddlewareRawResponse.defaultInstance() {
return const MiddlewareRawResponse(statusCode: -99, body: null);
}
+ /// The HTTP status code of the response.
final int statusCode;
+
+ /// The headers of the HTTP response, if any.
final Map? headers;
+
+ /// Additional metadata or context information about the response.
final Map? extra;
+
+ /// The body of the HTTP response. Can be of any type.
final dynamic body;
+
+ /// An optional message associated with the response.
final String? message;
+ /// Indicates whether the response contains valid data.
+ ///
+ /// Returns true if the body is not null and the status code is in the 2xx range.
+ ///
+ /// Example:
+ /// ```dart
+ /// final response = MiddlewareRawResponse(statusCode: 200, body: {'key': 'value'});
+ /// print(response.hasData); // Outputs: true
+ /// ```
bool get hasData => body != null && statusCode >= 200 && statusCode < 300;
@override
diff --git a/lib/src/middleware/wordpress_middleware_base.dart b/lib/src/middleware/wordpress_middleware_base.dart
index 4d46810..5638301 100644
--- a/lib/src/middleware/wordpress_middleware_base.dart
+++ b/lib/src/middleware/wordpress_middleware_base.dart
@@ -3,30 +3,93 @@ import 'dart:async';
import '../library_exports.dart';
/// The base interface for WordPress middleware.
+///
+/// Middleware allows you to intercept and modify requests and responses
+/// in the WordPress API client. This can be useful for tasks such as
+/// authentication, caching, logging, or modifying request/response data.
abstract class IWordpressMiddleware {
const IWordpressMiddleware();
/// The name of the middleware.
+ ///
+ /// This should be a unique identifier for the middleware.
+ /// Example: 'AuthenticationMiddleware'
String get name;
/// Called when the middleware is loaded.
+ ///
+ /// Use this method to initialize any resources needed by the middleware.
+ /// Example:
+ /// ```dart
+ /// @override
+ /// Future onLoad() async {
+ /// await _initializeCache();
+ /// }
+ /// ```
Future onLoad();
/// Called before sending a request to the WordPress server.
+ ///
+ /// This method allows you to modify the outgoing request.
+ /// Example: Adding an authentication token to the request headers
+ /// ```dart
+ /// @override
+ /// Future onRequest(WordpressRequest request) async {
+ /// return request.copyWith(
+ /// headers: {...request.headers, 'Authorization': 'Bearer $token'},
+ /// );
+ /// }
+ /// ```
Future onRequest(WordpressRequest request);
/// Called before executing a request to the WordPress server.
///
+ /// This method can be used to return a custom response (e.g., a cached response)
+ /// or to cancel the request by throwing an exception.
+ ///
/// By default, it returns a [MiddlewareRawResponse] with default values.
///
- /// This can used for returning a custom response (cached response), or to cancel the request (By throwing an exception).
+ /// Example: Returning a cached response
+ /// ```dart
+ /// @override
+ /// Future onExecute(WordpressRequest request) async {
+ /// final cachedResponse = await _cache.get(request.url);
+ /// if (cachedResponse != null) {
+ /// return MiddlewareRawResponse(
+ /// statusCode: 200,
+ /// body: cachedResponse,
+ /// headers: {'X-Cache': 'HIT'},
+ /// );
+ /// }
+ /// return MiddlewareRawResponse.defaultInstance();
+ /// }
+ /// ```
Future onExecute(WordpressRequest request) async {
return MiddlewareRawResponse.defaultInstance();
}
/// Called after receiving a response from the WordPress server.
+ ///
+ /// This method allows you to modify or process the incoming response.
+ /// Example: Logging the response
+ /// ```dart
+ /// @override
+ /// Future onResponse(WordpressRawResponse response) async {
+ /// _logger.info('Received response: ${response.statusCode}');
+ /// return response;
+ /// }
+ /// ```
Future onResponse(WordpressRawResponse response);
/// Called when the middleware is unloaded.
+ ///
+ /// Use this method to clean up any resources used by the middleware.
+ /// Example:
+ /// ```dart
+ /// @override
+ /// Future onUnload() async {
+ /// await _closeConnections();
+ /// }
+ /// ```
Future onUnload();
}
diff --git a/lib/src/operations/create.dart b/lib/src/operations/create.dart
index e501b89..e8b0635 100644
--- a/lib/src/operations/create.dart
+++ b/lib/src/operations/create.dart
@@ -1,14 +1,32 @@
import '../library_exports.dart';
-/// Represents the create operation.
+/// Represents the create operation for WordPress API requests.
+///
+/// This mixin provides methods to create new resources using the WordPress API.
+/// It is generic over the type [T] of the response data and [R] which extends [IRequest].
base mixin CreateOperation on IRequestInterface {
+ /// Creates a new resource using the provided [request].
+ ///
+ /// This method sends a create request to the WordPress API and returns
+ /// a [WordpressResponse] containing the created resource of type [T].
+ ///
+ /// [request] is an instance of [R] that contains the necessary data for the create operation.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressResponse].
Future> create(R request) async {
final wpRequest = await request.build(baseUrl);
return executor.create(wpRequest);
}
- /// Returns the raw response for the given [request].
+ /// Creates a new resource and returns the raw response for the given [request].
+ ///
+ /// This method is similar to [create], but instead of parsing the response,
+ /// it returns the raw response from the WordPress API.
+ ///
+ /// [request] is an instance of [R] that contains the necessary data for the create operation.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressRawResponse].
Future createRaw(R request) async {
final wpRequest = await request.build(baseUrl);
diff --git a/lib/src/operations/custom.dart b/lib/src/operations/custom.dart
index 20acec4..61d3c1b 100644
--- a/lib/src/operations/custom.dart
+++ b/lib/src/operations/custom.dart
@@ -1,10 +1,21 @@
import '../library_exports.dart';
-/// Represents the custom operation. This mixin is used to create custom operations.
+/// Represents a custom operation for interacting with WordPress APIs.
+/// This mixin is used to create custom operations that can execute requests
+/// and handle responses.
base mixin CustomOperation on IRequestInterface {
+ /// Decodes the JSON response into the desired type [T].
+ /// Implement this method to define how the API response should be parsed.
T decode(dynamic json);
- /// Executes the given [request] and returns the response.
+ /// Executes the given [request] and returns a typed response.
+ ///
+ /// This method performs the following steps:
+ /// 1. Builds the WordPress request using the provided [request] object.
+ /// 2. Executes the request using the [executor].
+ /// 3. Decodes the response using the [decode] method.
+ ///
+ /// Returns a [WordpressResponse] containing the decoded data of type [T].
Future> request(R request) async {
final wpRequest = await request.build(baseUrl);
@@ -13,7 +24,14 @@ base mixin CustomOperation on IRequestInterface {
return response.asResponse(decoder: decode);
}
- /// Returns the raw response for the given [request].
+ /// Returns the raw response for the given [request] without decoding.
+ ///
+ /// This method is useful when you need access to the unprocessed API response.
+ /// It performs the following steps:
+ /// 1. Builds the WordPress request using the provided [request] object.
+ /// 2. Executes the request using the [executor] in raw mode.
+ ///
+ /// Returns a [WordpressRawResponse] containing the unprocessed API response.
Future raw(R request) async {
final wpRequest = await request.build(baseUrl);
diff --git a/lib/src/operations/delete.dart b/lib/src/operations/delete.dart
index f6de97d..1aa5783 100644
--- a/lib/src/operations/delete.dart
+++ b/lib/src/operations/delete.dart
@@ -1,16 +1,35 @@
import '../library_exports.dart';
-/// Represents the delete operation.
+/// Represents the delete operation for WordPress API requests.
+///
+/// This mixin provides methods to delete resources using the WordPress API.
+/// It is generic over [R] which extends [IRequest].
base mixin DeleteOperation on IRequestInterface {
+ /// Deletes a resource using the provided [request].
+ ///
+ /// This method sends a delete request to the WordPress API and returns
+ /// a [WordpressResponse] containing a boolean indicating the success of the operation.
+ ///
+ /// [request] is an instance of [R] that contains the necessary data for the delete operation.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressResponse].
+ /// The boolean value is typically true if the deletion was successful.
Future> delete(R request) async {
final wpRequest = await request.build(baseUrl);
return executor.delete(wpRequest);
}
- /// Returns the raw response for the given [request].
+ /// Deletes a resource and returns the raw response for the given [request].
+ ///
+ /// This method is similar to [delete], but instead of parsing the response,
+ /// it returns the raw response from the WordPress API.
+ ///
+ /// [request] is an instance of [R] that contains the necessary data for the delete operation.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressRawResponse].
///
- /// **Note that for delete responses, Wordpress API returns an empty response body.**
+ /// **Note: For delete operations, the WordPress API typically returns an empty response body.**
Future deleteRaw(R request) async {
final wpRequest = await request.build(baseUrl);
diff --git a/lib/src/operations/list.dart b/lib/src/operations/list.dart
index 4a9f2a8..72dcc6c 100644
--- a/lib/src/operations/list.dart
+++ b/lib/src/operations/list.dart
@@ -1,7 +1,19 @@
import '../../wordpress_client.dart';
-/// Represents the list operation.
+/// Represents the list operation for WordPress API requests.
+///
+/// This mixin provides methods to retrieve lists of resources using the WordPress API.
+/// It is generic over the type [T] of the response data and [R] which extends [IRequest].
base mixin ListOperation on IRequestInterface {
+ /// Retrieves a list of resources using the provided [request].
+ ///
+ /// This method sends a list request to the WordPress API and returns
+ /// a [WordpressResponse] containing a list of resources of type [T].
+ ///
+ /// [request] is an instance of [R] that contains the necessary parameters for the list operation,
+ /// such as pagination, filtering, or sorting options.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressResponse>].
Future>> list(
R request,
) async {
@@ -10,7 +22,15 @@ base mixin ListOperation on IRequestInterface {
return executor.list(wpRequest);
}
- /// Returns the raw response for the given [request].
+ /// Retrieves a list of resources and returns the raw response for the given [request].
+ ///
+ /// This method is similar to [list], but instead of parsing the response,
+ /// it returns the raw response from the WordPress API.
+ ///
+ /// [request] is an instance of [R] that contains the necessary parameters for the list operation.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressRawResponse].
+ /// This can be useful for debugging or when you need access to the full, unprocessed API response.
Future listRaw(
R request,
) async {
diff --git a/lib/src/operations/retrieve.dart b/lib/src/operations/retrieve.dart
index 589492b..70a0ecd 100644
--- a/lib/src/operations/retrieve.dart
+++ b/lib/src/operations/retrieve.dart
@@ -1,14 +1,28 @@
import '../../wordpress_client.dart';
-/// Represents the retrive operation.
+/// Represents the retrieve operation for WordPress API requests.
+///
+/// This mixin provides methods to retrieve data from a WordPress site
+/// using the WordPress REST API.
base mixin RetrieveOperation on IRequestInterface {
+ /// Retrieves data from the WordPress API and returns a typed response.
+ ///
+ /// [request] is the request object containing the necessary parameters.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressResponse] containing
+ /// the retrieved data of type [T].
Future> retrieve(R request) async {
final wpRequest = await request.build(baseUrl);
return executor.retrive(wpRequest);
}
- /// Returns the raw response for the given [request].
+ /// Retrieves raw data from the WordPress API.
+ ///
+ /// [request] is the request object containing the necessary parameters.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressRawResponse] containing
+ /// the raw response data from the API.
Future retrieveRaw(R request) async {
final wpRequest = await request.build(baseUrl);
diff --git a/lib/src/operations/update.dart b/lib/src/operations/update.dart
index a16afa9..ad8fb53 100644
--- a/lib/src/operations/update.dart
+++ b/lib/src/operations/update.dart
@@ -1,14 +1,29 @@
import '../../wordpress_client.dart';
-/// Represents the update operation.
+/// Represents the update operation for WordPress API requests.
+///
+/// This mixin provides methods to perform update operations on WordPress resources.
+/// It is designed to be used with classes that implement [IRequestInterface].
base mixin UpdateOperation on IRequestInterface {
+ /// Performs an update operation using the provided [request].
+ ///
+ /// [T] is the type of the expected response data.
+ /// [R] is the type of the request, which must extend [IRequest].
+ ///
+ /// Returns a [Future] that resolves to a [WordpressResponse] containing
+ /// the updated resource data.
Future> update(R request) async {
final wpRequest = await request.build(baseUrl);
return executor.update(wpRequest);
}
- /// Returns the raw response for the given [request].
+ /// Performs an update operation and returns the raw response for the given [request].
+ ///
+ /// This method is useful when you need access to the full, unprocessed API response.
+ ///
+ /// Returns a [Future] that resolves to a [WordpressRawResponse] containing
+ /// the raw API response data.
Future updateRaw(R request) async {
final wpRequest = await request.build(baseUrl);
diff --git a/lib/src/parallel_wordpress/exports.dart b/lib/src/parallel_wordpress/exports.dart
index f228d09..582b116 100644
--- a/lib/src/parallel_wordpress/exports.dart
+++ b/lib/src/parallel_wordpress/exports.dart
@@ -1,3 +1,4 @@
+export 'extensions/parallel_result_exts.dart';
export 'parallel_request.dart';
export 'parallel_result.dart';
export 'parallel_wordpress.dart';
diff --git a/lib/src/parallel_wordpress/extensions/parallel_result_exts.dart b/lib/src/parallel_wordpress/extensions/parallel_result_exts.dart
new file mode 100644
index 0000000..d526457
--- /dev/null
+++ b/lib/src/parallel_wordpress/extensions/parallel_result_exts.dart
@@ -0,0 +1,46 @@
+import '../parallel_result.dart';
+
+/// Extension methods for [Iterable>].
+extension ParallelResultExts on Iterable> {
+ /// Converts this iterable of [ParallelResult] into a [Stream].
+ ///
+ /// This method allows you to work with the results as a stream, which can be
+ /// useful for processing results asynchronously or applying stream operations.
+ ///
+ /// Example:
+ /// ```dart
+ /// final results = [ParallelResult(1), ParallelResult(2), ParallelResult(3)];
+ /// final stream = results.streamed();
+ /// await for (final result in stream) {
+ /// print(result.value);
+ /// }
+ /// ```
+ Stream> streamed() {
+ return Stream.fromIterable(this);
+ }
+}
+
+/// Extension methods for [Iterable>].
+extension ParallelIterableResultExts on Iterable> {
+ /// Converts this iterable of [ParallelIterableResult] into a [Stream].
+ ///
+ /// This method is particularly useful when working with results that contain
+ /// iterables, allowing you to process them as a stream.
+ ///
+ /// Example:
+ /// ```dart
+ /// final results = [
+ /// ParallelIterableResult(['a', 'b']),
+ /// ParallelIterableResult(['c', 'd']),
+ /// ];
+ /// final stream = results.streamed();
+ /// await for (final result in stream) {
+ /// for (final item in result.value) {
+ /// print(item);
+ /// }
+ /// }
+ /// ```
+ Stream> streamed() {
+ return Stream.fromIterable(this);
+ }
+}
diff --git a/lib/src/parallel_wordpress/parallel_result.dart b/lib/src/parallel_wordpress/parallel_result.dart
index dcce6a8..8d1df73 100644
--- a/lib/src/parallel_wordpress/parallel_result.dart
+++ b/lib/src/parallel_wordpress/parallel_result.dart
@@ -1,8 +1,8 @@
import 'package:meta/meta.dart';
@immutable
-final class ParallelResult {
- const ParallelResult({
+final class ParallelIterableResult {
+ const ParallelIterableResult({
required this.page,
required this.results,
});
@@ -16,7 +16,7 @@ final class ParallelResult {
final Iterable results;
@override
- bool operator ==(covariant ParallelResult other) {
+ bool operator ==(covariant ParallelIterableResult other) {
if (identical(this, other)) return true;
return other.page == page && other.results == results;
@@ -27,3 +27,29 @@ final class ParallelResult {
T operator [](int index) => results.elementAt(index);
}
+
+@immutable
+final class ParallelResult {
+ const ParallelResult({
+ required this.page,
+ required this.result,
+ });
+
+ /// The page number of the results.
+ ///
+ /// Note that, if this instance contains results from the `initial()` method, then the `page` will be `0`.
+ final int page;
+
+ /// The results of the request.
+ final T result;
+
+ @override
+ bool operator ==(covariant ParallelResult other) {
+ if (identical(this, other)) return true;
+
+ return other.page == page && other.result == result;
+ }
+
+ @override
+ int get hashCode => page.hashCode ^ result.hashCode;
+}
diff --git a/lib/src/parallel_wordpress/parallel_wordpress.dart b/lib/src/parallel_wordpress/parallel_wordpress.dart
index f64bb14..d75b4e9 100644
--- a/lib/src/parallel_wordpress/parallel_wordpress.dart
+++ b/lib/src/parallel_wordpress/parallel_wordpress.dart
@@ -5,8 +5,8 @@ import 'package:collection/collection.dart';
import '../library_exports.dart';
import 'parallel_raw_result.dart';
-/// `ParallelWordpress` is a class that uses a `WordpressClient` to fetch data from a WordPress site.
-/// It provides a method to fetch a list of items in parallel, which can significantly speed up the fetching process.
+/// `ParallelWordpress` is a class that uses a `WordpressClient` to perform parallel operations on a WordPress site.
+/// It provides methods to fetch, create, update, delete, and retrieve items in parallel, which can significantly speed up the process.
final class ParallelWordpress {
/// Constructs a `ParallelWordpress` instance.
///
@@ -15,22 +15,24 @@ final class ParallelWordpress {
required this.client,
});
+ /// The WordPress client used to make API requests.
final WordpressClient client;
/// Fetches a list of items of type `T` in parallel.
///
- /// The `requestBuilder` parameter is a function that builds a list of requests to be made.
- /// The `interface` parameter is a function that performs the list operation for each request.
- /// The `transformer` parameter is an optional function that transforms the results of each request.
- /// The `onException` parameter is an optional function that handles exceptions that occur during the processing of the requests.
- /// The `initial` parameter is an optional function that provides an initial list of items.
+ /// Parameters:
+ /// - `requestBuilder`: A function that builds a list of requests to be made.
+ /// - `interface`: A function that performs the list operation for each request.
+ /// - `transformer`: An optional function that transforms the results of each request.
+ /// - `onException`: An optional function that handles exceptions that occur during the processing of the requests.
+ /// - `initial`: An optional function that provides an initial list of items.
///
- /// Returns a `Future` that completes with a list of items of type `T`.
- Future>> list({
+ /// Returns a `Future` that completes with an `Iterable` of `ParallelIterableResult`.
+ Future>> list({
required RequestBuilder requestBuilder,
required ListOperation interface,
- ParallelResultTransformer? transformer,
- ParallelExceptionHandler? onException,
+ ParallelIterableResultTransformer? transformer,
+ ParallelIterableExceptionHandler? onException,
ParallelInitialItems? initial,
}) async {
final requests = await requestBuilder();
@@ -54,7 +56,7 @@ final class ParallelWordpress {
final successResponse = response.results.asSuccess();
- return ParallelResult(
+ return ParallelIterableResult(
page: response.page,
results: successResponse.data,
);
@@ -76,8 +78,265 @@ final class ParallelWordpress {
return sortedResults;
}
- final initialItems = ParallelResult(page: 0, results: await initial());
+ final initialItems =
+ ParallelIterableResult(page: 0, results: await initial());
return [initialItems, ...sortedResults];
}
+
+ /// Creates multiple items of type `T` in parallel.
+ ///
+ /// Parameters:
+ /// - `requestBuilder`: A function that builds a list of requests to be made.
+ /// - `interface`: A function that performs the create operation for each request.
+ /// - `transformer`: An optional function that transforms the results of each request.
+ /// - `onException`: An optional function that handles exceptions that occur during the processing of the requests.
+ /// - `initial`: An optional function that provides an initial item.
+ ///
+ /// Returns a `Future` that completes with an `Iterable` of `ParallelResult`.
+ Future>> create({
+ required RequestBuilder requestBuilder,
+ required CreateOperation interface,
+ ParallelResultTransformer? transformer,
+ ParallelExceptionHandler? onException,
+ ParallelInitialItem? initial,
+ }) async {
+ final requests = await requestBuilder();
+
+ final responses = await Future.wait(
+ requests.map((x) async {
+ return ParallelRawResult(
+ page: x.page,
+ results: await interface.create(x.request),
+ );
+ }),
+ eagerError: true,
+ );
+
+ final results = await responses.mapAsync(
+ (response) async => guardAsync(
+ function: () async {
+ if (transformer != null) {
+ return await transformer(response.results);
+ }
+
+ final successResponse = response.results.asSuccess();
+
+ return ParallelResult(
+ page: response.page,
+ result: successResponse.data,
+ );
+ },
+ onError: (error, stackTrace) async {
+ if (onException != null) {
+ return await onException(error);
+ }
+
+ throw ParallelProcessingException(error, stackTrace);
+ },
+ ),
+ );
+
+ final sortedResults =
+ results.sorted((a, b) => a.page.compareTo(b.page)).map((e) => e);
+
+ if (initial == null) {
+ return sortedResults;
+ }
+
+ final initialItem = ParallelResult(page: 0, result: await initial());
+ return [initialItem, ...sortedResults];
+ }
+
+ /// Updates multiple items of type `T` in parallel.
+ ///
+ /// Parameters:
+ /// - `requestBuilder`: A function that builds a list of requests to be made.
+ /// - `interface`: A function that performs the update operation for each request.
+ /// - `transformer`: An optional function that transforms the results of each request.
+ /// - `onException`: An optional function that handles exceptions that occur during the processing of the requests.
+ /// - `initial`: An optional function that provides an initial item.
+ ///
+ /// Returns a `Future` that completes with an `Iterable` of `ParallelResult`.
+ Future>> update({
+ required RequestBuilder requestBuilder,
+ required UpdateOperation interface,
+ ParallelResultTransformer? transformer,
+ ParallelExceptionHandler? onException,
+ ParallelInitialItem? initial,
+ }) async {
+ final requests = await requestBuilder();
+
+ final responses = await Future.wait(
+ requests.map((x) async {
+ return ParallelRawResult(
+ page: x.page,
+ results: await interface.update(x.request),
+ );
+ }),
+ eagerError: true,
+ );
+
+ final results = await responses.mapAsync(
+ (response) async => guardAsync(
+ function: () async {
+ if (transformer != null) {
+ return await transformer(response.results);
+ }
+
+ final successResponse = response.results.asSuccess();
+
+ return ParallelResult(
+ page: response.page,
+ result: successResponse.data,
+ );
+ },
+ onError: (error, stackTrace) async {
+ if (onException != null) {
+ return await onException(error);
+ }
+
+ throw ParallelProcessingException(error, stackTrace);
+ },
+ ),
+ );
+
+ final sortedResults =
+ results.sorted((a, b) => a.page.compareTo(b.page)).map((e) => e);
+
+ if (initial == null) {
+ return sortedResults;
+ }
+
+ final initialItem = ParallelResult(page: 0, result: await initial());
+ return [initialItem, ...sortedResults];
+ }
+
+ /// Deletes multiple items in parallel.
+ ///
+ /// Parameters:
+ /// - `requestBuilder`: A function that builds a list of requests to be made.
+ /// - `interface`: A function that performs the delete operation for each request.
+ /// - `transformer`: An optional function that transforms the results of each request.
+ /// - `onException`: An optional function that handles exceptions that occur during the processing of the requests.
+ /// - `initial`: An optional function that provides an initial boolean value.
+ ///
+ /// Returns a `Future` that completes with an `Iterable` of `ParallelResult`.
+ Future>> delete({
+ required RequestBuilder requestBuilder,
+ required DeleteOperation interface,
+ ParallelResultTransformer? transformer,
+ ParallelExceptionHandler? onException,
+ ParallelInitialItem? initial,
+ }) async {
+ final requests = await requestBuilder();
+
+ final responses = await Future.wait(
+ requests.map((x) async {
+ return ParallelRawResult(
+ page: x.page,
+ results: await interface.delete(x.request),
+ );
+ }),
+ eagerError: true,
+ );
+
+ final results = await responses.mapAsync(
+ (response) async => guardAsync(
+ function: () async {
+ if (transformer != null) {
+ return await transformer(response.results);
+ }
+
+ final successResponse = response.results.asSuccess();
+
+ return ParallelResult(
+ page: response.page,
+ result: successResponse.data,
+ );
+ },
+ onError: (error, stackTrace) async {
+ if (onException != null) {
+ return await onException(error);
+ }
+
+ throw ParallelProcessingException(error, stackTrace);
+ },
+ ),
+ );
+
+ final sortedResults =
+ results.sorted((a, b) => a.page.compareTo(b.page)).map((e) => e);
+
+ if (initial == null) {
+ return sortedResults;
+ }
+
+ final initialItem = ParallelResult(page: 0, result: await initial());
+ return [initialItem, ...sortedResults];
+ }
+
+ /// Retrieves multiple items of type `T` in parallel.
+ ///
+ /// Parameters:
+ /// - `requestBuilder`: A function that builds a list of requests to be made.
+ /// - `interface`: A function that performs the retrieve operation for each request.
+ /// - `transformer`: An optional function that transforms the results of each request.
+ /// - `onException`: An optional function that handles exceptions that occur during the processing of the requests.
+ /// - `initial`: An optional function that provides an initial item.
+ ///
+ /// Returns a `Future` that completes with an `Iterable` of `ParallelResult`.
+ Future>> retrieve({
+ required RequestBuilder requestBuilder,
+ required RetrieveOperation interface,
+ ParallelResultTransformer? transformer,
+ ParallelExceptionHandler? onException,
+ ParallelInitialItem? initial,
+ }) async {
+ final requests = await requestBuilder();
+
+ final responses = await Future.wait(
+ requests.map((x) async {
+ return ParallelRawResult(
+ page: x.page,
+ results: await interface.retrieve(x.request),
+ );
+ }),
+ eagerError: true,
+ );
+
+ final results = await responses.mapAsync(
+ (response) async => guardAsync(
+ function: () async {
+ if (transformer != null) {
+ return await transformer(response.results);
+ }
+
+ final successResponse = response.results.asSuccess();
+
+ return ParallelResult(
+ page: response.page,
+ result: successResponse.data,
+ );
+ },
+ onError: (error, stackTrace) async {
+ if (onException != null) {
+ return await onException(error);
+ }
+
+ throw ParallelProcessingException(error, stackTrace);
+ },
+ ),
+ );
+
+ final sortedResults =
+ results.sorted((a, b) => a.page.compareTo(b.page)).map((e) => e);
+
+ if (initial == null) {
+ return sortedResults;
+ }
+
+ final initialItem = ParallelResult(page: 0, result: await initial());
+ return [initialItem, ...sortedResults];
+ }
}
diff --git a/lib/src/parallel_wordpress/typedefs.dart b/lib/src/parallel_wordpress/typedefs.dart
index 8d33625..dd7bbdf 100644
--- a/lib/src/parallel_wordpress/typedefs.dart
+++ b/lib/src/parallel_wordpress/typedefs.dart
@@ -2,10 +2,20 @@ import 'dart:async';
import '../../wordpress_client.dart';
-typedef ParallelResultTransformer = FutureOr> Function(
+typedef ParallelIterableResultTransformer
+ = FutureOr> Function(
WordpressResponse> response,
);
+typedef ParallelResultTransformer = FutureOr> Function(
+ WordpressResponse response,
+);
+
+typedef ParallelIterableExceptionHandler
+ = FutureOr> Function(
+ Object error,
+);
+
typedef ParallelExceptionHandler = FutureOr> Function(
Object error,
);
@@ -13,3 +23,4 @@ typedef ParallelExceptionHandler = FutureOr> Function(
typedef RequestBuilder = FutureOr>> Function();
typedef ParallelInitialItems = FutureOr> Function();
+typedef ParallelInitialItem = FutureOr Function();
diff --git a/lib/src/request_executor_base.dart b/lib/src/request_executor_base.dart
index f7672b9..b16f057 100644
--- a/lib/src/request_executor_base.dart
+++ b/lib/src/request_executor_base.dart
@@ -6,13 +6,30 @@ import 'library_exports.dart';
import 'responses/wordpress_error.dart';
import 'utilities/codable_map/codable_map.dart';
+/// Base class for request executors in the WordPress API client.
+///
+/// This class provides the core functionality for executing requests,
+/// handling middleware, and processing responses.
abstract base class IRequestExecutor {
+ /// The base URL for the WordPress API.
Uri get baseUrl;
+ /// The list of middleware to be applied to requests and responses.
Iterable get middlewares;
+ /// Configures the executor with the given bootstrap configuration.
+ ///
+ /// This method should be called before making any requests.
+ ///
+ /// [configuration] The configuration to apply.
void configure(BootstrapConfiguration configuration);
+ /// Applies request middleware to the given request.
+ ///
+ /// [request] The original request.
+ /// [middlewares] The list of middleware to apply.
+ ///
+ /// Returns the modified request after applying all middleware.
Future _handleRequestMiddlewares({
required WordpressRequest request,
required Iterable middlewares,
@@ -23,6 +40,12 @@ abstract base class IRequestExecutor {
);
}
+ /// Applies response middleware to the given response.
+ ///
+ /// [middlewares] The list of middleware to apply.
+ /// [response] The original response.
+ ///
+ /// Returns the modified response after applying all middleware.
Future _handleResponseMiddlewares({
required Iterable middlewares,
required WordpressRawResponse response,
@@ -33,6 +56,13 @@ abstract base class IRequestExecutor {
);
}
+ /// Executes a raw request and returns the response.
+ ///
+ /// This method applies middleware, executes the request, and handles errors.
+ ///
+ /// [request] The request to execute.
+ ///
+ /// Returns a [WordpressRawResponse] containing the raw response data.
@internal
Future raw(WordpressRequest request) async {
return guardAsync(
@@ -68,6 +98,13 @@ abstract base class IRequestExecutor {
);
}
+ /// Creates a new resource using the given request.
+ ///
+ /// This method is used for POST requests to create new items in the WordPress API.
+ ///
+ /// [request] The request containing the data for the new resource.
+ ///
+ /// Returns a [WordpressResponse] with the created item of type [T].
@internal
Future> create(
WordpressRequest request,
@@ -140,6 +177,13 @@ abstract base class IRequestExecutor {
);
}
+ /// Retrieves a resource using the given request.
+ ///
+ /// This method is used for GET requests to fetch existing items from the WordPress API.
+ ///
+ /// [request] The request specifying the resource to retrieve.
+ ///
+ /// Returns a [WordpressResponse] with the retrieved item of type [T].
@internal
Future> retrive(
WordpressRequest request,
@@ -212,6 +256,13 @@ abstract base class IRequestExecutor {
);
}
+ /// Deletes a resource using the given request.
+ ///
+ /// This method is used for DELETE requests to remove items from the WordPress API.
+ ///
+ /// [request] The request specifying the resource to delete.
+ ///
+ /// Returns a [WordpressResponse] with a boolean indicating success or failure.
@internal
Future> delete(
WordpressRequest request,
@@ -281,6 +332,13 @@ abstract base class IRequestExecutor {
);
}
+ /// Retrieves a list of resources using the given request.
+ ///
+ /// This method is used for GET requests to fetch multiple items from the WordPress API.
+ ///
+ /// [request] The request specifying the resources to list.
+ ///
+ /// Returns a [WordpressResponse] with a list of items of type [T].
@internal
Future>> list(
WordpressRequest request,
@@ -355,6 +413,13 @@ abstract base class IRequestExecutor {
);
}
+ /// Updates an existing resource using the given request.
+ ///
+ /// This method is used for PUT or PATCH requests to modify existing items in the WordPress API.
+ ///
+ /// [request] The request containing the updated data for the resource.
+ ///
+ /// Returns a [WordpressResponse] with the updated item of type [T].
@internal
Future> update(
WordpressRequest request,
@@ -427,7 +492,13 @@ abstract base class IRequestExecutor {
);
}
- /// Executes the given [request] on the associated base url and returns the result in raw format.
+ /// Executes the given [request] on the associated base URL and returns the result in raw format.
+ ///
+ /// This method should be implemented by subclasses to perform the actual HTTP request.
+ ///
+ /// [request] The request to execute.
+ ///
+ /// Returns a [WordpressRawResponse] containing the raw response data.
@internal
Future execute(WordpressRequest request);
}
diff --git a/lib/src/requests/wordpress_request.dart b/lib/src/requests/wordpress_request.dart
index 2c688e4..10faf51 100644
--- a/lib/src/requests/wordpress_request.dart
+++ b/lib/src/requests/wordpress_request.dart
@@ -3,8 +3,15 @@ import 'package:dio/dio.dart';
import '../../wordpress_client.dart';
import '../constants.dart';
-/// Represents a request to the Wordpress REST API.
+/// Represents a request to the WordPress REST API.
+///
+/// This class encapsulates all the necessary information for making a request
+/// to the WordPress REST API, including the URL, method, headers, and other
+/// configuration options.
final class WordpressRequest {
+ /// Creates a new [WordpressRequest] instance.
+ ///
+ /// [url] and [method] are required parameters. All other parameters are optional.
const WordpressRequest({
required this.url,
required this.method,
@@ -20,51 +27,54 @@ final class WordpressRequest {
this.receiveTimeout = DEFAULT_REQUEST_TIMEOUT,
});
- /// The request url.
+ /// The URL for the request.
final RequestUrl url;
- /// The request method.
+ /// The HTTP method for the request (e.g., GET, POST, PUT, DELETE).
final HttpMethod method;
- /// The request headers.
+ /// Optional headers to be included with the request.
final Map? headers;
- /// The request query parameters.
+ /// Optional query parameters to be appended to the URL.
final Map? queryParameters;
- /// The request body.
+ /// The body of the request, typically used for POST or PUT requests.
final dynamic body;
- /// Specifies if this request requires authentication.
+ /// Indicates whether this request requires authentication.
final bool requireAuth;
- /// The cancel token.
+ /// A token that can be used to cancel the request.
final CancelToken? cancelToken;
- /// The authorization instance.
+ /// The authorization instance to be used for this request, if required.
final IAuthorization? authorization;
- /// The request send timeout.
+ /// The timeout duration for sending the request.
final Duration sendTimeout;
- /// The request receive timeout.
+ /// The timeout duration for receiving the response.
final Duration receiveTimeout;
- /// The events instance.
+ /// An optional [WordpressEvents] instance for handling request lifecycle events.
final WordpressEvents? events;
- /// The validator callback.
+ /// An optional callback function for custom validation of the response.
final ValidatorCallback? validator;
- /// Gets if this request has events.
+ /// Indicates whether this request has associated events.
bool get hasEvents => events != null;
- /// Gets if this request has a validator overload.
+ /// Indicates whether this request has a custom validator.
bool get hasValidator => validator != null;
- /// Gets if this request has a authorization module.
+ /// Indicates whether this request has an associated authorization module.
bool get hasAuthorization => authorization != null;
+ /// Creates a copy of this [WordpressRequest] with the given fields replaced with new values.
+ ///
+ /// This method is useful for modifying a request while keeping the original intact.
WordpressRequest copyWith({
RequestUrl? url,
HttpMethod? method,
diff --git a/lib/src/responses/application_password_response.dart b/lib/src/responses/application_password_response.dart
index 0376bc9..ab4da7d 100644
--- a/lib/src/responses/application_password_response.dart
+++ b/lib/src/responses/application_password_response.dart
@@ -3,8 +3,22 @@ import 'package:meta/meta.dart';
import '../library_exports.dart';
import '../utilities/self_representive_base.dart';
+/// Represents an application password in the WordPress system.
+///
+/// Application passwords allow authentication for non-interactive systems, such as mobile or desktop applications,
+/// without providing your actual password. Each application password has limited capabilities compared to your main account password.
@immutable
final class ApplicationPassword implements ISelfRespresentive {
+ /// Creates a new [ApplicationPassword] instance.
+ ///
+ /// - [uuid]: The unique identifier for this application password.
+ /// - [appId]: The ID of the application associated with this password.
+ /// - [name]: The name of the application password.
+ /// - [created]: The date and time when the password was created.
+ /// - [lastUsed]: The date and time when the password was last used.
+ /// - [lastIp]: The IP address from which the password was last used.
+ /// - [password]: The actual password string (only available when first created).
+ /// - [self]: The raw JSON representation of this object.
const ApplicationPassword({
required this.uuid,
required this.appId,
@@ -16,6 +30,9 @@ final class ApplicationPassword implements ISelfRespresentive {
this.password,
});
+ /// Creates an [ApplicationPassword] instance from a JSON map.
+ ///
+ /// [json] is the JSON map containing the application password data.
factory ApplicationPassword.fromJson(Map json) {
return ApplicationPassword(
uuid: castOrElse(json['uuid']),
@@ -29,17 +46,32 @@ final class ApplicationPassword implements ISelfRespresentive {
);
}
+ /// The unique identifier for this application password.
final String uuid;
+
+ /// The ID of the application associated with this password.
final String? appId;
+
+ /// The name of the application password.
final String name;
+
+ /// The date and time when the password was created.
final DateTime? created;
+
+ /// The date and time when the password was last used.
final DateTime? lastUsed;
+
+ /// The IP address from which the password was last used.
final String? lastIp;
+
+ /// The actual password string (only available when first created).
final String? password;
+ /// The raw JSON representation of this object.
@override
final Map self;
+ /// Converts this [ApplicationPassword] instance to a JSON map.
Map toJson() {
return {
'uuid': uuid,
@@ -51,4 +83,35 @@ final class ApplicationPassword implements ISelfRespresentive {
'password': password,
};
}
+
+ @override
+ bool operator ==(covariant ApplicationPassword other) {
+ if (identical(this, other)) {
+ return true;
+ }
+
+ return other.uuid == uuid &&
+ other.appId == appId &&
+ other.name == name &&
+ other.created == created &&
+ other.lastUsed == lastUsed &&
+ other.lastIp == lastIp &&
+ other.password == password;
+ }
+
+ @override
+ int get hashCode {
+ return uuid.hashCode ^
+ appId.hashCode ^
+ name.hashCode ^
+ created.hashCode ^
+ lastUsed.hashCode ^
+ lastIp.hashCode ^
+ password.hashCode;
+ }
+
+ @override
+ String toString() {
+ return 'ApplicationPassword(uuid: $uuid, appId: $appId, name: $name, created: $created, lastUsed: $lastUsed, lastIp: $lastIp, password: $password)';
+ }
}
diff --git a/lib/src/responses/category_response.dart b/lib/src/responses/category_response.dart
index 8df2164..6a960e5 100644
--- a/lib/src/responses/category_response.dart
+++ b/lib/src/responses/category_response.dart
@@ -4,8 +4,23 @@ import 'package:meta/meta.dart';
import '../utilities/helpers.dart';
import '../utilities/self_representive_base.dart';
+/// Represents a category in WordPress.
+///
+/// Categories are a taxonomy in WordPress used to group and organize content.
+/// They can be hierarchical, allowing for parent-child relationships between categories.
@immutable
class Category implements ISelfRespresentive {
+ /// Creates a new [Category] instance.
+ ///
+ /// - [id]: Unique identifier for the category.
+ /// - [count]: Number of posts in the category.
+ /// - [description]: HTML description of the category.
+ /// - [link]: URL to the category's archive page.
+ /// - [slug]: An alphanumeric identifier for the category unique to its type.
+ /// - [taxonomy]: Type of taxonomy. For categories, this is always "category".
+ /// - [parent]: The parent category ID, if any.
+ /// - [self]: The raw JSON representation of this object.
+ /// - [name]: HTML title for the category.
const Category({
required this.id,
required this.count,
@@ -18,6 +33,9 @@ class Category implements ISelfRespresentive {
this.name,
});
+ /// Creates a [Category] instance from a JSON map.
+ ///
+ /// [json] is the JSON map containing the category data.
factory Category.fromJson(dynamic json) {
return Category(
id: castOrElse(json['id']),
@@ -32,18 +50,35 @@ class Category implements ISelfRespresentive {
);
}
+ /// Unique identifier for the category.
final int id;
+
+ /// Number of posts in the category.
final int count;
+
+ /// HTML description of the category.
final String? description;
+
+ /// URL to the category's archive page.
final String? link;
+
+ /// HTML title for the category.
final String? name;
+
+ /// An alphanumeric identifier for the category unique to its type.
final String slug;
+
+ /// Type of taxonomy. For categories, this is always "category".
final String? taxonomy;
+
+ /// The parent category ID, if any.
final int? parent;
+ /// The raw JSON representation of this object.
@override
final Map self;
+ /// Converts this [Category] instance to a JSON map.
Map toJson() {
return {
'id': id,
diff --git a/lib/src/responses/comment_response.dart b/lib/src/responses/comment_response.dart
index 3774c5d..eae9a7c 100644
--- a/lib/src/responses/comment_response.dart
+++ b/lib/src/responses/comment_response.dart
@@ -7,8 +7,15 @@ import '../utilities/self_representive_base.dart';
import 'properties/avatar_urls.dart';
import 'properties/content.dart';
+/// Represents a comment in the WordPress system.
+///
+/// This class encapsulates all the properties of a comment as defined in the WordPress REST API.
+/// For more details, see: https://developer.wordpress.org/rest-api/reference/comments/
@immutable
final class Comment implements ISelfRespresentive {
+ /// Creates a new [Comment] instance.
+ ///
+ /// All parameters correspond to the properties of a comment as defined in the WordPress REST API.
const Comment({
required this.self,
required this.status,
@@ -29,6 +36,9 @@ final class Comment implements ISelfRespresentive {
this.authorAvatarUrls,
});
+ /// Creates a [Comment] instance from a JSON map.
+ ///
+ /// [json] is the JSON map containing the comment data.
factory Comment.fromJson(Map json) {
return Comment(
id: castOrElse(json['id']),
@@ -59,26 +69,59 @@ final class Comment implements ISelfRespresentive {
);
}
+ /// Unique identifier for the comment.
final int id;
+
+ /// The ID of the associated post.
final int? post;
+
+ /// The ID of the parent comment (if this is a reply).
final int? parent;
+
+ /// The ID of the user who authored the comment (if they were logged in).
final int? author;
+
+ /// Display name of the comment author.
final String? authorName;
+
+ /// Email address of the comment author.
final String? authorEmail;
+
+ /// URL provided by the comment author.
final String authorUrl;
+
+ /// IP address of the comment author.
final String? authorIp;
+
+ /// User agent of the browser used to submit the comment.
final String? authorUserAgent;
+
+ /// The date the comment was published, in the site's timezone.
final DateTime? date;
+
+ /// The date the comment was published, as GMT.
final DateTime? dateGmt;
+
+ /// The content of the comment.
final Content? content;
+
+ /// URL to the comment.
final String link;
+
+ /// Status of the comment (approved, pending, spam).
final CommentStatus status;
+
+ /// Type of the comment (typically 'comment').
final String type;
+
+ /// Avatar URLs for the comment author.
final AvatarUrls? authorAvatarUrls;
+ /// The raw JSON representation of this object.
@override
final Map self;
+ /// Converts this [Comment] instance to a JSON map.
Map toJson() {
return {
'id': id,
diff --git a/lib/src/responses/media_response.dart b/lib/src/responses/media_response.dart
index 5e14e8d..19a42eb 100644
--- a/lib/src/responses/media_response.dart
+++ b/lib/src/responses/media_response.dart
@@ -7,8 +7,17 @@ import '../utilities/self_representive_base.dart';
import 'properties/content.dart';
import 'properties/media_details.dart';
+/// Represents a media item in the WordPress system.
+///
+/// This class encapsulates all the properties of a media item as defined in the WordPress REST API.
+/// Media items are attachments, such as images, documents, or videos.
+///
+/// For more details, see: https://developer.wordpress.org/rest-api/reference/media/
@immutable
final class Media implements ISelfRespresentive {
+ /// Creates a new [Media] instance.
+ ///
+ /// All parameters correspond to the properties of a media item as defined in the WordPress REST API.
const Media({
required this.self,
required this.id,
@@ -37,6 +46,9 @@ final class Media implements ISelfRespresentive {
this.sourceUrl,
});
+ /// Creates a [Media] instance from a JSON map.
+ ///
+ /// [json] is the JSON map containing the media item data.
factory Media.fromJson(Map json) {
return Media(
id: castOrElse(json['id']),
@@ -84,34 +96,83 @@ final class Media implements ISelfRespresentive {
);
}
+ /// Unique identifier for the media item.
final int id;
+
+ /// The date the media item was created, in the site's timezone.
final DateTime? date;
+
+ /// The date the media item was created, as GMT.
final DateTime? dateGmt;
+
+ /// The globally unique identifier for the media item.
final Content? guid;
+
+ /// The date the media item was last modified, in the site's timezone.
final DateTime? modified;
+
+ /// The date the media item was last modified, as GMT.
final DateTime? modifiedGmt;
+
+ /// An alphanumeric identifier for the media item unique to its type.
final String? slug;
+
+ /// The current status of the media item.
final MediaFilterStatus? status;
+
+ /// Type of media item.
final String? type;
+
+ /// URL to the media item.
final String? link;
+
+ /// The title of the media item.
final Content? title;
+
+ /// The ID of the user who uploaded the media item.
final int? author;
+
+ /// Whether or not comments are open on the media item.
final Status? commentStatus;
+
+ /// Whether or not the media item can be pinged.
final Status? pingStatus;
+
+ /// The theme file to use to display the media item.
final String? template;
+
+ /// Meta fields associated with the media item.
final dynamic meta;
+
+ /// The description of the media item.
final Content? description;
+
+ /// The caption for the media item.
final Content? caption;
+
+ /// The alternative text for the media item.
final String? altText;
+
+ /// The media type of the media item.
final String? mediaType;
+
+ /// The MIME type of the media item.
final String? mimeType;
+
+ /// Details about the media file, specific to its type.
final MediaDetails? mediaDetails;
+
+ /// The ID of the associated post of the media item.
final int? post;
+
+ /// The source URL of the media item.
final String? sourceUrl;
+ /// The raw JSON representation of this object.
@override
final Map self;
+ /// Converts this [Media] instance to a JSON map.
Map toJson() {
return {
'id': id,
diff --git a/lib/src/responses/page_response.dart b/lib/src/responses/page_response.dart
index 5a1e615..b52b3cc 100644
--- a/lib/src/responses/page_response.dart
+++ b/lib/src/responses/page_response.dart
@@ -5,8 +5,19 @@ import '../../wordpress_client.dart';
import '../utilities/self_representive_base.dart';
import 'properties/content.dart';
+/// Represents a WordPress page.
+///
+/// This class encapsulates all the properties of a WordPress page as defined in the
+/// WordPress REST API. It provides methods to create, manipulate, and serialize page data.
+///
+/// For more information, see the WordPress REST API handbook:
+/// https://developer.wordpress.org/rest-api/reference/pages/
@immutable
final class Page implements ISelfRespresentive {
+ /// Creates a new [Page] instance.
+ ///
+ /// All parameters correspond to the properties of a WordPress page as defined
+ /// in the REST API.
const Page({
required this.id,
required this.slug,
@@ -30,6 +41,10 @@ final class Page implements ISelfRespresentive {
this.featuredMedia,
});
+ /// Creates a [Page] instance from a JSON map.
+ ///
+ /// This factory constructor is used to deserialize page data received from
+ /// the WordPress REST API.
factory Page.fromJson(Map json) {
return Page(
id: castOrElse(json['id']),
@@ -64,29 +79,68 @@ final class Page implements ISelfRespresentive {
);
}
+ /// The unique identifier for the page.
final int id;
+
+ /// The date the page was published, in the site's timezone.
final DateTime? date;
+
+ /// The date the page was published, as GMT.
final DateTime? dateGmt;
+
+ /// The globally unique identifier for the page.
final Content? guid;
+
+ /// The date the page was last modified, in the site's timezone.
final DateTime? modified;
+
+ /// The date the page was last modified, as GMT.
final DateTime? modifiedGmt;
+
+ /// An alphanumeric identifier for the page unique to its type.
final String slug;
+
+ /// A named status for the page.
final ContentStatus status;
+
+ /// Type of Post for the page.
final String? type;
+
+ /// URL to the page.
final String? link;
+
+ /// The title of the page.
final Content? title;
+
+ /// The content of the page.
final Content? content;
+
+ /// The ID of the user who published the page.
final int author;
+
+ /// The ID of the featured media for the page.
final int? featuredMedia;
+
+ /// Whether or not comments are open on the page.
final Status commentStatus;
+
+ /// Whether or not the page can be pinged.
final Status pingStatus;
+
+ /// The theme file to use to display the page.
final String template;
+
+ /// The ID of the parent page.
final int parent;
+
+ /// The order of the page in relation to other pages.
final int menuOrder;
+ /// The raw data of the page as received from the API.
@override
final Map self;
+ /// Creates a copy of this [Page] but with the given fields replaced with the new values.
Page copyWith({
int? id,
DateTime? date,
@@ -133,6 +187,9 @@ final class Page implements ISelfRespresentive {
);
}
+ /// Converts this [Page] instance to a JSON map.
+ ///
+ /// This method is used to serialize the page data for sending to the WordPress REST API.
Map toJson() {
return {
'id': id,
diff --git a/lib/src/responses/post_response.dart b/lib/src/responses/post_response.dart
index f3519c6..a97f576 100644
--- a/lib/src/responses/post_response.dart
+++ b/lib/src/responses/post_response.dart
@@ -7,8 +7,14 @@ import '../utilities/self_representive_base.dart';
import 'properties/author_meta.dart';
import 'properties/content.dart';
+/// Represents a WordPress post.
+///
+/// This class encapsulates all the properties of a post as defined in the
+/// WordPress REST API. For more details, see:
+/// https://developer.wordpress.org/rest-api/reference/posts/
@immutable
class Post implements ISelfRespresentive {
+ /// Creates a new [Post] instance.
const Post({
required this.id,
required this.slug,
@@ -38,6 +44,7 @@ class Post implements ISelfRespresentive {
this.authorMeta,
});
+ /// Creates a [Post] instance from a JSON map.
factory Post.fromJson(Map json) {
return Post(
id: castOrElse(json['id']),
@@ -92,28 +99,73 @@ class Post implements ISelfRespresentive {
);
}
+ /// The unique identifier for the post.
final int id;
+
+ /// The date the post was published, in the site's timezone.
final DateTime? date;
+
+ /// The date the post was published, as GMT.
final DateTime? dateGmt;
+
+ /// The globally unique identifier for the post.
final Content? guid;
+
+ /// A password to protect access to the content and excerpt.
final String? password;
+
+ /// The date the post was last modified, in the site's timezone.
final DateTime? modified;
+
+ /// The date the post was last modified, as GMT.
final DateTime? modifiedGmt;
+
+ /// An alphanumeric identifier for the post unique to its type.
final String slug;
+
+ /// A named status for the post.
final ContentStatus status;
+
+ /// Type of post.
final String? type;
+
+ /// URL to the post.
final String link;
+
+ /// The title for the post.
final Content? title;
+
+ /// The content for the post.
final Content? content;
+
+ /// The excerpt for the post.
final Content? excerpt;
+
+ /// The ID for the author of the post.
final int author;
+
+ /// The ID of the featured media for the post.
final int? featuredMedia;
+
+ /// Whether or not comments are open on the post.
final Status commentStatus;
+
+ /// Whether or not the post can accept pings.
final Status pingStatus;
+
+ /// Whether or not the post should be treated as sticky.
final bool sticky;
+
+ /// The theme file to use to display the post.
final String? template;
+
+ /// The format for the post.
final PostFormat format;
+
+ /// The terms assigned to the post in the category taxonomy.
final List? categories;
+
+ /// The terms assigned to the post in the tag taxonomy.
final List? tags;
/// Field generated by https://wordpress.org/plugins/rest-api-featured-image/ plugin
@@ -125,6 +177,7 @@ class Post implements ISelfRespresentive {
@override
final Map self;
+ /// Converts the [Post] instance to a JSON map.
Map toJson() {
return {
'id': id,
diff --git a/lib/src/responses/properties/author_meta.dart b/lib/src/responses/properties/author_meta.dart
index 5fd5877..4c688a8 100644
--- a/lib/src/responses/properties/author_meta.dart
+++ b/lib/src/responses/properties/author_meta.dart
@@ -2,8 +2,20 @@ import 'package:meta/meta.dart';
import '../../utilities/helpers.dart';
+/// Represents metadata for an author in the WordPress REST API.
+///
+/// This class encapsulates various properties related to an author. It provides a structured way to handle
+/// author metadata in Dart applications interfacing with WordPress.
+///
+/// This class is only used if you have the additional plugin integrated in your wordpress site.
+///
+/// Reference: https://developer.wordpress.org/rest-api/reference/users/
@immutable
final class AuthorMeta {
+ /// Creates an instance of [AuthorMeta].
+ ///
+ /// [id] is required and represents the unique identifier for the author.
+ /// Other parameters are optional and correspond to various author attributes.
const AuthorMeta({
required this.id,
this.userNicename,
@@ -11,6 +23,12 @@ final class AuthorMeta {
this.displayName,
});
+ /// Creates an [AuthorMeta] instance from a JSON map.
+ ///
+ /// This factory constructor is used to deserialize author metadata from
+ /// the WordPress REST API response.
+ ///
+ /// [json] is a map containing the author metadata as returned by the API.
factory AuthorMeta.fromJson(Map json) {
return AuthorMeta(
id: castOrElse(json['ID']),
@@ -20,11 +38,33 @@ final class AuthorMeta {
);
}
+ /// The unique identifier for the author.
+ ///
+ /// This corresponds to the 'ID' field in the WordPress REST API.
final String id;
+
+ /// The 'nice name' of the author, used for URLs and slugs.
+ ///
+ /// This corresponds to the 'user_nicename' field in the WordPress REST API.
+ /// It's typically a URL-friendly version of the user's name or username.
final String? userNicename;
+
+ /// The date and time when the author's account was registered.
+ ///
+ /// This corresponds to the 'user_registered' field in the WordPress REST API.
+ /// It represents the date and time when the user account was created.
final DateTime? userRegistered;
+
+ /// The display name of the author.
+ ///
+ /// This corresponds to the 'display_name' field in the WordPress REST API.
+ /// It's the name chosen by the user to be displayed publicly on the site.
final String? displayName;
+ /// Converts the [AuthorMeta] instance to a JSON map.
+ ///
+ /// This method is used for serializing the author metadata, which can be
+ /// useful when sending data back to the WordPress REST API or storing it.
Map toJson() {
return {
'ID': id,
diff --git a/lib/src/responses/properties/avatar_urls.dart b/lib/src/responses/properties/avatar_urls.dart
index 231064a..d8478ae 100644
--- a/lib/src/responses/properties/avatar_urls.dart
+++ b/lib/src/responses/properties/avatar_urls.dart
@@ -2,14 +2,36 @@ import 'package:meta/meta.dart';
import '../../library_exports.dart';
+/// Represents the avatar URLs for a user in the WordPress REST API.
+///
+/// This class encapsulates the URLs for different sizes of a user's avatar image.
+/// The WordPress REST API provides avatar URLs in three standard sizes: 24x24, 48x48, and 96x96 pixels.
+///
+/// According to the WordPress REST API Handbook:
+/// - Avatar URLs are typically generated using the Gravatar service.
+/// - The size of the avatar can be adjusted by modifying the URL parameters.
+/// - If no avatar is available, a default image or generated avatar may be provided.
+///
+/// For more information, see:
+/// https://developer.wordpress.org/rest-api/reference/users/#schema-avatar_urls
@immutable
final class AvatarUrls {
+ /// Creates a new [AvatarUrls] instance.
+ ///
+ /// All parameters are optional and can be null if not provided.
+ /// - [small24]: URL for the 24x24 pixel avatar image.
+ /// - [medium48]: URL for the 48x48 pixel avatar image.
+ /// - [large96]: URL for the 96x96 pixel avatar image.
const AvatarUrls({
this.small24,
this.medium48,
this.large96,
});
+ /// Creates an [AvatarUrls] instance from a JSON map.
+ ///
+ /// This factory constructor is used to deserialize JSON data returned by the WordPress REST API.
+ /// It uses the [castOrElse] utility function to safely cast values or provide defaults.
factory AvatarUrls.fromJson(Map json) {
return AvatarUrls(
small24: castOrElse(json['24']),
@@ -18,10 +40,18 @@ final class AvatarUrls {
);
}
+ /// The URL for the 24x24 pixel avatar image.
final String? small24;
+
+ /// The URL for the 48x48 pixel avatar image.
final String? medium48;
+
+ /// The URL for the 96x96 pixel avatar image.
final String? large96;
+ /// Converts this [AvatarUrls] instance to a JSON-compatible map.
+ ///
+ /// This method is useful for serializing the object, for example, when sending data to the API.
Map toJson() {
return {
'24': small24,
@@ -30,6 +60,10 @@ final class AvatarUrls {
};
}
+ /// Creates a copy of this [AvatarUrls] instance with the given fields replaced with new values.
+ ///
+ /// This method is useful for updating specific fields of an [AvatarUrls] instance
+ /// while keeping the other fields unchanged.
AvatarUrls copyWith({
String? small24,
String? medium48,
diff --git a/lib/src/responses/properties/content.dart b/lib/src/responses/properties/content.dart
index 33bced8..0f4aa5c 100644
--- a/lib/src/responses/properties/content.dart
+++ b/lib/src/responses/properties/content.dart
@@ -2,13 +2,32 @@ import 'package:meta/meta.dart';
import '../../utilities/helpers.dart';
+/// Represents the content of a WordPress post or page.
+///
+/// This class encapsulates the content data as returned by the WordPress REST API.
+/// It includes both the rendered content and a flag indicating whether the content is protected.
+///
+/// According to the WordPress REST API Handbook:
+/// - The 'rendered' field contains the HTML content of the post, transformed for display.
+/// - The 'protected' field indicates whether the content is password-protected.
+///
+/// For more information, see:
+/// https://developer.wordpress.org/rest-api/reference/posts/#schema-content
@immutable
final class Content {
+ /// Creates a new [Content] instance.
+ ///
+ /// [protected] is required and indicates whether the content is password-protected.
+ /// [rendered] is optional and contains the HTML content of the post.
const Content({
required this.protected,
this.rendered,
});
+ /// Creates a [Content] instance from a JSON map.
+ ///
+ /// This factory constructor is used to deserialize JSON data returned by the WordPress REST API.
+ /// It handles potential null values and type casting for the 'rendered' and 'protected' fields.
factory Content.fromJson(Map json) {
return Content(
rendered: castOrElse(json['rendered']),
@@ -16,11 +35,26 @@ final class Content {
);
}
+ /// The HTML content of the post, transformed for display.
+ ///
+ /// This field may be null if the content is not available or not requested.
final String? rendered;
+
+ /// Indicates whether the content is password-protected.
+ ///
+ /// If true, the full content may not be available without authentication.
final bool protected;
+ /// Returns the parsed text content, stripped of HTML tags.
+ ///
+ /// This getter uses the [parseHtmlString] utility function to convert the rendered HTML
+ /// to plain text. Note that this will throw an error if [rendered] is null.
String get parsedText => parseHtmlString(rendered!);
+ /// Converts the [Content] instance to a JSON map.
+ ///
+ /// This method is useful for serializing the content data, potentially for sending
+ /// updates back to the WordPress REST API.
Map toJson() {
return {
'rendered': rendered,
diff --git a/lib/src/responses/properties/extra_capabilities.dart b/lib/src/responses/properties/extra_capabilities.dart
index d52d40d..458cc6d 100644
--- a/lib/src/responses/properties/extra_capabilities.dart
+++ b/lib/src/responses/properties/extra_capabilities.dart
@@ -2,20 +2,47 @@ import 'package:meta/meta.dart';
import '../../utilities/helpers.dart';
+/// Represents the extra capabilities of a user in the WordPress REST API.
+///
+/// This class encapsulates additional capabilities that a user might have,
+/// beyond the standard WordPress roles. In the current implementation,
+/// it focuses on the 'administrator' capability, but it can be extended
+/// to include other custom capabilities as needed.
+///
+/// According to the WordPress REST API Handbook:
+/// - Extra capabilities are specific to the site's configuration and plugins.
+/// - They are typically used to grant or restrict access to certain features or content.
+/// - The 'administrator' capability is one of the most powerful, granting full access to all areas of the WordPress admin.
+///
+/// For more information, see:
+/// https://developer.wordpress.org/rest-api/reference/users/#schema-extra_capabilities
@immutable
final class ExtraCapabilities {
+ /// Creates a new [ExtraCapabilities] instance.
+ ///
+ /// [administrator] is a required parameter indicating whether the user has administrator capabilities.
const ExtraCapabilities({
required this.administrator,
});
+ /// Creates an [ExtraCapabilities] instance from a JSON map.
+ ///
+ /// This factory constructor is used to deserialize JSON data returned by the WordPress REST API.
+ /// It uses the [castOrElse] utility function to safely cast the 'administrator' value or provide a default.
factory ExtraCapabilities.fromJson(Map json) {
return ExtraCapabilities(
administrator: castOrElse(json['administrator'], orElse: () => false)!,
);
}
+ /// Indicates whether the user has administrator capabilities.
+ ///
+ /// If true, the user has full access to all areas of the WordPress admin.
final bool administrator;
+ /// Converts this [ExtraCapabilities] instance to a JSON-compatible map.
+ ///
+ /// This method is useful for serializing the object, for example, when sending data to the API.
Map