Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unstructured data example #49

Merged
merged 16 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion AppServicesUsageSamples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ This sample demonstrates user presence detection with App Services.

A showcase for the different client reset resolution strategies with an sync error handling example.

### [Modelling unstructured data](apps/dynamic-data/README.md)

A showcase for storing and synchronizing unstructure data through collections in mixed properties.
rorbech marked this conversation as resolved.
Show resolved Hide resolved

## Demo app structure

The project has been structured in two main folders:
Expand Down Expand Up @@ -52,4 +56,4 @@ The `realm-cli import` command will prompt for the app configuration details, th

After deploying the Atlas apps, you will need to update [Constants.kt](demo/src/main/java/io/realm/appservicesusagesamples/Constants.kt) with their app ids.

Once you have completed these steps, you would be able to run the samples using the Kotlin demo app.
Once you have completed these steps, you would be able to run the samples using the Kotlin demo app.
7 changes: 7 additions & 0 deletions AppServicesUsageSamples/apps/dynamic-data/.mdb/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"config_version": 20230101,
"app_id": "6655920fc1ed444018db297b",
"group_id": "64c37d22e47344686a325451",
"client_app_id": "dynamic-data-fmzmcph",
"last_pulled": 1717066261
}
13 changes: 13 additions & 0 deletions AppServicesUsageSamples/apps/dynamic-data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Dynamic data

With the newly added ability to store collections in mixed properties, you can now store and synchronized
data without pre-known schema.

This app just holds a single data class that mixes strictly typed properties and a single mixed property
that will as entry for point for the, potentially deeply nested, dynamic data.
rorbech marked this conversation as resolved.
Show resolved Hide resolved

The Kotlin UI shows each entity and associated 'configuration' mixed property in a tree view. There is
currently no update options in the UI, so data has to be added thought the Atlas UI in the
rorbech marked this conversation as resolved.
Show resolved Hide resolved
"Data Service" section under _Database_'->'_Collections_`->`_dynamic-data_`->`_DynamicDataEntity_.
rorbech marked this conversation as resolved.
Show resolved Hide resolved

Updates will be reflected in the Kotlin UI.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"enabled": false
}
12 changes: 12 additions & 0 deletions AppServicesUsageSamples/apps/dynamic-data/auth/providers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"anon-user": {
"name": "anon-user",
"type": "anon-user",
"disabled": false
},
"api-key": {
"name": "api-key",
"type": "api-key",
"disabled": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "mongodb-atlas",
"type": "mongodb-atlas",
"config": {
"clusterName": "Cluster0",
"readPreference": "primary",
"wireProtocolEnabled": false
},
"version": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"roles": [
{
"name": "readAndWriteAll",
"apply_when": {},
"document_filters": {
"read": true,
"write": true
},
"insert": true,
"delete": true,
"search": true,
"read": true,
"write": true
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"title": "DynamicDataEntity",
"type": "object",
"required": [
"_id",
"name"
],
"properties": {
"_id": {
"bsonType": "objectId"
},
"configuration": {
"bsonType": "mixed"
},
"name": {
"bsonType": "string"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"values": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"values": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"values": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"values": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"values": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
4 changes: 4 additions & 0 deletions AppServicesUsageSamples/apps/dynamic-data/graphql/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"use_natural_pluralization": true,
"disable_schema_introspection": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"read_validation_action": "ERROR",
"read_validation_level": "STRICT",
"write_validation_action": "ERROR",
"write_validation_level": "STRICT"
}
3 changes: 3 additions & 0 deletions AppServicesUsageSamples/apps/dynamic-data/hosting/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"enabled": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
5 changes: 5 additions & 0 deletions AppServicesUsageSamples/apps/dynamic-data/root_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "dynamic-data",
"provider_region": "aws-eu-central-1",
"deployment_model": "LOCAL"
}
9 changes: 9 additions & 0 deletions AppServicesUsageSamples/apps/dynamic-data/sync/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "flexible",
"state": "enabled",
"development_mode_enabled": true,
"service_name": "mongodb-atlas",
"database_name": "dynamic_data",
"client_max_offline_days": 30,
"is_recovery_mode_disabled": false
}
9 changes: 9 additions & 0 deletions AppServicesUsageSamples/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@ plugins {
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.android) apply false
}

// Explicitly adding the plugin to the classpath as it makes it easier to control the version
// centrally (don't need version in the 'plugins' block). Further, snapshots are not published with
// marker interface so would need to be added to the classpath manually anyway.
buildscript {
dependencies {
classpath(libs.realm.plugin)
}
}
15 changes: 10 additions & 5 deletions AppServicesUsageSamples/demo/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.realm.kotlin)
// For some reason libsx.plugins.realm does not resolve directly so go through the provider
// alias(libs.plugins.realm.kotlin)
id(libs.plugins.realm.kotlin.get().pluginId)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.compose.compiler)
}

android {
namespace = "io.realm.appservicesusagesamples"
compileSdk = 33
compileSdk = 34

defaultConfig {
applicationId = "io.realm.appservicesusagesamples"
Expand All @@ -28,9 +32,6 @@ android {
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.7"
}
buildTypes {
release {
isMinifyEnabled = false
Expand Down Expand Up @@ -75,6 +76,10 @@ dependencies {
implementation(libs.koin.android)
implementation(libs.koin.androidx.compose)

implementation(libs.bonsai)
// Compile only dependency of libs.bonsai, so need to include it explicitly
implementation(compose.materialIconsExtended)

implementation(libs.realm.library.sync)

debugImplementation(composeBom)
Expand Down
3 changes: 2 additions & 1 deletion AppServicesUsageSamples/demo/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<activity android:name=".propertyencryption.PropertyEncryptionActivity" android:launchMode="singleTop"/>
<activity android:name=".presence.PresenceDetectionActivity"/>
<activity android:name=".errorhandling.ErrorHandlingActivity"/>
<activity android:name=".dynamicdata.DynamicDataActivity"/>
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.realm.appservicesusagesamples

import android.app.Application
import io.realm.appservicesusagesamples.dynamicdata.dynamicDataViewModel
import io.realm.appservicesusagesamples.errorhandling.errorHandlingModule
import io.realm.appservicesusagesamples.propertyencryption.propertyEncryptionModule
import io.realm.appservicesusagesamples.presence.presenceDetectionModule
Expand All @@ -42,6 +43,7 @@ class AppServicesUsageSamplesApp: Application() {
propertyEncryptionModule,
presenceDetectionModule,
errorHandlingModule,
dynamicDataViewModel,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ package io.realm.appservicesusagesamples
const val PROPERTY_ENCRYPTION_APP_ID = "<insert-app-id>"
const val USER_PRESENCE_APP_ID = "<insert-app-id>"
const val ERROR_HANDLING_APP_ID = "<insert-app-id>"
const val DYNAMIC_DATA_APP_ID = "<insert-app-id>"
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
*/
package io.realm.appservicesusagesamples

import io.realm.appservicesusagesamples.dynamicdata.DynamicDataActivity
import io.realm.appservicesusagesamples.propertyencryption.PropertyEncryptionActivity
import io.realm.appservicesusagesamples.ui.SampleSelectorScreenViewModel
import io.realm.appservicesusagesamples.presence.PresenceDetectionActivity
import io.realm.appservicesusagesamples.ui.EntryView
import io.realm.appservicesusagesamples.ui.buttonSelector
import io.realm.appservicesusagesamples.ui.errorHandlingSelector
import io.realm.kotlin.mongodb.App
import io.realm.kotlin.mongodb.AppConfiguration
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.qualifier.named
import org.koin.dsl.module
Expand Down Expand Up @@ -52,6 +54,13 @@ enum class Demos(
ERROR_HANDLING(
appId = ERROR_HANDLING_APP_ID,
addView = errorHandlingSelector,
),
DYNAMIC_DATA(
appId = DYNAMIC_DATA_APP_ID,
addView = buttonSelector(
"Dynamic data",
DynamicDataActivity::class.java,
)
);

val qualifier = named(appId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2023 Realm Inc.
rorbech marked this conversation as resolved.
Show resolved Hide resolved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.realm.appservicesusagesamples.dynamicdata

import io.realm.appservicesusagesamples.Demos
import io.realm.appservicesusagesamples.dynamicdata.ui.DynamicDataViewModel
import io.realm.appservicesusagesamples.presence.ui.UserStatusListViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module

val dynamicDataViewModel = module {
viewModel {
DynamicDataViewModel(
app = get(qualifier = Demos.DYNAMIC_DATA.qualifier),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2023 Realm Inc.
rorbech marked this conversation as resolved.
Show resolved Hide resolved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.realm.appservicesusagesamples.dynamicdata

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import io.realm.appservicesusagesamples.dynamicdata.ui.DynamicDataScreen
import io.realm.appservicesusagesamples.dynamicdata.ui.DynamicDataViewModel
import io.realm.appservicesusagesamples.ui.theme.AppServicesUsageSamplesTheme
import org.koin.android.scope.AndroidScopeComponent
import org.koin.androidx.scope.activityRetainedScope
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.scope.Scope

/**
* Activity that hosts the views that would demo property level encryption.
*/
class DynamicDataActivity : ComponentActivity(), AndroidScopeComponent {
override val scope: Scope by activityRetainedScope()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel: DynamicDataViewModel by viewModel()


setContent {
AppServicesUsageSamplesTheme {
DynamicDataScreen(
viewModel,
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.realm.appservicesusagesamples.dynamicdata.models
rorbech marked this conversation as resolved.
Show resolved Hide resolved

import io.realm.kotlin.types.RealmAny
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PersistedName
import io.realm.kotlin.types.annotations.PrimaryKey
import org.mongodb.kbson.BsonObjectId
import org.mongodb.kbson.ObjectId

class DynamicDataEntity: RealmObject {
@PersistedName("_id")
@PrimaryKey
var id: ObjectId = BsonObjectId()

var name: String = "<name>"

var configuration: RealmAny? = null
}
Loading