{"id":14991022,"url":"https://github.com/contentful/vault","last_synced_at":"2025-04-05T09:06:29.706Z","repository":{"id":29267163,"uuid":"32799814","full_name":"contentful/vault","owner":"contentful","description":"Easy persistence of Contentful data for Android over SQLite.","archived":false,"fork":false,"pushed_at":"2025-03-04T14:19:49.000Z","size":665,"stargazers_count":85,"open_issues_count":15,"forks_count":19,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-29T08:06:10.262Z","etag":null,"topics":["android","contentful","database","java","offline","sdk","storage","sync"],"latest_commit_sha":null,"homepage":"https://contentful.github.io/vault/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/contentful.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-03-24T13:17:18.000Z","updated_at":"2024-06-01T14:28:49.000Z","dependencies_parsed_at":"2025-01-05T06:01:28.108Z","dependency_job_id":null,"html_url":"https://github.com/contentful/vault","commit_stats":{"total_commits":227,"total_committers":10,"mean_commits":22.7,"dds":0.5991189427312775,"last_synced_commit":"4f6a5ed4f6c592f4ee68a4fac23c876b173b9291"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fvault","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fvault/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fvault/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fvault/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/contentful","download_url":"https://codeload.github.com/contentful/vault/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247312077,"owners_count":20918344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","contentful","database","java","offline","sdk","storage","sync"],"created_at":"2024-09-24T14:21:19.911Z","updated_at":"2025-04-05T09:06:29.657Z","avatar_url":"https://github.com/contentful.png","language":"Java","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/feature_graphic.png\" alt=\"Contentful Java SDK\"/\u003e\u003cbr/\u003e\n  \u003ca href=\"https://www.contentful.com/slack/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/-Join%20Community%20Slack-2AB27B.svg?logo=slack\u0026maxAge=31557600\" alt=\"Join Contentful Community Slack\"\u003e\n  \u003c/a\u003e\n  \u0026nbsp;\n  \u003ca href=\"https://www.contentfulcommunity.com/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/-Join%20Community%20Forum-3AB2E6.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MiA1OSI+CiAgPHBhdGggZmlsbD0iI0Y4RTQxOCIgZD0iTTE4IDQxYTE2IDE2IDAgMCAxIDAtMjMgNiA2IDAgMCAwLTktOSAyOSAyOSAwIDAgMCAwIDQxIDYgNiAwIDEgMCA5LTkiIG1hc2s9InVybCgjYikiLz4KICA8cGF0aCBmaWxsPSIjNTZBRUQyIiBkPSJNMTggMThhMTYgMTYgMCAwIDEgMjMgMCA2IDYgMCAxIDAgOS05QTI5IDI5IDAgMCAwIDkgOWE2IDYgMCAwIDAgOSA5Ii8+CiAgPHBhdGggZmlsbD0iI0UwNTM0RSIgZD0iTTQxIDQxYTE2IDE2IDAgMCAxLTIzIDAgNiA2IDAgMSAwLTkgOSAyOSAyOSAwIDAgMCA0MSAwIDYgNiAwIDAgMC05LTkiLz4KICA8cGF0aCBmaWxsPSIjMUQ3OEE0IiBkPSJNMTggMThhNiA2IDAgMSAxLTktOSA2IDYgMCAwIDEgOSA5Ii8+CiAgPHBhdGggZmlsbD0iI0JFNDMzQiIgZD0iTTE4IDUwYTYgNiAwIDEgMS05LTkgNiA2IDAgMCAxIDkgOSIvPgo8L3N2Zz4K\u0026maxAge=31557600\" alt=\"Join Contentful Community Forum\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nvault - Contentful Offline Persistence for Android\n==================================================\n\n[![Build Status](https://travis-ci.org/contentful/vault.svg)](https://travis-ci.org/contentful/vault/builds#)\n\n\u003e Vault is an Android library that simplifies persistence of Resources from Contentful via SQLite. It defines a Java representation of Contentful models. At compile-time Vault creates a corresponding database schema by generating all the required boilerplate code and injecting it into the classpath. It is also bundled with a complementary lightweight runtime which exposes a simple ORM-like API for pulling resources from the generated database.\n\nWhat is Contentful?\n-------------------\n\n[Contentful](https://www.contentful.com) provides a content infrastructure for digital teams to power content in websites, apps, and devices. \tContentful, unlike any other CMS, is built to integrate with the modern software stack. It offers a central hub for structured content, powerful management and delivery APIs, and a customizable web app that enable developers and content creators to ship digital products faster.\n\n\n\u003cdetails open\u003e\n  \u003csummary\u003eTable of contents\u003c/summary\u003e\n  \u003c!-- TOC --\u003e\n\n- [Setup](#setup)\n  - [Snapshots](#snapshots)\n- [Usage](#usage)\n  - [Models and Fields](#models-and-fields)\n  - [Spaces](#spaces)\n  - [Synchronization](#synchronization)\n  - [Queries](#queries)\n  - [Migrations](#migrations)\n  - [Preseeding](#preseeding)\n- [Documentation](#documentation)\n- [Licence](#licence)\n- [Reaching Contentful](#reaching-contentful)\n  - [Bugs and Feature Requests](#bugs-and-feature-requests)\n  - [Sharing Confidential Information](#sharing-confidential-information)\n  - [Getting involved](#getting-involved)\n- [Code of Conduct](#code-of-conduct)\n  \u003c!-- /TOC --\u003e\n\u003c/details\u003e\n\n\nSetup\n=====\n\nInstall the dependency by \n\n* _Maven_\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.contentful.vault\u003c/groupId\u003e\n  \u003cartifactId\u003ecompiler\u003c/artifactId\u003e\n  \u003cversion\u003e3.2.6\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.contentful.vault\u003c/groupId\u003e\n  \u003cartifactId\u003ecore\u003c/artifactId\u003e\n  \u003cversion\u003e3.2.6\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n* _Gradle_\n\n```groovy\napt 'com.contentful.vault:compiler:3.2.6'\ncompile 'com.contentful.vault:core:3.2.6'\n```\n* _Gradle 3.+_\n```groovy\nannotationProcessor 'com.contentful.vault:compiler:3.2.6'\nannotationProcessor 'com.contentful.vault:core:3.2.6'\ncompile 'com.contentful.vault:core:3.2.6'\n```\n\n\u003e Note for Gradle: Use the [android-apt][apt] Gradle plugin, which configures compile-time dependencies only.\n\u003e Note for Gradle 3.0 and newer: Use the `annotationProcessor` instead of `apt`.\n\nSnapshots\n---------\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository][snap].\n\nUsage\n=====\n\nModels and Fields\n-----------------\n\nModels are defined by declaring a subclass of the `Resource` class. Annotate the class with `@ContentType`, which takes the Content Type's ID as its value.\n\nFields are defined by annotating class attributes with the `@Field` annotation:\n\n```java\n@ContentType(\"cat\")\npublic class Cat extends Resource {\n  @Field public String name;\n  @Field public Cat bestFriend;\n  @Field public Asset image;\n}\n```\n\nBy default, the name of the attribute is used as the field's ID, but can also be specified explicitly:\n\n```java\n@Field(\"field-id-goes-here\") \npublic String someField; \n```\n\nField ids are escaped, however when making queries with a `WHERE` condition it is up to the caller to escape the field name in case it is a reserved keyword. For example:\n\n```java\n@ContentType(\"...\")\npublic class Foo extends Resource {\n  @Field public String order;\n}\n```\n\nSince `order` is a reserved SQLite keyword, making a query which references that field is done as following:\n\n```java\nvault.fetch(Foo.class)\n    .where(\"`\" + Foo$Fields.ORDER \"` = ?\", \"bar\")\n    .first();\n```\n\nSpaces\n------\n\nSpaces are classes annotated with the `@Space` annotation. It is required to specify the Space ID, an array of _Model_ classes and an array of locale codes wanted to be persisted:\n\n```java\n@Space(\n    value = \"cfexampleapi\",\n    models = { Cat.class },\n    locales = { \"en-US\", \"tlh\" }\n)\npublic class DemoSpace { }\n```\n\nSynchronization\n---------------\n\nOnce a Space is defined, Vault is invoked to synchronize the local database with Contentful:\n\n```java\n// Client\nCDAClient client = CDAClient.builder()\n    .setSpace(\"cfexampleapi\")\n    .setToken(\"b4c0n73n7fu1\")\n    .build();\n\n// Sync\nVault.with(context, DemoSpace.class).requestSync(client);\n```\n\nVault uses a worker thread to request updates from the Sync API and reflect the changes in its database.\nOnce sync is completed, Vault fires a broadcast with the action `Vault.ACTION_SYNC_COMPLETE`.\n\nProviding a `SyncCallback` results in it beeing invoked once sync is completed:\n\n```java\nclass SomeActivity extends Activity {\n  SyncCallback callback;\n  \n  @Override protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    \n    Vault.with(this, DemoSpace.class).requestSync(client, callback = new SyncCallback() {\n      @Override public void onResult(SyncResult result) {\n        if (result.isSuccessful()) {\n          // Success \\o/\n        } else {\n          // Failure\n        }\n      }\n    });\n  }\n  \n  @Override protected void onDestroy() {\n    Vault.cancel(callback);\n    super.onDestroy();\n  }\n}\n```\n\n\u003e Note: Extra care needs to be taken for the lifecycle: Cancelling the callback on lifecycle events is important.\n\nSimilarly using _RxJava_ notifies of sync results via an `Observable`:\n\n```java\nVault.observeSyncResults() // returns Observable\u003cSyncResult\u003e\n```\n\nQueries\n-------\n\nVault provides a wrapper around its generated database which fetches persisted objects:\n\n```java\nVault vault = Vault.with(context, DemoSpace.class);\n\n// Fetch the first Cat\nvault.fetch(Cat.class).first();\n    \n// Fetch the most recently updated Cat\nvault.fetch(Cat.class)\n    .order(Cat$Fields.UPDATED_AT + \" DESC\")\n    .first();\n\n// Fetch a Cat with a specific name\nvault.fetch(Cat.class)\n    .where(Cat$Fields.NAME + \" = ?\", \"Nyan Cat\")\n    .first();\n\n// Fetch a Cat with a specific name pattern\nvault.fetch(Cat.class)\n    .where(Cat$Fields.NAME + \" LIKE ?\", \"%Nyan%\")\n    .first();\n\n// Fetch a Cat with a specific boolean field\n// SQLite is storing booleans as 0/1 \nvault.fetch(Cat.class)\n    .where(Cat$Fields.IS_GRUMPY + \" = ?\", \"1\")\n    .first();\n    \n// Fetch all Cats, ordered by creation date:\nvault.fetch(Cat.class)\n    .order(Cat$Fields.CREATED_AT)\n    .all();\n\n// Fetch all Cats, using the Klingon locale:\nvault.fetch(Cat.class)\n    .all(\"tlh\");\n```\n\nUsing RxJava queries are created by the `observe()` method, for example:\n\n```java\nvault.observe(Cat.class)\n    .where(Cat$Fields.NAME + \" = ?\", \"Happy Cat\")\n    .all() // returns Observable\u003cCat\u003e\n```\n\nThe above example creates an `Observable` that subscribes and observes on the same thread initiating the query. However, it changes if this typical use-case is used:\n\n```java\nvault.observe(Cat.class)\n    .all()\n    .subscribeOn(Schedulers.io())\n    .observeOn(AndroidSchedulers.mainThread())\n```\n\nMigrations\n----------\n\nWhenever changes are introduced to any of the previously used Models, a migration has to be applied. Increment the version number to trigger a migration:\n\n```java\n@Space(value = \"cfexampleapi\", models = { Cat.class }, dbVersion = 2)\npublic class DemoSpace { }\n```\n\n\u003e Note: this deletes any previously persisted data and reaquires them.\n\nPreseeding\n----------\n\nDepending on the amount of content in a given space, initial synchronization might take some time. For that support to pre-seed the database with static content got added. \n\nFor creating an initial database file, use the `VaultDatabaseExporter`. This class takes an Android Context and a Vault Space. Calling the `.export(..)` method, it  creates a sqlite database in `src/main/assets/initial_seed.db`. Vault is instructed to use this as follows:\n\n```java\n@Space(\n    value = \"{spaceid}\", // space id of the space to use\n    models = { Cat.class },  // model classes to be used\n    copyPath = \"initial_seed.db\" // name of the just created database file.\n)\npublic class VaultSpace { }\n```\n\nThe database file is updated by leveraging a [robolectric](robolectric.org) test before releasing. This test syncs Contentful data to the existing database.\n\nA simple test suite looks like this:\n\n```java\n@RunWith(RobolectricTestRunner.class)\npublic class TestSeedDB {\n @literal @Test\n  public void testSyncDBtoSqlite() throws Exception {\n    final Activity activity = Robolectric.setupActivity(Activity.class);\n\n    assertTrue(new VaultDatabaseExporter().export(activity, VaultSpace.class, VaultSpace.TOKEN));\n  }\n}\n```\n\nThe database content will always be uptodate when those tests get executed. If an error happens this test will fail and information about next steps will be given.\n\n\u003e Note: In order to add this functionality to an already shipped app, the **dbVersion** value has to be increased, as it causes invalidation of any pre-existing content.\n\nProGuard\n--------\n\nGrab the [ProGuard configuration file][proguard] and apply to your project. \n\nDocumentation\n=============\n\nJavadoc is available [here][javadoc].\n\nLicense\n=======\n\n    Copyright 2017 Contentful, GmbH.\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n\nReaching Contentful\n===================\n\nQuestions\n---------\n\n* Use the community forum: [![Contentful Community Forum](https://img.shields.io/badge/-Join%20Community%20Forum-3AB2E6.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MiA1OSI+CiAgPHBhdGggZmlsbD0iI0Y4RTQxOCIgZD0iTTE4IDQxYTE2IDE2IDAgMCAxIDAtMjMgNiA2IDAgMCAwLTktOSAyOSAyOSAwIDAgMCAwIDQxIDYgNiAwIDEgMCA5LTkiIG1hc2s9InVybCgjYikiLz4KICA8cGF0aCBmaWxsPSIjNTZBRUQyIiBkPSJNMTggMThhMTYgMTYgMCAwIDEgMjMgMCA2IDYgMCAxIDAgOS05QTI5IDI5IDAgMCAwIDkgOWE2IDYgMCAwIDAgOSA5Ii8+CiAgPHBhdGggZmlsbD0iI0UwNTM0RSIgZD0iTTQxIDQxYTE2IDE2IDAgMCAxLTIzIDAgNiA2IDAgMSAwLTkgOSAyOSAyOSAwIDAgMCA0MSAwIDYgNiAwIDAgMC05LTkiLz4KICA8cGF0aCBmaWxsPSIjMUQ3OEE0IiBkPSJNMTggMThhNiA2IDAgMSAxLTktOSA2IDYgMCAwIDEgOSA5Ii8+CiAgPHBhdGggZmlsbD0iI0JFNDMzQiIgZD0iTTE4IDUwYTYgNiAwIDEgMS05LTkgNiA2IDAgMCAxIDkgOSIvPgo8L3N2Zz4K\u0026maxAge=31557600)](https://support.contentful.com/)\n* Use the community slack channel: [![Contentful Community Slack](https://img.shields.io/badge/-Join%20Community%20Slack-2AB27B.svg?logo=slack\u0026maxAge=31557600)](https://www.contentful.com/slack/)\n\nBugs and Feature Requests\n-------------------------\n\n* File an issue here [![File an issue](https://img.shields.io/badge/-Create%20Issue-6cc644.svg?logo=github\u0026maxAge=31557600)](https://github.com/contentful/vault/issues/new).\n\nSharing Confidential Information\n--------------------------------\n\n* File a support ticket at Contentful Customer Support: [![File support ticket](https://img.shields.io/badge/-Submit%20Support%20Ticket-3AB2E6.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MiA1OSI+CiAgPHBhdGggZmlsbD0iI0Y4RTQxOCIgZD0iTTE4IDQxYTE2IDE2IDAgMCAxIDAtMjMgNiA2IDAgMCAwLTktOSAyOSAyOSAwIDAgMCAwIDQxIDYgNiAwIDEgMCA5LTkiIG1hc2s9InVybCgjYikiLz4KICA8cGF0aCBmaWxsPSIjNTZBRUQyIiBkPSJNMTggMThhMTYgMTYgMCAwIDEgMjMgMCA2IDYgMCAxIDAgOS05QTI5IDI5IDAgMCAwIDkgOWE2IDYgMCAwIDAgOSA5Ii8+CiAgPHBhdGggZmlsbD0iI0UwNTM0RSIgZD0iTTQxIDQxYTE2IDE2IDAgMCAxLTIzIDAgNiA2IDAgMSAwLTkgOSAyOSAyOSAwIDAgMCA0MSAwIDYgNiAwIDAgMC05LTkiLz4KICA8cGF0aCBmaWxsPSIjMUQ3OEE0IiBkPSJNMTggMThhNiA2IDAgMSAxLTktOSA2IDYgMCAwIDEgOSA5Ii8+CiAgPHBhdGggZmlsbD0iI0JFNDMzQiIgZD0iTTE4IDUwYTYgNiAwIDEgMS05LTkgNiA2IDAgMCAxIDkgOSIvPgo8L3N2Zz4K\u0026maxAge=31557600)](https://www.contentful.com/support/)\n\nGetting involved\n----------------\n\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?maxAge=31557600)](http://makeapullrequest.com)\n\nCode of Conduct\n===============\n\nContentful wants to provide a safe, inclusive, welcoming, and harassment-free space and experience for all participants, regardless of gender identity and expression, sexual orientation, disability, physical appearance, socioeconomic status, body size, ethnicity, nationality, level of experience, age, religion (or lack thereof), or other identity markers.\n\n[Full Code of Conduct](https://github.com/contentful-developer-relations/community-code-of-conduct).\n\n\n [snap]: https://oss.sonatype.org/content/repositories/snapshots/\n [apt]: https://bitbucket.org/hvisser/android-apt\n [proguard]: proguard-vault.cfg\n [javadoc]: https://contentful.github.io/vault\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontentful%2Fvault","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontentful%2Fvault","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontentful%2Fvault/lists"}