@@ -2,13 +2,15 @@ package functions
22
33import (
44 "context"
5+ "fmt"
56 "io"
67
78 "github.com/hasura/ndc-sdk-go/scalar"
89 "github.com/hasura/ndc-sdk-go/schema"
910 "github.com/hasura/ndc-sdk-go/utils"
1011 "github.com/hasura/ndc-storage/connector/collection"
1112 "github.com/hasura/ndc-storage/connector/storage/common"
13+ "github.com/hasura/ndc-storage/connector/storage/common/encoding"
1214 "github.com/hasura/ndc-storage/connector/types"
1315)
1416
@@ -104,7 +106,7 @@ func FunctionStorageObject(ctx context.Context, state *types.State, args *common
104106
105107// FunctionDownloadStorageObjectAsBase64 returns a stream of the object data. Most of the common errors occur when reading the stream.
106108func FunctionDownloadStorageObjectAsBase64 (ctx context.Context , state * types.State , args * common.GetStorageObjectArguments ) (* DownloadStorageObjectResponse , error ) {
107- reader , err := downloadStorageObject (ctx , state , args )
109+ _ , reader , err := downloadStorageObject (ctx , state , args )
108110 if err != nil {
109111 return nil , err
110112 }
@@ -127,7 +129,7 @@ func FunctionDownloadStorageObjectAsBase64(ctx context.Context, state *types.Sta
127129
128130// FunctionDownloadStorageObjectAsText returns the object content in plain text. Use this function only if you know exactly the file as an text file.
129131func FunctionDownloadStorageObjectAsText (ctx context.Context , state * types.State , args * common.GetStorageObjectArguments ) (* DownloadStorageObjectTextResponse , error ) {
130- reader , err := downloadStorageObject (ctx , state , args )
132+ _ , reader , err := downloadStorageObject (ctx , state , args )
131133 if err != nil {
132134 return nil , err
133135 }
@@ -148,17 +150,89 @@ func FunctionDownloadStorageObjectAsText(ctx context.Context, state *types.State
148150 return & DownloadStorageObjectTextResponse {Data : dataStr }, nil
149151}
150152
151- func downloadStorageObject (ctx context.Context , state * types.State , args * common.GetStorageObjectArguments ) (io.ReadCloser , error ) {
153+ // FunctionDownloadStorageObjectAsJson returns the object content in arbitrary json. Returns error if the content is unable to be decoded.
154+ func FunctionDownloadStorageObjectAsJson (ctx context.Context , state * types.State , args * common.GetStorageObjectArguments ) (* DownloadStorageObjectJsonResponse , error ) {
155+ stat , reader , err := downloadStorageObject (ctx , state , args )
156+ if err != nil {
157+ return nil , err
158+ }
159+
160+ if reader == nil {
161+ return nil , nil
162+ }
163+
164+ defer reader .Close ()
165+
166+ var contentType string
167+ if stat .ContentType != nil {
168+ contentType = * stat .ContentType
169+ }
170+
171+ data , err := encoding .DecodeArbitraryData (ctx , stat .Name , contentType , reader )
172+ if err != nil {
173+ return nil , schema .UnprocessableContentError (err .Error (), nil )
174+ }
175+
176+ return & DownloadStorageObjectJsonResponse {Data : data }, nil
177+ }
178+
179+ // FunctionDownloadStorageObjectAsCsv downloads and decode the object content from CSV. Returns error if the content is unable to be decoded.
180+ func FunctionDownloadStorageObjectAsCsv (ctx context.Context , state * types.State , args * common.DownloadStorageObjectAsCsvArguments ) (* DownloadStorageObjectJsonResponse , error ) {
181+ getArgs := args .GetStorageObjectArguments
182+ getArgs .PreValidate = func (so * common.StorageObject ) error {
183+ var contentType string
184+ if so .ContentType != nil {
185+ contentType = * so .ContentType
186+ }
187+
188+ if ! encoding .IsValidCSVObject (so .Name , contentType ) {
189+ return fmt .Errorf ("failed to decode file %s to csv, unsupported content type %s" , so .Name , contentType )
190+ }
191+
192+ return nil
193+ }
194+
195+ stat , reader , err := downloadStorageObject (ctx , state , & args .GetStorageObjectArguments )
196+ if err != nil {
197+ return nil , err
198+ }
199+
200+ if reader == nil {
201+ return nil , nil
202+ }
203+
204+ defer reader .Close ()
205+
206+ decodeOptions := args .Options
207+
208+ if decodeOptions .Delimiter == "" {
209+ var contentType string
210+ if stat .ContentType != nil {
211+ contentType = * stat .ContentType
212+ }
213+
214+ decodeOptions .Delimiter = encoding .CSVCommaFromContentType (stat .Name , contentType )
215+ }
216+
217+ data , err := encoding .DecodeCSV (ctx , reader , decodeOptions )
218+ if err != nil {
219+ return nil , schema .UnprocessableContentError (err .Error (), nil )
220+ }
221+
222+ return & DownloadStorageObjectJsonResponse {Data : data }, nil
223+ }
224+
225+ func downloadStorageObject (ctx context.Context , state * types.State , args * common.GetStorageObjectArguments ) (* common.StorageObject , io.ReadCloser , error ) {
152226 request , err := collection .EvalObjectPredicate (args .StorageBucketArguments , & collection.StringComparisonOperator {
153227 Value : args .Name ,
154228 Operator : collection .OperatorEqual ,
155229 }, args .Where , types .QueryVariablesFromContext (ctx ))
156230 if err != nil {
157- return nil , err
231+ return nil , nil , err
158232 }
159233
160234 if ! request .IsValid {
161- return nil , nil
235+ return nil , nil , nil
162236 }
163237
164238 return state .Storage .GetObject (ctx , request .GetBucketArguments (), request .ObjectNamePredicate .GetPrefix (), args .GetStorageObjectOptions )
0 commit comments