Skip to content

Commit

Permalink
[react-native] Add a basic sample for AppConfiguration (Azure#21907)
Browse files Browse the repository at this point in the history
* [react-native] Add a basic sample for AppConfiguration

* Apply suggestions from code review

Co-authored-by: Harsha Nalluru <sanallur@microsoft.com>

Co-authored-by: Harsha Nalluru <sanallur@microsoft.com>
  • Loading branch information
jeremymeng and HarshaNalluru authored May 20, 2022
1 parent 47dd3d6 commit bbc4e39
Show file tree
Hide file tree
Showing 14 changed files with 6,220 additions and 0 deletions.
15 changes: 15 additions & 0 deletions samples/frameworks/react-native-expo/ts/appconfigBasic/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
node_modules/
.expo/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
.env

# macOS
.DS_Store
24 changes: 24 additions & 0 deletions samples/frameworks/react-native-expo/ts/appconfigBasic/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { StatusBar } from 'expo-status-bar';
import { Button, StyleSheet, Text, View } from 'react-native';

import "./shims";
import { runAppConfigSample } from "./appConfigSample";

export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
<Button title="Click me!" onPress={() => runAppConfigSample() } />
<StatusBar style="auto" />
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
128 changes: 128 additions & 0 deletions samples/frameworks/react-native-expo/ts/appconfigBasic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Azure SDK samples for React Native using Expo (TypeScript)

This sample application shows how to use the TypeScript client libraries for Azure in some common scenarios.

In this sample, we build a simple application in React Native using Expo and integrating with Azure App Configuration service.

## Prerequisites

The samples are compatible with [LTS versions of Node.js](https://nodejs.org/about/releases/).

Before running the samples in Node, they must be compiled to JavaScript using the TypeScript compiler. For more information on TypeScript, see the [TypeScript documentation][typescript].

The sample is using [Expo](https://expo.dev/), a framework and platform for universal React applications.

You need [an Azure subscription][freesub] and the following resources created to run this sample:

## Step by step

### Create the project

Run expo CLI to create a blank project with typescript support, give it a name of `appconf-rn-expo`.

```shell
expo init -t expo-template-blank-typescript
```

At this point, we should be able to see the app running if the environment is set up correctly

```shell
expo start --android
```

### Add a button to the app

Open App.tsx and add the following code so it looks similar to following code
```diff
-import { StyleSheet, Text, View } from 'react-native';
+import { Button, StyleSheet, Text, View } from 'react-native';
+import { runAppConfigSample } from "./appConfigSample";
...

<Text>Open up App.tsx to start working on your app!</Text>
+ <Button title="Click me!" onPress={() => runAppConfigSample() } />
<StatusBar style="auto" />
```

### Add the App Configuration sample file

create a new file `appConfigSample.ts` in the sample directory as `App.tsx` with following content from [appConfigSample.ts](./appConfigSample.ts) file

### Add dependencies

We need to add a dependency to the JavaScript Client SDK library for Azure App Configuration (version 1.4.0-beta.1 or later). We use `@babel/plugin-proposal-async-generator-functions` to help transform async iterator usage in our sample. We also use `babel-plugin-inline-dotenv` to help load secrets from our `.env` file while developing.

```shell
yarn add @azure/app-configuration@1.4.0-beta.1
yarn add --dev babel-plugin-inline-dotenv @babel/plugin-proposal-async-generator-functions
```

Then add the following into `babel.config.js` to enable the plugin

```diff
presets: ["babel-preset-expo"],
+ plugins: [
+ "@babel/plugin-proposal-async-generator-functions",
+ ["inline-dotenv", { unsafe: true }],
+ ],
```

### Add connection string to .env file

Create a `.env` file in the project directory. Retrieve your connection string from Azure portal and add them to the `.env` file. Since this file contains secrets you need to add it to the ignore list if your code is committed to a repository.

**Note** We use connection string directly here for testing purpose. You should consider the trade-off between security and convenience and better use a backend to dynamically provide secrets to only authenticated users.

```
APPCONFIG_CONNECTION_STRING="<your app configuration connection string>"
```

**Note**: if you update the .env file, you would need to clear expo cache and rebuild. It can be done by passing `-c` to the start command, for example, in `package.json`

```diff
- "android": "expo start --android",
+ "android": "expo start --android -c",
```

### Add polyfills

The `crypto` dependency of the JavaScript Client SDK library for Azure App Configuration are not available in React-Native, so we need to provide polyfills for them. We also need to polyfill the `TextEncoder` API. Add the following dependencies to the project

```shell
yarn add isomorphic-webcrypto react-native-get-random-values react-native-url-polyfill text-encoding-polyfill
```

Then add a `shims.ts` file in the project root with the following content

```typescript
import 'react-native-url-polyfill/auto';
import "react-native-get-random-values";
import "text-encoding-polyfill";
const getRandomValues = global.crypto.getRandomValues;
import * as crypto from "isomorphic-webcrypto";
global.crypto = crypto;
global.crypto.getRandomValues = getRandomValues;
```

Then add the following line to `App.tsx`

```diff
+import "./shims";
import { runAppConfigSample } from "./appConfigSample";
```

Now run the app in the emulator (Android in this case)

```shell
npm run android
```

Click on the **CLICK ME!** button, you should see the following logged to the console output!

```
Running helloworld sample
Adding in new setting Samples:Greeting
Samples:Greeting has been set to Hello!
Samples:Greeting has been set to Goodbye!
Samples:Greeting has been deleted
```
32 changes: 32 additions & 0 deletions samples/frameworks/react-native-expo/ts/appconfigBasic/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"expo": {
"name": "appconf-rn-expo",
"slug": "appconf-rn-expo",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#FFFFFF"
}
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* @summary Demonstrates the CRUD operations on the configuration settings.
*/
import { AppConfigurationClient } from "@azure/app-configuration";

export async function runAppConfigSample() {
console.log(`Running helloworld sample`);

// Set the following environment variable or edit the value on the following line.
const connectionString =
process.env["APPCONFIG_CONNECTION_STRING"] || "<connection string>";
const client = new AppConfigurationClient(connectionString);

const greetingKey = "Samples:Greeting";

await cleanupSampleValues([greetingKey], client);

// creating a new setting
console.log(`Adding in new setting ${greetingKey}`);
await client.addConfigurationSetting({ key: greetingKey, value: "Hello!" });

const newSetting = await client.getConfigurationSetting({ key: greetingKey });
console.log(`${greetingKey} has been set to ${newSetting.value}`);

// changing the value of a setting
await client.setConfigurationSetting({ key: greetingKey, value: "Goodbye!" });

const updatedSetting = await client.getConfigurationSetting({
key: greetingKey,
});
console.log(`${greetingKey} has been set to ${updatedSetting.value}`);

// removing the setting
await client.deleteConfigurationSetting({ key: greetingKey });
console.log(`${greetingKey} has been deleted`);

await cleanupSampleValues([greetingKey], client);
}

async function cleanupSampleValues(
keys: string[],
client: AppConfigurationClient
) {
const settingsIterator = client.listConfigurationSettings({
keyFilter: keys.join(","),
});

for await (const setting of settingsIterator) {
await client.deleteConfigurationSetting({
key: setting.key,
label: setting.label,
});
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: [
"@babel/plugin-proposal-async-generator-functions",
["inline-dotenv", { unsafe: true }],
],
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "appconf-rn-expo",
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@azure/app-configuration": "1.4.0-beta.1",
"expo": "~44.0.0",
"expo-status-bar": "~1.2.0",
"isomorphic-webcrypto": "^2.3.8",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-native": "0.64.3",
"react-native-get-random-values": "^1.7.2",
"react-native-url-polyfill": "^1.3.0",
"react-native-web": "0.17.1",
"text-encoding-polyfill": "^0.6.7"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/plugin-proposal-async-generator-functions": "^7.16.8",
"@types/react": "~17.0.21",
"@types/react-native": "~0.64.12",
"babel-plugin-inline-dotenv": "^1.7.0",
"typescript": "~4.3.5"
},
"private": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'react-native-url-polyfill/auto';
import "react-native-get-random-values";
import "text-encoding-polyfill";
const getRandomValues = global.crypto.getRandomValues;
import * as crypto from "isomorphic-webcrypto";
global.crypto = crypto;
global.crypto.getRandomValues = getRandomValues;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
}
}
Loading

0 comments on commit bbc4e39

Please sign in to comment.