зеркало из
1
0
Форкнуть 0

JavaScript Functions quickstart - ready for review (#267)

* first pass

* edits

* fixes

* 2 folders

* Update signalr client lib
This commit is contained in:
Dina Berry (MSFT) 2024-04-18 22:08:10 -07:00 коммит произвёл GitHub
Родитель 74df6bd4fb
Коммит 022f453f81
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
26 изменённых файлов: 4424 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,10 @@
*.js.map
*.ts
.git*
.vscode
__azurite_db*__.json
__blobstorage__
__queuestorage__
local.settings.json
test
tsconfig.json

Просмотреть файл

@ -0,0 +1,100 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TypeScript output
dist
out
# Azure Functions artifacts
bin
obj
appsettings.json
local.settings.json
# Azurite artifacts
**/__azurite__
__blobstorage__
__queuestorage__
__azurite_db*__.json

Просмотреть файл

@ -0,0 +1,38 @@
# SignalR Service Serverless Quick Start (JavaScript)
In this sample, we demonstrate how to broadcast messages with SignalR Service and Azure Function in serverless.
## Prerequisites
* [Azure Function Core Tools](https://www.npmjs.com/package/azure-functions-core-tools)
* [Node.js LTS](https://nodejs.org/en/download/)
## Azure Functions with CommonJS
Because ESM support is still in [preview for Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-reference-node?tabs=javascript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#ecmascript-modules), this quickstart uses CommonJS modules including node-fetch. If you update the node-fetch package to 3.x, you may need to update the code to use ESM modules, which requires changing from `const` to `import` and `*.js` to `*.mjs`.
## Setup and run locally
1. Start local storage emulator in terminal.
```bash
npm run start:azurite
```
1. Rename `local.settings.template.json` to `local.settings.json` and update env variable.
* `SIGNALR_CONNECTION_STRING`: The connection string of your Azure SignalR Service.
1. Run command to start Azure Function locally in different terminal.
```bash
npm start
```
1. Visit `http://localhost:7071/api/index`.
![Screenshot of index showing no star count](./media/screenshot-index.png)
1. Go to this repo's home page and click the star button to increase or decrease the star count.
1. The Azure Functions **broadcast** function is triggered every minute to see if the star count has changed. If it has chanaged base on checking the eTag, a new star count is sent as a message to the client.

Просмотреть файл

@ -0,0 +1,15 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "node",
"AzureWebJobsFeatureFlags": "EnableWorkerIndexing",
"SIGNALR_CONNECTION_STRING": "<SIGNALR_CONNECTION_STRING>"
}
}

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 30 KiB

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,19 @@
{
"name": "javascript",
"version": "1.0.0",
"description": "",
"scripts": {
"start:azurite": "azurite -s -l __azurite__",
"start": "func start",
"test": "echo \"No tests yet...\""
},
"dependencies": {
"@azure/functions": "^4.0.0",
"node-fetch": "2.7.0"
},
"devDependencies": {
"azurite": "3.29.0",
"azure-functions-core-tools": "^4.0.5611"
},
"main": "src/functions/*.js"
}

Просмотреть файл

@ -0,0 +1,27 @@
<html>
<body>
<h1>Azure SignalR Serverless Sample</h1>
<div>Instructions: Goto <a href="https://github.com/Azure/azure-signalr">GitHub repo</a> and star the repository.</div>
<hr>
<div>Star count: <div id="messages"></div></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js"></script>
<script>
let messages = document.querySelector('#messages');
const apiBaseUrl = window.location.origin;
console.log(`apiBaseUrl: ${apiBaseUrl}`);
const connection = new signalR.HubConnectionBuilder()
.withUrl(apiBaseUrl + '/api')
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on('newMessage', (message) => {
console.log(`message: ${message}`);
document.getElementById("messages").innerHTML = message;
});
connection.start()
.catch(console.error);
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,40 @@
const { app, output } = require('@azure/functions');
const getStars = require('../getStars');
var etag = '';
var star = 0;
const goingOutToSignalR = output.generic({
type: 'signalR',
name: 'signalR',
hubName: 'serverless',
connectionStringSetting: 'SIGNALR_CONNECTION_STRING',
});
app.timer('sendMessasge', {
schedule: '0 * * * * *',
extraOutputs: [goingOutToSignalR],
handler: async (myTimer, context) => {
try {
const response = await getStars(etag);
if(response.etag === etag){
console.log(`Same etag: ${response.etag}, no need to broadcast message`);
return;
}
etag = response.etag;
const message = `${response.stars}`;
context.extraOutputs.set(goingOutToSignalR,
{
'target': 'newMessage',
'arguments': [message]
});
} catch (error) {
context.log(error);
}
}
});

Просмотреть файл

@ -0,0 +1,32 @@
const { app } = require('@azure/functions');
const fs = require('fs').promises;
const path = require('path');
app.http('index', {
methods: ['GET', 'POST'],
authLevel: 'anonymous',
handler: async (request, context) => {
try {
context.log(`Http function processed request for url "${request.url}"`);
const filePath = path.join(__dirname,'../content/index.html');
const html = await fs.readFile(filePath);
return {
body: html,
headers: {
'Content-Type': 'text/html'
}
};
} catch (error) {
context.log(error);
return {
status: 500,
jsonBody: error
}
}
}
});

Просмотреть файл

@ -0,0 +1,25 @@
const { app, input } = require('@azure/functions');
const inputSignalR = input.generic({
type: 'signalRConnectionInfo',
name: 'connectionInfo',
hubName: 'serverless',
connectionStringSetting: 'SIGNALR_CONNECTION_STRING',
});
app.post('negotiate', {
authLevel: 'anonymous',
handler: (request, context) => {
try {
return { body: JSON.stringify(context.extraInputs.get(inputSignalR)) }
} catch (error) {
context.log(error);
return {
status: 500,
jsonBody: error
}
}
},
route: 'negotiate',
extraInputs: [inputSignalR],
});

Просмотреть файл

@ -0,0 +1,27 @@
const fetch = require('node-fetch');
const URL = "https://api.github.com/repos/azure/azure-signalr"
async function getStars(currentEtag){
console.log(`currentEtag: ${currentEtag}`);
const headers = {
'If-None-Match': currentEtag
};
const response = await fetch(URL, { headers });
if(response.ok){
const etag = response.headers.get('etag');
const { stargazers_count } = await response.json();
console.log(`Current star count is: ${stargazers_count}`);
return { etag, stars: stargazers_count };
} else {
console.log('Failed to fetch data: ' + response.status + ' ' + response.statusText);
return { etag: currentEtag, stars: undefined };
}
}
module.exports = getStars;