From 93cba97671e2f54fbdb23df521a85e80a6540475 Mon Sep 17 00:00:00 2001 From: Shekhar Wagh Date: Thu, 13 Mar 2025 01:12:04 +0530 Subject: [PATCH 1/4] add data source connection and query config base classes - Introduced `DataSourceConnection` and `DataSourceQuery` abstract classes for managing data source configurations and queries. - Created corresponding interfaces: `DataSourceConnectionInterface` and `DataSourceQueryInterface`. - Added schema generation methods in `ConfigSchemas` for validating connection and query configurations. This lays the groundwork for splitting the config from single data source object to connection and query part. --- .../DataSource/DataSourceConnection.php | 108 ++++++++++++++++++ .../DataSourceConnectionInterface.php | 18 +++ inc/Config/DataSource/DataSourceQuery.php | 106 +++++++++++++++++ .../DataSource/DataSourceQueryInterface.php | 19 +++ inc/Validation/ConfigSchemas.php | 41 +++++++ 5 files changed, 292 insertions(+) create mode 100644 inc/Config/DataSource/DataSourceConnection.php create mode 100644 inc/Config/DataSource/DataSourceConnectionInterface.php create mode 100644 inc/Config/DataSource/DataSourceQuery.php create mode 100644 inc/Config/DataSource/DataSourceQueryInterface.php diff --git a/inc/Config/DataSource/DataSourceConnection.php b/inc/Config/DataSource/DataSourceConnection.php new file mode 100644 index 00000000..e8ccd9a3 --- /dev/null +++ b/inc/Config/DataSource/DataSourceConnection.php @@ -0,0 +1,108 @@ +validate( $service_config ); + + if ( is_wp_error( $validated ) ) { + return $validated; + } + + return [ + 'service' => static::get_service_name(), + 'uuid' => $config['uuid'] ?? null, + 'service_config' => $service_config, + 'queries' => $config['queries'] ?? [], + ]; + } + + /** + * @inheritDoc + */ + public static function migrate_config( array $config ): array|WP_Error { + return $config; + } + + /** + * @inheritDoc + */ + public function to_array(): array { + return array_merge( + $this->config, + [ + self::CLASS_REF_ATTRIBUTE => static::class, + 'service' => static::get_service_name(), + ] + ); + } + + public function get_service_config(): array { + return $this->config['service_config']; + } + + /** + * Get the schema for validating the data source configuration. + * + * @return array The schema for validating the data source configuration. + */ + public static function get_config_schema(): array { + return ConfigSchemas::get_data_source_connection_config_schema(); + } + + /** + * Get the schema for validating the service-specific configuration. + * + * @return array The schema for validating the service-specific configuration. + */ + abstract public static function get_service_config_schema(): array; + + /** + * Get the UUID of the connection. + * + * @return string The UUID. + */ + public function get_uuid(): string { + return $this->config['uuid']; + } + + /** + * Get the display name of the data source. + * + * @return string The display name. + */ + public function get_display_name(): string { + return $this->config['service_config']['display_name']; + } + + /** + * Get the service name. + * + * @return string The service name. + */ + abstract public function get_service_name(): string; + + /** + * Get the queries for the data source. + * + * @return array The queries. + */ + abstract public function get_queries(): array; +} diff --git a/inc/Config/DataSource/DataSourceConnectionInterface.php b/inc/Config/DataSource/DataSourceConnectionInterface.php new file mode 100644 index 00000000..76dbc763 --- /dev/null +++ b/inc/Config/DataSource/DataSourceConnectionInterface.php @@ -0,0 +1,18 @@ +validate( $service_config ); + + if ( is_wp_error( $validated ) ) { + return $validated; + } + + return [ + 'service' => static::get_service_name(), + 'uuid' => $config['uuid'] ?? null, + 'connection_uuid' => $config['connection_uuid'] ?? null, + 'service_config' => $service_config, + ]; + } + + /** + * @inheritDoc + */ + public static function migrate_config( array $config ): array|WP_Error { + return $config; + } + + /** + * @inheritDoc + */ + public function to_array(): array { + return array_merge( + $this->config, + [ + self::CLASS_REF_ATTRIBUTE => static::class, + 'service' => static::get_service_name(), + ] + ); + } + + /** + * Get the schema for validating the query configuration. + * + * @return array The schema for validating the query configuration. + */ + public static function get_config_schema(): array { + return ConfigSchemas::get_data_source_query_config_schema(); + } + + /** + * Get the schema for validating the service-specific configuration. + * + * @return array The schema for validating the service-specific configuration. + */ + abstract public static function get_service_config_schema(): array; + + /** + * Get the UUID of the query. + * + * @return string The UUID. + */ + public function get_uuid(): string { + return $this->config['uuid']; + } + + /** + * Get the UUID of the associated data source connection. + * + * @return string|null The connection UUID. + */ + public function get_connection_uuid(): string|null { + return $this->config['connection_uuid']; + } + + /** + * Get the display name of the query. + * + * @return string The display name. + */ + public function get_display_name(): string { + return $this->config['service_config']['display_name']; + } + + /** + * Get the service name. + * + * @return string The service name. + */ + abstract public function get_service_name(): string; +} \ No newline at end of file diff --git a/inc/Config/DataSource/DataSourceQueryInterface.php b/inc/Config/DataSource/DataSourceQueryInterface.php new file mode 100644 index 00000000..bf3f2cb8 --- /dev/null +++ b/inc/Config/DataSource/DataSourceQueryInterface.php @@ -0,0 +1,19 @@ + Types::nullable( Types::string() ), @@ -401,4 +421,25 @@ private static function generate_remote_data_block_attribute_config_schema(): ar ), ] ); } + + private static function generate_data_source_connection_config_schema(): array { + return Types::object( [ + 'uuid' => Types::nullable( Types::uuid() ), + 'service' => Types::string(), + 'service_config' => Types::record( Types::string(), Types::any() ), + 'queries' => Types::list_of( Types::record( Types::string(), Types::any() ) ), + '__metadata' => Types::nullable( Types::record( Types::string(), Types::any() ) ), + ] ); + } + + private static function generate_data_source_query_config_schema(): array { + return Types::object( [ + 'uuid' => Types::nullable( Types::uuid() ), + 'service' => Types::string(), + 'service_config' => Types::record( Types::string(), Types::any() ), + 'connection_uuid' => Types::nullable( Types::uuid() ), + 'connection' => Types::nullable( Types::record( Types::string(), Types::any() ) ), + '__metadata' => Types::nullable( Types::record( Types::string(), Types::any() ) ), + ] ); + } } From b68b82e3eb218163bce22e06957dc19818b044b9 Mon Sep 17 00:00:00 2001 From: Shekhar Wagh Date: Thu, 13 Mar 2025 01:13:04 +0530 Subject: [PATCH 2/4] Add CRUD classes for data source connections and queries - Introduced `DataSourceConnectionCrud` and `DataSourceQueryCrud` classes for managing data source connections and queries stored in WordPress options. - Created an abstract base class `WpOptionsConfigStore` to provide common CRUD operations for configurations. - Added constant class maps for data source connection and query configurations. --- inc/Integrations/constants.php | 3 + inc/WpdbStorage/DataSourceConnectionCrud.php | 28 +++ inc/WpdbStorage/DataSourceQueryCrud.php | 28 +++ inc/WpdbStorage/WpOptionsConfigStore.php | 240 +++++++++++++++++++ 4 files changed, 299 insertions(+) create mode 100644 inc/WpdbStorage/DataSourceConnectionCrud.php create mode 100644 inc/WpdbStorage/DataSourceQueryCrud.php create mode 100644 inc/WpdbStorage/WpOptionsConfigStore.php diff --git a/inc/Integrations/constants.php b/inc/Integrations/constants.php index 2424e9ae..e51969a4 100644 --- a/inc/Integrations/constants.php +++ b/inc/Integrations/constants.php @@ -28,3 +28,6 @@ REMOTE_DATA_BLOCKS_SHOPIFY_SERVICE => \RemoteDataBlocks\Integrations\Shopify\ShopifyDataSource::class, REMOTE_DATA_BLOCKS_MOCK_SERVICE => \RemoteDataBlocks\Tests\Mocks\MockDataSource::class, ]; + +const REMOTE_DATA_BLOCKS__DATA_SOURCE_CONNECTION_CONFIG_CLASSMAP = []; +const REMOTE_DATA_BLOCKS__DATA_SOURCE_QUERY_CONFIG_CLASSMAP = []; diff --git a/inc/WpdbStorage/DataSourceConnectionCrud.php b/inc/WpdbStorage/DataSourceConnectionCrud.php new file mode 100644 index 00000000..4c5fafcd --- /dev/null +++ b/inc/WpdbStorage/DataSourceConnectionCrud.php @@ -0,0 +1,28 @@ + 404 ] + ); + } + + /** + * Get configs by service name. + * + * @param string $service_name The service name. + * @return array Array of configs for the specified service. + */ + public static function get_by_service( string $service_name ): array { + return array_values( array_filter( + static::get_all(), + function ( $config ) use ( $service_name ): bool { + return $config['service'] === $service_name; + } + ) ); + } + + /** + * Create a new config. + * + * @param array $config The config data. + * @return array|WP_Error The created config or an error. + */ + public static function create( array $config ): array|WP_Error { + return static::save( $config ); + } + + /** + * Update an existing config. + * + * @param string $uuid The UUID of the config to update. + * @param array $config_data The updated config data. + * @return array|WP_Error The updated config or an error. + */ + public static function update( string $uuid, array $config_data ): array|WP_Error { + $existing = static::get_by_uuid( $uuid ); + if ( is_wp_error( $existing ) ) { + return $existing; + } + + $config = array_merge( $existing, $config_data ); + $config['uuid'] = $uuid; // Ensure UUID remains the same + + return static::save( $config ); + } + + /** + * Save a config to the database. + * + * @param array $config The config data. + * @return array|WP_Error The saved config or an error. + */ + protected static function save( array $config ): array|WP_Error { + // Create a validated config instance + $config_instance = static::validate_and_instantiate( $config ); + if ( is_wp_error( $config_instance ) ) { + return $config_instance; + } + + // Convert the instance back to an array + $new_config = $config_instance->to_array(); + + // Ensure metadata is set + $now = gmdate( 'Y-m-d H:i:s' ); + $new_config['__metadata'] = [ + 'created_at' => $config['__metadata']['created_at'] ?? $now, + 'updated_at' => $now, + ]; + + // Ensure UUID is set + $new_config['uuid'] = $config['uuid'] ?? wp_generate_uuid4(); + + // Create or update the config + $configs = array_values( array_filter( + static::get_all(), + function ( $existing ) use ( $new_config ) { + return $existing['uuid'] !== $new_config['uuid']; + } + ) ); + $configs[] = $new_config; + + if ( true !== static::save_all( $configs ) ) { + return new WP_Error( + static::get_save_failed_error_code(), + static::get_save_failed_error_message() + ); + } + + return $new_config; + } + + /** + * Delete a config by UUID. + * + * @param string $uuid The UUID of the config to delete. + * @return bool|WP_Error True on success, WP_Error on failure. + */ + public static function delete( string $uuid ): bool|WP_Error { + $configs = array_values( array_filter( + static::get_all(), + function ( $config ) use ( $uuid ) { + return $config['uuid'] !== $uuid; + } + ) ); + + if ( true !== static::save_all( $configs ) ) { + return new WP_Error( + static::get_delete_failed_error_code(), + static::get_delete_failed_error_message() + ); + } + + return true; + } + + /** + * Save all configs to the database. + */ + protected static function save_all( array $configs ): bool { + return update_option( static::get_option_name(), $configs ); + } +} From e126eec695ea4abe13f316cf245f9e36bcc9c02f Mon Sep 17 00:00:00 2001 From: Shekhar Wagh Date: Thu, 13 Mar 2025 01:26:12 +0530 Subject: [PATCH 3/4] lint fixes --- inc/Config/DataSource/DataSourceQuery.php | 2 +- inc/WpdbStorage/WpOptionsConfigStore.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/inc/Config/DataSource/DataSourceQuery.php b/inc/Config/DataSource/DataSourceQuery.php index 2df91bc1..1d3dd0ba 100644 --- a/inc/Config/DataSource/DataSourceQuery.php +++ b/inc/Config/DataSource/DataSourceQuery.php @@ -103,4 +103,4 @@ public function get_display_name(): string { * @return string The service name. */ abstract public function get_service_name(): string; -} \ No newline at end of file +} diff --git a/inc/WpdbStorage/WpOptionsConfigStore.php b/inc/WpdbStorage/WpOptionsConfigStore.php index 2cba3c5f..acee6d10 100644 --- a/inc/WpdbStorage/WpOptionsConfigStore.php +++ b/inc/WpdbStorage/WpOptionsConfigStore.php @@ -50,12 +50,13 @@ protected static function validate_and_instantiate( array $config ): mixed { $config_class_map = static::get_config_class_map(); - $config_class = $config_class_map[$service] ?? null; + $config_class = $config_class_map[ $service ] ?? null; if ( null === $config_class ) { return new WP_Error( 'unsupported_service', sprintf( + // translators: %s is the name of the service that is not supported __( 'Unsupported service: %s', 'remote-data-blocks' ), $service ) @@ -70,6 +71,7 @@ private static function get_not_found_error_code(): string { } private static function get_not_found_error_message(): string { + // translators: %s is the error message prefix for the config type return sprintf( __( '%s not found', 'remote-data-blocks' ), static::get_error_message_prefix() ); } @@ -78,6 +80,7 @@ private static function get_save_failed_error_code(): string { } private static function get_save_failed_error_message(): string { + // translators: %s is the error message prefix for the config type return sprintf( __( 'Failed to save %s', 'remote-data-blocks' ), static::get_error_message_prefix() ); } @@ -86,6 +89,7 @@ private static function get_delete_failed_error_code(): string { } private static function get_delete_failed_error_message(): string { + // translators: %s is the error message prefix for the config type return sprintf( __( 'Failed to delete %s', 'remote-data-blocks' ), static::get_error_message_prefix() ); } From 4cb9625ac6d025e8f74ff2c9caaf9d288d006978 Mon Sep 17 00:00:00 2001 From: Shekhar Wagh Date: Thu, 13 Mar 2025 18:22:00 +0530 Subject: [PATCH 4/4] fix psalm issues --- inc/Config/DataSource/DataSourceConnection.php | 2 +- inc/Config/DataSource/DataSourceConnectionInterface.php | 2 +- inc/Config/DataSource/DataSourceQuery.php | 2 +- inc/Config/DataSource/DataSourceQueryInterface.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/inc/Config/DataSource/DataSourceConnection.php b/inc/Config/DataSource/DataSourceConnection.php index e8ccd9a3..39447d36 100644 --- a/inc/Config/DataSource/DataSourceConnection.php +++ b/inc/Config/DataSource/DataSourceConnection.php @@ -97,7 +97,7 @@ public function get_display_name(): string { * * @return string The service name. */ - abstract public function get_service_name(): string; + abstract public static function get_service_name(): string; /** * Get the queries for the data source. diff --git a/inc/Config/DataSource/DataSourceConnectionInterface.php b/inc/Config/DataSource/DataSourceConnectionInterface.php index 76dbc763..83e32664 100644 --- a/inc/Config/DataSource/DataSourceConnectionInterface.php +++ b/inc/Config/DataSource/DataSourceConnectionInterface.php @@ -13,6 +13,6 @@ interface DataSourceConnectionInterface extends ArraySerializableInterface { public function get_uuid(): string; public function get_display_name(): string; - public function get_service_name(): string; + public static function get_service_name(): string; public function get_service_config(): array; } diff --git a/inc/Config/DataSource/DataSourceQuery.php b/inc/Config/DataSource/DataSourceQuery.php index 1d3dd0ba..aac9b33e 100644 --- a/inc/Config/DataSource/DataSourceQuery.php +++ b/inc/Config/DataSource/DataSourceQuery.php @@ -102,5 +102,5 @@ public function get_display_name(): string { * * @return string The service name. */ - abstract public function get_service_name(): string; + abstract public static function get_service_name(): string; } diff --git a/inc/Config/DataSource/DataSourceQueryInterface.php b/inc/Config/DataSource/DataSourceQueryInterface.php index bf3f2cb8..2d94267d 100644 --- a/inc/Config/DataSource/DataSourceQueryInterface.php +++ b/inc/Config/DataSource/DataSourceQueryInterface.php @@ -14,6 +14,6 @@ interface DataSourceQueryInterface extends ArraySerializableInterface { public function get_uuid(): string; public function get_connection_uuid(): string|null; public function get_display_name(): string; - public function get_service_name(): string; + public static function get_service_name(): string; public function get_service_config(): array; }