Skip to main content

Flat-file definition

Every field of a flat-file connector's definition.json, the declaration Owlie derives from it, how uploaded files are routed and read, and the rules Owlie checks. A flat-file definition lists feeds: which uploaded file holds which entity or relationship, how it is parsed, and which column is which. The machine-readable form is flat-file.schema.json.

Editable in the tables says whether a value can be install-editable: written as { "$oxl": "config.<name>" }, naming a declared config field whose default is the seed every install copies and may then edit. Only two values can be: a feed's fileMatch and an entity feed's columns.

A complete definition is in Example: CSV feed.

Top level​

FieldTypeRequiredWhat it does
engineintegeryesAlways 1, the version of the definition format.
descriptionstringnoWhat the catalog shows. Non-empty.
iconstringnoThe catalog icon: @ plus a domain, or an image URL. Non-empty.
configobjectnoThe install's configuration fields. Its one key is properties.
config.propertiesobject of JSON SchemasnoOne entry per config field, keyed by name. In practice, the seeds of install-editable values.
accountEntityKeystringnoThe entity whose records are accounts: the entityKey of one of the entity feeds. Without it, no entity is the account.
feedsobjectyesThe feeds, keyed by feed name. A feed name cannot contain /.

A config field is required in the install form unless its default is "" or null. A field that an install-editable value reads must have the matching type: string for fileMatch, object for columns.

Feed fields​

Every feed has these fields:

FieldTypeRequiredEditableWhat it does
kindentity or relationshipyesnoWhat the feed's rows are: records of one entity, or links between two entities' records.
fileMatchstringyesyesThe file name pattern that routes an uploaded file to the feed.
parseobjectyesnoHow the feed's files are read.
parse.delimiter",", ";" or "\t"yesnoThe cell delimiter: comma, semicolon or tab.
parse.headerbooleanyesnoWhether the first row names the columns.
parse.encoding"utf-8"yesnoThe file encoding. UTF-8 is the only one.

Entity feeds​

A feed with "kind": "entity" also has:

FieldTypeRequiredEditableWhat it does
entityKeystringyesnoThe entity the rows are records of. One feed per entity.
columnsobjectyesyesThe columns, keyed by column name. At least one column.
columns.<name>.positioninteger, 0 or moreyeswith columnsThe column's position, 0 for the first. Unique within the feed.
columns.<name>.typestring, number or booleanyeswith columnsHow the column's cells are converted.
columns.<name>.identifierbooleannoseeded identifiers stay fixedWhether the column identifies a record.
isActiveExprstringnonoOn the account entity's feed only: an OXL expression over the record, true when the account is active.
isDeletedExprstringnonoOn the account entity's feed only: an OXL expression over the record, true when the account is deleted.

An entity feed needs at least one identifier column and may have several. The identifier with the lowest position is the record's external id. Owlie takes it from the first identifier column, in position order, that has a value in the row, so a row whose first identifier is blank is keyed by the next one; a row with every identifier blank is a failed record. When two rows share an external id, the later row wins. The expressions read the record's columns by name, such as employment_status == 'active'.

Relationship feeds​

A feed with "kind": "relationship" also has:

FieldTypeRequiredWhat it does
edgeobjectyesThe relationship the rows describe.
edge.edgeKeystringyesThe relationship's name. Unique among the relationship feeds.
edge.sourceEntityKeystringyesThe entity each row links from: an entity one of the entity feeds provides.
edge.targetEntityKeystringyesThe entity each row links to: an entity one of the entity feeds provides.
edge.sourceColumnstringyesThe header name of the column that holds the source record's external id.
edge.targetColumnstringyesThe header name of the column that holds the target record's external id.

A relationship feed requires parse.header: true. It has no entityKey, no install-editable columns, and no isActiveExpr or isDeletedExpr. Its edge.sourceColumn must be a column of the source entity's feed, and its edge.targetColumn a column of the target entity's feed, in each install's columns.

How files are routed and read​

StepRule
RoutingAn upload that names a feed goes to it. Otherwise the file goes to the first feed, in definition order, whose fileMatch matches the file name, using each install's own patterns. In a pattern, * matches any run of characters and ? one character; case is ignored. No match refuses the upload.
SizeA file larger than 8 MiB fails ingestion.
ParsingCells split on parse.delimiter. A quoted cell may hold the delimiter, doubled quotes and line breaks. Blank lines are skipped.
Columns, with a headerEach cell goes to the column whose name is its header text exactly. A header column with no declared column is kept on the record as text.
Columns, without a headerCells go to the declared columns in position order: the first cell to the lowest position, and so on.
stringThe cell's text as it is.
numberThe number the trimmed cell holds, or the text when it holds none. A blank cell is null.
booleantrue, 1, false or 0, trimmed, in any case. A blank cell is null. Any other value makes the row unreadable.
Relationship rowsEach row links the record whose external id is under edge.sourceColumn to the record whose external id is under edge.targetColumn. An id no record has yet gets a placeholder record, which the entity file fills in when it brings that record. A blank end, or a header without either column, fails the run.
LifecycleFor each account record, isDeletedExpr giving true makes it deleted; otherwise isActiveExpr giving true makes it active and false disabled. Any other result sets no state.

Each file is its feed's complete snapshot. A successful run removes the records, or relationships, that earlier files contained and this file does not. A run that finds no records after a previous successful run found some fails and removes nothing. A run with unreadable rows, or rows with no identifier value, ingests the rest and skips every removal; a file where unreadable rows pile up fails the run.

Rules Owlie checks​

Owlie refuses a definition, or an install's configuration, that breaks any of these:

  • engine is 1 and feeds is an object.
  • description and icon, when present, are non-empty literal strings.
  • A feed name has no /, kind is entity or relationship, and fileMatch is a non-empty string. No two feeds have the same fileMatch, compared exactly (routing ignores case, so patterns that differ only in case are accepted and the first one takes the files).
  • An entity feed has an entityKey no other feed has, and no edge. Its columns are an object of at least one column, each with a position that is a non-negative integer no other column has, and a type of string, number or boolean. At least one column is an identifier.
  • isActiveExpr and isDeletedExpr appear only on the feed whose entityKey is accountEntityKey.
  • A relationship feed has parse.header: true, no entityKey, no install-editable columns, and an edge with an edgeKey no other relationship feed has. edge.sourceEntityKey and edge.targetEntityKey are entities that entity feeds provide.
  • accountEntityKey, when present, is an entity that an entity feed provides.
  • An install-editable value is exactly { "$oxl": "config.<name>" }, naming a declared config field of the matching type.
  • An install's own columns keep every seeded identifier column with the same name, type and position, still marked identifier, and mark no other column as an identifier.

The derived declaration​

PartDerived value
provides["sync"]: no provisioning.
protocols["s3"]: files arrive in an Owlie-hosted drop.
authOne option, { "key": "none" }: installs enter no credentials.
configconfig.properties, each required unless its default is "" or null.
EntitiesOne per entity feed, keyed by its entityKey.
Grant kindsNone. Relationship feeds are not part of the declaration; their rows are read when their files are ingested.

Each entity has:

  • name: its entityKey, and the description Flat-file feed "<feed name>";
  • is_account: true for the entity named by accountEntityKey;
  • sync_support: never incremental;
  • a record schema with one property per column, of the column's type, identifier columns marked x-identifier, and other properties allowed;
  • identifier_paths: the identifier columns in position order.

The account entity's feed passes isActiveExpr and isDeletedExpr to its sync job as the account's lifecycle expressions. Every feed's sync job has no schedule: it runs when a file arrives, or by hand.

Operations​

HandlerWhat it does
connection.validateConfigDerives the feeds with the install's values, and reports any rule they break.
connection.testConnectionAlways succeeds: the drop is Owlie's.

There are no other handlers. Owlie supplies both, so a flat-file connector has nothing to override.