array( 'name' => __( 'REST Collections', 'advanced-rest-api-devtools' ), 'singular_name' => __( 'REST Collection', 'advanced-rest-api-devtools' ), ), 'public' => false, 'show_ui' => false, 'show_in_rest' => false, 'supports' => array( 'title' ), 'capability_type' => 'post', 'map_meta_cap' => true, ) ); } /** * Register AJAX handlers. */ public static function register_ajax() { add_action( 'wp_ajax_aradt_collections_list', array( __CLASS__, 'ajax_list' ) ); add_action( 'wp_ajax_aradt_collections_get', array( __CLASS__, 'ajax_get' ) ); add_action( 'wp_ajax_aradt_collections_save', array( __CLASS__, 'ajax_save' ) ); add_action( 'wp_ajax_aradt_collections_delete', array( __CLASS__, 'ajax_delete' ) ); add_action( 'wp_ajax_aradt_collections_import', array( __CLASS__, 'ajax_import' ) ); add_action( 'wp_ajax_aradt_collections_export', array( __CLASS__, 'ajax_export' ) ); add_action( 'wp_ajax_aradt_collections_run', array( __CLASS__, 'ajax_run' ) ); } /** * AJAX: List all collections. */ public static function ajax_list() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $posts = get_posts( array( 'post_type' => 'aradt_collection', 'posts_per_page' => 100, 'orderby' => 'title', 'order' => 'ASC', ) ); $collections = array(); foreach ( $posts as $post ) { $requests = get_post_meta( $post->ID, '_aradt_col_requests', true ); $collections[] = array( 'id' => $post->ID, 'name' => $post->post_title, 'request_count' => is_array( $requests ) ? count( $requests ) : 0, 'modified' => $post->post_modified, ); } wp_send_json_success( array( 'collections' => $collections ) ); } /** * AJAX: Get a single collection with its requests. */ public static function ajax_get() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $id = absint( $_POST['collection_id'] ?? 0 ); if ( ! $id ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid collection ID.', 'advanced-rest-api-devtools' ) ) ); } $post = get_post( $id ); if ( ! $post || 'aradt_collection' !== $post->post_type ) { wp_send_json_error( array( 'message' => esc_html__( 'Collection not found.', 'advanced-rest-api-devtools' ) ) ); } $requests = get_post_meta( $id, '_aradt_col_requests', true ); $variables = get_post_meta( $id, '_aradt_col_variables', true ); wp_send_json_success( array( 'collection' => array( 'id' => $post->ID, 'name' => $post->post_title, 'requests' => is_array( $requests ) ? $requests : array(), 'variables' => is_array( $variables ) ? $variables : array(), 'modified' => $post->post_modified, ), ) ); } /** * AJAX: Save (create/update) a collection. */ public static function ajax_save() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $id = isset( $_POST['collection_id'] ) ? absint( $_POST['collection_id'] ) : 0; $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['name'] ) ) : ''; $requests = isset( $_POST['requests'] ) ? Aradt_Utils::sanitize_recursive( Aradt_Utils::safe_json_decode( sanitize_textarea_field( wp_unslash( (string) $_POST['requests'] ) ) ) ) : array(); $variables = isset( $_POST['variables'] ) ? Aradt_Utils::sanitize_recursive( Aradt_Utils::safe_json_decode( sanitize_textarea_field( wp_unslash( (string) $_POST['variables'] ) ) ) ) : array(); if ( empty( $name ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Collection name is required.', 'advanced-rest-api-devtools' ) ) ); } if ( ! is_array( $requests ) ) { $requests = array(); } if ( ! is_array( $variables ) ) { $variables = array(); } $post_data = array( 'post_type' => 'aradt_collection', 'post_title' => $name, 'post_status' => 'publish', ); if ( $id ) { $post_data['ID'] = $id; $result = wp_update_post( $post_data ); } else { $result = wp_insert_post( $post_data ); } if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } $post_id = $id ? $id : $result; update_post_meta( $post_id, '_aradt_col_requests', $requests ); update_post_meta( $post_id, '_aradt_col_variables', $variables ); wp_send_json_success( array( 'id' => $post_id, 'message' => esc_html__( 'Collection saved.', 'advanced-rest-api-devtools' ), ) ); } /** * AJAX: Delete a collection. */ public static function ajax_delete() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $id = absint( $_POST['collection_id'] ?? 0 ); if ( ! $id ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid collection ID.', 'advanced-rest-api-devtools' ) ) ); } $result = wp_delete_post( $id, true ); if ( ! $result ) { wp_send_json_error( array( 'message' => esc_html__( 'Failed to delete collection.', 'advanced-rest-api-devtools' ) ) ); } wp_send_json_success( array( 'message' => esc_html__( 'Collection deleted.', 'advanced-rest-api-devtools' ) ) ); } /** * AJAX: Export a collection as JSON. */ public static function ajax_export() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $id = absint( $_POST['collection_id'] ?? 0 ); if ( ! $id ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid collection ID.', 'advanced-rest-api-devtools' ) ) ); } $post = get_post( $id ); if ( ! $post || 'aradt_collection' !== $post->post_type ) { wp_send_json_error( array( 'message' => esc_html__( 'Collection not found.', 'advanced-rest-api-devtools' ) ) ); } $requests = get_post_meta( $id, '_aradt_col_requests', true ); $variables = get_post_meta( $id, '_aradt_col_variables', true ); $export = array( '_type' => 'aradt_collection', 'version' => ARADT_VERSION, 'name' => $post->post_title, 'requests' => is_array( $requests ) ? $requests : array(), 'variables' => is_array( $variables ) ? $variables : array(), 'exported' => current_time( 'mysql' ), ); wp_send_json_success( array( 'export' => $export ) ); } /** * AJAX: Import a collection from JSON. */ public static function ajax_import() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $json = isset( $_POST['import_data'] ) ? Aradt_Utils::sanitize_recursive( Aradt_Utils::safe_json_decode( sanitize_textarea_field( wp_unslash( (string) $_POST['import_data'] ) ) ) ) : array(); if ( ! is_array( $json ) || ! isset( $json['_type'] ) || 'aradt_collection' !== $json['_type'] ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid collection format.', 'advanced-rest-api-devtools' ) ) ); } $name = sanitize_text_field( $json['name'] ?? __( 'Imported Collection', 'advanced-rest-api-devtools' ) ); $requests = isset( $json['requests'] ) && is_array( $json['requests'] ) ? $json['requests'] : array(); $variables = isset( $json['variables'] ) && is_array( $json['variables'] ) ? $json['variables'] : array(); $post_id = wp_insert_post( array( 'post_type' => 'aradt_collection', 'post_title' => $name, 'post_status' => 'publish', ) ); if ( is_wp_error( $post_id ) ) { wp_send_json_error( array( 'message' => $post_id->get_error_message() ) ); } update_post_meta( $post_id, '_aradt_col_requests', $requests ); update_post_meta( $post_id, '_aradt_col_variables', $variables ); wp_send_json_success( array( 'id' => $post_id, 'message' => esc_html__( 'Collection imported.', 'advanced-rest-api-devtools' ), ) ); } /** * AJAX: Run a collection (execute all requests sequentially). */ public static function ajax_run() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $id = absint( $_POST['collection_id'] ?? 0 ); if ( ! $id ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid collection ID.', 'advanced-rest-api-devtools' ) ) ); } $requests = get_post_meta( $id, '_aradt_col_requests', true ); if ( ! is_array( $requests ) || empty( $requests ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Collection has no requests.', 'advanced-rest-api-devtools' ) ) ); } $coll_vars = get_post_meta( $id, '_aradt_col_variables', true ); if ( ! is_array( $coll_vars ) ) { $coll_vars = array(); } $active_vars = Aradt_Utils::get_active_variables( $coll_vars ); $chain_vars = array(); // For variable chaining between requests. $results = array(); foreach ( $requests as $index => $req ) { $method = isset( $req['method'] ) ? strtoupper( $req['method'] ) : 'GET'; $url = isset( $req['url'] ) ? $req['url'] : ''; $headers = isset( $req['headers'] ) && is_array( $req['headers'] ) ? $req['headers'] : array(); $body = isset( $req['body'] ) ? $req['body'] : ''; $assertions = isset( $req['assertions'] ) ? $req['assertions'] : ''; // Replace variables (active + chained). $all_vars = array_merge( $active_vars, $chain_vars ); $url = Aradt_Utils::replace_variables( $url, $all_vars ); $body = Aradt_Utils::replace_variables( $body, $all_vars ); $headers = Aradt_Utils::replace_variables_in_headers( $headers, $all_vars ); // Make absolute. if ( ! str_starts_with( $url, 'http' ) ) { $url = rtrim( rest_url(), '/' ) . '/' . ltrim( $url, '/' ); } $request_args = array( 'method' => $method, 'headers' => $headers, 'timeout' => 30, 'sslverify' => false, ); if ( ! empty( $body ) && in_array( $method, array( 'POST', 'PUT', 'PATCH' ), true ) ) { $request_args['body'] = $body; } $start = microtime( true ); $response = wp_remote_request( $url, $request_args ); $time_ms = round( ( microtime( true ) - $start ) * 1000, 2 ); if ( is_wp_error( $response ) ) { $results[] = array( 'index' => $index, 'name' => $req['name'] ?? "Request #" . ( $index + 1 ), 'method' => $method, 'url' => $url, 'status' => 0, 'error' => $response->get_error_message(), 'time_ms' => $time_ms, 'assertions' => array(), 'passed' => false, ); continue; } $resp_code = wp_remote_retrieve_response_code( $response ); $resp_body = wp_remote_retrieve_body( $response ); $resp_headers = wp_remote_retrieve_headers( $response )->getAll(); // Run assertions if defined. $assertion_results = array(); if ( ! empty( $assertions ) ) { $assertion_results = Aradt_Utils::run_assertions( $assertions, $resp_code, $resp_body, $resp_headers ); } $all_passed = empty( $assertion_results ) || ! in_array( false, array_column( $assertion_results, 'passed' ), true ); // Variable chaining: extract pm.variables.set from response scripts. $resp_json = json_decode( $resp_body, true ); if ( isset( $req['chain_vars'] ) && is_array( $req['chain_vars'] ) && is_array( $resp_json ) ) { foreach ( $req['chain_vars'] as $var_def ) { if ( isset( $var_def['key'], $var_def['json_path'] ) && isset( $resp_json[ $var_def['json_path'] ] ) ) { $chain_vars[ $var_def['key'] ] = (string) $resp_json[ $var_def['json_path'] ]; } } } $results[] = array( 'index' => $index, 'name' => $req['name'] ?? "Request #" . ( $index + 1 ), 'method' => $method, 'url' => $url, 'status' => $resp_code, 'time_ms' => $time_ms, 'assertions' => $assertion_results, 'passed' => $all_passed, ); } $total_passed = count( array_filter( $results, fn( $r ) => $r['passed'] ) ); $total_failed = count( $results ) - $total_passed; wp_send_json_success( array( 'results' => $results, 'total' => count( $results ), 'passed' => $total_passed, 'failed' => $total_failed, ) ); } }