github.com/qiniu/go-sdk/v7@v7.27.0/sandbox/types_injection_rule.go (about)

     1  package sandbox
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/qiniu/go-sdk/v7/sandbox/internal/apis"
     8  )
     9  
    10  // ---------------------------------------------------------------------------
    11  // SDK 自有类型 — 注入配置(含注入规则)
    12  // ---------------------------------------------------------------------------
    13  
    14  // OpenAIInjection OpenAI 兼容 API 注入配置。自动设置 Authorization: Bearer 头。
    15  // 默认 host: api.openai.com
    16  type OpenAIInjection struct {
    17  	// APIKey API 密钥,可选。
    18  	APIKey *string
    19  
    20  	// BaseURL 可选 base URL,未指定时使用 api.openai.com。host 精确匹配;
    21  	// path 存在时精确匹配规范化路径,尾部 * 表示前缀匹配。
    22  	BaseURL *string
    23  
    24  	// IfHeaders 可选请求 Header 匹配条件,满足后才注入。
    25  	IfHeaders *map[string]string
    26  
    27  	// IfQueries 可选请求 query 匹配条件,满足后才注入。
    28  	IfQueries *map[string]string
    29  }
    30  
    31  // AnthropicInjection Anthropic API 注入配置。自动设置 x-api-key 头。
    32  // 默认 host: api.anthropic.com
    33  type AnthropicInjection struct {
    34  	// APIKey API 密钥,可选。
    35  	APIKey *string
    36  
    37  	// BaseURL 可选 base URL,未指定时使用 api.anthropic.com。host 精确匹配;
    38  	// path 存在时精确匹配规范化路径,尾部 * 表示前缀匹配。
    39  	BaseURL *string
    40  
    41  	// IfHeaders 可选请求 Header 匹配条件,满足后才注入。
    42  	IfHeaders *map[string]string
    43  
    44  	// IfQueries 可选请求 query 匹配条件,满足后才注入。
    45  	IfQueries *map[string]string
    46  }
    47  
    48  // GeminiInjection Google Gemini API 注入配置。自动设置 x-goog-api-key 头。
    49  // 默认 host: generativelanguage.googleapis.com
    50  type GeminiInjection struct {
    51  	// APIKey API 密钥,可选。
    52  	APIKey *string
    53  
    54  	// BaseURL 可选 base URL,未指定时使用 generativelanguage.googleapis.com。
    55  	// host 精确匹配;path 存在时精确匹配规范化路径,尾部 * 表示前缀匹配。
    56  	BaseURL *string
    57  
    58  	// IfHeaders 可选请求 Header 匹配条件,满足后才注入。
    59  	IfHeaders *map[string]string
    60  
    61  	// IfQueries 可选请求 query 匹配条件,满足后才注入。
    62  	IfQueries *map[string]string
    63  }
    64  
    65  // QiniuInjection 七牛 AI API 注入配置。七牛网关同时兼容 OpenAI 和 Anthropic 协议,
    66  // api_key 会注入到请求实际携带的认证头(Authorization 或 x-api-key)。
    67  // 默认 host: api.qnaigc.com
    68  type QiniuInjection struct {
    69  	// APIKey API 密钥。
    70  	APIKey *string
    71  
    72  	// BaseURL 可选 base URL,未指定时使用 api.qnaigc.com。host 精确匹配;
    73  	// path 存在时精确匹配规范化路径,尾部 * 表示前缀匹配。
    74  	BaseURL *string
    75  
    76  	// IfHeaders 可选请求 Header 匹配条件,满足后才注入。
    77  	IfHeaders *map[string]string
    78  
    79  	// IfQueries 可选请求 query 匹配条件,满足后才注入。
    80  	IfQueries *map[string]string
    81  }
    82  
    83  // GithubInjection GitHub 凭证注入。平台用此凭证克隆并校验仓库,且在沙箱运行期间为
    84  // 匹配 github.com / api.github.com 的 HTTPS 请求自动注入认证;token 不会以明文形式
    85  // 暴露到沙箱内。
    86  type GithubInjection struct {
    87  	// BaseURL 可选 base URL,未指定时平台会匹配 github.com 和 api.github.com。
    88  	// 指定时 host 必须为 github.com 或 api.github.com。
    89  	BaseURL *string
    90  
    91  	// IfHeaders 可选请求 Header 匹配条件,满足后才注入。
    92  	IfHeaders *map[string]string
    93  
    94  	// IfQueries 可选请求 query 匹配条件,满足后才注入。
    95  	IfQueries *map[string]string
    96  
    97  	// Token GitHub token,对所有请求的仓库具备访问权限。
    98  	Token *string
    99  }
   100  
   101  // HTTPInjection 自定义 HTTP 注入配置。
   102  type HTTPInjection struct {
   103  	// BaseURL 匹配 HTTPS 请求的 base URL。host 精确匹配;path 存在时精确匹配
   104  	// 规范化路径,尾部 * 表示前缀匹配。若未指定 scheme 默认为 https。
   105  	BaseURL string
   106  
   107  	// Headers 需要注入或覆盖的 HTTP Headers,可选。
   108  	Headers *map[string]string
   109  
   110  	// IfHeaders 可选请求 Header 匹配条件,满足后才注入。
   111  	IfHeaders *map[string]string
   112  
   113  	// IfQueries 可选请求 query 匹配条件,满足后才注入。
   114  	IfQueries *map[string]string
   115  }
   116  
   117  // InjectionSpec 注入配置(discriminated union),各字段互斥,只能设置一个。
   118  type InjectionSpec struct {
   119  	// OpenAI OpenAI 兼容 API 注入。
   120  	OpenAI *OpenAIInjection
   121  
   122  	// Anthropic Anthropic API 注入。
   123  	Anthropic *AnthropicInjection
   124  
   125  	// Gemini Google Gemini API 注入。
   126  	Gemini *GeminiInjection
   127  
   128  	// Qiniu 七牛 AI API 注入。
   129  	Qiniu *QiniuInjection
   130  
   131  	// Github GitHub 凭证注入。
   132  	Github *GithubInjection
   133  
   134  	// HTTP 自定义 HTTP 注入。
   135  	HTTP *HTTPInjection
   136  }
   137  
   138  // SandboxInjectionSpec 沙箱注入配置(discriminated union)。
   139  // 可引用已保存的注入规则 ID,或直接指定注入配置,各字段互斥。
   140  type SandboxInjectionSpec struct {
   141  	// ByID 引用已保存的注入规则 ID。
   142  	ByID *string
   143  
   144  	// OpenAI OpenAI 兼容 API 注入。
   145  	OpenAI *OpenAIInjection
   146  
   147  	// Anthropic Anthropic API 注入。
   148  	Anthropic *AnthropicInjection
   149  
   150  	// Gemini Google Gemini API 注入。
   151  	Gemini *GeminiInjection
   152  
   153  	// Qiniu 七牛 AI API 注入。
   154  	Qiniu *QiniuInjection
   155  
   156  	// Github GitHub 凭证注入。
   157  	Github *GithubInjection
   158  
   159  	// HTTP 自定义 HTTP 注入。
   160  	HTTP *HTTPInjection
   161  }
   162  
   163  // MaskedSandboxInjection 沙箱当前的请求注入配置查询结果。
   164  // APIKey、Token 和 Headers 等敏感字段仅包含服务端返回的脱敏值。
   165  // 该类型与可写的 SandboxInjectionSpec 保持分离,不能直接用于更新配置。
   166  type MaskedSandboxInjection struct {
   167  	// ByID 引用的已保存注入规则 ID。
   168  	ByID *string
   169  
   170  	// OpenAI 已脱敏的 OpenAI 兼容 API 注入配置。
   171  	OpenAI *OpenAIInjection
   172  
   173  	// Anthropic 已脱敏的 Anthropic API 注入配置。
   174  	Anthropic *AnthropicInjection
   175  
   176  	// Gemini 已脱敏的 Google Gemini API 注入配置。
   177  	Gemini *GeminiInjection
   178  
   179  	// Qiniu 已脱敏的七牛 AI API 注入配置。
   180  	Qiniu *QiniuInjection
   181  
   182  	// Github 已脱敏的 GitHub 凭证注入配置。
   183  	Github *GithubInjection
   184  
   185  	// HTTP 已脱敏的自定义 HTTP 注入配置。
   186  	HTTP *HTTPInjection
   187  }
   188  
   189  // InjectionRule 预定义的请求注入规则。
   190  type InjectionRule struct {
   191  	// RuleID 规则唯一标识。
   192  	RuleID string
   193  
   194  	// Name 规则名称,同一用户下唯一。
   195  	Name string
   196  
   197  	// Injection 注入配置。
   198  	Injection InjectionSpec
   199  
   200  	// CreatedAt 创建时间。
   201  	CreatedAt time.Time
   202  
   203  	// UpdatedAt 最后更新时间。
   204  	UpdatedAt time.Time
   205  }
   206  
   207  // CreateInjectionRuleParams 创建注入规则的请求参数。
   208  type CreateInjectionRuleParams struct {
   209  	// Name 规则名称(必填),同一用户下唯一。
   210  	Name string
   211  
   212  	// Injection 注入配置(必填)。
   213  	Injection InjectionSpec
   214  }
   215  
   216  func (p *CreateInjectionRuleParams) toAPI() (apis.PostInjectionRulesJSONRequestBody, error) {
   217  	inj, err := injectionSpecToAPI(p.Injection)
   218  	if err != nil {
   219  		return apis.PostInjectionRulesJSONRequestBody{}, err
   220  	}
   221  	return apis.PostInjectionRulesJSONRequestBody{
   222  		Name:      p.Name,
   223  		Injection: inj,
   224  	}, nil
   225  }
   226  
   227  // UpdateInjectionRuleParams 更新注入规则的请求参数。
   228  type UpdateInjectionRuleParams struct {
   229  	// Name 规则名称,可选。
   230  	Name *string
   231  
   232  	// Injection 注入配置,可选。
   233  	Injection *InjectionSpec
   234  }
   235  
   236  func (p *UpdateInjectionRuleParams) toAPI() (apis.PutInjectionRulesRuleIDJSONRequestBody, error) {
   237  	body := apis.PutInjectionRulesRuleIDJSONRequestBody{
   238  		Name: p.Name,
   239  	}
   240  	if p.Injection != nil {
   241  		inj, err := injectionSpecToAPI(*p.Injection)
   242  		if err != nil {
   243  			return body, err
   244  		}
   245  		body.Injection = &inj
   246  	}
   247  	return body, nil
   248  }
   249  
   250  // ---------------------------------------------------------------------------
   251  // 转换函数 — SDK → apis
   252  // ---------------------------------------------------------------------------
   253  
   254  func injectionSpecToAPI(spec InjectionSpec) (apis.Injection, error) {
   255  	count := 0
   256  	if spec.OpenAI != nil {
   257  		count++
   258  	}
   259  	if spec.Anthropic != nil {
   260  		count++
   261  	}
   262  	if spec.Gemini != nil {
   263  		count++
   264  	}
   265  	if spec.HTTP != nil {
   266  		count++
   267  	}
   268  	if spec.Qiniu != nil {
   269  		count++
   270  	}
   271  	if spec.Github != nil {
   272  		count++
   273  	}
   274  	if count == 0 {
   275  		return apis.Injection{}, fmt.Errorf("InjectionSpec: exactly one injection type must be set (OpenAI, Anthropic, Gemini, Qiniu, GitHub, or HTTP), got none")
   276  	}
   277  	if count > 1 {
   278  		return apis.Injection{}, fmt.Errorf("InjectionSpec: exactly one injection type must be set, but got %d", count)
   279  	}
   280  
   281  	var inj apis.Injection
   282  	var err error
   283  	switch {
   284  	case spec.OpenAI != nil:
   285  		err = inj.FromOpenaiInjection(apis.OpenaiInjection{
   286  			APIKey:    spec.OpenAI.APIKey,
   287  			BaseURL:   spec.OpenAI.BaseURL,
   288  			IfHeaders: spec.OpenAI.IfHeaders,
   289  			IfQueries: spec.OpenAI.IfQueries,
   290  			Type:      apis.Openai,
   291  		})
   292  	case spec.Anthropic != nil:
   293  		err = inj.FromAnthropicInjection(apis.AnthropicInjection{
   294  			APIKey:    spec.Anthropic.APIKey,
   295  			BaseURL:   spec.Anthropic.BaseURL,
   296  			IfHeaders: spec.Anthropic.IfHeaders,
   297  			IfQueries: spec.Anthropic.IfQueries,
   298  			Type:      apis.Anthropic,
   299  		})
   300  	case spec.Gemini != nil:
   301  		err = inj.FromGeminiInjection(apis.GeminiInjection{
   302  			APIKey:    spec.Gemini.APIKey,
   303  			BaseURL:   spec.Gemini.BaseURL,
   304  			IfHeaders: spec.Gemini.IfHeaders,
   305  			IfQueries: spec.Gemini.IfQueries,
   306  			Type:      apis.Gemini,
   307  		})
   308  	case spec.Qiniu != nil:
   309  		err = inj.FromQiniuInjection(apis.QiniuInjection{
   310  			APIKey:    spec.Qiniu.APIKey,
   311  			BaseURL:   spec.Qiniu.BaseURL,
   312  			IfHeaders: spec.Qiniu.IfHeaders,
   313  			IfQueries: spec.Qiniu.IfQueries,
   314  			Type:      apis.Qiniu,
   315  		})
   316  	case spec.Github != nil:
   317  		err = inj.FromGithubInjection(apis.GithubInjection{
   318  			BaseURL:   spec.Github.BaseURL,
   319  			IfHeaders: spec.Github.IfHeaders,
   320  			IfQueries: spec.Github.IfQueries,
   321  			Token:     spec.Github.Token,
   322  			Type:      apis.Github,
   323  		})
   324  	case spec.HTTP != nil:
   325  		err = inj.FromHTTPInjection(apis.HTTPInjection{
   326  			BaseURL:   spec.HTTP.BaseURL,
   327  			Headers:   spec.HTTP.Headers,
   328  			IfHeaders: spec.HTTP.IfHeaders,
   329  			IfQueries: spec.HTTP.IfQueries,
   330  			Type:      apis.HTTP,
   331  		})
   332  	}
   333  	return inj, err
   334  }
   335  
   336  func sandboxInjectionSpecToAPI(spec SandboxInjectionSpec) (apis.SandboxInjection, error) {
   337  	count := 0
   338  	if spec.ByID != nil {
   339  		count++
   340  	}
   341  	if spec.OpenAI != nil {
   342  		count++
   343  	}
   344  	if spec.Anthropic != nil {
   345  		count++
   346  	}
   347  	if spec.Gemini != nil {
   348  		count++
   349  	}
   350  	if spec.Qiniu != nil {
   351  		count++
   352  	}
   353  	if spec.Github != nil {
   354  		count++
   355  	}
   356  	if spec.HTTP != nil {
   357  		count++
   358  	}
   359  	if count == 0 {
   360  		return apis.SandboxInjection{}, fmt.Errorf("SandboxInjectionSpec: exactly one injection type must be set (ByID, OpenAI, Anthropic, Gemini, Qiniu, GitHub, or HTTP), got none")
   361  	}
   362  	if count > 1 {
   363  		return apis.SandboxInjection{}, fmt.Errorf("SandboxInjectionSpec: exactly one injection type must be set, but got %d", count)
   364  	}
   365  
   366  	var si apis.SandboxInjection
   367  	var err error
   368  	switch {
   369  	case spec.ByID != nil:
   370  		err = si.FromInjectionByID(apis.InjectionByID{
   371  			ID:   *spec.ByID,
   372  			Type: apis.ID,
   373  		})
   374  	case spec.OpenAI != nil:
   375  		err = si.FromOpenaiInjection(apis.OpenaiInjection{
   376  			APIKey:    spec.OpenAI.APIKey,
   377  			BaseURL:   spec.OpenAI.BaseURL,
   378  			IfHeaders: spec.OpenAI.IfHeaders,
   379  			IfQueries: spec.OpenAI.IfQueries,
   380  			Type:      apis.Openai,
   381  		})
   382  	case spec.Anthropic != nil:
   383  		err = si.FromAnthropicInjection(apis.AnthropicInjection{
   384  			APIKey:    spec.Anthropic.APIKey,
   385  			BaseURL:   spec.Anthropic.BaseURL,
   386  			IfHeaders: spec.Anthropic.IfHeaders,
   387  			IfQueries: spec.Anthropic.IfQueries,
   388  			Type:      apis.Anthropic,
   389  		})
   390  	case spec.Gemini != nil:
   391  		err = si.FromGeminiInjection(apis.GeminiInjection{
   392  			APIKey:    spec.Gemini.APIKey,
   393  			BaseURL:   spec.Gemini.BaseURL,
   394  			IfHeaders: spec.Gemini.IfHeaders,
   395  			IfQueries: spec.Gemini.IfQueries,
   396  			Type:      apis.Gemini,
   397  		})
   398  	case spec.Qiniu != nil:
   399  		err = si.FromQiniuInjection(apis.QiniuInjection{
   400  			APIKey:    spec.Qiniu.APIKey,
   401  			BaseURL:   spec.Qiniu.BaseURL,
   402  			IfHeaders: spec.Qiniu.IfHeaders,
   403  			IfQueries: spec.Qiniu.IfQueries,
   404  			Type:      apis.Qiniu,
   405  		})
   406  	case spec.Github != nil:
   407  		err = si.FromGithubInjection(apis.GithubInjection{
   408  			BaseURL:   spec.Github.BaseURL,
   409  			IfHeaders: spec.Github.IfHeaders,
   410  			IfQueries: spec.Github.IfQueries,
   411  			Token:     spec.Github.Token,
   412  			Type:      apis.Github,
   413  		})
   414  	case spec.HTTP != nil:
   415  		err = si.FromHTTPInjection(apis.HTTPInjection{
   416  			BaseURL:   spec.HTTP.BaseURL,
   417  			Headers:   spec.HTTP.Headers,
   418  			IfHeaders: spec.HTTP.IfHeaders,
   419  			IfQueries: spec.HTTP.IfQueries,
   420  			Type:      apis.HTTP,
   421  		})
   422  	}
   423  	return si, err
   424  }
   425  
   426  // ---------------------------------------------------------------------------
   427  // 转换函数 — apis → SDK
   428  // ---------------------------------------------------------------------------
   429  
   430  func injectionSpecFromAPI(inj apis.Injection) (InjectionSpec, error) {
   431  	disc, err := inj.Discriminator()
   432  	if err != nil {
   433  		return InjectionSpec{}, err
   434  	}
   435  	switch disc {
   436  	case string(apis.Openai):
   437  		v, err := inj.AsOpenaiInjection()
   438  		if err != nil {
   439  			return InjectionSpec{}, err
   440  		}
   441  		return InjectionSpec{OpenAI: &OpenAIInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   442  	case string(apis.Anthropic):
   443  		v, err := inj.AsAnthropicInjection()
   444  		if err != nil {
   445  			return InjectionSpec{}, err
   446  		}
   447  		return InjectionSpec{Anthropic: &AnthropicInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   448  	case string(apis.Gemini):
   449  		v, err := inj.AsGeminiInjection()
   450  		if err != nil {
   451  			return InjectionSpec{}, err
   452  		}
   453  		return InjectionSpec{Gemini: &GeminiInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   454  	case string(apis.Qiniu):
   455  		v, err := inj.AsQiniuInjection()
   456  		if err != nil {
   457  			return InjectionSpec{}, err
   458  		}
   459  		return InjectionSpec{Qiniu: &QiniuInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   460  	case string(apis.Github):
   461  		v, err := inj.AsGithubInjection()
   462  		if err != nil {
   463  			return InjectionSpec{}, err
   464  		}
   465  		return InjectionSpec{Github: &GithubInjection{BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries, Token: v.Token}}, nil
   466  	case string(apis.HTTP):
   467  		v, err := inj.AsHTTPInjection()
   468  		if err != nil {
   469  			return InjectionSpec{}, err
   470  		}
   471  		return InjectionSpec{HTTP: &HTTPInjection{BaseURL: v.BaseURL, Headers: v.Headers, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   472  	default:
   473  		return InjectionSpec{}, fmt.Errorf("unknown injection type: %s", disc)
   474  	}
   475  }
   476  
   477  func maskedSandboxInjectionFromAPI(inj apis.SandboxInjection) (MaskedSandboxInjection, error) {
   478  	disc, err := inj.Discriminator()
   479  	if err != nil {
   480  		return MaskedSandboxInjection{}, err
   481  	}
   482  	switch disc {
   483  	case string(apis.ID):
   484  		v, err := inj.AsInjectionByID()
   485  		if err != nil {
   486  			return MaskedSandboxInjection{}, err
   487  		}
   488  		return MaskedSandboxInjection{ByID: &v.ID}, nil
   489  	case string(apis.Openai):
   490  		v, err := inj.AsOpenaiInjection()
   491  		if err != nil {
   492  			return MaskedSandboxInjection{}, err
   493  		}
   494  		return MaskedSandboxInjection{OpenAI: &OpenAIInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   495  	case string(apis.Anthropic):
   496  		v, err := inj.AsAnthropicInjection()
   497  		if err != nil {
   498  			return MaskedSandboxInjection{}, err
   499  		}
   500  		return MaskedSandboxInjection{Anthropic: &AnthropicInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   501  	case string(apis.Gemini):
   502  		v, err := inj.AsGeminiInjection()
   503  		if err != nil {
   504  			return MaskedSandboxInjection{}, err
   505  		}
   506  		return MaskedSandboxInjection{Gemini: &GeminiInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   507  	case string(apis.Qiniu):
   508  		v, err := inj.AsQiniuInjection()
   509  		if err != nil {
   510  			return MaskedSandboxInjection{}, err
   511  		}
   512  		return MaskedSandboxInjection{Qiniu: &QiniuInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   513  	case string(apis.Github):
   514  		v, err := inj.AsGithubInjection()
   515  		if err != nil {
   516  			return MaskedSandboxInjection{}, err
   517  		}
   518  		return MaskedSandboxInjection{Github: &GithubInjection{BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries, Token: v.Token}}, nil
   519  	case string(apis.HTTP):
   520  		v, err := inj.AsHTTPInjection()
   521  		if err != nil {
   522  			return MaskedSandboxInjection{}, err
   523  		}
   524  		return MaskedSandboxInjection{HTTP: &HTTPInjection{BaseURL: v.BaseURL, Headers: v.Headers, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil
   525  	default:
   526  		return MaskedSandboxInjection{}, fmt.Errorf("unknown sandbox injection type: %s", disc)
   527  	}
   528  }
   529  
   530  func maskedSandboxInjectionsFromAPI(injections []apis.SandboxInjection) ([]MaskedSandboxInjection, error) {
   531  	if injections == nil {
   532  		return nil, nil
   533  	}
   534  	result := make([]MaskedSandboxInjection, len(injections))
   535  	for i, injection := range injections {
   536  		spec, err := maskedSandboxInjectionFromAPI(injection)
   537  		if err != nil {
   538  			return nil, err
   539  		}
   540  		result[i] = spec
   541  	}
   542  	return result, nil
   543  }
   544  
   545  func injectionRuleFromAPI(a apis.InjectionRule) (InjectionRule, error) {
   546  	spec, err := injectionSpecFromAPI(a.Injection)
   547  	if err != nil {
   548  		return InjectionRule{}, err
   549  	}
   550  	return InjectionRule{
   551  		RuleID:    a.RuleID,
   552  		Name:      a.Name,
   553  		Injection: spec,
   554  		CreatedAt: a.CreatedAt,
   555  		UpdatedAt: a.UpdatedAt,
   556  	}, nil
   557  }
   558  
   559  func injectionRulesFromAPI(a []apis.InjectionRule) ([]InjectionRule, error) {
   560  	if a == nil {
   561  		return nil, nil
   562  	}
   563  	result := make([]InjectionRule, len(a))
   564  	for i, r := range a {
   565  		rule, err := injectionRuleFromAPI(r)
   566  		if err != nil {
   567  			return nil, err
   568  		}
   569  		result[i] = rule
   570  	}
   571  	return result, nil
   572  }