/** * WPML compatibility functions * * @global array $duplicated_posts Array to store the posts being duplicated. * * @package Yoast\WP\Duplicate_Post * @since 3.2 */ add_action( 'admin_init', 'duplicate_post_wpml_init' ); /** * Add handlers for WPML compatibility. */ function duplicate_post_wpml_init() { if ( defined( 'ICL_SITEPRESS_VERSION' ) ) { add_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'shutdown', 'duplicate_wpml_string_packages', 11 ); } } global $duplicated_posts; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: Renaming a global variable is a BC break. $duplicated_posts = []; /** * Copy post translations. * * @global SitePress $sitepress Instance of the Main WPML class. * @global array $duplicated_posts Array of duplicated posts. * * @param int $post_id ID of the copy. * @param WP_Post $post Original post object. * @param string $status Status of the new post. */ function duplicate_post_wpml_copy_translations( $post_id, $post, $status = '' ) { global $sitepress; global $duplicated_posts; remove_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10 ); remove_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10 ); $current_language = $sitepress->get_current_language(); $trid = $sitepress->get_element_trid( $post->ID ); if ( ! empty( $trid ) ) { $translations = $sitepress->get_element_translations( $trid ); $new_trid = $sitepress->get_element_trid( $post_id ); foreach ( $translations as $code => $details ) { if ( $code !== $current_language ) { if ( $details->element_id ) { $translation = get_post( $details->element_id ); if ( ! $translation ) { continue; } $new_post_id = duplicate_post_create_duplicate( $translation, $status ); if ( ! is_wp_error( $new_post_id ) ) { $sitepress->set_element_language_details( $new_post_id, 'post_' . $translation->post_type, $new_trid, $code, $current_language ); } } } } // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: see above. $duplicated_posts[ $post->ID ] = $post_id; } } /** * Duplicate string packages. * * @global array() $duplicated_posts Array of duplicated posts. */ function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: renaming the function would be a BC-break. global $duplicated_posts; foreach ( $duplicated_posts as $original_post_id => $duplicate_post_id ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $original_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $original_post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $new_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $duplicate_post_id ); if ( is_array( $original_string_packages ) ) { foreach ( $original_string_packages as $original_string_package ) { $translated_original_strings = $original_string_package->get_translated_strings( [] ); foreach ( $new_string_packages as $new_string_package ) { $cache = new WPML_WP_Cache( 'WPML_Package' ); $cache->flush_group_cache(); $new_strings = $new_string_package->get_package_strings(); foreach ( $new_strings as $new_string ) { if ( isset( $translated_original_strings[ $new_string->name ] ) ) { foreach ( $translated_original_strings[ $new_string->name ] as $language => $translated_string ) { do_action( // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. 'wpml_add_string_translation', $new_string->id, $language, $translated_string['value'], $translated_string['status'] ); } } } } } } } } dev-portalの追加開発部分 – Raqqa

dev-portalの追加開発部分

openapi: 3.1.0
info:
  title: Dev-Portal NLQ SQL Params API
  version: 0.1.0
  description: |
    nlq-dev から SQL/Cypher を送らず、sql_params のみで Dev-Portal 側が
    (1) 必要なら ChromaDB でモデル/フィールド候補を特定し
    (2) SQL を生成し
    (3) 安全に実行して
    tabular_result を返すためのインターフェース。

servers:
  - url: https://{DEVPORTAL_HOST}
    variables:
      DEVPORTAL_HOST:
        default: api.dev-portal.local

security:
  - DevPortalApiKeyAuth: []
  - BearerAuth: []
  - {}: []

paths:
  /health:
    get:
      tags: [Health]
      summary: Health check (no auth recommended)
      operationId: healthCheck
      security:
        - {}: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string, enum: [ok] }
                  service: { type: string, example: dev-portal }
                  version: { type: string, example: "0.1.0" }
                required: [status]

  /sql/execute_params:
    post:
      tags: [NLQ]
      summary: Execute sql_params (Dev-Portal generates SQL and runs safely)
      operationId: executeSqlParams
      description: |
        nlq-dev は SQL を送らない。自然文を params 化した sql_params を送信する。
        Dev-Portal 側が sql_params と (任意で) Chroma 検索を用いて対象モデル/フィールドを確定し、
        SQL を生成して安全に実行し、表形式結果を返す。

        実行は原則 READ-ONLY。
        Dev-Portal 側は max_rows に基づき LIMIT を強制し、危険なクエリを拒否すること。
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SqlParamsExecuteRequest"
            examples:
              aggregate_example:
                summary: Simple aggregate
                value:
                  sql_params:
                    intent: kpi_aggregate
                    model: sale.order
                    measures:
                      - name: amount_total
                        agg: sum
                    group_by:
                      - field: date_order
                        granularity: month
                    filters:
                      - field: date_order
                        op: between
                        value: ["2025-01-01", "2025-12-31"]
                  max_rows: 200
                  dry_run: false
      responses:
        "200":
          description: Tabular result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TabularResult"
        "400":
          description: Invalid params / validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Problem"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Problem"
        "403":
          description: Forbidden (policy/permission)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Problem"
        "422":
          description: Unprocessable (params are syntactically valid but not executable)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Problem"
        "500":
          description: Internal error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Problem"

components:
  securitySchemes:
    DevPortalApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Optional API key auth (if enabled in prod).
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Optional JWT/JWKS auth (if enabled in prod).

  schemas:
    SqlParamsExecuteRequest:
      type: object
      additionalProperties: false
      properties:
        sql_params:
          $ref: "#/components/schemas/SqlParams"
        max_rows:
          type: integer
          minimum: 1
          maximum: 5000
          default: 200
          description: |
            Dev-Portal 側で LIMIT を強制するための最大行数。
            実装側は必ずこの値を上限として適用すること。
        dry_run:
          type: boolean
          default: false
          description: |
            true の場合、SQL を生成し、可能なら EXPLAIN/validate まで行うが、実データ取得は行わない。
        explain:
          type: boolean
          default: false
          description: |
            true の場合、可能なら EXPLAIN を返す(dry_run と併用推奨)。
        timeout_ms:
          type: integer
          minimum: 1000
          maximum: 120000
          default: 30000
          description: |
            実行タイムアウト(ms)。実装側はドライバ/DB側タイムアウトも含めて尊重すること。
        trace_id:
          type: string
          description: |
            呼び出し側(nlq-dev)が指定する任意の相関ID。指定がなければ Dev-Portal が生成して返す。
      required: [sql_params]

    SqlParams:
      type: object
      description: |
        nlq-dev が自然文から生成する params。
        Dev-Portal はこの params を元に(必要なら Chroma 検索を併用して)SQL を生成する。

        NOTE: 初期は柔軟性のため additionalProperties=true とし、nlq-dev 側の進化に追従しやすくする。
      additionalProperties: true
      properties:
        intent:
          type: string
          description: |
            実行意図(例:kpi_aggregate, list_records, trend, compare_periods など)
        model:
          type: string
          description: Odoo model technical name (e.g., sale.order)
        measures:
          type: array
          items:
            $ref: "#/components/schemas/Measure"
        group_by:
          type: array
          items:
            $ref: "#/components/schemas/GroupBy"
        filters:
          type: array
          items:
            $ref: "#/components/schemas/Filter"
        order_by:
          type: array
          items:
            $ref: "#/components/schemas/OrderBy"

    Measure:
      type: object
      additionalProperties: false
      properties:
        name: { type: string, description: Measure field or logical metric name }
        agg:
          type: string
          enum: [sum, avg, min, max, count, count_distinct]
      required: [name, agg]

    GroupBy:
      type: object
      additionalProperties: false
      properties:
        field: { type: string }
        granularity:
          type: string
          enum: [day, week, month, quarter, year]
      required: [field]

    Filter:
      type: object
      additionalProperties: false
      properties:
        field: { type: string }
        op:
          type: string
          enum: [eq, ne, lt, lte, gt, gte, in, not_in, like, ilike, between, is_null, is_not_null]
        value:
          description: |
            op に応じて型が変わる(例:betweenは [from,to] 配列)。
          oneOf:
            - type: string
            - type: number
            - type: boolean
            - type: array
              items: {}
            - type: "null"
      required: [field, op]

    OrderBy:
      type: object
      additionalProperties: false
      properties:
        field: { type: string }
        dir:
          type: string
          enum: [asc, desc]
          default: asc
      required: [field]

    TabularResult:
      type: object
      additionalProperties: false
      properties:
        columns:
          type: array
          items:
            $ref: "#/components/schemas/Column"
        rows:
          type: array
          items:
            type: array
            items: {}
        row_count:
          type: integer
          description: Returned row count
        truncated:
          type: boolean
          description: True if max_rows caused truncation
        warnings:
          type: array
          items:
            type: string
        generated_sql:
          type: string
          description: |
            オプション。デバッグ用。prodでは返さない運用でもよい(設定で制御)。
        explain_text:
          type: string
          description: Optional EXPLAIN output (if explain=true or dry_run=true)
        trace_id:
          type: string
          description: Correlation id
      required: [columns, rows, row_count, truncated, trace_id]

    Column:
      type: object
      additionalProperties: false
      properties:
        name: { type: string }
        type: { type: string, description: logical type hint (e.g., number, string, date) }
      required: [name]

    Problem:
      type: object
      additionalProperties: false
      properties:
        type: { type: string, example: "about:blank" }
        title: { type: string, example: "Bad Request" }
        status: { type: integer, example: 400 }
        detail: { type: string }
        instance: { type: string }
        trace_id: { type: string }
        errors:
          type: array
          items:
            type: object
            additionalProperties: true
      required: [title, status]

Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です