csvimport

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

Documentation

Overview

Package csvimport is Atlas's in-process worker for the CSV-to-JSON Worker Type task (ADR-0139), and the CSV parser the API's upload-validation endpoint shares with it (ADR-0084).

Like every package under connector/, it rides the standard service-task seam: a CSV task compiles to a job carrying compiler.CsvImportJobTypeIndex, and Handler picks that job up off the processor goroutine, after fsync, so parsing a file never allocates on the hot path or runs on the recovery path (I1/I4). Unlike its siblings it talks to no external system — the "connector" reads a variable and writes rows back — which is why it also serves as the parser behind the operator-facing CSV upload check.

Index

Constants

View Source
const (
	// FormatCSV is a delimited text file — MIM's *Delimited text file* agent.
	FormatCSV = "csv"
	// FormatFixedWidth is a positional file whose columns are found by character
	// width — MIM's *Fixed-Width text file* agent, and how a mainframe HR extract
	// usually arrives.
	FormatFixedWidth = "fixed-width"
	// FormatAVP is an attribute-value pair file: "name: value" lines, one record per
	// blank-line-separated block — MIM's *Attribute-Value Pair text file* agent.
	FormatAVP = "avp"
)

The text-table formats this worker reads and writes (ADR-0139, amended).

All three describe the same thing — a table of records in a text file — and differ only in how a record is delimited and how a field is found inside it. So they share the Column layout, the type coercion, and everything downstream: what a process gets back is a list of row objects whichever format the file arrived in.

They are formats of one worker rather than three workers for the same reason: unlike the SQL products (ADR-0173), nothing here can be pointed at the wrong one silently. A fixed-width layout applied to an LDIF file does not quietly produce plausible rows; it fails on the first record, loudly, at the moment the file is read.

View Source
const (
	OperationRead  = "read"
	OperationWrite = "write"
)

The two directions a file task runs in.

Variables

This section is empty.

Functions

func Formats added in v0.3.0

func Formats() []string

Formats lists the readable/writable formats, sorted, for the messages that have to say what was expected.

func Handler

func Handler(store VarStore, lookup ProcessLookup) job.OutputHandler

Handler is the in-process worker for a CSV-import service task (compiler.CsvImportJobType). It converts an uploaded CSV into a JSON `rows` collection so a batch of records is ingested entirely within the process, the file having arrived through a user-task form.

It serves two authoring shapes on the one reserved job type:

  • A first-class CSV-to-JSON task (ADR-0139): the source variable, delimiter, header handling, columns, and result variable are authored on the task and compiled into a worker detail, which the worker reads from the compiled process (like the mail/rest workers). This is preferred when present.
  • The ADR-0087 variable convention: with no worker detail, the worker reads `csvText` and `columnConfig` up the task's scope chain and writes `rows` + `rowCount`, so already-deployed models keep running unchanged.

A missing/empty source, an absent or malformed layout, or an unparseable CSV is a worker error: the job fails and (retries exhausted) raises an incident, rather than silently producing an empty batch.

func KnownFormat added in v0.3.0

func KnownFormat(name string) bool

KnownFormat reports whether name is a format this worker handles. The empty string is CSV, which is what every model authored before formats existed.

func Parse added in v0.3.0

func Parse(cfg Config, data []byte) ([]map[string]any, error)

Parse reads data in the configured format into row objects.

func ParseRows

func ParseRows(cfg Config, data []byte) ([]map[string]any, error)

ParseRows parses CSV data against the predefined column layout into a list of row objects ({fieldName: value}). It is a pure, side-effect-free transform that runs in the API/side-effect phase, never on the processor hot path (ADR-0084, invariant 1/5).

Type coercion is deliberately lenient: a cell that will not coerce to its declared type is kept as its raw string, so dirty records flow through to be validated and corrected rather than being rejected at ingestion — that is the point of the feature. A *structural* mismatch (a configured header absent from the file, an unparseable CSV, an invalid layout) is an error.

func Render added in v0.3.0

func Render(cfg Config, rows []map[string]any) (string, error)

Render writes rows out in the configured format. It is the inverse of Parse and exists because an identity feed is as often produced as consumed: MIM's file agents both import and export, and a process that has assembled a set of accounts needs somewhere to put them.

Types

type Column

type Column struct {
	Name   string `json:"name"`             // field name in the row object (required, unique)
	Header string `json:"header,omitempty"` // CSV header to read; defaults to Name when the file has a header row
	Index  *int   `json:"index,omitempty"`  // 0-based source column, used when the file has no header row
	Type   string `json:"type,omitempty"`   // "string" (default), "number", "integer", "boolean"
	// Width is the column's character count in a fixed-width file, where a field is
	// found by position rather than by a delimiter or a header. Unused by the other
	// formats.
	Width int `json:"width,omitempty"`
}

Column maps one CSV column into a field of the produced row object. The source column is located by Header (when the file has a header row) or by the 0-based Index (when it does not); the cell is coerced to Type. This is the "predefined column layout" a Quality Manager's upload is checked against (ADR-0084).

type Config

type Config struct {
	Columns   []Column `json:"columns"`
	Delimiter string   `json:"delimiter,omitempty"` // single character; defaults to ","
	HasHeader *bool    `json:"hasHeader,omitempty"` // defaults to true (a header row is present)
	// Format selects how records and fields are delimited; empty is CSV, which is
	// what every layout authored before the other formats existed means.
	Format string `json:"format,omitempty"`
}

Config is the predefined column layout an uploaded CSV is parsed against.

type Job added in v0.3.0

type Job struct {
	Source    string   `json:"source"`
	Delimiter string   `json:"delimiter,omitempty"`
	HasHeader bool     `json:"hasHeader"`
	Columns   []Column `json:"columns,omitempty"`
	// Format is csv (the default and what every model authored before formats
	// existed), fixed-width, or avp. Operation is read (the default) or write.
	Format    string `json:"format,omitempty"`
	Operation string `json:"operation,omitempty"`
	// Result names the process variable the outcome is written to; empty means the
	// default. On a read that is the rows; on a write it is the rendered file.
	Result string `json:"resultVariable,omitempty"`
}

Job is a CSV-import task with everything already looked up: the text to parse and the layout to parse it against. It is what travels with a leased job.

func Resolve added in v0.3.0

func Resolve(store VarStore, cp *compiler.CompiledProcess, detail *compiler.ConnectorTaskDetail, elementInstanceKey uint64) (Job, error)

Resolve turns a compiled CSV task into a Job: the authored layout from the detail, and the source text read up the task's scope chain. It is engine work by necessity — both of its inputs live only there.

func (Job) Writing added in v0.3.0

func (j Job) Writing() bool

Writing reports whether the job renders a file rather than parsing one.

type ProcessLookup

type ProcessLookup func(defKey uint64) *compiler.CompiledProcess

ProcessLookup resolves a process-definition key to its compiled process, so the worker can read a CSV worker task's authored layout from the model it belongs to (ADR-0139) — mirroring the mail/rest/DMN workers' ProcessLookup.

type Result added in v0.3.0

type Result struct {
	ResultVariable string
	RowsJSON       string
	Text           string
	IsText         bool
	RowCount       int
}

Result is what running a Job produces: on a read the rows as JSON, on a write the rendered file, and either way how many records were involved and the variable to put the outcome in.

func Run added in v0.3.0

func Run(j Job) (Result, error)

Run parses a resolved job. It applies the defaults itself rather than leaving them to callers, so the in-process path and a worker cannot disagree about what an unset delimiter or result variable means.

A missing source or an unparseable file is an error, never an empty batch: a silently empty result is the failure this worker exists to avoid.

func (Result) Variables added in v0.3.0

func (r Result) Variables() (map[string]any, error)

Variables is what the job completes with. Both the in-process handler and the worker go through it, so neither can decide on its own what a result looks like — which is the same discipline that keeps Run shared between them.

type VarStore

type VarStore interface {
	VariablesOfScope(scope uint64, fn func(v *model.VariableValue) error) error
	GetElementInstance(key uint64) (*model.ElementInstanceValue, bool, error)
}

VarStore is the slice of the state store the CSV-import worker reads — a scope's variables and an element instance's parent scope. A narrow interface so the scope-chain walk (and its error paths) are testable with a fake (*state.Store satisfies it).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL