Skip to content

[WIP] split config schema #417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions inc/Config/DataSource/DataSourceConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php declare(strict_types = 1);

namespace RemoteDataBlocks\Config\DataSource;

use RemoteDataBlocks\Config\ArraySerializable;
use RemoteDataBlocks\Validation\ConfigSchemas;
use RemoteDataBlocks\Validation\Validator;
use WP_Error;

/**
* Base class for data source connection configurations.
*
* This class represents the authentication and connection information
* for a data source, separate from the query-specific parameters.
*/
abstract class DataSourceConnection extends ArraySerializable implements DataSourceConnectionInterface {

Check failure on line 16 in inc/Config/DataSource/DataSourceConnection.php

View workflow job for this annotation

GitHub Actions / Psalm

UnusedClass

inc/Config/DataSource/DataSourceConnection.php:16:16: UnusedClass: Class RemoteDataBlocks\Config\DataSource\DataSourceConnection is never used (see https://psalm.dev/075)
/**
* @inheritDoc
*/
public static function preprocess_config( array $config ): array|WP_Error {
$service_config = $config['service_config'] ?? [];
$validator = new Validator( static::get_service_config_schema() );
$validated = $validator->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 static function get_service_name(): string;

/**
* Get the queries for the data source.
*
* @return array The queries.
*/
abstract public function get_queries(): array;
}
18 changes: 18 additions & 0 deletions inc/Config/DataSource/DataSourceConnectionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types = 1);

namespace RemoteDataBlocks\Config\DataSource;

use RemoteDataBlocks\Config\ArraySerializableInterface;

/**
* Interface for data source connection configurations.
*
* This interface represents the authentication and connection information
* for a data source, separate from the query-specific parameters.
*/
interface DataSourceConnectionInterface extends ArraySerializableInterface {
public function get_uuid(): string;
public function get_display_name(): string;
public static function get_service_name(): string;
public function get_service_config(): array;
}
106 changes: 106 additions & 0 deletions inc/Config/DataSource/DataSourceQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php declare(strict_types = 1);

namespace RemoteDataBlocks\Config\DataSource;

use RemoteDataBlocks\Config\ArraySerializable;
use RemoteDataBlocks\Validation\ConfigSchemas;
use RemoteDataBlocks\Validation\Validator;
use WP_Error;

/**
* Base class for data source query configurations.
*
* This class represents the query-specific parameters for a data source,
* separate from the connection/authentication information.
Comment on lines +13 to +14
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just merge this into the query, then? I don't think I understand the need for separation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HttpDataSource and HttpQuery classes are different in that HttpDataSource only needs the config as input and nothing else, while for HttpQuery, those configs are just one of the inputs required amongst others.

The query configs (base/table for Airtable, spreadsheet/sheet for Google Sheets, etc.) currently don't map 1:1 with our Query/HttpQuery classes since one such query config would need multiple HttpQuery instances to register blocks. For example, selecting 2 tables in Airtable would need at least 2 queries - list and search queries for each of the tables. I tend to think of configs as minimum data required for us to generate the queries and register blocks which for Airtable would mean just knowing the tables and the schema for each and our integration code would use that to generate the HttpQuery class.

The new DataSourceConnection and DataSourceQuery classes can focus on just one thing - validating the configs. Having the config split would allow reusing the connections.

Slack thread for more context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open to suggestions if this sounds like its complicating things

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those configs are just one of the inputs required amongst others

What are the others?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

input_schema, output_schema, preprocess_response which might not always be derived from the query config.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we provide defaults that could then be edited in the UI, if needed?

*/
abstract class DataSourceQuery extends ArraySerializable implements DataSourceQueryInterface {

Check failure on line 16 in inc/Config/DataSource/DataSourceQuery.php

View workflow job for this annotation

GitHub Actions / Psalm

UnusedClass

inc/Config/DataSource/DataSourceQuery.php:16:16: UnusedClass: Class RemoteDataBlocks\Config\DataSource\DataSourceQuery is never used (see https://psalm.dev/075)
/**
* @inheritDoc
*/
public static function preprocess_config( array $config ): array|WP_Error {
$service_config = $config['service_config'] ?? [];
$validator = new Validator( static::get_service_config_schema() );
$validated = $validator->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 static function get_service_name(): string;
}
19 changes: 19 additions & 0 deletions inc/Config/DataSource/DataSourceQueryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types = 1);

namespace RemoteDataBlocks\Config\DataSource;

use RemoteDataBlocks\Config\ArraySerializableInterface;

/**
* Interface for data source query configurations.
*
* This interface represents the query-specific parameters for a data source,
* separate from the connection/authentication information.
*/
interface DataSourceQueryInterface extends ArraySerializableInterface {
public function get_uuid(): string;
public function get_connection_uuid(): string|null;
public function get_display_name(): string;
public static function get_service_name(): string;
public function get_service_config(): array;
}
3 changes: 3 additions & 0 deletions inc/Integrations/constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
41 changes: 41 additions & 0 deletions inc/Validation/ConfigSchemas.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,26 @@ public static function get_remote_data_block_attribute_config_schema(): array {
return $schema;
}

public static function get_data_source_connection_config_schema(): array {
static $schema = null;

if ( null === $schema ) {
$schema = self::generate_data_source_connection_config_schema();
}

return $schema;
}

public static function get_data_source_query_config_schema(): array {
static $schema = null;

if ( null === $schema ) {
$schema = self::generate_data_source_query_config_schema();
}

return $schema;
}

private static function generate_remote_data_block_config_schema(): array {
return Types::object( [
'icon' => Types::nullable( Types::string() ),
Expand Down Expand Up @@ -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() ) ),
] );
}
}
28 changes: 28 additions & 0 deletions inc/WpdbStorage/DataSourceConnectionCrud.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types = 1);

namespace RemoteDataBlocks\WpdbStorage;

/**
* CRUD operations for data source connections stored in WordPress options.
*/
class DataSourceConnectionCrud extends WpOptionsConfigStore {

Check failure on line 8 in inc/WpdbStorage/DataSourceConnectionCrud.php

View workflow job for this annotation

GitHub Actions / Psalm

UnusedClass

inc/WpdbStorage/DataSourceConnectionCrud.php:8:7: UnusedClass: Class RemoteDataBlocks\WpdbStorage\DataSourceConnectionCrud is never used (see https://psalm.dev/075)
protected static function get_error_code_prefix(): string {
return 'data_source_connection';
}

protected static function get_error_message_prefix(): string {
return 'data source connection';
}

protected static function get_option_name(): string {
return 'remote_data_blocks_data_source_connections';
}

protected static function get_config_class_map(): array {
if ( defined( 'REMOTE_DATA_BLOCKS__DATA_SOURCE_CONNECTION_CLASSMAP' ) ) {
return constant( 'REMOTE_DATA_BLOCKS__DATA_SOURCE_CONNECTION_CLASSMAP' );
}

return [];
}
}
28 changes: 28 additions & 0 deletions inc/WpdbStorage/DataSourceQueryCrud.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types = 1);

namespace RemoteDataBlocks\WpdbStorage;

/**
* CRUD operations for data source queries stored in WordPress options.
*/
class DataSourceQueryCrud extends WpOptionsConfigStore {

Check failure on line 8 in inc/WpdbStorage/DataSourceQueryCrud.php

View workflow job for this annotation

GitHub Actions / Psalm

UnusedClass

inc/WpdbStorage/DataSourceQueryCrud.php:8:7: UnusedClass: Class RemoteDataBlocks\WpdbStorage\DataSourceQueryCrud is never used (see https://psalm.dev/075)
protected static function get_error_code_prefix(): string {
return 'data_source_query';
}

protected static function get_error_message_prefix(): string {
return 'data source query';
}

protected static function get_option_name(): string {
return 'remote_data_blocks_data_source_queries';
}

protected static function get_config_class_map(): array {
if ( defined( 'REMOTE_DATA_BLOCKS__DATA_SOURCE_QUERY_CLASSMAP' ) ) {
return constant( 'REMOTE_DATA_BLOCKS__DATA_SOURCE_QUERY_CLASSMAP' );
}

return [];
}
}
Loading
Loading