-
Instalar e iniciar o Visual Studio Code: https://code.visualstudio.com/download
-
Instalar e ativar as extensões mínimas necessárias
- Azure Account: https://github.com/microsoft/vscode-azure-account
- Azure Functions: https://github.com/Microsoft/vscode-azurefunctions
- Após o login, escolher a assinatura desejada e clicar em "Create New Project..."
-
Especificar o nome de função (eg Thumbnail Image)
-
Definir Authorization Level (eg Function)
- Analise o projeto criado
- Em package.json, incluir a dependência "image-thumbnail" na propriedade "dependencies:
"dependencies": {
"image-thumbnail": "1.0.2"
}
- Alterar o código da função em si. abrir o arquivo "ThumbnailImage/index.ts" e alterar incluindo o seguinte conteúdo:
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import * as imageThumbnail from 'image-thumbnail';
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
context.log('HTTP trigger function processed a request.');
const imageUrl = (req.query.imageUrl || (req.body && req.body.imageUrl));
try {
if (imageUrl) {
let options = { responseType: 'base64' }
const thumbnail = await imageThumbnail({ uri: imageUrl }, options);
context.res = {
// status: 200, /* Defaults to 200 */
body: thumbnail
};
}
else {
context.res = {
status: 400,
body: "Please pass a image URL to get the thumbnail"
};
}
}
catch (err) {
console.error(err);
context.res = {
status: 500,
body: err
};
}
};
export default httpTrigger;
- Executar a função na máquina local. Para isso, acesse a aba de Debug e execute a configuração já existente "Attach to Node Functions":
-
Este procedimento já irá instalar os pacotes NPM, fazer a compilação do Typescript e executar a função no host local
-
Ao término da execução, será disponibilizado acesso a função via URL local (eg http://localhost:7071/api/ThumbnailImage)
- Abrir o Postman ou outra ferramenta para testes de APIs para testar a função recém criada.
- Incluir no body da requisição a URL de uma imagem, especificando como content type "application/json":
{
"imageUrl": "https://cdn.pixabay.com/photo/2016/04/15/04/02/water-1330252__340.jpg"
}
- A função deverá retornar o base64 da imagem conforme programado na função:
- Funcionou! Podemos agora interromper a execução.
- Agora vamos publicar esta função no Azure. Voltando para a aba azure, escolha a assinatura desejada e então escolha a opção "Create Function app In Azure"
- Especificar um nome único. Será criada uma função em um grupo de recursos novo definido pela extensão.
- Para visualizar a nova função criada, acesse seu menu de contexto e seleciona aopção "Open in Portal"
- Agora vamos fazer o deploy da nossa função local no azure escolhendo a opção "Deploy to Funcation App..." do menu de contexto da função.
- Confirmar o deploy da função
- Acessar novamente o portal para verificar a função que acaba de ser implantada:
-
Testar usando o mesmo Postman, mas agora apontando para a URL da função no azure.
-
Em alguns casos pode ser retornado erro 500. Seguem passos para analisar erro e corrigir:
- No VSCode, clicar na opção "Start Streaming Logs" da função no Azure através dessa opção no menu de contexto:
- Testar novamente a função no Postman. Será retornado o erro:
-
Note que esse erro em questão diz respeito a versão do Node sendo usada no host da Function no Azure. Para corrigir esse erro, é necessário especificar NodeJS 64 bits no host da função no Azure.
-
Acesse o Kudu do host do Azure Function:
-
Acesse o Debug Console CMD
-
Arraste o executável no Node 64 bits na pasta d:\home
- Voltando para o Azure Function, acessar App Settings e incluir a seguinte configuração:
languageWorkers:node:defaultExecutablePath = d:\home\node
- Agora vamos testar novamente a função e Voialá!
- Pronto! Agora vamos incluir alguns testes para nossa função. Para isso, vamos incluir nova dependencia de desenvolvimento no package.json. No final, nosso devDependencies ficará da seguinte forma:
"devDependencies": {
"@azure/functions": "^1.0.1-beta2",
"@types/jest": "^24.0.11",
"jest": "^24.7.1",
"ts-jest": "^24.0.2",
"typescript": "^3.3.3"
}
- Atualizar também a Task de teste para:
"test": "jest"
- Adicionar novo arquivo chamado index.test.ts na pasta da função:
/**
* @jest-environment node
*/
import * as httpTrigger from './index';
test('Http trigger should receive a imageUrl', async () => {
const context: any = {
log: jest.fn()
};
const request = {
query: { name: 'Bill' }
};
await httpTrigger.default(context, request);
expect(context.log.mock.calls.length).toBe(1);
expect(context.res.body).toEqual('Please pass a image URL to get the thumbnail');
});
test('Http trigger should return thumbnail image', async () => {
const context: any = {
log: jest.fn()
};
let imageUrl: string = "https://cdn.pixabay.com/photo/2016/04/15/04/02/water-1330252__340.jpg";
let imageThumbnail: string = "/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAiADcDASIAAhEBAxEB/8QAGwAAAwEAAwEAAAAAAAAAAAAAAAUGBAECAwf/xAArEAACAQMDAwMCBwAAAAAAAAABAgMABBEFEiETMVEyQWEigRQkQnGhwdH/xAAaAQACAwEBAAAAAAAAAAAAAAAEBQIDBgAB/8QAIBEAAgMBAAICAwAAAAAAAAAAAQIAAxEEEjEhMgUTFP/aAAwDAQACEQMRAD8A+k2sqRAbg2R8inVrqKKAAAT8mom2Fx0+oW7c4JrYs05h6olRFz22/wB1qLOXTMNV+SCruyl1TUcw+rk9gOajNbM8q5ZmKHvhRxTq2nQBWuo1bH6jJj+K2NqNiCFYDYfSDH/tSqT9R+F2L+3s/oGFwJN6Np1u+wJdL1iM7QucfGaoJIGsoSxlY8eynNeJ1a0iUmIRoSeCErBda/KVMZVgpHBx3HmrHre07kr5uxKFwNsRavfF59g3nyAM0V56jFdyOJbddyNzuPGaKMSlQIJZ3OzbHNi8MgXegIB4plMlvMY0lyqrzhTwakLK9Quozj2qjtLqNWDFlbPGPHzXX0lTogtV5zxaPY7a0kKTdNCw7NXF2IrgCOSNX2nK7uB9q6QsuzsBR+JjQhJAEbx8eaX4djAlcw4NmBoLWFmzsDAYYBSwA81z+WdcLsk2/Tkg8V2e9SLerHeme4x2pbqeqw26FfpTxRKI7nIL5og+MmXVFl3jZtA9gBRSm91WJQCJBgnsaKZJWwXMgLKznyAMT6Z6vtT+AnPfxRRVlsj1faOy7jZhm9I9/isLsfo5PoNFFAV+57d6EVXbERNgnv5qf1JmMfLE8+f3oopjXJ8n2ER3DHp9zRRRVsep6n//2Q==";
const request = {
query: { imageUrl: imageUrl }
};
await httpTrigger.default(context, request);
expect(context.log.mock.calls.length).toBe(1);
expect(context.res.body).toEqual(imageThumbnail);
});
- Feito isso, vamos incluir o arquivo de configuração do jest na raiz do projeto:
module.exports = {
"roots": [
"<rootDir>/ThumbnailImage"
],
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
],
"testEnvironment": "node",
}
- Feito isso, instalar os novos pacotes com "npm install" e depois executar o script de teste com "npm run test"
- Note que no teste estamos verificando o resultado base64 do thumbnail da imagem. Podemos incluir outras imagens e outros testes para verificar nossa função.
-
Está tudo pronto. O próximo passo é criar um projeto no Azure Devops para realizar a integração e entrega contínua do projeto.
-
Acesse o site https://devops.azure.com e forneça suas credenciais de acesso.
-
Clique na opção para criar novo projeto:
- Você pode neste momento importar o projeto do GitHub para o repositório dentro do Azure Devops ou simplesmente manter seu código somente no GitHub. Se desejar importar o código, acessar o item "Repos" no menu e então escolher a opção "Import Repository", fornecendo as informações e credenciais do seu repositório no Git Hub.
- Agora acesse a item Pipelines > Builds e clique no botão para criar um novo pipeline de build (New Pipeline)
- Você pode optar por obter o código do repositório do Azure Devops ou diretamente do GitHub e de outras fontes. Vou escolher a opção "GitHub".
-
Você precisa autorizar acesso ao Github via OAuth
-
Depois, você deve selecionar o repositório
- Na hora de selecionar o template, você pode optar por escolher o Yaml ou diretamente um template pronto.
- Yaml:
pool:
name: Hosted
#Your build pipeline references an undefined variable named ‘which node.exe’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972
steps:
- task: NodeTool@0
displayName: 'Use Node version 10.14.1'
inputs:
versionSpec: 10.14.1
- task: Npm@1
displayName: 'Install Application Dependencies'
inputs:
workingDir: '$(System.DefaultWorkingDirectory)'
verbose: false
- task: Npm@1
displayName: 'Run ''build' script'
inputs:
command: custom
verbose: false
customCommand: 'run build --if-present'
- task: Npm@1
displayName: 'Remove extraneous packages'
inputs:
command: custom
verbose: false
customCommand: 'prune --production'
- bash: 'cp "$(which node.exe)" .'
displayName: 'Bash Script'
- task: ArchiveFiles@2
displayName: 'Archive files'
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
includeRootFolder: false
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: drop'
- Template "Azure Functions for Node.js"
-
Especificar o Source e o Agent Pool. No meu caso, vou especificar o "Hosted"
-
Agora vamos tratar o problema do Node.JS 64 bits do Azure. Inclua a task "Bash Script" antes do "Archive Files" para copiar o executável do node na pasta do resultado de build que será emcacotada:
-
Adicionalmente você pode cinluir um triger para realizar o build automaticamente após novo commit.
-
Terminada a configuração, você pode disparar tanto manualmente como através do trigger o novo build.
-
Feito o Pipeline de Build, o próximo passo é criar o pipeline de release.
-
Acesse a item Pipelines > Releases e clique no botão para criar um novo pipeline de release (New Pipeline)
-
Escolha o template "Deploy a function app to Azure Functions"
- Clique em "Add an Artifact" e selecione o Build que acabamos de criar
-
Você pode ainda definir regras de disparo da release a partir do build para que isso seja feito continuamente.
-
Depois clique em "1 job, 1 task" do stage de release padrão.
-
Entre com as credenciais de sua assinatura Azure e selecione o Azure Function Azure onde será feito o deploy da função.
-
Por fim, inclua configuração do Applications Settings durante o release para especificar o executável node que será utilizado pela function. Lembre-se que esse executável foi copiado junto com o pacote gerado na Build:
- Terminada a configuração, você pode disparar tanto manualmente como através do trigger a nova release.
- https://docs.microsoft.com/pt-br/azure/azure-functions/functions-test-a-function#javascript-in-vs-code
- https://basarat.gitbooks.io/typescript/docs/testing/jest.html
- https://www.npmjs.com/package/image-thumbnail
- https://jestjs.io/docs/en/configuration.html#testenvironment-string
- https://aurelia.ninja/2019/04/04/a-strategy-for-deploying-a-custom-node-executable-to-azure-functions-on-windows/