diff --git a/forms-bridge/addons/bigin/class-bigin-addon.php b/forms-bridge/addons/bigin/class-bigin-addon.php
index e0c146a1..cd43dd6e 100644
--- a/forms-bridge/addons/bigin/class-bigin-addon.php
+++ b/forms-bridge/addons/bigin/class-bigin-addon.php
@@ -103,6 +103,38 @@ public function ping( $backend ) {
return true;
}
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Target backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ $bridge = new Bigin_Form_Bridge(
+ array(
+ 'name' => '__bigin-' . time(),
+ 'endpoint' => '/bigin/v2/settings/modules',
+ 'method' => 'GET',
+ 'backend' => $backend,
+ )
+ );
+
+ $response = $bridge->submit();
+
+ if ( is_wp_error( $response ) ) {
+ return array();
+ }
+
+ return array_map(
+ function ( $module ) {
+ return '/bigin/v2/' . $module['api_name'];
+ },
+ $response['data']['modules'],
+ );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields.
*
diff --git a/forms-bridge/addons/bigin/hooks.php b/forms-bridge/addons/bigin/hooks.php
index c1f247e9..fce06ca9 100644
--- a/forms-bridge/addons/bigin/hooks.php
+++ b/forms-bridge/addons/bigin/hooks.php
@@ -43,12 +43,12 @@ function ( $defaults, $addon, $schema ) {
'ref' => '#credential',
'name' => 'scope',
'value' =>
- 'ZohoBigin.modules.ALL,ZohoBigin.settings.layouts.READ,ZohoBigin.users.READ',
+ 'ZohoBigin.modules.ALL,ZohoBigin.settings.modules.READ,ZohoBigin.settings.layouts.READ,ZohoBigin.users.READ',
),
),
'credential' => array(
'scope' =>
- 'ZohoBigin.modules.ALL,ZohoBigin.settings.layouts.READ,ZohoBigin.users.READ',
+ 'ZohoBigin.modules.ALL,ZohoBigin.settings.modules.READ,ZohoBigin.settings.layouts.READ,ZohoBigin.users.READ',
),
),
$defaults,
diff --git a/forms-bridge/addons/brevo/class-brevo-addon.php b/forms-bridge/addons/brevo/class-brevo-addon.php
index d52cf4fa..99ff6be1 100644
--- a/forms-bridge/addons/brevo/class-brevo-addon.php
+++ b/forms-bridge/addons/brevo/class-brevo-addon.php
@@ -46,7 +46,8 @@ class Brevo_Addon extends Addon {
*
* @var string
*/
- public const OAS_URL = 'https://developers.brevo.com/reference/get_companies?json=on';
+ // public const OAS_URL = 'https://developers.brevo.com/reference/get_companies?json=on';
+ public const OAS_URL = 'https://api.brevo.com/v3/swagger_definition_v3.yml';
/**
* Performs a request against the backend to check the connexion status.
@@ -79,41 +80,51 @@ public function ping( $backend ) {
/**
* Fetch available models from the OAS spec.
*
- * @param Backend $backend HTTP backend object.
+ * @param string $backend Backend name.
+ * @param string|null $method HTTP method.
*
* @return array
*
* @todo Implementar el endpoint de consulta de endpoints disponibles.
*/
- public function get_endpoints( $backend ) {
- $response = wp_remote_get(
- self::OAS_URL,
- array(
- 'headers' => array(
- 'Accept' => 'application/json',
- 'Host' => 'developers.brevo.com',
- 'Referer' => 'https://developers.brevo.com/reference/get_companies',
- 'Alt-Used' => 'developers.brevo.com',
- 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0',
- ),
- )
- );
+ public function get_endpoints( $backend, $method = null ) {
+ if ( function_exists( 'yaml_parse' ) ) {
+ $response = wp_remote_get( self::OAS_URL );
- if ( is_wp_error( $response ) ) {
- return array();
- }
+ if ( ! is_wp_error( $response ) ) {
+ $data = yaml_parse( $response['body'] );
- $data = json_decode( $response['body'], true );
- $oa_explorer = new OpenAPI( $data['oasDefinition'] );
+ if ( $data ) {
+ $oa_explorer = new OpenAPI( $data );
- $paths = $oa_explorer->paths();
+ $paths = $oa_explorer->paths();
- return array_map(
- function ( $path ) {
- return '/v3' . $path;
- },
- $paths,
- );
+ if ( $method ) {
+ $method = strtolower( $method );
+ $method_paths = array();
+
+ foreach ( $paths as $path ) {
+ $path_obj = $oa_explorer->path_obj( $path );
+
+ if ( $path_obj && isset( $path_obj[ $method ] ) ) {
+ $method_paths[] = $path;
+ }
+ }
+
+ $paths = $method_paths;
+ }
+
+ return array_map(
+ function ( $path ) {
+ return '/v3' . $path;
+ },
+ $paths,
+ );
+ }
+ }
+
+ return array( '/v3/contacts' );
+ }
}
/**
diff --git a/forms-bridge/addons/dolibarr/class-dolibarr-addon.php b/forms-bridge/addons/dolibarr/class-dolibarr-addon.php
index 9f398437..8f606373 100644
--- a/forms-bridge/addons/dolibarr/class-dolibarr-addon.php
+++ b/forms-bridge/addons/dolibarr/class-dolibarr-addon.php
@@ -157,6 +157,62 @@ public function get_endpoint_schema( $endpoint, $backend, $method = null ) {
return $fields;
}
+
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ $bridge = new Dolibarr_Form_Bridge(
+ array(
+ 'name' => '__dolibarr-' . time(),
+ 'endpoint' => self::SWAGGER_ENDPOINT,
+ 'backend' => $backend,
+ 'method' => 'GET',
+ )
+ );
+
+ $response = $bridge->submit();
+
+ if ( is_wp_error( $response ) ) {
+ return array();
+ }
+
+ $version = $response['data']['swagger'] ?? null;
+ if ( ! $version ) {
+ return array();
+ }
+
+ $oa_explorer = new OpenAPI( $response['data'] );
+
+ $paths = $oa_explorer->paths();
+
+ if ( $method ) {
+ $method = strtolower( $method );
+ $method_paths = array();
+
+ foreach ( $paths as $path ) {
+ $path_obj = $oa_explorer->path_obj( $path );
+
+ if ( $path_obj && isset( $path_obj[ $method ] ) ) {
+ $method_paths[] = $path;
+ }
+ }
+
+ $paths = $method_paths;
+ }
+
+ return array_map(
+ function ( $path ) {
+ return '/api/index.php' . $path;
+ },
+ $paths,
+ );
+ }
}
Dolibarr_Addon::setup();
diff --git a/forms-bridge/addons/financoop/class-financoop-addon.php b/forms-bridge/addons/financoop/class-financoop-addon.php
index cbde8b4e..42c1715c 100644
--- a/forms-bridge/addons/financoop/class-financoop-addon.php
+++ b/forms-bridge/addons/financoop/class-financoop-addon.php
@@ -69,6 +69,22 @@ public function ping( $backend ) {
return true;
}
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Target backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ return array(
+ '/api/campaign/{campaign_id}/subscription_request',
+ '/api/campaign/{campaign_id}/donation_request',
+ '/api/campaign/{campaign_id}/loan_request',
+ );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields
* and accepted content type.
diff --git a/forms-bridge/addons/gcalendar/class-gcalendar-addon.php b/forms-bridge/addons/gcalendar/class-gcalendar-addon.php
index 5985589d..6cb32604 100644
--- a/forms-bridge/addons/gcalendar/class-gcalendar-addon.php
+++ b/forms-bridge/addons/gcalendar/class-gcalendar-addon.php
@@ -130,6 +130,29 @@ public function fetch( $endpoint, $backend ) {
return $response;
}
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Target backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ $response = $this->fetch( null, $backend );
+
+ if ( is_wp_error( $response ) || empty( $response['data']['items'] ) ) {
+ return array();
+ }
+
+ return array_map(
+ function ( $calendar ) {
+ return '/calendar/v3/calendars/' . $calendar['id'] . '/events';
+ },
+ $response['data']['items']
+ );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields
* and accepted content type.
diff --git a/forms-bridge/addons/gsheets/class-gsheets-addon.php b/forms-bridge/addons/gsheets/class-gsheets-addon.php
index 9379e030..1cd1db2f 100644
--- a/forms-bridge/addons/gsheets/class-gsheets-addon.php
+++ b/forms-bridge/addons/gsheets/class-gsheets-addon.php
@@ -152,6 +152,29 @@ public function fetch( $endpoint, $backend ) {
return $response;
}
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Target backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ $response = $this->fetch( null, $backend );
+
+ if ( is_wp_error( $response ) || empty( $response['data']['files'] ) ) {
+ return array();
+ }
+
+ return array_map(
+ function ( $file ) {
+ return '/v4/spreadsheets/' . $file['id'];
+ },
+ $response['data']['files']
+ );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields
* and accepted content type.
diff --git a/forms-bridge/addons/holded/class-holded-addon.php b/forms-bridge/addons/holded/class-holded-addon.php
index 90cbe362..8ad44cba 100644
--- a/forms-bridge/addons/holded/class-holded-addon.php
+++ b/forms-bridge/addons/holded/class-holded-addon.php
@@ -91,6 +91,74 @@ public function ping( $backend ) {
return true;
}
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Target backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ $paths = array();
+
+ foreach ( self::OAS_URLS as $module => $oas_path ) {
+ $oas_url = self::OAS_BASE_URL . $oas_path . '?dereference=false&reduce=false';
+
+ $response = wp_remote_get(
+ $oas_url,
+ array(
+ 'headers' => array(
+ 'Accept' => 'application/json',
+ 'Host' => 'developers.holded.com',
+ 'Alt-Used' => 'developers.holded.com',
+ 'Referer' => 'https://developers.holded.com/reference/list-contacts-1',
+ 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0',
+ ),
+ ),
+ );
+
+ if ( is_wp_error( $response ) ) {
+ continue;
+ }
+
+ $data = json_decode( $response['body'], true );
+ if ( ! $data ) {
+ continue;
+ }
+
+ $oa_explorer = new OpenAPI( $data['data']['api']['schema'] );
+
+ $module_paths = $oa_explorer->paths();
+
+ if ( $method ) {
+ $method = strtolower( $method );
+ $method_paths = array();
+
+ foreach ( $module_paths as $path ) {
+ $path_obj = $oa_explorer->path_obj( $path );
+
+ if ( $path_obj && isset( $path_obj[ $method ] ) ) {
+ $method_paths[] = $path;
+ }
+ }
+
+ $module_paths = $method_paths;
+ }
+
+ $module_paths = array_map(
+ function ( $path ) use ( $module ) {
+ return '/api/' . $module . '/v1' . $path;
+ },
+ $module_paths,
+ );
+
+ $paths = array_merge( $paths, $module_paths );
+ }
+
+ return $paths;
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields
* and accepted content type.
diff --git a/forms-bridge/addons/listmonk/class-listmonk-addon.php b/forms-bridge/addons/listmonk/class-listmonk-addon.php
index 51903e95..c17a1112 100644
--- a/forms-bridge/addons/listmonk/class-listmonk-addon.php
+++ b/forms-bridge/addons/listmonk/class-listmonk-addon.php
@@ -78,34 +78,50 @@ public function ping( $backend ) {
/**
* Fetch available models from the OAS spec.
*
- * @param Backend $backend HTTP backend object.
+ * @param string $backend Backend name.
+ * @param string|null $method HTTP method.
*
* @return array
*
* @todo Implementar el endpoint de consulta de endpoints disponibles.
*/
- public function get_endpoints( $backend ) {
+ public function get_endpoints( $backend, $method = null ) {
if ( function_exists( 'yaml_parse' ) ) {
$response = wp_remote_get( self::OAS_URL );
if ( ! is_wp_error( $response ) ) {
$data = yaml_parse( $response['body'] );
- $oa_explorer = new OpenAPI( $data );
- $paths = $oa_explorer->paths();
+ if ( $data ) {
+ $oa_explorer = new OpenAPI( $data );
+ $paths = $oa_explorer->paths();
+
+ if ( $method ) {
+ $method = strtolower( $method );
+ $method_paths = array();
+
+ foreach ( $paths as $path ) {
+ $path_obj = $oa_explorer->path_obj( $path );
- return array_map(
- function ( $path ) {
- return '/api' . $path;
- },
- $paths
- );
+ if ( $path_obj && isset( $path_obj[ $method ] ) ) {
+ $method_paths[] = $path;
+ }
+ }
+
+ $paths = $method_paths;
+ }
+
+ return array_map(
+ function ( $path ) {
+ return '/api' . $path;
+ },
+ $paths
+ );
+ }
}
}
- return array(
- '/api/subscribers',
- );
+ return array( '/api/subscribers' );
}
/**
diff --git a/forms-bridge/addons/mailchimp/class-mailchimp-addon.php b/forms-bridge/addons/mailchimp/class-mailchimp-addon.php
index 1ad5ace3..ae208de6 100644
--- a/forms-bridge/addons/mailchimp/class-mailchimp-addon.php
+++ b/forms-bridge/addons/mailchimp/class-mailchimp-addon.php
@@ -76,6 +76,62 @@ public function ping( $backend ) {
return true;
}
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Target backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ $response = wp_remote_get(
+ self::SWAGGER_URL,
+ array(
+ 'headers' => array(
+ 'Accept' => 'application/json',
+ 'Host' => 'mailchimp.com',
+ 'Referer' => 'https://mailchimp.com/developer/marketing/api/',
+ 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0',
+ ),
+ ),
+ );
+
+ if ( ! is_wp_error( $response ) ) {
+ $data = json_decode( $response['body'], true );
+
+ if ( $data ) {
+ $oa_explorer = new OpenAPI( $data );
+
+ $paths = $oa_explorer->paths();
+
+ if ( $method ) {
+ $method = strtolower( $method );
+ $method_paths = array();
+
+ foreach ( $paths as $path ) {
+ $path_obj = $oa_explorer->path_obj( $path );
+
+ if ( $path_obj && isset( $path_obj[ $method ] ) ) {
+ $method_paths[] = $path;
+ }
+ }
+
+ $paths = $method_paths;
+ }
+
+ return array_map(
+ function ( $path ) {
+ return '/3.0' . $path;
+ },
+ $paths,
+ );
+ }
+ }
+
+ return array( '/3.0/lists/{list_id}/members' );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields
* and accepted content type.
diff --git a/forms-bridge/addons/odoo/class-odoo-addon.php b/forms-bridge/addons/odoo/class-odoo-addon.php
index 824bcde2..3e80d536 100644
--- a/forms-bridge/addons/odoo/class-odoo-addon.php
+++ b/forms-bridge/addons/odoo/class-odoo-addon.php
@@ -93,13 +93,14 @@ public function fetch( $endpoint, $backend ) {
/**
* Fetch available models from the backend
*
- * @param Backend $backend HTTP backend object.
+ * @param string $backend Backend name.
+ * @param string|null $method RPC method.
*
* @return array
*
* @todo Implementar el endpoint de consulta de endpoints disponibles.
*/
- public function get_endpoints( $backend ) {
+ public function get_endpoints( $backend, $method = null ) {
$bridge = new Odoo_Form_Bridge(
array(
'name' => '__odoo-' . time(),
diff --git a/forms-bridge/addons/rocketchat/class-rocketchat-addon.php b/forms-bridge/addons/rocketchat/class-rocketchat-addon.php
index 10829b22..8a866b62 100644
--- a/forms-bridge/addons/rocketchat/class-rocketchat-addon.php
+++ b/forms-bridge/addons/rocketchat/class-rocketchat-addon.php
@@ -76,6 +76,50 @@ public function ping( $backend ) {
return true;
}
+ /**
+ * Fetch available models from the OAS spec.
+ *
+ * @param string $backend Backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array
+ *
+ * @todo Implementar el endpoint de consulta de endpoints disponibles.
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ if ( function_exists( 'yaml_parse' ) ) {
+ $response = wp_remote_get( self::OAS_URL );
+
+ if ( ! is_wp_error( $response ) ) {
+ $data = yaml_parse( $response['body'] );
+
+ if ( $data ) {
+ $oa_explorer = new OpenAPI( $data );
+ $paths = $oa_explorer->paths();
+
+ if ( $method ) {
+ $method = strtolower( $method );
+ $method_paths = array();
+
+ foreach ( $paths as $path ) {
+ $path_obj = $oa_explorer->path_obj( $path );
+
+ if ( $path_obj && isset( $path_obj[ $method ] ) ) {
+ $method_paths[] = $path;
+ }
+ }
+
+ $paths = $method_paths;
+ }
+
+ return $paths;
+ }
+ }
+ }
+
+ return array( '/api/v1/chat.postMessage' );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields.
*
diff --git a/forms-bridge/addons/slack/class-slack-addon.php b/forms-bridge/addons/slack/class-slack-addon.php
index bcda565e..9f9dc55d 100644
--- a/forms-bridge/addons/slack/class-slack-addon.php
+++ b/forms-bridge/addons/slack/class-slack-addon.php
@@ -76,6 +76,53 @@ public function ping( $backend ) {
return true;
}
+ /**
+ * Fetch available models from the OAS spec.
+ *
+ * @param string $backend Backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array
+ *
+ * @todo Implementar el endpoint de consulta de endpoints disponibles.
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ $response = wp_remote_get( self::OAS_URL );
+
+ if ( ! is_wp_error( $response ) ) {
+ $data = json_decode( $response['body'], true );
+
+ if ( $data ) {
+ $oa_explorer = new OpenAPI( $data );
+ $paths = $oa_explorer->paths();
+
+ if ( $method ) {
+ $method = strtolower( $method );
+ $method_paths = array();
+
+ foreach ( $paths as $path ) {
+ $path_obj = $oa_explorer->path_obj( $path );
+
+ if ( $path_obj && isset( $path_obj[ $method ] ) ) {
+ $method_paths[] = $path;
+ }
+ }
+
+ $paths = $method_paths;
+ }
+
+ return array_map(
+ function ( $path ) {
+ return '/api' . $path;
+ },
+ $paths,
+ );
+ }
+ }
+
+ return array( '/api/chat.postMessage' );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields.
*
diff --git a/forms-bridge/addons/suitecrm/class-suitecrm-addon.php b/forms-bridge/addons/suitecrm/class-suitecrm-addon.php
index f261def8..73e8ecda 100644
--- a/forms-bridge/addons/suitecrm/class-suitecrm-addon.php
+++ b/forms-bridge/addons/suitecrm/class-suitecrm-addon.php
@@ -94,11 +94,12 @@ public function fetch( $endpoint, $backend ) {
/**
* Fetch available modules from the backend.
*
- * @param Backend $backend HTTP backend object.
+ * @param string $backend Backend name.
+ * @param string|null $method API method.
*
* @return array
*/
- public function get_endpoints( $backend ) {
+ public function get_endpoints( $backend, $method = null ) {
$bridge = new SuiteCRM_Form_Bridge(
array(
'name' => '__suitecrm-' . time(),
diff --git a/forms-bridge/addons/vtiger/class-vtiger-addon.php b/forms-bridge/addons/vtiger/class-vtiger-addon.php
index a0f213f2..ab0453b7 100644
--- a/forms-bridge/addons/vtiger/class-vtiger-addon.php
+++ b/forms-bridge/addons/vtiger/class-vtiger-addon.php
@@ -98,11 +98,12 @@ public function fetch( $endpoint, $backend ) {
/**
* Fetch available modules from the backend.
*
- * @param Backend $backend HTTP backend object.
+ * @param string $backend HTTP backend object.
+ * @param string|null $method API method.
*
* @return array
*/
- public function get_endpoints( $backend ) {
+ public function get_endpoints( $backend, $method = null ) {
$bridge = new Vtiger_Form_Bridge(
array(
'name' => '__vtiger-' . time(),
diff --git a/forms-bridge/addons/zoho/class-zoho-addon.php b/forms-bridge/addons/zoho/class-zoho-addon.php
index 70c127b5..5ddee35b 100644
--- a/forms-bridge/addons/zoho/class-zoho-addon.php
+++ b/forms-bridge/addons/zoho/class-zoho-addon.php
@@ -102,6 +102,38 @@ public function ping( $backend ) {
return true;
}
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Target backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ $bridge = new Zoho_Form_Bridge(
+ array(
+ 'name' => '__zoho-' . time(),
+ 'endpoint' => '/crm/v7/settings/modules',
+ 'method' => 'GET',
+ 'backend' => $backend,
+ )
+ );
+
+ $response = $bridge->submit();
+
+ if ( is_wp_error( $response ) ) {
+ return array();
+ }
+
+ return array_map(
+ function ( $module ) {
+ return '/crm/v7/' . $module['api_name'];
+ },
+ $response['data']['modules'],
+ );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields.
*
diff --git a/forms-bridge/addons/zoho/hooks.php b/forms-bridge/addons/zoho/hooks.php
index bd270dca..a2303bb9 100644
--- a/forms-bridge/addons/zoho/hooks.php
+++ b/forms-bridge/addons/zoho/hooks.php
@@ -107,7 +107,7 @@ function ( $defaults, $addon, $schema ) {
'name' => 'scope',
'label' => __( 'Scope', 'forms-bridge' ),
'type' => 'text',
- 'value' => 'ZohoCRM.modules.ALL,ZohoCRM.settings.layouts.READ,ZohoCRM.users.READ',
+ 'value' => 'ZohoCRM.modules.ALL,ZohoCRM.settings.modules.READ,ZohoCRM.settings.layouts.READ,ZohoCRM.users.READ',
'required' => true,
),
array(
@@ -174,7 +174,7 @@ function ( $defaults, $addon, $schema ) {
'name' => '',
'schema' => 'Bearer',
'oauth_url' => 'https://accounts.{region}/oauth/v2',
- 'scope' => 'ZohoCRM.modules.ALL,ZohoCRM.settings.layouts.READ,ZohoCRM.users.READ',
+ 'scope' => 'ZohoCRM.modules.ALL,ZohoCRM.settings.modules.READ,ZohoCRM.settings.layouts.READ,ZohoCRM.users.READ',
'client_id' => '',
'client_secret' => '',
'access_token' => '',
diff --git a/forms-bridge/addons/zulip/class-zulip-addon.php b/forms-bridge/addons/zulip/class-zulip-addon.php
index b37d6fc2..8643804c 100644
--- a/forms-bridge/addons/zulip/class-zulip-addon.php
+++ b/forms-bridge/addons/zulip/class-zulip-addon.php
@@ -77,6 +77,55 @@ public function ping( $backend ) {
return true;
}
+ /**
+ * Fetch available models from the OAS spec.
+ *
+ * @param string $backend Backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array
+ *
+ * @todo Implementar el endpoint de consulta de endpoints disponibles.
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ if ( function_exists( 'yaml_parse' ) ) {
+ $response = wp_remote_get( self::OAS_URL );
+
+ if ( ! is_wp_error( $response ) ) {
+ $data = yaml_parse( $response['body'] );
+
+ if ( $data ) {
+ $oa_explorer = new OpenAPI( $data );
+ $paths = $oa_explorer->paths();
+
+ if ( $method ) {
+ $method = strtolower( $method );
+ $method_paths = array();
+
+ foreach ( $paths as $path ) {
+ $path_obj = $oa_explorer->path_obj( $path );
+
+ if ( $path_obj && isset( $path_obj[ $method ] ) ) {
+ $method_paths[] = $path;
+ }
+ }
+
+ $paths = $method_paths;
+ }
+
+ return array_map(
+ function ( $path ) {
+ return '/api/v1' . $path;
+ },
+ $paths,
+ );
+ }
+ }
+ }
+
+ return array( '/api/v1/messages' );
+ }
+
/**
* Performs an introspection of the backend endpoint and returns API fields.
*
diff --git a/forms-bridge/includes/class-addon.php b/forms-bridge/includes/class-addon.php
index 93092be9..a722cb9d 100644
--- a/forms-bridge/includes/class-addon.php
+++ b/forms-bridge/includes/class-addon.php
@@ -595,6 +595,18 @@ public function get_endpoint_schema( $endpoint, $backend, $method = null ) {
return array();
}
+ /**
+ * Performs an introspection of the backend API and returns a list of available endpoints.
+ *
+ * @param string $backend Target backend name.
+ * @param string|null $method HTTP method.
+ *
+ * @return array|WP_Error
+ */
+ public function get_endpoints( $backend, $method = null ) {
+ return array();
+ }
+
/**
* Get posts from the database based on a post type and an addon name.
*
diff --git a/forms-bridge/includes/class-rest-settings-controller.php b/forms-bridge/includes/class-rest-settings-controller.php
index 789896b1..3bced50e 100644
--- a/forms-bridge/includes/class-rest-settings-controller.php
+++ b/forms-bridge/includes/class-rest-settings-controller.php
@@ -332,10 +332,7 @@ private static function register_backend_routes() {
'callback' => static function ( $request ) use ( $addon ) {
return self::ping_backend( $addon, $request );
},
- 'permission_callback' => array(
- self::class,
- 'permission_callback',
- ),
+ 'permission_callback' => array( self::class, 'permission_callback' ),
'args' => array(
'backend' => FBAPI::get_backend_schema(),
'credential' => FBAPI::get_credential_schema(),
@@ -344,6 +341,27 @@ private static function register_backend_routes() {
)
);
+ register_rest_route(
+ 'forms-bridge/v1',
+ "/{$addon}/backend/endpoints",
+ array(
+ array(
+ 'methods' => WP_REST_Server::CREATABLE,
+ 'callback' => static function ( $request ) use ( $addon ) {
+ return self::get_backend_endpoints( $addon, $request );
+ },
+ 'permission_callback' => array( self::class, 'permission_callback' ),
+ 'args' => array(
+ 'backend' => FBAPI::get_backend_schema(),
+ 'method' => array(
+ 'description' => __( 'HTTP method used to filter the list of endpoints', 'forms-bridge' ),
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ );
+
register_rest_route(
'forms-bridge/v1',
"/{$addon}/backend/endpoint/schema",
@@ -353,25 +371,16 @@ private static function register_backend_routes() {
'callback' => static function ( $request ) use ( $addon ) {
return self::get_endpoint_schema( $addon, $request );
},
- 'permission_callback' => array(
- self::class,
- 'permission_callback',
- ),
+ 'permission_callback' => array( self::class, 'permission_callback' ),
'args' => array(
'backend' => FBAPI::get_backend_schema(),
'endpoint' => array(
- 'description' => __(
- 'Target endpoint name',
- 'forms-bridge'
- ),
+ 'description' => __( 'Target endpoint name', 'forms-bridge' ),
'type' => 'string',
'required' => true,
),
'method' => array(
- 'description' => __(
- 'HTTP method',
- 'forms-bridge',
- ),
+ 'description' => __( 'HTTP method', 'forms-bridge' ),
'type' => 'string',
),
),
@@ -818,6 +827,39 @@ private static function ping_backend( $addon, $request ) {
return array( 'success' => $result );
}
+ /**
+ * Backend endpoints route callback.
+ *
+ * @param string $addon Addon name.
+ * @param WP_REST_Request $request Request object.
+ *
+ * @return array|WP_Error
+ */
+ private static function get_backend_endpoints( $addon, $request ) {
+ $handler = self::prepare_addon_backend_request_handler( $addon, $request );
+
+ if ( is_wp_error( $handler ) ) {
+ return $handler;
+ }
+
+ [$addon, $backend] = $handler;
+
+ $endpoints = $addon->get_endpoints( $backend, $request['method'] );
+
+ if ( is_wp_error( $endpoints ) ) {
+ $error = self::internal_server_error();
+ $error->add(
+ $endpoints->get_error_code(),
+ $endpoints->get_error_message(),
+ $endpoints->get_error_data()
+ );
+
+ return $error;
+ }
+
+ return $endpoints;
+ }
+
/**
* Backend endpoint schema route callback.
*
@@ -827,10 +869,7 @@ private static function ping_backend( $addon, $request ) {
* @return array|WP_Error
*/
private static function get_endpoint_schema( $addon, $request ) {
- $handler = self::prepare_addon_backend_request_handler(
- $addon,
- $request
- );
+ $handler = self::prepare_addon_backend_request_handler( $addon, $request );
if ( is_wp_error( $handler ) ) {
return $handler;
diff --git a/src/components/Bridge/Fields.jsx b/src/components/Bridge/Fields.jsx
index 102e1180..a4815138 100644
--- a/src/components/Bridge/Fields.jsx
+++ b/src/components/Bridge/Fields.jsx
@@ -2,8 +2,9 @@ import { useBackends } from "../../hooks/useHttp";
import { useForms } from "../../providers/Forms";
import { isset, prependEmptyOption } from "../../lib/utils";
import FieldWrapper from "../FieldWrapper";
+import { useApiEndpoints } from "../../providers/ApiSchema";
-const { TextControl, SelectControl } = wp.components;
+const { BaseControl, SelectControl } = wp.components;
const { useEffect, useMemo } = wp.element;
export const INTERNALS = [
@@ -45,6 +46,8 @@ export default function BridgeFields({ data, setData, schema, errors = {} }) {
});
}, [forms]);
+ const endpoints = useApiEndpoints();
+
const fields = useMemo(() => {
if (!schema) return [];
@@ -135,6 +138,7 @@ export default function BridgeFields({ data, setData, schema, errors = {} }) {
label={field.label}
value={data[field.name] || ""}
setValue={(value) => setData({ ...data, [field.name]: value })}
+ datalist={field.name === "endpoint" ? endpoints : []}
/>
);
case "select":
@@ -152,18 +156,41 @@ export default function BridgeFields({ data, setData, schema, errors = {} }) {
});
}
-export function StringField({ label, value, setValue, error, disabled }) {
+export function StringField({
+ label,
+ value,
+ setValue,
+ error,
+ disabled,
+ datalist = [],
+}) {
return (
-
+
+ setValue(ev.target.value)}
+ disabled={disabled}
+ style={{
+ height: "40px",
+ paddingRight: "12px",
+ paddingLeft: "12px",
+ borderColor: "var(--wp-components-color-gray-600,#949494)",
+ fontSize: "13px",
+ width: "100%",
+ border: "1px solid #949494",
+ borderRadius: "2px",
+ }}
+ />
+
+
);
}
diff --git a/src/providers/ApiSchema.jsx b/src/providers/ApiSchema.jsx
index ef2e9ca2..4e5e2085 100644
--- a/src/providers/ApiSchema.jsx
+++ b/src/providers/ApiSchema.jsx
@@ -11,32 +11,78 @@ export default function ApiSchemaProvider({ children, bridge }) {
const [addon] = useTab();
const [backends] = useBackends();
- const [loading, setLoading] = useState(false);
+ const [loadingEndpoints, setLoadingEndpoints] = useState(false);
+ const [loadingSchema, setLoadingSchema] = useState(false);
+
+ const endpoints = useRef(new Map()).current;
const schemas = useRef(new Map()).current;
- const [, updates] = useState(0);
+
+ const [, endpointUpdates] = useState(0);
+ const [, schemaUpdates] = useState(0);
const backend = useMemo(
() => backends.find(({ name }) => bridge?.backend === name),
[backends, bridge]
);
- const key = useMemo(
- () =>
- JSON.stringify({
- method: bridge?.method,
- endpoint: bridge?.endpoint,
- backend,
- }),
- [bridge?.endpoint, bridge?.method, backend]
- );
+ const endpointsKey = useMemo(() => {
+ if (!backend?.name) return "";
+
+ return JSON.stringify({
+ addon,
+ method: bridge?.method || "",
+ backend: backend?.name,
+ });
+ }, [addon, bridge?.method, backend?.name]);
+
+ const addEndpoints = (key, list) => {
+ endpoints.set(key, list);
+ endpointUpdates((i) => i + 1);
+ };
+
+ const fetchEndpoints = (key, method, backend) => {
+ setLoadingEndpoints(true);
+
+ apiFetch({
+ path: `forms-bridge/v1/${addon}/backend/endpoints`,
+ method: "POST",
+ data: { method, backend },
+ })
+ .then((endpoints) => addEndpoints(key, endpoints))
+ .catch(() => addEndpoints(key, []))
+ .finally(() => setLoadingEndpoints(false));
+ };
+
+ const endpointsTimeout = useRef();
+ useEffect(() => {
+ clearTimeout(endpointsTimeout.current);
+
+ if (!bridge || loadingEndpoints || endpoints.get(endpointsKey)) return;
+
+ endpointsTimeout.current = setTimeout(
+ () => fetchEndpoints(endpointsKey, bridge.method, backend),
+ 400
+ );
+ }, [endpointsKey, bridge, backend]);
+
+ const schemaKey = useMemo(() => {
+ if (!bridge?.method || !backend?.name) return "";
+
+ return JSON.stringify({
+ addon,
+ method: bridge.method,
+ endpoint: bridge.endpoint || "/",
+ backend: backend.name,
+ });
+ }, [addon, bridge?.method, bridge?.endpoint, backend?.name]);
const addSchema = (key, schema) => {
schemas.set(key, schema);
- updates((i) => i + 1);
+ schemaUpdates((i) => i + 1);
};
- const fetch = (key, { endpoint, method }, backend) => {
- setLoading(true);
+ const fetchSchema = (key, endpoint, method, backend) => {
+ setLoadingSchema(true);
apiFetch({
path: `forms-bridge/v1/${addon}/backend/endpoint/schema`,
@@ -45,27 +91,40 @@ export default function ApiSchemaProvider({ children, bridge }) {
})
.then((schema) => addSchema(key, schema))
.catch(() => addSchema(key, []))
- .finally(() => setLoading(false));
+ .finally(() => setLoadingSchema(false));
};
- const timeout = useRef();
+ const schemaTimeout = useRef();
useEffect(() => {
- clearTimeout(timeout.current);
+ clearTimeout(schemaTimeout.current);
- if (!backend || !bridge?.endpoint || loading || schemas.get(key)) return;
+ if (!bridge || !backend || loadingSchema || schemas.get(schemaKey)) return;
- timeout.current = setTimeout(() => fetch(key, bridge, backend), 400);
- }, [key, bridge, backend]);
+ schemaTimeout.current = setTimeout(
+ () =>
+ fetchSchema(schemaKey, bridge.endpoint || "/", bridge.method, backend),
+ 400
+ );
+ }, [schemaKey, bridge, backend]);
- const schema = schemas.get(key);
return (
-
+
{children}
);
}
export function useApiFields() {
- const schema = useContext(ApiSchemaContext);
+ const { schema } = useContext(ApiSchemaContext);
return schema || [];
}
+
+export function useApiEndpoints() {
+ const { endpoints } = useContext(ApiSchemaContext);
+ return endpoints || [];
+}