11package cmd
22
33import (
4+ "bytes"
5+ "context"
46 "encoding/json"
57 "fmt"
68 "io"
@@ -13,15 +15,15 @@ import (
1315 "github.com/spf13/cobra"
1416)
1517
18+ const masterDataPageSize = 1000
19+
1620var (
1721 systemMasterListOutput string
1822 systemMasterListJQ string
1923 systemMasterShowOutput string
2024 systemMasterShowJQ string
2125
2226 systemMasterDataType int
23- systemMasterDataRows int
24- systemMasterDataOffset int
2527 systemMasterDataFormat string
2628 systemMasterDataFileName string
2729 systemMasterDataDelimiter string
@@ -118,15 +120,13 @@ func init() {
118120
119121 df := systemMasterDataCmd .Flags ()
120122 df .IntVar (& systemMasterDataType , "type" , - 1 , "master_type: 0=simple master, 1=user-specific master (required)" )
121- df .IntVar (& systemMasterDataRows , "rows" , 0 , "number of rows to fetch (0 = omit; server default 100; max 1000)" )
122- df .IntVar (& systemMasterDataOffset , "offset" , 0 , "offset (0 = omit; server default 0)" )
123123 df .StringVar (& systemMasterDataFormat , "format" , "json" , "output format: json | csv" )
124124 df .StringVar (& systemMasterDataFileName , "file-name" , "" , "CSV file name hint (CSV only; default: {master_code}.csv)" )
125125 df .StringVar (& systemMasterDataDelimiter , "delimiter" , "" , "CSV delimiter: comma | tab (CSV only; default comma)" )
126126 df .BoolVar (& systemMasterDataTitle , "title" , false , "CSV only (user-specific master): include field names on the first row (default: true)" )
127127 df .BoolVar (& systemMasterDataNoTitle , "no-title" , false , "CSV only (user-specific master): omit field names from the first row" )
128128 df .StringVar (& systemMasterDataFields , "fields" , "" , "CSV only (simple master): comma-separated list of field names to include" )
129- df .StringVarP (& systemMasterDataOutput , "output" , "o" , "" , "output path: FILE, DIR/, - for stdout (default: stdout for JSON, server-provided filename for CSV )" )
129+ df .StringVarP (& systemMasterDataOutput , "output" , "o" , "" , "output path: FILE, DIR/, - for stdout (default: stdout)" )
130130 df .StringVar (& systemMasterDataJQ , "jq" , "" , "apply a gojq filter to the JSON response (JSON format only)" )
131131 _ = systemMasterDataCmd .MarkFlagRequired ("type" )
132132
@@ -218,60 +218,51 @@ func runSystemMasterData(cmd *cobra.Command, args []string) error {
218218 return fmt .Errorf ("--title and --no-title are mutually exclusive" )
219219 }
220220
221- p := xpoint.MasterDataParams {MasterType : systemMasterDataType }
222- if cmd .Flags ().Changed ("rows" ) {
223- v := systemMasterDataRows
224- p .Rows = & v
225- }
226- if cmd .Flags ().Changed ("offset" ) {
227- v := systemMasterDataOffset
228- p .Offset = & v
229- }
230- if format == "csv" {
231- p .FileName = systemMasterDataFileName
232- p .Delimiter = systemMasterDataDelimiter
233- p .Fields = systemMasterDataFields
234- if systemMasterDataNoTitle {
235- b := false
236- p .Title = & b
237- } else if cmd .Flags ().Changed ("title" ) {
238- v := systemMasterDataTitle
239- p .Title = & v
240- }
241- }
242-
243221 client , err := newClientFromFlags (cmd .Context ())
244222 if err != nil {
245223 return err
246224 }
247- filename , body , _ , err := client .GetMasterData (cmd .Context (), masterCode , format , p )
248- if err != nil {
249- return err
250- }
251225
252226 if format == "json" {
227+ merged , err := fetchAllMasterDataJSON (cmd .Context (), client , masterCode , systemMasterDataType )
228+ if err != nil {
229+ return err
230+ }
253231 if systemMasterDataJQ != "" {
254- return runJQ (json .RawMessage (body ), systemMasterDataJQ )
232+ return runJQ (json .RawMessage (merged ), systemMasterDataJQ )
255233 }
256234 switch systemMasterDataOutput {
257235 case "" , "-" :
258- _ , werr := os .Stdout .Write (body )
236+ _ , werr := os .Stdout .Write (merged )
259237 return werr
260238 }
261- dst := resolveDownloadPath (systemMasterDataOutput , fallbackName ( filename , masterCode + ".json" ) , 0 )
262- if err := os .WriteFile (dst , body , 0o600 ); err != nil {
239+ dst := resolveDownloadPath (systemMasterDataOutput , masterCode + ".json" , 0 )
240+ if err := os .WriteFile (dst , merged , 0o600 ); err != nil {
263241 return fmt .Errorf ("write master data: %w" , err )
264242 }
265- fmt .Fprintf (os .Stderr , "saved: %s (%d bytes)\n " , dst , len (body ))
243+ fmt .Fprintf (os .Stderr , "saved: %s (%d bytes)\n " , dst , len (merged ))
266244 return nil
267245 }
268246
269- // CSV
270- if systemMasterDataOutput == "-" {
271- _ , werr := os .Stdout .Write (body )
272- return werr
247+ p := xpoint.MasterDataParams {
248+ MasterType : systemMasterDataType ,
249+ FileName : systemMasterDataFileName ,
250+ Delimiter : systemMasterDataDelimiter ,
251+ Fields : systemMasterDataFields ,
273252 }
274- if systemMasterDataOutput == "" && ! isTerminal (os .Stdout ) {
253+ if systemMasterDataNoTitle {
254+ b := false
255+ p .Title = & b
256+ } else if cmd .Flags ().Changed ("title" ) {
257+ v := systemMasterDataTitle
258+ p .Title = & v
259+ }
260+ filename , body , err := fetchAllMasterDataCSV (cmd .Context (), client , masterCode , p )
261+ if err != nil {
262+ return err
263+ }
264+
265+ if systemMasterDataOutput == "" || systemMasterDataOutput == "-" {
275266 _ , werr := os .Stdout .Write (body )
276267 return werr
277268 }
@@ -344,6 +335,112 @@ func runSystemMasterUpload(cmd *cobra.Command, args []string) error {
344335 return writeJSON (os .Stdout , res )
345336}
346337
338+ // fetchAllMasterDataJSON fetches master data as JSON with rows=1000, paging
339+ // until total_count (or a short/empty page) is reached. The merged JSON keeps
340+ // the first page's envelope with the concatenated data array.
341+ func fetchAllMasterDataJSON (ctx context.Context , client * xpoint.Client , masterCode string , masterType int ) ([]byte , error ) {
342+ rows := masterDataPageSize
343+ offset := 0
344+ p := xpoint.MasterDataParams {MasterType : masterType , Rows : & rows , Offset : & offset }
345+
346+ var (
347+ firstMaster map [string ]json.RawMessage
348+ allData []json.RawMessage
349+ totalCount int
350+ haveTotal bool
351+ )
352+
353+ for {
354+ p .Offset = & offset
355+ _ , body , _ , err := client .GetMasterData (ctx , masterCode , "json" , p )
356+ if err != nil {
357+ return nil , err
358+ }
359+ var env struct {
360+ Master map [string ]json.RawMessage `json:"master"`
361+ }
362+ if err := json .Unmarshal (body , & env ); err != nil {
363+ return nil , fmt .Errorf ("parse master data: %w" , err )
364+ }
365+
366+ if offset == 0 {
367+ firstMaster = env .Master
368+ if raw , ok := env .Master ["total_count" ]; ok {
369+ if err := json .Unmarshal (raw , & totalCount ); err == nil {
370+ haveTotal = true
371+ }
372+ }
373+ }
374+
375+ var data []json.RawMessage
376+ if raw , ok := env .Master ["data" ]; ok {
377+ if err := json .Unmarshal (raw , & data ); err != nil {
378+ return nil , fmt .Errorf ("parse master.data: %w" , err )
379+ }
380+ }
381+ allData = append (allData , data ... )
382+
383+ if haveTotal && len (allData ) >= totalCount {
384+ break
385+ }
386+ if len (data ) < masterDataPageSize {
387+ break
388+ }
389+ offset += masterDataPageSize
390+ }
391+
392+ if firstMaster == nil {
393+ firstMaster = map [string ]json.RawMessage {}
394+ }
395+ encoded , err := json .Marshal (allData )
396+ if err != nil {
397+ return nil , err
398+ }
399+ firstMaster ["data" ] = encoded
400+ return json .Marshal (struct {
401+ Master map [string ]json.RawMessage `json:"master"`
402+ }{Master : firstMaster })
403+ }
404+
405+ // fetchAllMasterDataCSV fetches master data as CSV with rows=1000, paging
406+ // until the server returns an empty page. The first page honors the caller's
407+ // title setting; subsequent pages force title=false so the server omits the
408+ // header and the bodies can simply be concatenated.
409+ func fetchAllMasterDataCSV (ctx context.Context , client * xpoint.Client , masterCode string , base xpoint.MasterDataParams ) (string , []byte , error ) {
410+ rows := masterDataPageSize
411+ offset := 0
412+ p := base
413+ p .Rows = & rows
414+ p .Offset = & offset
415+
416+ titleFalse := false
417+ var (
418+ firstFilename string
419+ csvBuf bytes.Buffer
420+ )
421+ for {
422+ p .Offset = & offset
423+ if offset > 0 {
424+ p .Title = & titleFalse
425+ }
426+ filename , body , _ , err := client .GetMasterData (ctx , masterCode , "csv" , p )
427+ if err != nil {
428+ return "" , nil , err
429+ }
430+ if offset == 0 {
431+ firstFilename = filename
432+ csvBuf .Write (body )
433+ } else {
434+ if len (bytes .TrimSpace (body )) == 0 {
435+ break
436+ }
437+ csvBuf .Write (body )
438+ }
439+ offset += masterDataPageSize
440+ }
441+ return firstFilename , csvBuf .Bytes (), nil
442+ }
443+
347444// fallbackName returns name when non-empty, else alt.
348445func fallbackName (name , alt string ) string {
349446 if name != "" {
0 commit comments