@@ -2,13 +2,19 @@ package dresources
22
33import (
44 "context"
5+ "encoding/json"
6+ "fmt"
7+ "net/http"
58
69 "github.com/databricks/cli/bundle/config/resources"
710 "github.com/databricks/cli/bundle/deployplan"
11+ "github.com/databricks/cli/bundle/env"
12+ "github.com/databricks/cli/libs/auth"
813 "github.com/databricks/cli/libs/structs/structdiff"
914 "github.com/databricks/cli/libs/structs/structpath"
1015 "github.com/databricks/cli/libs/utils"
1116 "github.com/databricks/databricks-sdk-go"
17+ "github.com/databricks/databricks-sdk-go/client"
1218 "github.com/databricks/databricks-sdk-go/marshal"
1319 "github.com/databricks/databricks-sdk-go/service/pipelines"
1420)
@@ -22,6 +28,9 @@ type PipelineState struct {
2228 // STs, Views). Nil means the server default (cascade) applies. Read from persisted state at
2329 // delete time; never sent on create/update.
2430 CascadeOnDestroy * bool `json:"cascade_on_destroy,omitempty"`
31+
32+ // Testing is PipelineSpec.testing, which the Go SDK does not have yet.
33+ Testing * resources.PipelineTesting `json:"testing,omitempty"`
2534}
2635
2736func (s * PipelineState ) UnmarshalJSON (b []byte ) error {
@@ -52,6 +61,9 @@ type PipelineRemote struct {
5261 PipelineId string `json:"pipeline_id,omitempty"`
5362 RunAsUserName string `json:"run_as_user_name,omitempty"`
5463 State pipelines.PipelineState `json:"state,omitempty"`
64+
65+ // Testing is spec.testing of the Get response, read by DoRead because the Go SDK does not have it yet.
66+ Testing * resources.PipelineTesting `json:"testing,omitempty"`
5567}
5668
5769// Custom marshaler needed because embedded CreatePipeline has its own MarshalJSON
@@ -78,6 +90,7 @@ func (*ResourcePipeline) PrepareState(input *resources.Pipeline) *PipelineState
7890 return & PipelineState {
7991 CreatePipeline : input .CreatePipeline ,
8092 CascadeOnDestroy : input .CascadeOnDestroy ,
93+ Testing : input .Testing ,
8194 }
8295}
8396
@@ -86,17 +99,48 @@ func (*ResourcePipeline) RemapState(remote *PipelineRemote) *PipelineState {
8699 CreatePipeline : remote .CreatePipeline ,
87100 // cascade_on_destroy is input-only and absent from PipelineRemote, so it stays nil here.
88101 CascadeOnDestroy : nil ,
102+ Testing : remote .Testing ,
89103 }
90104}
91105
106+ // pipelineTestingResponse holds spec.testing of a Get Pipeline response.
107+ type pipelineTestingResponse struct {
108+ Spec struct {
109+ Testing * resources.PipelineTesting `json:"testing"`
110+ } `json:"spec"`
111+ }
112+
92113func (r * ResourcePipeline ) DoRead (ctx context.Context , id string ) (* PipelineRemote , error ) {
114+ if env .ExperimentalPipelineTesting (ctx ) {
115+ return r .doReadWithTesting (ctx , id )
116+ }
93117 resp , err := r .client .Pipelines .GetByPipelineId (ctx , id )
94118 if err != nil {
95119 return nil , err
96120 }
97121 return makePipelineRemote (resp ), nil
98122}
99123
124+ // doReadWithTesting sends the same request as GetByPipelineId, but raw, so that it can also read
125+ // spec.testing, which GetPipelineResponse does not have yet. Remove it once GetPipelineResponse has it.
126+ func (r * ResourcePipeline ) doReadWithTesting (ctx context.Context , id string ) (* PipelineRemote , error ) {
127+ var raw json.RawMessage
128+ if err := r .doRaw (ctx , http .MethodGet , "/api/2.0/pipelines/" + id , nil , & raw ); err != nil {
129+ return nil , err
130+ }
131+ var resp pipelines.GetPipelineResponse
132+ if err := json .Unmarshal (raw , & resp ); err != nil {
133+ return nil , err
134+ }
135+ var testing pipelineTestingResponse
136+ if err := json .Unmarshal (raw , & testing ); err != nil {
137+ return nil , err
138+ }
139+ remote := makePipelineRemote (& resp )
140+ remote .Testing = testing .Spec .Testing
141+ return remote , nil
142+ }
143+
100144func makePipelineRemote (p * pipelines.GetPipelineResponse ) * PipelineRemote {
101145 var createPipeline pipelines.CreatePipeline
102146 if p .Spec != nil {
@@ -155,10 +199,20 @@ func makePipelineRemote(p *pipelines.GetPipelineResponse) *PipelineRemote {
155199 PipelineId : p .PipelineId ,
156200 RunAsUserName : p .RunAsUserName ,
157201 State : p .State ,
202+ // GetPipelineResponse has no spec.testing; DoRead sets it from the raw response.
203+ Testing : nil ,
158204 }
159205}
160206
161207func (r * ResourcePipeline ) DoCreate (ctx context.Context , config * PipelineState ) (string , * PipelineRemote , error ) {
208+ if config .Testing != nil {
209+ var response pipelines.CreatePipelineResponse
210+ err := r .doWithTesting (ctx , http .MethodPost , "/api/2.0/pipelines" , config .CreatePipeline , config .Testing , & response )
211+ if err != nil {
212+ return "" , nil , err
213+ }
214+ return response .PipelineId , nil , nil
215+ }
162216 response , err := r .client .Pipelines .Create (ctx , config .CreatePipeline )
163217 if err != nil {
164218 return "" , nil , err
@@ -212,6 +266,9 @@ func (r *ResourcePipeline) DoUpdate(ctx context.Context, id string, config *Pipe
212266 ForceSendFields : utils.FilterFields [pipelines.EditPipeline ](config .ForceSendFields ),
213267 }
214268
269+ if config .Testing != nil {
270+ return nil , r .doWithTesting (ctx , http .MethodPut , "/api/2.0/pipelines/" + id , request , config .Testing , nil )
271+ }
215272 return nil , r .client .Pipelines .Update (ctx , request )
216273}
217274
@@ -242,6 +299,35 @@ func (*ResourcePipeline) OverrideChangeDesc(_ context.Context, path *structpath.
242299 return nil
243300}
244301
302+ // doWithTesting sends a Create or Edit request with the testing field, which the Go SDK does not
303+ // have yet. The body is the SDK's JSON for the request plus testing. Remove this once
304+ // CreatePipeline and EditPipeline have the field; pipelines without testing already use the SDK.
305+ func (r * ResourcePipeline ) doWithTesting (ctx context.Context , method , path string , request any , testing * resources.PipelineTesting , response any ) error {
306+ data , err := json .Marshal (request )
307+ if err != nil {
308+ return err
309+ }
310+ // RawMessage values keep the SDK's encoding, including large integers, unchanged.
311+ var body map [string ]json.RawMessage
312+ if err := json .Unmarshal (data , & body ); err != nil {
313+ return err
314+ }
315+ body ["testing" ], err = json .Marshal (testing )
316+ if err != nil {
317+ return err
318+ }
319+ return r .doRaw (ctx , method , path , body , response )
320+ }
321+
322+ // doRaw sends a request that the Go SDK cannot express yet because it lacks the testing field.
323+ func (r * ResourcePipeline ) doRaw (ctx context.Context , method , path string , request , response any ) error {
324+ apiClient , err := client .New (r .client .Config )
325+ if err != nil {
326+ return fmt .Errorf ("failed to create API client: %w" , err )
327+ }
328+ return apiClient .Do (ctx , method , path , auth .WorkspaceIDHeaders (r .client .Config ), nil , request , response )
329+ }
330+
245331// Note, terraform provider either
246332// a) reads back state at least once and fails create if state is "failed"
247333// b) repeatededly reads state until state is "running" (if spec.Contionous is set).
0 commit comments