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

mc/in-app-purchases-example #5

Merged
merged 41 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
a55880c
Add IAP sample app and App Services app
marcabreracast Oct 16, 2023
4470fa0
Fix Swiftlint warnings
marcabreracast Oct 16, 2023
589ce78
Update .Readme file
marcabreracast Oct 16, 2023
3fc649c
Remove App ID
marcabreracast Oct 16, 2023
25a2999
Remove Singleton for AuthenticationManager
marcabreracast Nov 7, 2023
f94d185
Add headers to mention copyrights to Realm
marcabreracast Nov 7, 2023
09fd301
Remove commented code
marcabreracast Nov 7, 2023
9a4f070
Remove typo
marcabreracast Nov 7, 2023
564d117
Update SPM Realm dependency to latest stable version
marcabreracast Nov 7, 2023
6a2c093
Update InAppPurchasesAtlasAppServices/FoodieFolio/FoodieFolio/Manager…
marcabreracast Nov 7, 2023
4a041a4
Remove properties that are not needed
marcabreracast Nov 7, 2023
19ada49
Fix indentation
marcabreracast Nov 7, 2023
7a7411e
Add modifier to textfield void having upper case letters by default
marcabreracast Nov 7, 2023
63d748b
Remove var declaration since it won't be used
marcabreracast Nov 7, 2023
bfac083
Change of method used to capitalized title since it's better suited
marcabreracast Nov 7, 2023
5549de7
Remove reference to unused variable
marcabreracast Nov 7, 2023
82155a0
Remove note that's not needed
marcabreracast Nov 7, 2023
256fd9a
Update InAppPurchasesAtlasAppServices/FoodieFolio/FoodieFolio/Manager…
marcabreracast Nov 7, 2023
119d0f4
Merge branch 'mc/in_app_purchases_example' of github.com:realm/realm-…
marcabreracast Nov 7, 2023
7484d41
Remove extension that's not needed due to this style only being used …
marcabreracast Nov 7, 2023
1815340
Remove use of MainActor that's not needed
marcabreracast Nov 7, 2023
e69fee6
Remove duplicated alert
marcabreracast Nov 7, 2023
a9f1e7d
Remove type declaration for vars since type is inferred on the wrapper
marcabreracast Nov 7, 2023
83e0a9e
Remove reference to rerun subscriptions once they were added since it…
marcabreracast Nov 8, 2023
6592ec1
Replace .none with never to avoid capitalization in textfield
marcabreracast Nov 8, 2023
a4116c8
Revert change to avoid screen loading forever
marcabreracast Nov 8, 2023
350b2af
Replace ingredients type to use RealmList instead of an array of strings
marcabreracast Nov 8, 2023
082a6f9
Fix indentation
marcabreracast Nov 8, 2023
54bf82b
Remove unused Button extension
marcabreracast Nov 8, 2023
04b6218
Change navigation modifiers to the main VStack instead of attaching i…
marcabreracast Nov 8, 2023
53e26e5
Update model name for Purchase so it matches camel case formatting
marcabreracast Nov 10, 2023
e83c139
Update model name to IngredientList in order to match camel case form…
marcabreracast Nov 10, 2023
0ea7f80
Remove unnecesary comment
marcabreracast Nov 10, 2023
e0ba895
Perform schema change for cuisine type so a string is returned instea…
marcabreracast Nov 10, 2023
26d44e4
Remove redundant code for login navigation
marcabreracast Nov 10, 2023
7efdd27
Update App Services app
marcabreracast Nov 10, 2023
0f38f93
Update Readme file to include steps for copying Atlas App Services app
marcabreracast Nov 10, 2023
85c4ebd
Refactor functionality to avoid hardcoded recipe strings and use bett…
marcabreracast Nov 17, 2023
291f016
Update App Services app with backend changes
marcabreracast Nov 17, 2023
452d783
Update Readme.md details to match Realm SDK version in the app
marcabreracast Nov 22, 2023
9e9ec63
Add Readme description of app scope and explain integration purposes …
marcabreracast Nov 22, 2023
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
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,23 @@
{
"anon-user": {
"name": "anon-user",
"type": "anon-user",
"disabled": false
},
"api-key": {
"name": "api-key",
"type": "api-key",
"disabled": true
},
"local-userpass": {
"name": "local-userpass",
"type": "local-userpass",
"config": {
"autoConfirm": true,
"resetFunctionName": "resetFunc",
"runConfirmationFunction": false,
"runResetFunction": true
},
"disabled": false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "mongodb-atlas",
"type": "mongodb-atlas",
"config": {
"clusterName": "YOUR_CLUSTER_NAME",
"readPreference": "primary",
"wireProtocolEnabled": false
},
"version": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"roles": [
{
"name": "read-not-write",
"apply_when": {},
"document_filters": {
"write": false,
"read": true
},
"read": true,
"write": false,
"insert": true,
"delete": true,
"search": true
}
]
}
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,14 @@
exports = async function(userId, transactionResult){
var serviceName = "mongodb-atlas";
var dbName = "foodie-folio";
var collName = "Purchase";

var purchasesCollection = context.services.get(serviceName).db(dbName).collection(collName);

var receipt = JSON.parse(transactionResult);

return purchasesCollection.insertOne({
userId: userId,
receipt: receipt
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"name": "resetFunc",
"private": true
},
{
"name": "fetchRecipes",
"private": false,
"disable_arg_logs": true
},
{
"name": "fetchPremiumRecipes",
"private": false,
"disable_arg_logs": true
},
{
"name": "addPurchaseReceipt",
"private": false
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
exports = async function(arg){
const axios = require('axios');

var serviceName = "mongodb-atlas";
var dbName = "foodie-folio";
var collName = "Recipe";

var edamamAppID = context.values.get('edamam-app-id-link-to-secret');
var edamamAPIKey = context.values.get('edamam-api-key-link-to-secret');

// Change this and add it as url query parameters instead
const diet = "low-carb";
const premiumRecipes = "Japanese"
var queryString = `?type=any&app_id=${edamamAppID}&app_key=${edamamAPIKey}&cuisineType=${premiumRecipes}&diet=${diet}`;
var baseURL = "https://api.edamam.com/api/recipes/v2";

const endpoint = baseURL + queryString;

console.log(endpoint);

// Get a collection from the context
var collection = context.services.get(serviceName).db(dbName).collection(collName);

try {
const response = await axios.get(endpoint);
const recipes = response.data.hits;

for (const recipe of recipes) {
const recipeObject = recipe.recipe // Avoid duplication of field and improve readability
const recipeURI = recipeObject.uri; // Assuming the URI is accessible this way

// Use the recipe URI as the unique identifier
const filter = { uri: recipeURI };

// SELECTION OF DATA I WANT TO insert
const dataToUpsert = {
category: "purchased",
uri: recipeObject.uri,
calories: recipeObject.calories,
cautions: recipeObject.cautions,
cuisineType: recipeObject.cuisineType[0],
dietLabels: recipeObject.dietLabels,
dishType: recipeObject.dishType,
healthLabels: recipeObject.healthLabels,
image: recipeObject.image,
ingredientLines: recipeObject.ingredientLines,
ingredients: recipeObject.ingredients,
label: recipeObject.label,
mealType: recipeObject.mealType,
shareAs: recipeObject.shareAs,
source: recipeObject.source,
totalCO2Emissions: recipeObject.totalCO2Emissions,
totalTime: recipeObject.totalTime,
totalWeight: recipeObject.totalWeight,
url: recipeObject.url,
yield: recipeObject.yield
};

// Update document with the new data, or insert if it doesn't exist
const updateData = {
$set: dataToUpsert
};

const updateResult = await collection.updateOne(
filter,
updateData,
{ upsert: true } // Enable upsert option
);

console.log("Upsert result:", updateResult);
}

return "Upsert operation completed";

} catch(err) {
console.log("Error occurred while executing findOne:", err.message);

return { error: err.message };
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
exports = async function(arg){
const axios = require('axios');

var serviceName = "mongodb-atlas";
var dbName = "foodie-folio";
var collName = "Recipe";

var edamamAppID = context.values.get('edamam-app-id-link-to-secret');
var edamamAPIKey = context.values.get('edamam-api-key-link-to-secret');

// Change this and add it as url query parameters instead
const diet = "balanced";
var queryString = `?type=any&app_id=${edamamAppID}&app_key=${edamamAPIKey}&diet=${diet}`;
var baseURL = "https://api.edamam.com/api/recipes/v2";

const endpoint = baseURL + queryString;

console.log(endpoint);

// Get a collection from the context
var collection = context.services.get(serviceName).db(dbName).collection(collName);

try {
const response = await axios.get(endpoint);
const recipes = response.data.hits;

for (const recipe of recipes) {
const recipeObject = recipe.recipe // Avoid duplication of field and improve readability
const recipeURI = recipeObject.uri; // Assuming the URI is accessible this way

// Use the recipe URI as the unique identifier
const filter = { uri: recipeURI };

// SELECTION OF DATA I WANT TO insert
const dataToUpsert = {
category: "free",
uri: recipeObject.uri,
calories: recipeObject.calories,
cautions: recipeObject.cautions,
cuisineType: recipeObject.cuisineType[0],
dietLabels: recipeObject.dietLabels,
dishType: recipeObject.dishType,
healthLabels: recipeObject.healthLabels,
image: recipeObject.image,
ingredientLines: recipeObject.ingredientLines,
ingredients: recipeObject.ingredients,
label: recipeObject.label,
mealType: recipeObject.mealType,
shareAs: recipeObject.shareAs,
source: recipeObject.source,
totalCO2Emissions: recipeObject.totalCO2Emissions,
totalTime: recipeObject.totalTime,
totalWeight: recipeObject.totalWeight,
url: recipeObject.url,
yield: recipeObject.yield
};

// Update document with the new data, or insert if it doesn't exist
const updateData = {
$set: dataToUpsert
};

const updateResult = await collection.updateOne(
filter,
updateData,
{ upsert: true } // Enable upsert option
);

console.log("Upsert result:", updateResult);
}

return "Upsert operation completed";

} catch(err) {
console.log("Error occurred while executing findOne:", err.message);

return { error: err.message };
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

/*
This function will be run when the client SDK 'callResetPasswordFunction' and is called with an object parameter
which contains four keys: 'token', 'tokenId', 'username', and 'password', and additional parameters
for each parameter passed in as part of the argument list from the SDK.

The return object must contain a 'status' key which can be empty or one of three string values:
'success', 'pending', or 'fail'

'success': the user's password is set to the passed in 'password' parameter.

'pending': the user's password is not reset and the UserPasswordAuthProviderClient 'resetPassword' function would
need to be called with the token, tokenId, and new password via an SDK. (see below)

const Realm = require("realm");
const appConfig = {
id: "my-app-id",
timeout: 1000,
app: {
name: "my-app-name",
version: "1"
}
};
let app = new Realm.App(appConfig);
let client = app.auth.emailPassword;
await client.resetPassword(token, tokenId, newPassword);

'fail': the user's password is not reset and will not be able to log in with that password.

If an error is thrown within the function the result is the same as 'fail'.

Example below:

exports = ({ token, tokenId, username, password }, sendEmail, securityQuestionAnswer) => {
// process the reset token, tokenId, username and password
if (sendEmail) {
context.functions.execute('sendResetPasswordEmail', username, token, tokenId);
// will wait for SDK resetPassword to be called with the token and tokenId
return { status: 'pending' };
} else if (context.functions.execute('validateSecurityQuestionAnswer', username, securityQuestionAnswer)) {
// will set the users password to the password parameter
return { status: 'success' };
}

// will not reset the password
return { status: 'fail' };
};

The uncommented function below is just a placeholder and will result in failure.
*/

exports = ({ token, tokenId, username, password }) => {
// will not reset the password
return { status: 'fail' };
};
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 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"app_id": "YOUR_APP_ID",
"config_version": 20210101,
"name": "YOUR_APP_NAME",
"location": "IE",
"provider_region": "aws-eu-west-1",
"deployment_model": "LOCAL"
}
16 changes: 16 additions & 0 deletions InAppPurchasesAtlasAppServices/App Services app/sync/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"type": "flexible",
"state": "enabled",
"development_mode_enabled": false,
"service_name": "mongodb-atlas",
"database_name": "foodie-folio",
"client_max_offline_days": 30,
"is_recovery_mode_disabled": false,
"permissions": {
"rules": {},
"defaultRoles": []
},
"queryable_fields_names": [
"label"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "fetchPremiumRecipes",
"type": "SCHEDULED",
"config": {
"schedule": "*/30 * * * *",
"skip_catchup_events": false
},
"disabled": false,
"event_processors": {
"FUNCTION": {
"config": {
"function_name": "fetchPremiumRecipes"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "fetchRecipes",
"type": "SCHEDULED",
"config": {
"schedule": "*/30 * * * *",
"skip_catchup_events": false
},
"disabled": false,
"event_processors": {
"FUNCTION": {
"config": {
"function_name": "fetchRecipes"
}
}
}
}
Loading