geni

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: May 13, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

README

go-geni

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

Disclaimer

This library uses the Geni API but is not endorsed, operated, or sponsored by Geni.com.

Install

go get github.com/dmalch/go-geni

Usage

package main

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

    "github.com/dmalch/go-geni"
    "golang.org/x/oauth2"
)

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

    client := geni.NewClient(
        oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}),
        true, // true = sandbox, false = production
    )

    profile, err := client.GetProfile(context.Background(), "profile-1")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("name: %s %s\n",
        derefString(profile.FirstName), derefString(profile.LastName))
}

func derefString(p *string) string {
    if p == nil {
        return ""
    }
    return *p
}

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

OAuth

The auth subpackage offers a browser-based OAuth implicit-flow helper and a token cache, suitable for interactive CLI tools:

import (
    "golang.org/x/oauth2"
    "github.com/dmalch/go-geni/auth"
)

source := oauth2.ReuseTokenSource(nil,
    auth.NewCachingTokenSource("~/.geni/token.json",
        auth.NewAuthTokenSource(&oauth2.Config{
            ClientID: "1855",
            Endpoint: oauth2.Endpoint{
                AuthURL: "https://www.geni.com/platform/oauth/authorize",
            },
        })))

Headless callers can skip auth entirely and supply any oauth2.TokenSource to geni.NewClient.

Behaviour

  • 1 request/second rate limit, adjusted on the fly from X-API-Rate-Limit response headers.
  • Retries on 429 (rate limited), 401 (token expired), and transient transport errors via github.com/avast/retry-go.
  • Bulk-read coalescing for profile/document/union endpoints when multiple concurrent reads target the same family of resources.
  • Sandbox or production environment selectable per client.

Documentation

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

License

Apache-2.0. See LICENSE.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrAccessDenied = fmt.Errorf("access denied")
View Source
var ErrResourceNotFound = fmt.Errorf("resource not found")

Functions

func BaseURL

func BaseURL(useSandboxEnv bool) string

Types

type AddOption

type AddOption func(*http.Request)

AddOption modifies an outgoing request for the profile/union add-* endpoints (add-child, add-sibling, add-partner).

func WithModifier

func WithModifier(modifier string) AddOption

WithModifier sets the relationship_modifier query parameter. An empty value is a no-op.

type Client

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

func NewClient

func NewClient(tokenSource oauth2.TokenSource, useSandboxEnv bool) *Client

func (*Client) AddChild

func (c *Client) AddChild(ctx context.Context, profileId string, opts ...AddOption) (*ProfileResponse, error)

func (*Client) AddDocumentToProject

func (c *Client) AddDocumentToProject(ctx context.Context, docimentId, projectId string) (*DocumentBulkResponse, error)

func (*Client) AddPartner

func (c *Client) AddPartner(ctx context.Context, profileId string) (*ProfileResponse, error)

func (*Client) AddProfileToProject

func (c *Client) AddProfileToProject(ctx context.Context, profileId, projectId string) (*ProfileResponse, error)

func (*Client) AddSibling

func (c *Client) AddSibling(ctx context.Context, profileId string, opts ...AddOption) (*ProfileResponse, error)

func (*Client) CreateDocument

func (c *Client) CreateDocument(ctx context.Context, request *DocumentRequest) (*DocumentResponse, error)

func (*Client) CreateProfile

func (c *Client) CreateProfile(ctx context.Context, request *ProfileRequest) (*ProfileResponse, error)

func (*Client) DeleteDocument

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

func (*Client) DeleteProfile

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

func (*Client) GetDocument

func (c *Client) GetDocument(ctx context.Context, documentId string) (*DocumentResponse, error)

func (*Client) GetDocuments

func (c *Client) GetDocuments(ctx context.Context, documentIds []string) (*DocumentBulkResponse, error)

func (*Client) GetManagedProfiles

func (c *Client) GetManagedProfiles(ctx context.Context, page int) (*ProfileBulkResponse, error)

func (*Client) GetProfile

func (c *Client) GetProfile(ctx context.Context, profileId string) (*ProfileResponse, error)

func (*Client) GetProfiles

func (c *Client) GetProfiles(ctx context.Context, profileIds []string) (*ProfileBulkResponse, error)

func (*Client) GetProject

func (c *Client) GetProject(ctx context.Context, projectId string) (*ProjectResponse, error)

func (*Client) GetUnion

func (c *Client) GetUnion(ctx context.Context, unionId string) (*UnionResponse, error)

func (*Client) GetUnions

func (c *Client) GetUnions(ctx context.Context, unionIds []string) (*UnionBulkResponse, error)

func (*Client) GetUploadedDocuments

func (c *Client) GetUploadedDocuments(ctx context.Context, page int) (*DocumentBulkResponse, error)

func (*Client) MergeProfiles

func (c *Client) MergeProfiles(ctx context.Context, profile1Id, profile2Id string) error

func (*Client) TagDocument

func (c *Client) TagDocument(ctx context.Context, documentId, profileId string) (*ProfileBulkResponse, error)

func (*Client) UntagDocument

func (c *Client) UntagDocument(ctx context.Context, documentId, profileId string) (*ProfileBulkResponse, error)

func (*Client) UpdateDocument

func (c *Client) UpdateDocument(ctx context.Context, documentId string, request *DocumentRequest) (*DocumentResponse, error)

func (*Client) UpdateProfile

func (c *Client) UpdateProfile(ctx context.Context, profileId string, request *ProfileRequest) (*ProfileResponse, error)

func (*Client) UpdateUnion

func (c *Client) UpdateUnion(ctx context.Context, unionId string, request *UnionRequest) (*UnionResponse, error)

func (*Client) WipeEventDates

func (c *Client) WipeEventDates(ctx context.Context, resourceId string, eventKeys []string) error

WipeEventDates issues a targeted PATCH against /api/<resourceId>/update that nulls only the `date` sub-object of each named event (e.g. `birth`, `baptism`, `death`, `burial` on a profile; `marriage` or `divorce` on a union). Geni's API deep-merges nested objects per-key, which means sending `"end_month": null` inside an otherwise-populated `date` is a no-op — the only way to clear individual date sub-fields is to first wipe the whole `date` and then re-PATCH the desired subset (#94).

The request body is hand-crafted to touch only the named events' `date` keys; it deliberately omits `location`, `name`, and `description` to avoid accidentally clearing those alongside the date.

type DateElement

type DateElement struct {
	// Circa is a boolean that indicates whether the date is approximate
	Circa *bool `json:"circa"`
	// Day is the day of the month
	Day *int32 `json:"day"`
	// Month is the month of the year
	Month *int32 `json:"month"`
	// Year is the year
	Year *int32 `json:"year"`
	// EndCirca is a boolean that indicates whether the end date is approximate
	EndCirca *bool `json:"end_circa"`
	// EndDay is the end day of the month (only valid if range is between)
	EndDay *int32 `json:"end_day"`
	// EndMonth is the end month of the year (only valid if range is between)
	EndMonth *int32 `json:"end_month"`
	// EndYear is the end year (only valid if range is between)
	EndYear *int32 `json:"end_year"`
	// Range is the range (before, after, or between)
	Range *string `json:"range"`
}

DateElement is the response for a date.

type DetailsString

type DetailsString struct {
	// AboutMe is the profile's about me section
	AboutMe *string `json:"about_me"`
}

type DocumentBulkResponse

type DocumentBulkResponse struct {
	Results    []DocumentResponse `json:"results,omitempty"`
	Page       int                `json:"page,omitempty"`
	TotalCount int                `json:"total_count,omitempty"`
}

type DocumentRequest

type DocumentRequest struct {
	// Title is the document's title
	Title string `json:"title,omitempty"`
	// Description is the document's description
	Description *string `json:"description,omitempty"`
	// ContentType is the document's content type
	ContentType *string `json:"content_type,omitempty"`
	// Date is the document's date
	Date *DateElement `json:"date,omitempty"`
	// Location is the document's location
	Location *LocationElement `json:"location,omitempty"`
	// Labels is the document's comma separated labels
	Labels *string `json:"labels,omitempty"`
	// File is the Base64 encoded file to create a document from
	File *string `json:"file,omitempty"`
	// FileName is the name of the file, required if the file is provided
	FileName *string `json:"file_name,omitempty"`
	// SourceUrl is the source URL for the document
	SourceUrl *string `json:"source_url,omitempty"`
	// Text is the text to create a document from
	Text *string `json:"text,omitempty"`
}

type DocumentResponse

type DocumentResponse struct {
	// Id is the document's id
	Id string `json:"id,omitempty"`
	// Title is the document's title
	Title string `json:"title,omitempty"`
	// Description is the document's description
	Description *string `json:"description"`
	// SourceUrl is the document's source URL
	SourceUrl *string `json:"source_url"`
	// ContentType is the document's content type
	ContentType *string `json:"content_type"`
	// Date is the document's date
	Date *DateElement `json:"date"`
	// Location is the document's location
	Location *LocationElement `json:"location,omitempty"`
	// Profiles is the list of profiles tagged in the document
	Tags []string `json:"tags"`
	// Labels is the list of labels associated with the document
	Labels []string `json:"labels"`
	// UpdatedAt is the timestamp of when the document was last updated
	UpdatedAt string `json:"updated_at"`
	// CreatedAt is the timestamp of when the document was created
	CreatedAt string `json:"created_at"`
}

type EventElement

type EventElement struct {
	Date        *DateElement     `json:"date"`
	Description *string          `json:"description,omitempty"`
	Location    *LocationElement `json:"location"`
	Name        string           `json:"name,omitempty"`
}

EventElement is the response for an event.

type LocationElement

type LocationElement struct {
	// City is the city name
	City *string `json:"city"`
	// Country is the country name
	Country *string `json:"country"`
	// County is the county name
	County *string `json:"county"`
	// Latitude is the latitude
	Latitude *float64 `json:"latitude,omitempty"`
	// Longitude is the longitude
	Longitude *float64 `json:"longitude,omitempty"`
	// PlaceName is the place name
	PlaceName *string `json:"place_name"`
	// State is the state name
	State *string `json:"state"`
	// StreetAddress1 is the street address line 1
	StreetAddress1 *string `json:"street_address1"`
	// StreetAddress2 is the street address line 2
	StreetAddress2 *string `json:"street_address2"`
	// StreetAddress3 is the street address line 3
	StreetAddress3 *string `json:"street_address3"`
}

LocationElement is the response for a location.

type NameElement

type NameElement struct {
	// FirstName is the profile's first name
	FirstName *string `json:"first_name"`
	// LastName is the profile's last name
	LastName *string `json:"last_name"`
	// MiddleName is the profile's middle name
	MiddleName *string `json:"middle_name"`
	// MaidenName is the profile's maiden name
	MaidenName *string `json:"maiden_name"`
	// DisplayName is the profile's display name
	DisplayName *string `json:"display_name"`
	// Nicknames is the profile's comma-separated list of nicknames
	Nicknames *string `json:"nicknames"`
}

NameElement is the response for a name.

type ProfileBulkResponse

type ProfileBulkResponse struct {
	Results    []ProfileResponse `json:"results,omitempty"`
	Page       int               `json:"page,omitempty"`
	TotalCount int               `json:"total_count,omitempty"`
}

type ProfileRequest

type ProfileRequest struct {
	// DisplayName is the profile's display name
	DisplayName *string `json:"display_name,omitempty"`
	// Nicknames is the profile's nicknames
	Nicknames []string `json:"nicknames,omitempty"`
	// Gender is the profile's gender
	Gender *string `json:"gender,omitempty"`
	// Names is the name info
	Names map[string]NameElement `json:"names,omitempty"`
	// Birth is the birth event info
	Birth *EventElement `json:"birth,omitempty"`
	// Baptism is the baptism event info
	Baptism *EventElement `json:"baptism,omitempty"`
	// Death is the death event info
	Death *EventElement `json:"death,omitempty"`
	// CauseOfDeath is the cause of death
	CauseOfDeath *string `json:"cause_of_death"`
	// Burial is the burial event info
	Burial *EventElement `json:"burial,omitempty"`
	// IsAlive is a boolean that indicates whether the profile is living
	IsAlive bool `json:"is_alive"`
	// Title is the profile's name title. Sent without omitempty so that an
	// empty string clears any previously-set value (Geni accepts "" as a
	// clear sentinel for flat scalar fields).
	Title string `json:"title"`
	// CurrentResidence is the profile's current residence
	CurrentResidence *LocationElement `json:"current_residence"`
	// AboutMe is the profile's about me section
	AboutMe *string `json:"about_me"`
	// DetailStrings are nested maps of locales to details fields (e.g. about me) to values
	DetailStrings map[string]DetailsString `json:"detail_strings"`
	// Occupation is the profile's occupation. Sent without omitempty — see Title.
	Occupation string `json:"occupation"`
	// Suffix is the profile's suffix. Sent without omitempty — see Title.
	Suffix string `json:"suffix"`
	// Public is a boolean that indicates whether the profile is public
	Public bool `json:"public"`
	// Locked is a boolean that indicates whether the profile is locked down by a curator
	Locked bool `json:"locked"`
	// MergeNote is the note explaining the profile's merge status
	MergeNote []string `json:"merge_note,omitempty"`
}

type ProfileResponse

type ProfileResponse struct {
	// Id is the profile's node id
	Id string `json:"id,omitempty"`
	// Guid is the profile's globally unique identifier
	Guid string `json:"guid,omitempty"`
	// FirstName is the profile's first name
	FirstName *string `json:"first_name,omitempty"`
	// LastName is the profile's last name
	LastName *string `json:"last_name,omitempty"`
	// MiddleName is the profile's middle name
	MiddleName *string `json:"middle_name,omitempty"`
	// MaidenName is the profile's maiden name
	MaidenName *string `json:"maiden_name,omitempty"`
	// DisplayName is the profile's display name
	DisplayName *string `json:"display_name,omitempty"`
	// Nicknames is the profile's nicknames
	Nicknames []string `json:"nicknames,omitempty"`
	// Gender is the profile's gender
	Gender *string `json:"gender,omitempty"`
	// Names is the name info
	Names map[string]NameElement `json:"names,omitempty"`
	// Birth is the birth event info
	Birth *EventElement `json:"birth,omitempty"`
	// Baptism is the baptism event info
	Baptism *EventElement `json:"baptism,omitempty"`
	// Death is the death event info
	Death *EventElement `json:"death,omitempty"`
	// CauseOfDeath is the cause of death
	CauseOfDeath *string `json:"cause_of_death,omitempty"`
	// Burial is the burial event info
	Burial *EventElement `json:"burial,omitempty"`
	// Events is the events associated with this profile
	Events []EventElement `json:"events,omitempty"`
	// IsAlive is a boolean that indicates whether the profile is living
	IsAlive bool `json:"is_alive"`
	// Title is the profile's name title
	Title string `json:"title,omitempty"`
	// CurrentResidence is the profile's current residence
	CurrentResidence *LocationElement `json:"current_residence"`
	// AboutMe is the profile's about me section
	AboutMe *string `json:"about_me,omitempty"`
	// DetailStrings are nested maps of locales to details fields (e.g. about me) to values
	DetailStrings map[string]DetailsString `json:"detail_strings,omitempty"`
	// Occupation is the profile's occupation
	Occupation string `json:"occupation,omitempty"`
	// Suffix is the profile's suffix
	Suffix string `json:"suffix,omitempty"`
	// Public is a boolean that indicates whether the profile is public
	Public bool `json:"public"`
	// Locked is a boolean that indicates whether the profile is locked down by a curator
	Locked bool `json:"locked"`
	// Language is the profile's language
	Language string `json:"language,omitempty"`
	// ProfileUrl is the URL to access profile in a browser
	ProfileUrl string `json:"profile_url,omitempty"`
	// MergePending is a boolean that indicates whether the profile has a pending merge
	MergePending bool `json:"merge_pending,omitempty"`
	// MergedInto is the ID of the profile this profile is currently merged into
	MergedInto string `json:"merged_into,omitempty"`
	// MergeNote is the note explaining the profile's merge status
	MergeNote []string `json:"merge_note,omitempty"`
	// Url is the URL to access profile through the API
	Url string `json:"url,omitempty"`
	// Unions is the URLs to unions
	Unions []string `json:"unions,omitempty"`
	// Projects are the IDs of projects this profile is a member of
	Projects []string `json:"project_ids,omitempty"`
	// Deleted is a boolean that indicates whether the profile is deleted
	Deleted bool `json:"deleted"`
	// UpdatedAt is the timestamp of when the profile was last updated
	UpdatedAt string `json:"updated_at,omitempty"`
	// CreatedAt is the timestamp of when the profile was created
	CreatedAt string `json:"created_at,omitempty"`
}

type ProjectBulkResponse

type ProjectBulkResponse struct {
	Results []ProjectResponse `json:"results,omitempty"`
}

type ProjectResponse

type ProjectResponse struct {
	// The project's id
	Id string `json:"id,omitempty"`
	// The project's name
	Name string `json:"name,omitempty"`
	// The project's description
	Description *string `json:"description,omitempty"`
	// UpdatedAt is the timestamp of when the project was last updated
	UpdatedAt string `json:"updated_at,omitempty"`
	// CreatedAt is the timestamp of when the project was created
	CreatedAt string `json:"created_at,omitempty"`
}

type ResultResponse

type ResultResponse struct {
	Result string `json:"result,omitempty"`
}

type UnionBulkResponse

type UnionBulkResponse struct {
	Results []UnionResponse `json:"results,omitempty"`
}

type UnionRequest

type UnionRequest struct {
	// Marriage date and location
	Marriage *EventElement `json:"marriage,omitempty"`
	// Divorce date and location
	Divorce *EventElement `json:"divorce,omitempty"`
}

type UnionResponse

type UnionResponse struct {
	// The union's id
	Id string `json:"id,omitempty"`
	// AdoptedChildren is a subset of the children array, indicating which children are adopted
	AdoptedChildren []string `json:"adopted_children,omitempty"`
	// Children is an array of children in the union (urls or ids, if requested)
	Children []string `json:"children,omitempty"`
	// FosterChildren is a subset of the children array, indicating which children are foster
	FosterChildren []string `json:"foster_children,omitempty"`
	// Partners is an array of partners in the union (urls or ids, if requested)
	Partners []string `json:"partners,omitempty"`
	// Marriage date and location
	Marriage *EventElement `json:"marriage,omitempty"`
	// Divorce date and location
	Divorce *EventElement `json:"divorce,omitempty"`
	// Status of the union (spouse|ex_spouse)
	Status string `json:"status,omitempty"`
}

Directories

Path Synopsis
examples
getprofile command
Command getprofile is a minimal smoke example that constructs a go-geni Client against the sandbox and fetches a single profile.
Command getprofile is a minimal smoke example that constructs a go-geni Client against the sandbox and fetches a single profile.

Jump to

Keyboard shortcuts

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