Skip to content

Commit

Permalink
React sample integrating with Azure SDKs (#12924)
Browse files Browse the repository at this point in the history
## What
Adds a React sample that integrates with multiple Azure services

## Why
As part of our investigation into how well our SDKs integrate into existing
frameworks we created a few sample apps that showcase how to use the
various SDKs. Adding a sample here would ensure that we have something
to reference and provide to folks who are interested in using our SDKs in React.
  • Loading branch information
maorleger authored Dec 21, 2020
1 parent 122bc4b commit 48e8212
Show file tree
Hide file tree
Showing 19 changed files with 894 additions and 0 deletions.
154 changes: 154 additions & 0 deletions samples/frameworks/react/arm-template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"baseName": {
"type": "string",
"defaultValue": "[resourceGroup().name]",
"metadata": {
"description": "The base resource name."
}
},
"origin": {
"type": "string",
"defaultValue": "http://localhost:3000",
"metadata": {
"description": "The application origin for configuring CORS policies. By default it'll be set to http://localhost:3000 locally."
}
}
},
"variables": {
"eventHubsName": "events",
"eventHubsNamespace": "[concat(parameters('baseName'), 'reactevents')]",
"storageAccount": "[concat(parameters('baseName'), 'reactstorage')]",
"storageContainer": "[concat(parameters('baseName'), 'reactstorage', '/default/blobs')]",
"location": "[resourceGroup().location]"
},
"resources": [
{
"type": "Microsoft.EventHub/namespaces",
"apiVersion": "2018-01-01-preview",
"name": "[variables('eventHubsNamespace')]",
"location": "[variables('location')]",
"sku": {
"name": "Basic",
"tier": "Basic",
"capacity": 1
},
"properties": {
"zoneRedundant": false,
"isAutoInflateEnabled": false,
"maximumThroughputUnits": 0,
"kafkaEnabled": false
}
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2020-08-01-preview",
"name": "[variables('storageAccount')]",
"location": "[variables('location')]",
"sku": {
"name": "Standard_RAGRS",
"tier": "Standard"
},
"kind": "StorageV2",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"accessTier": "Hot"
}
},
{
"type": "Microsoft.EventHub/namespaces/eventhubs",
"apiVersion": "2017-04-01",
"name": "[concat(variables('eventHubsNamespace'), '/', variables('eventHubsName'))]",
"location": "[variables('location')]",
"dependsOn": [
"[resourceId('Microsoft.EventHub/namespaces', variables('eventHubsNamespace'))]"
],
"properties": {
"messageRetentionInDays": 1,
"partitionCount": 2,
"status": "Active"
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2020-08-01-preview",
"name": "[concat(variables('storageAccount'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccount'))]"
],
"sku": {
"name": "Standard_RAGRS",
"tier": "Standard"
},
"properties": {
"cors": {
"corsRules": [
{
"allowedOrigins": ["[parameters('origin')]"],
"allowedMethods": ["GET", "OPTIONS", "PUT", "POST"],
"maxAgeInSeconds": 0,
"exposedHeaders": ["*"],
"allowedHeaders": ["*"]
}
]
},
"deleteRetentionPolicy": {
"enabled": false
}
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2020-08-01-preview",
"name": "[variables('storageContainer')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccount'), 'default')]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccount'))]"
],
"properties": {
"defaultEncryptionScope": "$account-encryption-key",
"denyEncryptionScopeOverride": false,
"publicAccess": "None"
}
}
],
"outputs": {
"react_app_event_hubs_namespace": {
"type": "string",
"value": "[concat(variables('eventHubsNamespace'), '.servicebus.windows.net')]"
},
"react_app_event_hubs_name": {
"type": "string",
"value": "[variables('eventHubsName')]"
},
"react_app_blob_uri": {
"type": "string",
"value": "[reference(variables('storageAccount')).primaryEndpoints.blob]"
},
"react_app_blob_container": {
"type": "string",
"value": "[last(split(variables('storageContainer'), '/'))]"
}
}
}
24 changes: 24 additions & 0 deletions samples/frameworks/react/ts/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"react/prop-types": 0
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
]
}
74 changes: 74 additions & 0 deletions samples/frameworks/react/ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Azure SDK samples for React (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 Todo application in React using [create-react-app][react] and integrating with various Azure services.

- Integration with Azure Event Hubs to support real-time updates across multiple instances of the application.
- Integration with Azure Storage Blob for persisting ToDo items.

## Prerequisites

The samples are compatible with Node.js >= 8.0.0.

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].

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

- An Azure Event Hubs namespace. Please refer to the [Event Hubs documentation][eventhubs] for additional information on Event Hubs.
- An Azure Storage Blob container. Please refer to the [Storage Blob documentation][storageblob] for additional information on Azure Storage Blob. This file will be fetched from Azure Storage Blob and displayed on the screen.
- Finally, you'll need a way to authenticate the application with Azure. Please refer to the [@azure/identity][identity] package for information on authentication. The instructions below will walk you through the necessary steps.

To quickly create the needed resources in Azure and to receive the necessary environment variables for them, you can deploy our sample template by clicking:

[![](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-sdk-for-js%2F6e519929f154b919e8a47245715076e73cd7915c%2Fsamples%2Fframeworks%2Freact%2Farm-template.json)

The above template will create the necessary resources for you and the output tab will contain the exact environment variables that you'll need as soon as deployment succeeds. When the deployment is finished, head over to the "outputs" tab and copy the outputs to a local file - you'll need them in the next step.

### Register a new application in AAD and assign the "Azure Event Hubs Data Owner" and "Azure Storage Blob Data Contributor" role to it.

Authentication will still need to be set-up manually using the following instructions:

- See https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app
to register a new application in the Azure Active Directory.
- Note down the client id and tenant id from the above step.
You will need to set these in the .env file below.

Ensure your app registration has been configured properly to allow the [implicit grant flow][implicitgrantflow]
and allow both `Access tokens` and `ID tokens` to be issued by the authorization endpoint.
In your app registration, you will also need to add a permission for the `Microsoft.EventHubs` and `Azure Storage` apps.
When adding permission for `Microsoft.EventHubs` and `Azure Storage`, the type should be `delegated permissions` and the permission should be `user_impersonation`.

## Running the sample

Once the above created you'll want to ensure React has the necessary environment variables. To do this, copy `sample.env` as `.env` and provide the necessary environment variables to configure the application. You can get most values from the output tab of the deployment, and the client and tenant ID from the App registration step. Please note that environment variables should be upper case. For example: REACT_APP_TENANT_ID and REACT_APP_EVENT_HUBS_NAME.

Install the various packages as well as the TypeScript compiler using:

```bash
npm install
```

Run the sample app:

```bash
npm start
```

Since this is a contrived example, you can only create new Todos and complete existing Todos. As you interact with the application, you'll notice EventHubs messages get written out to the console in the browser's developer tools. You may attach a note which will get uploaded as Blobs or fetch an existing note to display it on the screen.

Additionally, you may open multiple instances of this sample application and watch as Todos synchronize in real-time.

## Next Steps

Take a look at our [API Documentation][apiref] for more information about the APIs that are avaiable.

[react]: https://create-react-app.dev/
[typescript]: https://www.typescriptlang.org/docs/home.html
[freesub]: https://azure.microsoft.com/free
[eventhubs]: https://docs.microsoft.com/javascript/api/@azure/event-hubs
[servicebus]: https://docs.microsoft.com/javascript/api/@azure/service-bus
[storageblob]: https://docs.microsoft.com/javascript/api/@azure/storage-blob
[identity]: https://docs.microsoft.com/javascript/api/@azure/identity
[apiref]: https://docs.microsoft.com/javascript/api/
[implicitgrantflow]: https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-implicit-grant-flow
48 changes: 48 additions & 0 deletions samples/frameworks/react/ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "typescript-react-sample",
"version": "0.1.0",
"private": true,
"dependencies": {
"@azure/event-hubs": "^5.3.1",
"@azure/identity": "^1.2.0",
"@azure/storage-blob": "^12.3.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1",
"uuid": "^8.3.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/node": "^12.0.0",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^4.9.0",
"@typescript-eslint/parser": "^4.9.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4",
"typescript": "^4.0.3",
"prettier": "^1.16.4"
}
}
48 changes: 48 additions & 0 deletions samples/frameworks/react/ts/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="https://c.s-microsoft.com/favicon.ico?v2" type="image/x-icon" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Todos React Sample</title>
<!--
Adding basic styling with Bootstrap
-->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
/>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
8 changes: 8 additions & 0 deletions samples/frameworks/react/ts/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
3 changes: 3 additions & 0 deletions samples/frameworks/react/ts/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
21 changes: 21 additions & 0 deletions samples/frameworks/react/ts/sample.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Rename this to .env in order to have React automatically
# pull in all these variables (anything that starts with REACT_APP_ will be
# made available. See the documentation for adding custom environment variables
# at the following link: https://create-react-app.dev/docs/adding-custom-environment-variables/)

# fully qualified namespace for event hubs, typically: <namespace>.servicebus.windows.net
REACT_APP_EVENT_HUBS_NAMESPACE=""
# The name of the Event Hubs hub
REACT_APP_EVENT_HUBS_NAME=""

# URI for the Azure Blob Storage, typically https://<name>.blob.core.windows.net/
REACT_APP_BLOB_URI=""
# the name of the Azure Blob Storage container
REACT_APP_BLOB_CONTAINER=""

# Used to authenticate using Azure AD as a service principal for role-based authentication.
#
# See the documentation for `InteractiveBrowserCredential` at the following link:
# https://docs.microsoft.com/javascript/api/@azure/identity/interactivebrowsercredential
REACT_APP_CLIENT_ID=""
REACT_APP_TENANT_ID=""
Loading

0 comments on commit 48e8212

Please sign in to comment.