View Source CFSync (CFSync v0.15.1)

CFSync is an Elixir client for Contentful sync API.

features

Features

  • Provides functions to extract field values from Contentful entries JSON payloads.
  • Maps Contenful entries, assets, and links to Elixir structs. Using structs is better for readability, performance and reliability.
  • Works with a single or multiple locales.
  • Maps RichText data to Elixir structs.
  • Keeps a cache of your entries and assets in a ETS table. This allows for fast (way faster than REST API calls) and concurrent reads.
  • Keeps this cache up to date by regularly fetching changes from Contentful sync API

installation

Installation

Add :cf_sync to your mix.exs dependencies.

def deps do
[
  {:cf_sync, "~> 0.15.0"},
]
end

basic-usage

Basic usage

# Define your fields mappings, one per content-type
defmodule MyApp.PageFields do
  @behaviour CFSync.Entry.Fields
  import CFSync.Entry.Extractors

  defstruct :name, :body, :author

  # See CFSync.Entry.Extractors for extract_* documentation
  def new(data) do
    %__MODULE__{
      name: extract_binary(data, "name"),
      body: extract_richtext(data, "body"),
      author: extract_link(data, "author"),
    }
  end
end

# Define a mapping to map Contentful "contentType" ids to an atom name and a fields struct:
content_types = %{
  # "page" key is the content_type ID as configured in Contentful
  "page" => %{
    content_type: :page,
    fields_module: MyApp.PageFields,
  },
  "author" => %{
    content_type: :author,
    fields_module: MyApp.AuthorFields,
  }
  # ...
}

# Define your locales (optional):
locales = %{en: "en-US", fr: "fr-FR"}

# If you have a single locale, but it is not "en-US", you can use:
# locales = %{nil: "fr-FR"}

# Start a CFSync process
{:ok, pid} =
  CFSync.start_link(
    name: MyApp.MyCFSync,
    space_id: "Your_contentful_space_id",
    delivery_token: "Your_contentful_delivery_api_token",
    content_types: content_types,
    locales: locales
  )



# Use it
store = CFSync.from(MyApp.MyCFSync)
entry =  CFSync.get_entry(store, "entry_id")

# OR, with multiple locales:
# entry_fr =  CFSync.get_entry(store, "entry_id", :fr)
# entry_en =  CFSync.get_entry(store, "entry_id", :en)

# entry ->
  %CFSync.Entry{
    id: "entry_id",
    content_type: :page,
    space_id: "your_space_id",
    fields: %MyApp.PageFields{
      name: "Lorem ipsum",
      body: %CFSync.RichText{ #... },
      author: %CFSync.Link{
        id: "autor_id",
        #... },
      # ...
    }
  }

author = CFSync.get_link_target(entry.fields.author)
# Or
# author = CFSync.get_child(entry, :author)

# author ->
  %CFSync.Entry{
    id: "author_id",
    content_type: :author,
    fields: %MyApp.AuthorFields{
      # ...
    }
  }

!! Put your API token and space_id in your ENV, NOT in your code.

You should start the CFSync process in a supervision tree.

Link to this section Summary

Types

Reference to a CFSync store.

Functions

Forces the store for name to sync immediately. Use the name you provided to start_link/4.

Get the CFSync store for name. Use the name you provided to start_link/4.

Get the asset specified by id for the given locale from the CFSync store store.

Returns a list containg all assets from the CFSync store store.

Resolves a link (child entry or asset) from an entry field and returns the corresponding asset or entry.

Resolves a list of link (children entries or assets) from an entry field and returns the corresponding assets or entries.

Returns a list containg all entries for a given locale from the CFSync store store.

Returns a list containg all entries of specified content_type for a given locale from the CFSync store store.

Get the entry specified by id for the given locale from the CFSync store store.

Resolves link in the CFSync store store and returns the corresponding asset or entry.

Phoenix.Component that renders RichText.

Starts CFSync GenServer

Link to this section Types

@type store() :: :ets.tid()

Reference to a CFSync store.

Link to this section Functions

@spec force_sync(atom()) :: :ok

Forces the store for name to sync immediately. Use the name you provided to start_link/4.

CFSync regularly synces entries from Contentful API (see start_link/4). Calling this function triggers an immediate sync and resets the timer for the next regular sync, so it will only happen after the full delay (:delta_sync_interval) counting from the immediate sync.

You can use Contentful publish and unpublish webhooks to trigger calls to this function in order to achieve almost instant synchronisation. You can then relax :delta_sync_interval in the configuration to lower the number of API requests made to Contentful.

CFSync.force_sync(MyApp.MyCFSync)
@spec from(atom()) :: store()

Get the CFSync store for name. Use the name you provided to start_link/4.

store = CFSync.from(MyApp.MyCFSync)
get_entries(store)
get_entry(store, "an_entry_id")
# ...
Link to this function

get_asset(store, id, locale \\ nil)

View Source
@spec get_asset(store(), binary(), atom()) :: nil | CFSync.Asset.t()

Get the asset specified by id for the given locale from the CFSync store store.

Returns nil if the asset is not found.

See get_entries/1 about locale.

Link to this function

get_assets(store, locale \\ nil)

View Source
@spec get_assets(store(), atom()) :: [CFSync.Asset.t()]

Returns a list containg all assets from the CFSync store store.

See get_entries/1 about performance and locale.

Link to this function

get_child(entry, field_name)

View Source
@spec get_child(CFSync.Entry.t(), atom()) :: nil | CFSync.Entry.t() | CFSync.Asset.t()

Resolves a link (child entry or asset) from an entry field and returns the corresponding asset or entry.

Returns nil if the child entry or asset is not found.

Link to this function

get_children(entry, field_name)

View Source
@spec get_children(CFSync.Entry.t(), atom()) :: [
  nil | CFSync.Entry.t() | CFSync.Asset.t()
]

Resolves a list of link (children entries or assets) from an entry field and returns the corresponding assets or entries.

Returns the list of links mapped to the corresponding assets or entries. If an entry or asset is not found, it is not included in the result.

Link to this function

get_entries(store, locale \\ nil)

View Source
@spec get_entries(store(), atom()) :: [CFSync.Entry.t()]

Returns a list containg all entries for a given locale from the CFSync store store.

The locale is optional and defaults to nil. By default, CFSync is configured to work with a single locale (the nil locale), which is en-US. You can change the default locale by passing locales: %{nil: "fr-FR"} to start_link/1.

If you have multiple locales, you must pass the locale to this function to get entries for that locale.

This function will retrieve ALL entries currently stored in the store's ETS table and deep copy them to the current process. Using this on a large Contentful space will be slow. If you want to retrieve all the entries to filter them, consider using something like memoize to cache the results.

Link to this function

get_entries_for_content_type(store, content_type, locale \\ nil)

View Source
@spec get_entries_for_content_type(store(), atom(), atom()) :: [CFSync.Entry.t()]

Returns a list containg all entries of specified content_type for a given locale from the CFSync store store.

See get_entries/1 about performance and locale.

Link to this function

get_entry(store, id, locale \\ nil)

View Source
@spec get_entry(store(), binary(), atom()) :: nil | CFSync.Entry.t()

Get the entry specified by id for the given locale from the CFSync store store.

Returns nil if the entry is not found.

See get_entries/1 about locale.

@spec rich_text(map()) :: Phoenix.LiveView.Rendered.t()

Phoenix.Component that renders RichText.

You can pass these assigns:

  • content: the %RichText{} struct to render
  • class: a class attribute that will be added to root element of rendered HTML
  • delegate: a module with custom components to use for rendering

Delegate and class are optional. To use delegate module, see RichTextRenderer and RichTextRendererTest modules

@spec start_link(keyword()) :: :ignore | {:error, any()} | {:ok, pid()}

Starts CFSync GenServer

Should be started in a supervision tree

options

Options

  • name (required) is an atom to reference this CFSync process. Use the same name in from/1 to query entries.
  • space_id (required) is your Contentful space's ID
  • delivery_token (required) is your Contentful API token
  • content_types (required) is a map describing how to map Contentful entries to elixir structs. See module doc.
  • locales (optional) is a map of locales. The key is the locale name in Elixir (an atom), the value is the locale name in Contentful.
  • :root_url (optional) - Default is "https://cdn.contentful.com/"
  • :initial_sync_interval (optional) - The server will wait for this interval between two page requests during initial sync. Defaults to 30 milliseconds.
  • :delta_sync_interval (optional) - The server will wait for this interval between two sync requests. Defaults to 5000 milliseconds. You can use a shorter delay to get updates faster, but you will be rate limited by Contentful if you set it too short.
  • :invalidation_callbacks (optional) - List of 0-arity anonymous functions that will be called after each sync operation that actually adds, updates or deletes some entries.