familio

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

README

go-familio

Go client for the familio.org genealogy API. Extracted from terraform-provider-familio so the same HTTP layer is usable from CLI tools, migration scripts, and other projects.

Disclaimer

This library is an unofficial integration. familio.org publishes no write API; its endpoints were reverse-engineered from the tree editor. It is not endorsed, operated, or sponsored by familio.org, and the endpoints may change or break without notice. Use it only on your own genealogy data, with a session you established yourself.

Install

go get github.com/dmalch/go-familio

Usage

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    familio "github.com/dmalch/go-familio"
)

func main() {
    cookies := os.Getenv("FAMILIO_COOKIES")
    if cookies == "" {
        log.Fatal("set FAMILIO_COOKIES")
    }

    client, err := familio.NewClient(familio.Options{
        Cookies: familio.CookiesFromHeader(cookies),
    })
    if err != nil {
        log.Fatal(err)
    }

    person, err := client.GetPersonBasic(context.Background(), "<person-uuid>")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("name: %s\n", person.DisplayName)
}

A runnable version of this example lives in examples/getperson/.

Command-line tool

cmd/familio is a CLI façade over the library — handy for quick lookups (familio person get, familio tree, familio settlement get, …) and a few targeted writes (familio marriage create/delete, familio person set-biography) without writing Go:

go install github.com/dmalch/go-familio/cmd/familio@latest
familio settlement persons <uuid>      # public, no auth
FAMILIO_COOKIES='t=eyJ…' familio whoami
familio tree <uuid> -up -surname Иванов

See cmd/familio/README.md for the full command list, auth, and flags.

Auth

familio's auth is two-layer. The t session cookie alone is rejected by the authed API; familio's Next.js SSR embeds a short-lived JWT bearer in the page's __NEXT_DATA__. The client takes the t cookie, scrapes that JWT from an HTML page, caches it (refreshing ~5 minutes before its exp), and sends it as Authorization: Bearer on /api/v2/* calls. The JWT's uuid claim is the account id, exposed via Client.AccountUUID.

Supply the session cookie via Options.Cookies, built with one of:

familio.CookiesFromHeader("t=eyJ…; …")  // raw DevTools / $FAMILIO_COOKIES header
familio.CookieFromSessionToken("eyJ…")   // bare `t` value / $FAMILIO_SESSION
familio.CookiesFromBrowser("chrome")     // a logged-in browser (via sweetcookie)

The settlement-persons read is public and needs no credentials.

Behaviour

  • Rate-limited requests (via golang.org/x/time/rate); override with Options.RateLimit.
  • Retries on 429 and transient 5xx responses.
  • JWT bearer cached and refreshed automatically with a 5-minute skew before expiry.
  • ErrNotLoggedIn, ErrNotFound, and ErrAccessDenied map the auth/404/403 cases for errors.Is checks.
  • Derived views on top of the raw events: DeriveRelations(events, uuid) → normalized parents/spouses/children (spouses carry the wedding-event "union" uuid), BirthYear/DeathYear, and Client.CrawlTree for a bounded BFS over the connected persons.

Documentation

API reference: https://pkg.go.dev/github.com/dmalch/go-familio

The reverse-engineered HTTP surface (endpoints, request/response shapes, the auth model) is documented in API.md.

Contributing

make test     # unit tests (no network)
make lint     # golangci-lint
make check    # build + vet + lint + test (CI parity)

The live decode test self-skips unless FAMILIO_NETWORK_TEST=1 is set; run make test-acceptance to exercise it against production data before pushing changes that touch endpoint or wire-shape code. CI does not run it.

License

Apache-2.0. See LICENSE.

Documentation

Overview

Package familio is a Go client for the familio.org genealogy API. Auth is two-layer: a session `t` cookie (installed on a jar scoped to https://familio.org/) bootstraps a JWT bearer scraped from the page's __NEXT_DATA__ (see auth.go), which authenticates the /api/v2 calls.

It covers the public settlement-persons read plus full person CRUD, life-fact events, sources, and the wedding-event (marriage) endpoints. See API.md for the endpoint reference.

Index

Constants

View Source
const (
	RangeBefore  = "before"
	RangeAfter   = "after"
	RangeBetween = "between"
)

Range kinds accepted on a DateRange (the provider's user-facing vocabulary, mirroring the sibling terraform-provider-genealogy date model).

View Source
const (
	GenderMale   = "male"
	GenderFemale = "female"

	PrivacyVisibleForAll = "visible_for_all"
	PrivacyInvisible     = "invisible"
)

Gender and privacy literals accepted by familio's /api/v2/persons surface.

View Source
const (
	EventBirth   = "birth"
	EventDeath   = "death"
	EventBaptism = "baptism" // христианское крещение (christening)
	EventWedding = "wedding"

	RoleChild  = "child"
	RoleParent = "parent"
	RoleOwner  = "owner"
	RoleSpouse = "spouse"

	// SelfRef is the placeholder personUuid for the person being created in the
	// same POST /api/v2/persons request (resolved server-side to the new uuid).
	SelfRef = "self"
)

Event types and the participant roles seen in the tree editor.

View Source
const (
	// SourceTypeCase is an archive document (дело) from the digitised
	// organization → fund → register → case catalog. Its CatalogKey is null.
	SourceTypeCase = "case"
	// SourceTypeCatalogPerson is a record from a people index
	// (persons?type=catalogPerson). Its CatalogKey names the source catalog.
	SourceTypeCatalogPerson = "catalog_person"
)

Source types — the two "add source" flows familio offers. See API.md.

View Source
const (
	// TreeUp follows parents only (ancestors).
	TreeUp = "up"
	// TreeDown follows children only (descendants).
	TreeDown = "down"
	// TreeComponent follows parents, spouses, and children (the whole connected
	// component). It is the default.
	TreeComponent = "component"
)

Tree traversal directions for CrawlTree.

Variables

View Source
var (
	// ErrNoCookies is returned when no familio.org cookies were found in any
	// browser store on the host. Log in to familio.org in a browser first.
	ErrNoCookies = errors.New("familio: no familio.org cookies found in any browser")

	// ErrFullDiskAccessRequired wraps macOS "operation not permitted" failures
	// reading browser cookie stores (e.g. Safari's container needs Full Disk
	// Access). Grant it in System Settings → Privacy & Security → Full Disk
	// Access for the binary running this code.
	ErrFullDiskAccessRequired = errors.New(
		"familio: cannot read browser cookie store (on macOS, grant Full Disk " +
			"Access in System Settings → Privacy & Security)")
)
View Source
var (
	// ErrNotFound is returned when a person/event UUID resolves to nothing
	// (HTTP 404). Resource Read paths use it to drop the resource from state.
	ErrNotFound = errors.New("familio: resource not found")

	// ErrNotLoggedIn is returned when familio.org redirects to a login page,
	// meaning the session cookie (`t`) is missing or expired.
	ErrNotLoggedIn = errors.New("familio: not logged in (session cookie missing or expired)")

	// ErrAccessDenied is returned on HTTP 401/403.
	ErrAccessDenied = errors.New("familio: access denied")
)
View Source
var FactEventTypes = []string{
	"arrest", "barMitzvah", "batMitzvah", "blessing", "militaryAward", "militaryService",
	"citizenship", "titleOfNobility", "demobilization", "immigration", "naming", "confirmation",
	"concentrationCamp", "location", "award", "education", "circumcision", "condemnation",
	"sentenceServing", "reburial", "militaryRankObtaining", "captured", "ordination", "burial",
	"conscription", "missing", "profession", "convictRehabilitation", "renaming", "resurnaming",
	"crime", "hajj", "exhumation", "emigration", "injury", "travel", "pilgrimage", "collectiveFarm",
	"party", "evacuation", "scienceDegree", "dekulakization", "treatment", "combat",
	"militaryCemetery", "heroicAct", "reference", "godparent", "warranter",
}

FactEventTypes is the set of single-subject "fact" event types a familio_event resource may carry (role "owner"). It EXCLUDES the events managed by dedicated surfaces — birth/death/baptism (folded into familio_person) and the true two-person relationship events wedding/divorce/affiance/nikah — so the two never fight over one event.

godparent (Восприемник) and warranter (Поручитель) ARE included: per Familio's own model (confirmed by their team in the official group) these are single-subject events recorded on the godparent/witness themselves — Familio deliberately does NOT link them to the specific godchild/party, so the godchild is noted in the comment, exactly as Familio's power users do. Source: the editor's event catalogue (internal/familio/API.md).

View Source
var SupportedBrowsers = []string{
	"chrome", "edge", "brave", "arc", "chromium",
	"vivaldi", "opera", "firefox", "safari",
}

SupportedBrowsers lists the browser names accepted by CookiesFromBrowser.

Functions

func BirthYear added in v0.3.0

func BirthYear(events []Event, personUUID string) (int, bool)

BirthYear returns personUUID's birth year (from their own birth event) and whether it is known.

func ChildrenOf

func ChildrenOf(events []Event, personUUID string) []string

ChildrenOf returns the uuids of personUUID's children: the child participant of every birth event in which personUUID is a parent. It is the inverse of OwnBirthEvent (which finds the event where personUUID is the child), so a person's own birth never counts as a child.

func CookieFromSessionToken

func CookieFromSessionToken(token string) []*http.Cookie

CookieFromSessionToken wraps a bare `t` session token value (from the session_token provider attr / $FAMILIO_SESSION) as a single `t` cookie.

func CookiesFromBrowser

func CookiesFromBrowser(browsers ...string) ([]*http.Cookie, error)

CookiesFromBrowser reads valid (non-expired) familio.org cookies from the host's browser stores. With no arguments, sweetcookie's default browser priority is used; with names, only those backends are queried in order. Mirrors go-geni's browsercookies.FromGeniCom.

func CookiesFromHeader

func CookiesFromHeader(header string) []*http.Cookie

CookiesFromHeader parses a "name=value; name=value" cookie header (the form copied out of a browser's DevTools Network panel, or the $FAMILIO_COOKIES env var) into a slice of *http.Cookie suitable for Options.Cookies. Lifted from go-geni's web.CookiesFromHeader.

func DeathYear added in v0.3.0

func DeathYear(events []Event, personUUID string) (int, bool)

DeathYear returns personUUID's death year (from their own death event) and whether it is known.

func SpousesOf

func SpousesOf(events []Event, personUUID string) []string

SpousesOf returns the uuids of personUUID's spouses across the wedding events they take part in (every spouse participant other than the person). Weddings that do not include the person are ignored.

Types

type BasicFields

type BasicFields struct {
	FirstName      string `json:"firstName"`
	LastName       string `json:"lastName"`
	MiddleName     string `json:"middleName"`     // отчество (patronymic)
	BirthFirstName string `json:"birthFirstName"` // maiden given name
	BirthLastName  string `json:"birthLastName"`  // maiden surname
	Gender         string `json:"gender"`
	Privacy        string `json:"privacy"`
}

BasicFields are the editable name/gender/privacy fields of a tree person — the "basic" object of POST /persons and the (flat) body of PUT /persons/<id>/basic.

type BasicRecord

type BasicRecord struct {
	BasicFields
	UUID        string `json:"uuid"`
	DisplayName string `json:"displayName"`
	CreatedAt   string `json:"createdAt"`
	UpdatedAt   string `json:"updatedAt"`
}

BasicRecord is the basic person view. GET /persons/<uuid>/basic returns it flat (no displayName); the POST /persons 201 nests it under "basic" and does include displayName, so the field is populated on create but empty on a /basic read (use GetPersonDisplay for reads).

type Biography added in v0.2.0

type Biography struct {
	Text      string `json:"text"`
	UpdatedAt string `json:"updatedAt"`
}

Biography is a person's free-text life description (the familio "tab=2" panel). It is its own sub-resource carrying its own optimistic-lock version: UpdatedAt is the token to send back in X-Base-Version on the next edit, and is distinct from the basic record's updatedAt.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to familio.org's /api/v2 surface with a session cookie.

Authenticated calls need a JWT bearer, which familio does not mint via an API endpoint — it embeds it in the page's __NEXT_DATA__. The client scrapes it (using the session cookie) on first use and re-scrapes when it nears expiry; see auth.go.

func NewClient

func NewClient(opts Options) (*Client, error)

NewClient builds a Client from Options.

func (*Client) AccountUUID

func (c *Client) AccountUUID(ctx context.Context) (string, error)

AccountUUID returns the uuid of the authenticated account, read from the `uuid` claim of the scraped JWT (the same value sent as ?owner= on creates). It triggers a token scrape when one has not happened yet, so it doubles as a credential check: a missing or expired session surfaces as ErrNotLoggedIn.

func (*Client) CrawlTree added in v0.3.0

func (c *Client) CrawlTree(ctx context.Context, rootUUID string, opts TreeOptions) ([]TreeNode, error)

CrawlTree walks the connected persons around rootUUID and returns them with structured relations, in breadth-first discovery order. It replaces hand- written BFS crawlers that called person-get repeatedly and reduced the result to {uuid, name, year, parents}. Direction, Surname, and Depth bound the walk (see TreeOptions). Every node fetches the person's events once; relations, name, and birth year are derived from them.

func (*Client) CreateEvent

func (c *Client) CreateEvent(ctx context.Context, personUUID string, ev Event) (*Event, error)

CreateEvent posts a life event anchored on a person (POST /api/v2/persons/<uuid>/events). The event is visible on every participant's /events; anchor on any participant. Returns the created event (with its new uuid).

func (*Client) CreatePerson

func (c *Client) CreatePerson(ctx context.Context, in CreatePersonInput) (*createResponse, error)

CreatePerson mints a new tree person (POST /api/v2/persons), owned by the authenticated account. Returns the server's basic record (incl. the new uuid) and the events it created.

func (*Client) CreateSource

func (c *Client) CreateSource(ctx context.Context, personUUID string, ref SourceRef) (*Source, error)

CreateSource attaches a source citation to a person (POST /api/v2/persons/<uuid>/sources). Returns the enriched source.

func (*Client) DeleteEvent

func (c *Client) DeleteEvent(ctx context.Context, personUUID, eventUUID string) error

DeleteEvent removes an event (DELETE /api/v2/persons/<uuid>/events/<eventUuid>). personUUID may be any participant of the event.

func (*Client) DeletePerson

func (c *Client) DeletePerson(ctx context.Context, uuid string) error

DeletePerson removes a tree person (DELETE /api/v2/persons/<uuid>).

func (*Client) DeleteSource

func (c *Client) DeleteSource(ctx context.Context, personUUID, sourceUUID string) error

DeleteSource removes a source citation from a person (DELETE /api/v2/persons/<uuid>/sources/<sourceUuid>) → 204.

func (*Client) GetHistoryFilters added in v0.4.0

func (c *Client) GetHistoryFilters(ctx context.Context) (*HistoryFilters, error)

GetHistoryFilters fetches the change-history facet vocabularies via POST /api/v2/persons/history/<accountUuid>/get-filters-data. The counts are for the unfiltered history (the UI narrows them by POSTing the currently applied filters; this client always asks for the full vocabulary).

func (*Client) GetPersonBasic

func (c *Client) GetPersonBasic(ctx context.Context, uuid string) (*BasicRecord, error)

GetPersonBasic reads the editable basic fields (+ timestamps) of a person. ErrNotFound ⇒ the person is gone; the Read path drops it from state.

func (*Client) GetPersonBiography added in v0.2.0

func (c *Client) GetPersonBiography(ctx context.Context, uuid string) (*Biography, error)

GetPersonBiography reads a person's biography sub-resource. The returned UpdatedAt is the version to pass back to UpdatePersonBiography.

func (*Client) GetPersonDisplay

func (c *Client) GetPersonDisplay(ctx context.Context, uuid string) (*personDisplay, error)

GetPersonDisplay reads the computed display name from the regularPerson view.

func (*Client) GetPersonEvents

func (c *Client) GetPersonEvents(ctx context.Context, uuid string) ([]Event, error)

GetPersonEvents reads a person's life events (for birth/death dates).

func (*Client) GetPersonRegular

func (c *Client) GetPersonRegular(ctx context.Context, uuid string) (*RegularRecord, error)

GetPersonRegular reads the regularPerson view, including the owning account (ownerId) used to tell one's own tree from other researchers' profiles.

func (*Client) GetPersonSources

func (c *Client) GetPersonSources(ctx context.Context, personUUID string) ([]Source, error)

GetPersonSources lists a person's source citations (GET /api/v2/persons/<uuid>/sources).

func (*Client) GetSettlement

func (c *Client) GetSettlement(ctx context.Context, uuid string) (*SettlementDetail, error)

GetSettlement reads a settlement's full record (GET /api/v2/settlements/<uuid>, Bearer). Returns ErrNotFound for an unknown uuid.

func (*Client) ListPersonsHistory added in v0.4.0

func (c *Client) ListPersonsHistory(ctx context.Context, filter HistoryFilter) (*HistoryPage, error)

ListPersonsHistory fetches one page of the account's person change history («История изменений», a Familio Plus feature) via GET /api/v2/persons/history/<accountUuid>. The history lives under the authenticated account's uuid, so the owner is resolved from the session; page through by advancing HistoryFilter.Page until Pager.TotalItems is reached.

func (*Client) ListSettlementPersons

func (c *Client) ListSettlementPersons(ctx context.Context, settlement string) ([]Person, error)

ListSettlementPersons returns every person (catalog-sourced + user-created) linked to a settlement, paging through GET /api/v2/persons?settlement=<uuid>. This is the one fully-known, public endpoint and the provider's working read path. Callers filter by CatalogKey client-side (there is no server-side catalog facet).

func (*Client) UpdatePersonBasic

func (c *Client) UpdatePersonBasic(ctx context.Context, uuid string, fields BasicFields, version string) (*BasicRecord, error)

UpdatePersonBasic edits a person's basic fields. version is the optimistic- lock token (the updatedAt last read from GetPersonBasic), sent in the X-Base-Version header that familio's editor uses; a stale value is rejected with HTTP 409. Returns the refreshed basic record.

func (*Client) UpdatePersonBiography added in v0.2.0

func (c *Client) UpdatePersonBiography(ctx context.Context, uuid, text, version string) (*Biography, error)

UpdatePersonBiography sets a person's biography text in place. version is the optimistic-lock token (the biography's own updatedAt, last read from GetPersonBiography), sent in the X-Base-Version header; a stale value is rejected with HTTP 409. Returns the refreshed biography (bumped UpdatedAt).

func (*Client) UpdateSourceComment

func (c *Client) UpdateSourceComment(ctx context.Context, personUUID, sourceUUID, comment string) (*Source, error)

UpdateSourceComment edits a source's comment in place (PATCH /api/v2/persons/<uuid>/sources/<sourceUuid>). Only the comment is mutable; the reference is fixed. Returns the updated source.

familio guards the PATCH with the optimistic-lock X-Base-Version header (the same one that guards /basic and /biography): its value is the source's own updatedAt, and a missing token is rejected with «Не указана дата-время последнего обновления источника». The current updatedAt is read fresh here so callers do not have to thread a version token through.

type Coordinate

type Coordinate struct {
	Type        string    `json:"type"`
	Coordinates []float64 `json:"coordinates"`
}

Coordinate is a GeoJSON Point: Coordinates is [longitude, latitude].

func (*Coordinate) LatLon

func (c *Coordinate) LatLon() (lat, lon float64, ok bool)

LatLon returns (latitude, longitude, ok); ok is false when the coordinate is absent or malformed.

type CreatePersonInput

type CreatePersonInput struct {
	Basic     BasicFields
	Events    []Event
	Biography *string
}

CreatePersonInput is the caller-facing create payload: the basic fields plus the life events to attach. Exactly one birth event is required; build them with SelfBirthEvent / SelfDeathEvent. Biography, when non-nil, sets the person's initial biography text in the same request.

type DatePart

type DatePart struct {
	Day   *int   `json:"day"`
	Month *int   `json:"month"`
	Year  int    `json:"year"`
	Type  string `json:"type"`
}

DatePart is one endpoint of a complex date. Year is required; Month/Day are optional (nil ⇒ unspecified). Type carries the per-part calendar.

type DateRange

type DateRange struct {
	Year  int
	Month *int
	Day   *int
	Circa bool

	Range    string // "" | RangeBefore | RangeAfter | RangeBetween
	EndYear  *int
	EndMonth *int
	EndDay   *int
	EndCirca bool

	Calendar string // "" => gregorian
}

DateRange is the provider's domain value object for a genealogical date: a primary {Year,Month,Day}, an optional approximation (Circa) or bound/range (Range plus the End* second date), and a Calendar. It mirrors the date model of the sibling terraform-provider-genealogy so configs read the same across both providers.

familio's wire shape (EventDate{calendar,type,first,second}) is an infrastructure detail; translate with EventDateFromRange / RangeFromEventDate. familio carries a single whole-date `type`, so Circa maps to type "about" and Range to before/after/between — the two are mutually exclusive on familio and the schema rejects combining them (familio has no per-endpoint approximation, so EndCirca is accepted only for cross-provider config symmetry and never reaches the wire).

func RangeFromEventDate

func RangeFromEventDate(date EventDate) *DateRange

RangeFromEventDate translates a wire EventDate back into the domain DateRange for read-back. It returns nil when the date is unknown (no first part). gregorian is the default calendar, so it surfaces as an empty Calendar (no spurious diff against a config that omits calendar).

type Event

type Event struct {
	UUID         *string       `json:"uuid"` // null on create; real uuid on read-back
	Type         string        `json:"type"`
	Date         EventDate     `json:"date"`
	Participants []Participant `json:"participants"`
	Settlement   *Settlement   `json:"settlement"` // place (Место); nil ⇒ null (no place)
	Comment      string        `json:"comment"`
	CreatedAt    string        `json:"createdAt,omitempty"`
	UpdatedAt    string        `json:"updatedAt,omitempty"`
}

Event is a life event (birth/death/wedding) with its participants.

func BaptismEvent

func BaptismEvent(date *DateRange, ownerRef, place, comment string) Event

BaptismEvent builds a christening (familio "baptism") event owned by ownerRef (SelfRef on create, or the person's uuid otherwise). Unlike birth/death, a baptism is a repeatable fact event: re-POSTing does NOT replace it, so editing a christening date means deleting the old event and creating a new one. place is the christening settlement uuid ("" ⇒ no place); comment is free text.

func BirthEvent

func BirthEvent(date *DateRange, childRef string, parents []string, place, comment string) Event

BirthEvent builds a birth event for childRef (SelfRef when the child is being created in the same POST /persons request, or the child's uuid when rebuilding an existing person's birth event), attaching 0–2 parent persons (role "parent"). familio upserts a person's single birth event by its child participant, so POSTing this event replaces the whole event (participants + date) in place — that is how parents are added/removed and the birth date is edited without recreating the person. place is the birth settlement uuid ("" ⇒ no place) and comment is free text; like the date they are part of the event and must be re-sent on every upsert (a full replace would otherwise clear them).

func DeathEvent

func DeathEvent(date *DateRange, ownerRef, place, comment string) Event

DeathEvent builds a death event owned by ownerRef (SelfRef on create, or the person's uuid when upserting an existing person's death event). Like the birth event, re-POSTing it replaces the person's single death event in place. place is the death settlement uuid ("" ⇒ no place); comment is free text.

func FactEvent

func FactEvent(eventType string, date *DateRange, ownerRef, comment string) Event

FactEvent builds a single-subject fact event (role "owner") of the given type for ownerRef, with a free-text comment. Pass nil for an unknown date.

func FindByID

func FindByID(events []Event, uuid string) *Event

FindByID returns the event with the given uuid, or nil.

func OwnBirthEvent

func OwnBirthEvent(events []Event, personUUID string) *Event

OwnBirthEvent returns the birth event in which personUUID is the child — the person's *own* birth. A person who is also a parent has the births of their children on their /events too (where they are the parent), so a plain type-filter is not enough. Returns nil when there is no such event yet. On a create read-back the child participant carries the resolved uuid, so passing the new uuid works there too.

func OwnDeathEvent added in v0.3.0

func OwnDeathEvent(events []Event, personUUID string) *Event

OwnDeathEvent returns the death event owned by personUUID, or nil. A person's /events lists only their own death (death is single-subject, one per person), so a plain type filter suffices; this mirrors OwnBirthEvent for symmetry.

func SelfBaptismEvent

func SelfBaptismEvent(date *DateRange, place, comment string) Event

SelfBaptismEvent builds an optional christening event for the person being created.

func SelfBirthEvent

func SelfBirthEvent(date *DateRange, place, comment string) Event

SelfBirthEvent builds the mandatory birth event for the person being created. Pass nil for an unknown date and "" for no place/comment.

func SelfDeathEvent

func SelfDeathEvent(date *DateRange, place, comment string) Event

SelfDeathEvent builds an optional death event for the person being created.

func WeddingEvent

func WeddingEvent(date *DateRange, partnerA, partnerB, comment string) Event

WeddingEvent builds a marriage event linking two existing persons as spouses. Pass nil for an unknown date and "" for no comment.

func (*Event) ID

func (e *Event) ID() string

ID returns the event's uuid (empty when unset, e.g. a request-side event).

func (*Event) ParentUUIDs

func (e *Event) ParentUUIDs() []string

ParentUUIDs returns the personUuids of an event's parent participants.

func (*Event) SettlementUUID

func (e *Event) SettlementUUID() string

SettlementUUID returns the event's place uuid, or "" when no place is set.

func (*Event) SpouseUUIDs

func (e *Event) SpouseUUIDs() []string

SpouseUUIDs returns the personUuids of an event's spouse participants.

type EventDate

type EventDate struct {
	Calendar string    `json:"calendar"`
	Type     string    `json:"type"`
	First    *DatePart `json:"first"`
	Second   *DatePart `json:"second"`
	// Formatted is server-computed on reads; never sent.
	Formatted string `json:"formatted,omitempty"`
}

EventDate is familio's complex-date object. A nil First means "date unknown".

func EventDateFromRange

func EventDateFromRange(r *DateRange) EventDate

EventDateFromRange translates the domain DateRange into familio's wire EventDate. A nil DateRange is an unknown date (no parts). This is the single place that knows familio's calendar/type/first/second encoding.

type FlexDate

type FlexDate struct {
	Formatted string
	Present   bool
}

FlexDate tolerates familio's heterogeneous date encodings: a JSON null, a plain string, or a structured object {type, calendar, first, second, formatted}. It exposes the human-readable "formatted" form.

func (*FlexDate) UnmarshalJSON

func (d *FlexDate) UnmarshalJSON(b []byte) error

func (FlexDate) Value

func (d FlexDate) Value() (string, bool)

Value returns the formatted date and whether a non-empty value is present.

type Georequisite

type Georequisite struct {
	Level1 string `json:"level1"`
	Level2 string `json:"level2"`
	Year   int    `json:"year"`
}

Georequisite is one administrative «реквизит» of a settlement: the level-1 (region/oblast) and level-2 (district/city) administrative units as of Year.

type HistoryAuthor added in v0.4.0

type HistoryAuthor struct {
	ID          string `json:"id"`
	DisplayName string `json:"displayName"`
}

HistoryAuthor is who made the change. The zero uuid ("00000000-0000-0000-0000-000000000000") marks system-recorded changes (cause "initialization").

type HistoryDataType added in v0.4.0

type HistoryDataType struct {
	PersonDataBlock string
	EventType       string
	SourceType      string
}

HistoryDataType is one personDataType[N] list filter: a data block ("basic", "event", "source", "biography"), optionally narrowed by the event-type key (block "event") or the source type — "register", "case", "catalog_person" (block "source").

type HistoryDataTypeFacet added in v0.4.0

type HistoryDataTypeFacet struct {
	Item struct {
		Value struct {
			PersonDataBlock string  `json:"personDataBlock"`
			EventType       *string `json:"eventType"`
			SourceType      *string `json:"sourceType"`
		} `json:"value"`
		DisplayValue struct {
			Type    string  `json:"type"`
			Subtype *string `json:"subtype"`
		} `json:"displayValue"`
	} `json:"item"`
	Count int `json:"count"`
}

HistoryDataTypeFacet is one value of the data-type facet; its value is the {personDataBlock, eventType, sourceType} triple used by HistoryFilter.DataTypes.

type HistoryEntry added in v0.4.0

type HistoryEntry struct {
	Record HistoryRecord    `json:"record"`
	Person HistoryPersonRef `json:"person"`
	Author HistoryAuthor    `json:"author"`
}

HistoryEntry is one row of the change-history list.

type HistoryFacet added in v0.4.0

type HistoryFacet struct {
	Item struct {
		Value        string `json:"value"`
		DisplayValue string `json:"displayValue"`
	} `json:"item"`
	Count int `json:"count"`
}

HistoryFacet is one selectable value of a change-history filter facet, with the number of history entries carrying it.

type HistoryFilter added in v0.4.0

type HistoryFilter struct {
	Text       string            // free-text search over the entries
	Operations []string          // "create", "update", "delete"
	Causes     []string          // "user", "initialization"
	AuthorIDs  []string          // editor user uuids; the zero uuid is the system author
	PersonIDs  []string          // limit to specific persons
	DataTypes  []HistoryDataType // limit to specific data blocks / event or source types
	From       time.Time         // happened-at range start (zero = unbounded)
	Till       time.Time         // happened-at range end (zero = unbounded)

	Page         int  // 1-based page number; 0 means 1
	ItemsPerPage int  // page size; 0 means historyPageSize
	Ascending    bool // oldest first instead of the default newest first
}

HistoryFilter narrows and pages the change-history list. The zero value requests the first page of everything, newest first.

type HistoryFilters added in v0.4.0

type HistoryFilters struct {
	Authors        []HistoryFacet         `json:"authorFilter"`
	Operations     []HistoryFacet         `json:"operationFilter"`
	Causes         []HistoryFacet         `json:"causeFilter"`
	Persons        []HistoryFacet         `json:"personFilter"`
	PersonsHasMore bool                   `json:"personFilterHasMore"`
	DataTypes      []HistoryDataTypeFacet `json:"personDataTypeFilter"`
}

HistoryFilters is the facet vocabulary of the change-history list: every author, operation, cause, person and data type present in the account's history, each with its entry count.

type HistoryPage added in v0.4.0

type HistoryPage struct {
	Data  []HistoryEntry `json:"data"`
	Pager Pager          `json:"pager"`
}

HistoryPage is one page of GET /api/v2/persons/history/<owner>.

type HistoryPersonRef added in v0.4.0

type HistoryPersonRef struct {
	ID             string `json:"id"`
	LastName       string `json:"lastName"`
	FirstName      string `json:"firstName"`
	MiddleName     string `json:"middleName"`
	BirthLastName  string `json:"birthLastName"`
	BirthFirstName string `json:"birthFirstName"`
	Gender         string `json:"gender"`
}

HistoryPersonRef identifies the person a history record belongs to.

type HistoryRecord added in v0.4.0

type HistoryRecord struct {
	ID              int64           `json:"id"`
	HappenedAt      string          `json:"happenedAt"`
	Cause           string          `json:"cause"`
	Operation       string          `json:"operation"`
	PersonDataBlock string          `json:"personDataBlock"`
	Changes         json.RawMessage `json:"changes"`
}

HistoryRecord is the change itself: what happened, when, and to which data block. Changes is the block's snapshot after the operation (for a delete, the state that was removed) and its shape follows PersonDataBlock — "basic" carries the flat basic fields, "event" an event-like object, "biography" {text}, "source" the source read shape — so it is kept as raw JSON. The API has no before/after diff; the UI derives it by comparing consecutive records.

type Options

type Options struct {
	// Cookies carries the familio.org session. NewClient installs them on a
	// cookie jar scoped to BaseURL. Build with CookiesFromHeader (paste from
	// DevTools / $FAMILIO_COOKIES) or CookiesFromBrowser (sweetcookie).
	Cookies []*http.Cookie

	// BaseURL overrides https://familio.org/. Useful for tests; production
	// callers should leave it empty.
	BaseURL string

	// UserAgent is sent on every request. Defaults to defaultUserAgent.
	UserAgent string

	// RateLimit caps outgoing requests in requests-per-second. Defaults to
	// defaultRateLimit if unset or non-positive.
	RateLimit float64

	// HTTPClient overrides the default *http.Client. NewClient always sets its
	// Jar from Cookies and its CheckRedirect to detect login bounces, so the
	// override does not need to.
	HTTPClient *http.Client
}

Options configures a Client. At least one cookie carrying the `t` session token should be supplied for any authenticated (write, or gated read) call; the public settlement-persons read works without one.

type Pager added in v0.4.0

type Pager struct {
	Page         int `json:"page"`
	ItemsPerPage int `json:"itemsPerPage"`
	TotalItems   int `json:"totalItems"`
}

Pager is familio's paging envelope, shared by the paginated list endpoints.

type Participant

type Participant struct {
	PersonUUID  string `json:"personUuid"`
	Role        string `json:"role"`
	DisplayName string `json:"displayName,omitempty"`
	Gender      string `json:"gender,omitempty"`
}

Participant links a person into an event by role.

type Person

type Person struct {
	UUID                string   `json:"uuid"`
	DisplayName         string   `json:"displayName"`
	ShortDisplayName    string   `json:"shortDisplayName"`
	CatalogKey          *string  `json:"catalogKey"`
	CatalogName         string   `json:"catalogName"`
	Type                string   `json:"type"`
	Gender              string   `json:"gender"`
	BirthDate           FlexDate `json:"birthDate"`
	DeathDate           FlexDate `json:"deathDate"`
	HasDeathEvent       bool     `json:"hasDeathEvent"`
	BirthSettlementText string   `json:"birthSettlementText"`
	UpdatedAt           string   `json:"updatedAt"`
}

Person is one entry from GET /api/v2/persons. The data array is heterogeneous: catalog persons carry catalogKey/catalogName, while user-created tree persons instead carry gender/birthPlace/ownerId/etc. CatalogKey is a pointer so a JSON null (tree person) is distinguishable from an empty string; birth/death dates use FlexDate because familio encodes them as null, a string, or an object depending on the record.

type PersonRef added in v0.3.0

type PersonRef struct {
	UUID string `json:"uuid"`
	Name string `json:"name,omitempty"`
}

PersonRef is a minimal reference to a related person: the full uuid and, when an event carried it, a display name. UUID is always the complete uuid (never a truncated prefix) so downstream tooling can key on it safely.

type RegularRecord

type RegularRecord struct {
	UUID        string `json:"uuid"`
	DisplayName string `json:"displayName"`
	OwnerID     string `json:"ownerId"`
	Gender      string `json:"gender"`
	PrivacyType string `json:"privacyType"`
}

RegularRecord is the slice of the regularPerson view (GET /persons/<uuid>) the provider surfaces beyond the basic record: notably ownerId, the account that owns the profile, which is not present on the public settlement list.

type Relations added in v0.3.0

type Relations struct {
	Parents  []PersonRef `json:"parents"`
	Spouses  []Spouse    `json:"spouses"`
	Children []PersonRef `json:"children"`
}

Relations is the normalized kinship of one person, derived from their events: parents/spouses/children as flat reference lists rather than per-event participant roles. It is the convenience view every consumer would otherwise reconstruct from events[].participants[] by hand.

func DeriveRelations added in v0.3.0

func DeriveRelations(events []Event, personUUID string) Relations

DeriveRelations reduces a person's events into normalized parents, spouses, and children. Names come from the events' participant display names when present. Spouses carry the wedding event uuid as their MarriageUUID. All uuids are full uuids.

type Settlement

type Settlement struct {
	UUID string `json:"uuid"`
}

Settlement is a place attached to an event — familio's «Место рождения / смерти / etc.». The write contract is a structured object, NOT a bare uuid: a bare string is rejected (HTTP 400). The minimal accepted body is `{"uuid": "<id>"}`; the server enriches the name and administrative requisites («реквизиты»), which come back on read but are not needed by the provider, so only the uuid is modelled (extra read fields are ignored). See the "Settlement / place on events" section of internal/familio/API.md.

func SettlementRef

func SettlementRef(uuid string) *Settlement

SettlementRef wraps a settlement uuid as an event place. An empty uuid yields nil — i.e. no place (which clears any existing place on an upsert).

type SettlementDetail

type SettlementDetail struct {
	UUID             string        `json:"uuid"`
	PrimaryName      string        `json:"primaryName"`
	AdditionalNames  []string      `json:"additionalNames"`
	MainGeorequisite *Georequisite `json:"mainGeorequisite"`
	Type             string        `json:"type"`   // settlement kind, e.g. «село», «город».
	Status           string        `json:"status"` // e.g. «жилой» (inhabited).
	Coordinate       *Coordinate   `json:"coordinate"`
}

SettlementDetail is the full settlement record from GET /api/v2/settlements/<uuid>: the canonical name, its administrative requisites («реквизиты»), classification and coordinates. Distinct from the Settlement write-ref above (which carries only the uuid). nearestSettlements is returned by the API but not surfaced by the provider.

type Source

type Source struct {
	UUID       string         `json:"uuid"`
	Type       string         `json:"type"`
	Comment    string         `json:"comment"`
	Name       string         `json:"name,omitempty"`
	Requisites string         `json:"requisites,omitempty"`
	Years      string         `json:"years,omitempty"`
	Catalog    *SourceCatalog `json:"catalog,omitempty"`
	CreatedAt  string         `json:"createdAt,omitempty"`
	UpdatedAt  string         `json:"updatedAt,omitempty"`
}

Source is a person's source citation (the «Источники» sub-resource). It is an immutable reference to a catalogued entity (UUID + Type + CatalogKey) plus an editable Comment. Name/Requisites/Years/Catalog are server-derived and read back only (never sent). The referenced entity's UUID is the source's identity within the person, so a person cannot cite the same entity twice.

func FindSourceByID

func FindSourceByID(sources []Source, uuid string) *Source

FindSourceByID returns the source with the given (entity) uuid, or nil.

type SourceCatalog

type SourceCatalog struct {
	Key    string `json:"key"`
	Hidden bool   `json:"hidden"`
}

SourceCatalog is the server-derived catalog descriptor on a catalog_person source. The API returns it as an OBJECT ({key, hidden}); a `case` source omits it. UnmarshalJSON also tolerates a bare string or null for forward-compat.

func (*SourceCatalog) String

func (c *SourceCatalog) String() string

String renders the catalog for the computed `catalog` attribute: its key (the stable catalog id, e.g. "vss"), or "" when absent.

func (*SourceCatalog) UnmarshalJSON

func (c *SourceCatalog) UnmarshalJSON(b []byte) error

type SourceRef

type SourceRef struct {
	UUID       string  `json:"uuid"`
	Type       string  `json:"type"`
	CatalogKey *string `json:"catalogKey"`
}

SourceRef is the write body of a source create: the reference triple familio accepts. CatalogKey is sent explicitly (null for a `case`, the catalog id for a `catalog_person`) — it is write-only and never echoed on reads.

type Spouse added in v0.3.0

type Spouse struct {
	UUID         string `json:"uuid"`
	Name         string `json:"name,omitempty"`
	MarriageUUID string `json:"marriageUuid,omitempty"`
}

Spouse is a spouse reference plus the uuid of the underlying wedding event — familio's "union"/marriage identity, needed to import a familio_marriage or to target that marriage for deletion. familio has no separate union resource; the wedding event uuid *is* the marriage uuid.

type TreeNode added in v0.3.0

type TreeNode struct {
	UUID     string      `json:"uuid"`
	Name     string      `json:"name,omitempty"`
	Year     int         `json:"year,omitempty"`
	Parents  []PersonRef `json:"parents"`
	Spouses  []Spouse    `json:"spouses"`
	Children []PersonRef `json:"children"`
}

TreeNode is one person in a crawled tree with their normalized relations. UUID and every relation uuid are full uuids. Year is the birth year (omitted when unknown).

type TreeOptions added in v0.3.0

type TreeOptions struct {
	// Direction is TreeUp, TreeDown, or TreeComponent. Empty means TreeComponent.
	Direction string

	// Surname, when set, keeps the crawl from expanding through people whose last
	// name (or maiden last name) does not match — the way to avoid pulling living
	// in-law branches into an otherwise on-surname component. Non-matching people
	// are still emitted as nodes (they are referenced), they are just not
	// expanded. The root is always expanded. Matching is case-insensitive.
	Surname string

	// Depth caps the BFS distance from the root (root = 0). Zero means unlimited.
	Depth int
}

TreeOptions bounds a CrawlTree walk.

Directories

Path Synopsis
cmd
familio command
Command familio is a read-only command-line client for the familio.org genealogy API.
Command familio is a read-only command-line client for the familio.org genealogy API.
examples
getperson command
Command getperson is a minimal smoke example that constructs a go-familio Client from a session cookie and fetches a single person's basic record.
Command getperson is a minimal smoke example that constructs a go-familio Client from a session cookie and fetches a single person's basic record.

Jump to

Keyboard shortcuts

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