@@ -32,6 +32,7 @@ type Configurations struct {
3232type internalChangeSet interface {
3333 noop () // unexported function to prevent arbitrary structs from implementing ChangeSet.
3434 Apply (env fdeployment.Environment ) (fdeployment.ChangesetOutput , error )
35+ applyWithInput (env fdeployment.Environment , inputStr string ) (fdeployment.ChangesetOutput , error )
3536 Configurations () (Configurations , error )
3637}
3738
@@ -77,6 +78,35 @@ type TypedJSON struct {
7778 ChainOverrides []uint64 `json:"chainOverrides"` // Optional field for chain overrides
7879}
7980
81+ func parseTypedInput (inputStr string ) (TypedJSON , error ) {
82+ if inputStr == "" {
83+ return TypedJSON {}, errors .New ("input is empty" )
84+ }
85+
86+ var inputObject TypedJSON
87+ if err := json .Unmarshal ([]byte (inputStr ), & inputObject ); err != nil {
88+ return TypedJSON {}, fmt .Errorf ("JSON must be in JSON format with 'payload' fields: %w" , err )
89+ }
90+ if len (inputObject .Payload ) == 0 {
91+ return TypedJSON {}, errors .New ("'payload' field is required" )
92+ }
93+
94+ return inputObject , nil
95+ }
96+
97+ func decodePayload [C any ](payload json.RawMessage ) (C , error ) {
98+ var config C
99+
100+ payloadDecoder := json .NewDecoder (strings .NewReader (string (payload )))
101+ payloadDecoder .UseNumber ()
102+ payloadDecoder .DisallowUnknownFields ()
103+ if err := payloadDecoder .Decode (& config ); err != nil {
104+ return config , fmt .Errorf ("failed to unmarshal payload: %w" , err )
105+ }
106+
107+ return config , nil
108+ }
109+
80110// WithJSON returns a fully configured changeset, which pairs a [fdeployment.ChangeSet] with its configuration based
81111// a JSON input. It also allows extensions, such as a PostProcessing function.
82112// InputStr must be a JSON object with a "payload" field that contains the actual input data for a Durable Pipeline.
@@ -92,31 +122,13 @@ type TypedJSON struct {
92122// Note: Prefer WithEnvInput for durable_pipelines.go
93123func (f WrappedChangeSet [C ]) WithJSON (_ C , inputStr string ) ConfiguredChangeSet {
94124 return ChangeSetImpl [C ]{changeset : f , configProvider : func () (C , error ) {
95- var config C
96-
97- if inputStr == "" {
98- return config , errors .New ("input is empty" )
99- }
100-
101- var inputObject TypedJSON
102- if err := json .Unmarshal ([]byte (inputStr ), & inputObject ); err != nil {
103- return config , fmt .Errorf ("JSON must be in JSON format with 'payload' fields: %w" , err )
104- }
105-
106- // If payload is null, decode it as null (which will give zero value)
107- // If payload is missing, return an error
108- if len (inputObject .Payload ) == 0 {
109- return config , errors .New ("'payload' field is required" )
110- }
111-
112- payloadDecoder := json .NewDecoder (strings .NewReader (string (inputObject .Payload )))
113- payloadDecoder .UseNumber ()
114- payloadDecoder .DisallowUnknownFields ()
115- if err := payloadDecoder .Decode (& config ); err != nil {
116- return config , fmt .Errorf ("failed to unmarshal payload: %w" , err )
125+ inputObject , err := parseTypedInput (inputStr )
126+ if err != nil {
127+ var zero C
128+ return zero , err
117129 }
118130
119- return config , nil
131+ return decodePayload [ C ]( inputObject . Payload )
120132 },
121133 inputChainOverrides : func () ([]uint64 , error ) {
122134 return loadInputChainOverrides (inputStr )
@@ -152,42 +164,35 @@ func (f WrappedChangeSet[C]) WithEnvInput(opts ...EnvInputOption[C]) ConfiguredC
152164
153165 inputStr := os .Getenv ("DURABLE_PIPELINE_INPUT" )
154166
155- return ChangeSetImpl [C ]{changeset : f , configProvider : func () (C , error ) {
156- var config C
157-
158- if inputStr == "" {
159- return config , errors .New ("input is empty" )
160- }
161-
162- var inputObject TypedJSON
163- if err := json .Unmarshal ([]byte (inputStr ), & inputObject ); err != nil {
164- return config , fmt .Errorf ("JSON must be in JSON format with 'payload' fields: %w" , err )
165- }
167+ providerFromInput := func (rawInput string ) (C , error ) {
168+ var zero C
166169
167- // If payload is null, decode it as null (which will give zero value)
168- // If payload is missing, return an error
169- if len (inputObject .Payload ) == 0 {
170- return config , errors .New ("'payload' field is required" )
170+ inputObject , err := parseTypedInput (rawInput )
171+ if err != nil {
172+ return zero , err
171173 }
172-
173- payloadDecoder := json .NewDecoder (strings .NewReader (string (inputObject .Payload )))
174- payloadDecoder .UseNumber ()
175- payloadDecoder .DisallowUnknownFields ()
176- if err := payloadDecoder .Decode (& config ); err != nil {
177- return config , fmt .Errorf ("failed to unmarshal payload: %w" , err )
174+ config , err := decodePayload [C ](inputObject .Payload )
175+ if err != nil {
176+ return zero , err
178177 }
179178
180179 if options .inputModifier != nil {
181- conf , err := options .inputModifier (config )
182- if err != nil {
183- return conf , fmt .Errorf ("failed to apply input modifier: %w" , err )
180+ conf , modifierErr := options .inputModifier (config )
181+ if modifierErr != nil {
182+ return conf , fmt .Errorf ("failed to apply input modifier: %w" , modifierErr )
184183 }
185184
186185 return conf , nil
187186 }
188187
189188 return config , nil
190- },
189+ }
190+
191+ return ChangeSetImpl [C ]{changeset : f ,
192+ configProvider : func () (C , error ) {
193+ return providerFromInput (inputStr )
194+ },
195+ configProviderWithInput : providerFromInput ,
191196 inputChainOverrides : func () ([]uint64 , error ) {
192197 return loadInputChainOverrides (inputStr )
193198 },
@@ -223,25 +228,14 @@ func (f WrappedChangeSet[C]) WithConfigResolver(resolver fresolvers.ConfigResolv
223228 // Read input from environment variable
224229 inputStr := os .Getenv ("DURABLE_PIPELINE_INPUT" )
225230
226- configProvider := func () (C , error ) {
231+ configProviderFromInput := func (rawInput string ) (C , error ) {
227232 var zero C
228233
229- if inputStr == "" {
230- return zero , errors .New ("input is empty" )
231- }
232-
233- // Parse JSON input
234- var inputObject TypedJSON
235- if err := json .Unmarshal ([]byte (inputStr ), & inputObject ); err != nil {
234+ inputObject , err := parseTypedInput (rawInput )
235+ if err != nil {
236236 return zero , fmt .Errorf ("failed to parse resolver input as JSON: %w" , err )
237237 }
238238
239- // If payload is null, pass it to the resolver (which will receive null)
240- // If payload field is missing, return an error
241- if len (inputObject .Payload ) == 0 {
242- return zero , errors .New ("'payload' field is required" )
243- }
244-
245239 // Call resolver – automatically unmarshal into its expected input type.
246240 typedConfig , err := fresolvers .CallResolver [C ](resolver , inputObject .Payload )
247241 if err != nil {
@@ -251,8 +245,12 @@ func (f WrappedChangeSet[C]) WithConfigResolver(resolver fresolvers.ConfigResolv
251245 return typedConfig , nil
252246 }
253247
254- return ChangeSetImpl [C ]{changeset : f , configProvider : configProvider ,
255- ConfigResolver : resolver ,
248+ return ChangeSetImpl [C ]{changeset : f ,
249+ configProvider : func () (C , error ) {
250+ return configProviderFromInput (inputStr )
251+ },
252+ configProviderWithInput : configProviderFromInput ,
253+ ConfigResolver : resolver ,
256254 inputChainOverrides : func () ([]uint64 , error ) {
257255 return loadInputChainOverrides (inputStr )
258256 },
@@ -262,9 +260,10 @@ func (f WrappedChangeSet[C]) WithConfigResolver(resolver fresolvers.ConfigResolv
262260var _ ConfiguredChangeSet = ChangeSetImpl [any ]{}
263261
264262type ChangeSetImpl [C any ] struct {
265- changeset WrappedChangeSet [C ]
266- configProvider func () (C , error )
267- inputChainOverrides func () ([]uint64 , error )
263+ changeset WrappedChangeSet [C ]
264+ configProvider func () (C , error )
265+ configProviderWithInput func (inputStr string ) (C , error )
266+ inputChainOverrides func () ([]uint64 , error )
268267
269268 // Present only when the changeset was wired with
270269 // Configure(...).WithConfigResolver(...)
@@ -289,6 +288,25 @@ func (ccs ChangeSetImpl[C]) Apply(env fdeployment.Environment) (fdeployment.Chan
289288 return ccs .changeset .operation .Apply (env , c )
290289}
291290
291+ func (ccs ChangeSetImpl [C ]) applyWithInput (env fdeployment.Environment , inputStr string ) (fdeployment.ChangesetOutput , error ) {
292+ if inputStr == "" {
293+ return ccs .Apply (env )
294+ }
295+ if ccs .configProviderWithInput == nil {
296+ return ccs .Apply (env )
297+ }
298+
299+ c , err := ccs .configProviderWithInput (inputStr )
300+ if err != nil {
301+ return fdeployment.ChangesetOutput {}, err
302+ }
303+ if err := ccs .changeset .operation .VerifyPreconditions (env , c ); err != nil {
304+ return fdeployment.ChangesetOutput {}, err
305+ }
306+
307+ return ccs .changeset .operation .Apply (env , c )
308+ }
309+
292310func (ccs ChangeSetImpl [C ]) Configurations () (Configurations , error ) {
293311 var chainOverrides []uint64
294312 var err error
0 commit comments