-n resiliency_test --service-objective S0
66 | ```
67 |
68 | Remember that if you don't have Linux environment where you can run [AZ CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) you can always use the [Cloud Shell](https://docs.microsoft.com/en-us/azure/cloud-shell/quickstart). If you prefer to do everything via the portal, here's a tutorial: [Create an Azure SQL Database single database](https://docs.microsoft.com/en-us/azure/azure-sql/database/single-database-create-quickstart?tabs=azure-portal).
69 |
70 | If you are completely new to Azure SQL, no worries! Here's a full playlist that will help you: [Azure SQL for beginners](https://www.youtube.com/playlist?list=PLlrxD0HtieHi5c9-i_Dnxw9vxBY-TqaeN).
71 |
72 | ## Running local
73 |
74 | Now use the `/api/.env.template` file to create an `.env` file and add the correct information needed to access your SQL Server or Azure SQL.
75 |
76 | Details on how to run Azure Static WebApps locally can be found here:
77 |
78 | [Set up local development for Azure Static Web Apps](https://docs.microsoft.com/en-us/azure/static-web-apps/local-development)
79 |
80 | Long story short:
81 |
82 | - Install the Azure Static Web Apps CLI: `npm install -g @azure/static-web-apps-cli`
83 | - From the root folder run: `swa start --api ./api`
84 |
85 | Easy, right?
86 |
87 | ## Running on Azure
88 |
89 | This is the amazing part of using Azure Static Web Apps. Deploying to Azure is completely automated via GitHub actions. There are some manual steps because the Static Web Apps CLI is still in Preview at the moment of writing and because Prisma and the Azure Static Web App GitHub Action need some help the get along.
90 |
91 | 1. Fork this repository
92 | 1. Get a [GitHub Token](https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token)
93 | 1. Run `./azure-deploy.sh`. Please note that if this is the first time you run it, it will create an `.env` file in the root folder. Fill the `.env` file. Run the `./azure-deploy.sh` again.
94 | 1. Once the deployment is done go to the Azure portal, and open the Azure Static Web App resource just created.
95 | 1. Open the "Configuration" pane and add a new environment variable named `DATABASE_URL` and assign the value of the database connection string mentioned before in the local development section.
96 | 1. Done! Well, not really, read next.
97 |
98 | ### Azure Static Web App Free Tier
99 |
100 | Azure Static Web App support a Free tier...which is absolutely great so that you can try them for free, but of course don't expect great performances. REST API response will be in the 500 msec area. Keep this in mind if you are planning to use them for something different than testing. If you need better performance right now and cannot when for when Azure Static Web App will be out of preview, you can always deploy the REST API using plain Azure Functions where you can have amazing scalability and performance.
101 |
--------------------------------------------------------------------------------
/api/.env.template:
--------------------------------------------------------------------------------
1 | db_server=".database.windows.net"
2 | db_user="webapp"
3 | db_password="Super_Str0ng*P@ZZword!"
4 | db_database=""
--------------------------------------------------------------------------------
/api/.funcignore:
--------------------------------------------------------------------------------
1 | *.js.map
2 | *.ts
3 | .git*
4 | .vscode
5 | local.settings.json
6 | test
7 | tsconfig.json
--------------------------------------------------------------------------------
/api/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 |
24 | # nyc test coverage
25 | .nyc_output
26 |
27 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
28 | .grunt
29 |
30 | # Bower dependency directory (https://bower.io/)
31 | bower_components
32 |
33 | # node-waf configuration
34 | .lock-wscript
35 |
36 | # Compiled binary addons (https://nodejs.org/api/addons.html)
37 | build/Release
38 |
39 | # Dependency directories
40 | node_modules/
41 | jspm_packages/
42 |
43 | # TypeScript v1 declaration files
44 | typings/
45 |
46 | # Optional npm cache directory
47 | .npm
48 |
49 | # Optional eslint cache
50 | .eslintcache
51 |
52 | # Optional REPL history
53 | .node_repl_history
54 |
55 | # Output of 'npm pack'
56 | *.tgz
57 |
58 | # Yarn Integrity file
59 | .yarn-integrity
60 |
61 | # dotenv environment variables file
62 | .env
63 | .env.test
64 |
65 | # parcel-bundler cache (https://parceljs.org/)
66 | .cache
67 |
68 | # next.js build output
69 | .next
70 |
71 | # nuxt.js build output
72 | .nuxt
73 |
74 | # vuepress build output
75 | .vuepress/dist
76 |
77 | # Serverless directories
78 | .serverless/
79 |
80 | # FuseBox cache
81 | .fusebox/
82 |
83 | # DynamoDB Local files
84 | .dynamodb/
85 |
86 | # TypeScript output
87 | dist
88 | out
89 |
90 | # Azure Functions artifacts
91 | bin
92 | obj
93 | appsettings.json
94 | local.settings.json
95 | .env
--------------------------------------------------------------------------------
/api/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "ms-azuretools.vscode-azurefunctions"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/api/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Attach to Node Functions",
6 | "type": "node",
7 | "request": "attach",
8 | "port": 9229,
9 | "preLaunchTask": "func: host start"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/api/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "azureFunctions.deploySubpath": ".",
3 | "azureFunctions.postDeployTask": "npm install",
4 | "azureFunctions.projectLanguage": "JavaScript",
5 | "azureFunctions.projectRuntime": "~2",
6 | "debug.internalConsoleOptions": "neverOpen",
7 | "azureFunctions.preDeployTask": "npm prune"
8 | }
--------------------------------------------------------------------------------
/api/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "func",
6 | "command": "host start",
7 | "problemMatcher": "$func-watch",
8 | "isBackground": true,
9 | "dependsOn": "npm install"
10 | },
11 | {
12 | "type": "shell",
13 | "label": "npm install",
14 | "command": "npm install"
15 | },
16 | {
17 | "type": "shell",
18 | "label": "npm prune",
19 | "command": "npm prune --production",
20 | "problemMatcher": []
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/api/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "logging": {
4 | "applicationInsights": {
5 | "samplingSettings": {
6 | "isEnabled": true,
7 | "excludedTypes": "Request"
8 | }
9 | }
10 | },
11 | "extensionBundle": {
12 | "id": "Microsoft.Azure.Functions.ExtensionBundle",
13 | "version": "[1.*, 3.0.0)"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/api/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "requires": true,
3 | "lockfileVersion": 1,
4 | "dependencies": {
5 | "@azure/ms-rest-azure-env": {
6 | "version": "1.1.2",
7 | "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-1.1.2.tgz",
8 | "integrity": "sha512-l7z0DPCi2Hp88w12JhDTtx5d0Y3+vhfE7JKJb9O7sEz71Cwp053N8piTtTnnk/tUor9oZHgEKi/p3tQQmLPjvA=="
9 | },
10 | "@azure/ms-rest-js": {
11 | "version": "1.8.15",
12 | "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.8.15.tgz",
13 | "integrity": "sha512-kIB71V3DcrA4iysBbOsYcxd4WWlOE7OFtCUYNfflPODM0lbIR23A236QeTn5iAeYwcHmMjR/TAKp5KQQh/WqoQ==",
14 | "requires": {
15 | "@types/tunnel": "0.0.0",
16 | "axios": "^0.19.0",
17 | "form-data": "^2.3.2",
18 | "tough-cookie": "^2.4.3",
19 | "tslib": "^1.9.2",
20 | "tunnel": "0.0.6",
21 | "uuid": "^3.2.1",
22 | "xml2js": "^0.4.19"
23 | }
24 | },
25 | "@azure/ms-rest-nodeauth": {
26 | "version": "2.0.2",
27 | "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-2.0.2.tgz",
28 | "integrity": "sha512-KmNNICOxt3EwViAJI3iu2VH8t8BQg5J2rSAyO4IUYLF9ZwlyYsP419pdvl4NBUhluAP2cgN7dfD2V6E6NOMZlQ==",
29 | "requires": {
30 | "@azure/ms-rest-azure-env": "^1.1.2",
31 | "@azure/ms-rest-js": "^1.8.7",
32 | "adal-node": "^0.1.28"
33 | }
34 | },
35 | "@js-joda/core": {
36 | "version": "2.0.0",
37 | "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-2.0.0.tgz",
38 | "integrity": "sha512-OWm/xa9O9e4ugzNHoRT3IsXZZYfaV6Ia1aRwctOmCQ2GYWMnhKBzMC1WomqCh/oGxEZKNtPy5xv5//VIAOgMqw=="
39 | },
40 | "@types/node": {
41 | "version": "14.0.27",
42 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz",
43 | "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g=="
44 | },
45 | "@types/tunnel": {
46 | "version": "0.0.0",
47 | "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.0.tgz",
48 | "integrity": "sha512-FGDp0iBRiBdPjOgjJmn1NH0KDLN+Z8fRmo+9J7XGBhubq1DPrGrbmG4UTlGzrpbCpesMqD0sWkzi27EYkOMHyg==",
49 | "requires": {
50 | "@types/node": "*"
51 | }
52 | },
53 | "adal-node": {
54 | "version": "0.1.28",
55 | "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz",
56 | "integrity": "sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU=",
57 | "requires": {
58 | "@types/node": "^8.0.47",
59 | "async": ">=0.6.0",
60 | "date-utils": "*",
61 | "jws": "3.x.x",
62 | "request": ">= 2.52.0",
63 | "underscore": ">= 1.3.1",
64 | "uuid": "^3.1.0",
65 | "xmldom": ">= 0.1.x",
66 | "xpath.js": "~1.1.0"
67 | },
68 | "dependencies": {
69 | "@types/node": {
70 | "version": "8.10.62",
71 | "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.62.tgz",
72 | "integrity": "sha512-76fupxOYVxk36kb7O/6KtrAPZ9jnSK3+qisAX4tQMEuGNdlvl7ycwatlHqjoE6jHfVtXFM3pCrCixZOidc5cuw=="
73 | }
74 | }
75 | },
76 | "ajv": {
77 | "version": "6.12.3",
78 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
79 | "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
80 | "requires": {
81 | "fast-deep-equal": "^3.1.1",
82 | "fast-json-stable-stringify": "^2.0.0",
83 | "json-schema-traverse": "^0.4.1",
84 | "uri-js": "^4.2.2"
85 | }
86 | },
87 | "asn1": {
88 | "version": "0.2.4",
89 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
90 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
91 | "requires": {
92 | "safer-buffer": "~2.1.0"
93 | }
94 | },
95 | "assert-plus": {
96 | "version": "1.0.0",
97 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
98 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
99 | },
100 | "async": {
101 | "version": "3.2.0",
102 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
103 | "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
104 | },
105 | "asynckit": {
106 | "version": "0.4.0",
107 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
108 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
109 | },
110 | "aws-sign2": {
111 | "version": "0.7.0",
112 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
113 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
114 | },
115 | "aws4": {
116 | "version": "1.10.0",
117 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
118 | "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
119 | },
120 | "axios": {
121 | "version": "0.19.2",
122 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
123 | "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
124 | "requires": {
125 | "follow-redirects": "1.5.10"
126 | }
127 | },
128 | "bcrypt-pbkdf": {
129 | "version": "1.0.2",
130 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
131 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
132 | "requires": {
133 | "tweetnacl": "^0.14.3"
134 | }
135 | },
136 | "bl": {
137 | "version": "3.0.1",
138 | "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz",
139 | "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==",
140 | "requires": {
141 | "readable-stream": "^3.0.1"
142 | }
143 | },
144 | "buffer-equal-constant-time": {
145 | "version": "1.0.1",
146 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
147 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
148 | },
149 | "caseless": {
150 | "version": "0.12.0",
151 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
152 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
153 | },
154 | "combined-stream": {
155 | "version": "1.0.8",
156 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
157 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
158 | "requires": {
159 | "delayed-stream": "~1.0.0"
160 | }
161 | },
162 | "core-util-is": {
163 | "version": "1.0.2",
164 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
165 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
166 | },
167 | "dashdash": {
168 | "version": "1.14.1",
169 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
170 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
171 | "requires": {
172 | "assert-plus": "^1.0.0"
173 | }
174 | },
175 | "date-utils": {
176 | "version": "1.2.21",
177 | "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz",
178 | "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q="
179 | },
180 | "debug": {
181 | "version": "3.1.0",
182 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
183 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
184 | "requires": {
185 | "ms": "2.0.0"
186 | }
187 | },
188 | "delayed-stream": {
189 | "version": "1.0.0",
190 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
191 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
192 | },
193 | "depd": {
194 | "version": "2.0.0",
195 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
196 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
197 | },
198 | "dotenv": {
199 | "version": "8.2.0",
200 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
201 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
202 | },
203 | "ecc-jsbn": {
204 | "version": "0.1.2",
205 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
206 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
207 | "requires": {
208 | "jsbn": "~0.1.0",
209 | "safer-buffer": "^2.1.0"
210 | }
211 | },
212 | "ecdsa-sig-formatter": {
213 | "version": "1.0.11",
214 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
215 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
216 | "requires": {
217 | "safe-buffer": "^5.0.1"
218 | }
219 | },
220 | "extend": {
221 | "version": "3.0.2",
222 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
223 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
224 | },
225 | "extsprintf": {
226 | "version": "1.3.0",
227 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
228 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
229 | },
230 | "fast-deep-equal": {
231 | "version": "3.1.3",
232 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
233 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
234 | },
235 | "fast-json-stable-stringify": {
236 | "version": "2.1.0",
237 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
238 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
239 | },
240 | "follow-redirects": {
241 | "version": "1.5.10",
242 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
243 | "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
244 | "requires": {
245 | "debug": "=3.1.0"
246 | }
247 | },
248 | "forever-agent": {
249 | "version": "0.6.1",
250 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
251 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
252 | },
253 | "form-data": {
254 | "version": "2.5.1",
255 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
256 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
257 | "requires": {
258 | "asynckit": "^0.4.0",
259 | "combined-stream": "^1.0.6",
260 | "mime-types": "^2.1.12"
261 | }
262 | },
263 | "getpass": {
264 | "version": "0.1.7",
265 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
266 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
267 | "requires": {
268 | "assert-plus": "^1.0.0"
269 | }
270 | },
271 | "har-schema": {
272 | "version": "2.0.0",
273 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
274 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
275 | },
276 | "har-validator": {
277 | "version": "5.1.5",
278 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
279 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
280 | "requires": {
281 | "ajv": "^6.12.3",
282 | "har-schema": "^2.0.0"
283 | }
284 | },
285 | "http-signature": {
286 | "version": "1.2.0",
287 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
288 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
289 | "requires": {
290 | "assert-plus": "^1.0.0",
291 | "jsprim": "^1.2.2",
292 | "sshpk": "^1.7.0"
293 | }
294 | },
295 | "iconv-lite": {
296 | "version": "0.5.2",
297 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
298 | "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
299 | "requires": {
300 | "safer-buffer": ">= 2.1.2 < 3"
301 | }
302 | },
303 | "inherits": {
304 | "version": "2.0.4",
305 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
306 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
307 | },
308 | "is-typedarray": {
309 | "version": "1.0.0",
310 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
311 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
312 | },
313 | "isstream": {
314 | "version": "0.1.2",
315 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
316 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
317 | },
318 | "jsbi": {
319 | "version": "3.1.3",
320 | "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.3.tgz",
321 | "integrity": "sha512-nBJqA0C6Qns+ZxurbEoIR56wyjiUszpNy70FHvxO5ervMoCbZVE3z3kxr5nKGhlxr/9MhKTSUBs7cAwwuf3g9w=="
322 | },
323 | "jsbn": {
324 | "version": "0.1.1",
325 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
326 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
327 | },
328 | "json-schema": {
329 | "version": "0.2.3",
330 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
331 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
332 | },
333 | "json-schema-traverse": {
334 | "version": "0.4.1",
335 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
336 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
337 | },
338 | "json-stringify-safe": {
339 | "version": "5.0.1",
340 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
341 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
342 | },
343 | "jsprim": {
344 | "version": "1.4.1",
345 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
346 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
347 | "requires": {
348 | "assert-plus": "1.0.0",
349 | "extsprintf": "1.3.0",
350 | "json-schema": "0.2.3",
351 | "verror": "1.10.0"
352 | }
353 | },
354 | "jwa": {
355 | "version": "1.4.1",
356 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
357 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
358 | "requires": {
359 | "buffer-equal-constant-time": "1.0.1",
360 | "ecdsa-sig-formatter": "1.0.11",
361 | "safe-buffer": "^5.0.1"
362 | }
363 | },
364 | "jws": {
365 | "version": "3.2.2",
366 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
367 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
368 | "requires": {
369 | "jwa": "^1.4.1",
370 | "safe-buffer": "^5.0.1"
371 | }
372 | },
373 | "mime-db": {
374 | "version": "1.44.0",
375 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
376 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
377 | },
378 | "mime-types": {
379 | "version": "2.1.27",
380 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
381 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
382 | "requires": {
383 | "mime-db": "1.44.0"
384 | }
385 | },
386 | "ms": {
387 | "version": "2.0.0",
388 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
389 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
390 | },
391 | "native-duplexpair": {
392 | "version": "1.0.0",
393 | "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz",
394 | "integrity": "sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A="
395 | },
396 | "oauth-sign": {
397 | "version": "0.9.0",
398 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
399 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
400 | },
401 | "performance-now": {
402 | "version": "2.1.0",
403 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
404 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
405 | },
406 | "psl": {
407 | "version": "1.8.0",
408 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
409 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
410 | },
411 | "punycode": {
412 | "version": "2.1.1",
413 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
414 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
415 | },
416 | "qs": {
417 | "version": "6.5.2",
418 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
419 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
420 | },
421 | "readable-stream": {
422 | "version": "3.6.0",
423 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
424 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
425 | "requires": {
426 | "inherits": "^2.0.3",
427 | "string_decoder": "^1.1.1",
428 | "util-deprecate": "^1.0.1"
429 | }
430 | },
431 | "request": {
432 | "version": "2.88.2",
433 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
434 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
435 | "requires": {
436 | "aws-sign2": "~0.7.0",
437 | "aws4": "^1.8.0",
438 | "caseless": "~0.12.0",
439 | "combined-stream": "~1.0.6",
440 | "extend": "~3.0.2",
441 | "forever-agent": "~0.6.1",
442 | "form-data": "~2.3.2",
443 | "har-validator": "~5.1.3",
444 | "http-signature": "~1.2.0",
445 | "is-typedarray": "~1.0.0",
446 | "isstream": "~0.1.2",
447 | "json-stringify-safe": "~5.0.1",
448 | "mime-types": "~2.1.19",
449 | "oauth-sign": "~0.9.0",
450 | "performance-now": "^2.1.0",
451 | "qs": "~6.5.2",
452 | "safe-buffer": "^5.1.2",
453 | "tough-cookie": "~2.5.0",
454 | "tunnel-agent": "^0.6.0",
455 | "uuid": "^3.3.2"
456 | },
457 | "dependencies": {
458 | "form-data": {
459 | "version": "2.3.3",
460 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
461 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
462 | "requires": {
463 | "asynckit": "^0.4.0",
464 | "combined-stream": "^1.0.6",
465 | "mime-types": "^2.1.12"
466 | }
467 | }
468 | }
469 | },
470 | "safe-buffer": {
471 | "version": "5.2.1",
472 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
473 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
474 | },
475 | "safer-buffer": {
476 | "version": "2.1.2",
477 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
478 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
479 | },
480 | "sax": {
481 | "version": "1.2.4",
482 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
483 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
484 | },
485 | "sprintf-js": {
486 | "version": "1.1.2",
487 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
488 | "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
489 | },
490 | "sshpk": {
491 | "version": "1.16.1",
492 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
493 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
494 | "requires": {
495 | "asn1": "~0.2.3",
496 | "assert-plus": "^1.0.0",
497 | "bcrypt-pbkdf": "^1.0.0",
498 | "dashdash": "^1.12.0",
499 | "ecc-jsbn": "~0.1.1",
500 | "getpass": "^0.1.1",
501 | "jsbn": "~0.1.0",
502 | "safer-buffer": "^2.0.2",
503 | "tweetnacl": "~0.14.0"
504 | }
505 | },
506 | "string_decoder": {
507 | "version": "1.3.0",
508 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
509 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
510 | "requires": {
511 | "safe-buffer": "~5.2.0"
512 | }
513 | },
514 | "tedious": {
515 | "version": "9.0.1",
516 | "resolved": "https://registry.npmjs.org/tedious/-/tedious-9.0.1.tgz",
517 | "integrity": "sha512-KTvcG7ByQgkuHm+m1+a2lUXDorE/ayOFUhJqwWdOnqwjZ9A9NXtwZOU6YbNWO4dkpPaZkvA22+XQ5ZEMfIRdvg==",
518 | "requires": {
519 | "@azure/ms-rest-nodeauth": "2.0.2",
520 | "@js-joda/core": "^2.0.0",
521 | "bl": "^3.0.0",
522 | "depd": "^2.0.0",
523 | "iconv-lite": "^0.5.0",
524 | "jsbi": "^3.1.1",
525 | "native-duplexpair": "^1.0.0",
526 | "punycode": "^2.1.0",
527 | "readable-stream": "^3.6.0",
528 | "sprintf-js": "^1.1.2"
529 | }
530 | },
531 | "tough-cookie": {
532 | "version": "2.5.0",
533 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
534 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
535 | "requires": {
536 | "psl": "^1.1.28",
537 | "punycode": "^2.1.1"
538 | }
539 | },
540 | "tslib": {
541 | "version": "1.13.0",
542 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
543 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
544 | },
545 | "tunnel": {
546 | "version": "0.0.6",
547 | "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
548 | "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
549 | },
550 | "tunnel-agent": {
551 | "version": "0.6.0",
552 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
553 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
554 | "requires": {
555 | "safe-buffer": "^5.0.1"
556 | }
557 | },
558 | "tweetnacl": {
559 | "version": "0.14.5",
560 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
561 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
562 | },
563 | "underscore": {
564 | "version": "1.13.1",
565 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
566 | "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
567 | },
568 | "uri-js": {
569 | "version": "4.2.2",
570 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
571 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
572 | "requires": {
573 | "punycode": "^2.1.0"
574 | }
575 | },
576 | "util-deprecate": {
577 | "version": "1.0.2",
578 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
579 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
580 | },
581 | "uuid": {
582 | "version": "3.4.0",
583 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
584 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
585 | },
586 | "verror": {
587 | "version": "1.10.0",
588 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
589 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
590 | "requires": {
591 | "assert-plus": "^1.0.0",
592 | "core-util-is": "1.0.2",
593 | "extsprintf": "^1.2.0"
594 | }
595 | },
596 | "xml2js": {
597 | "version": "0.4.23",
598 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
599 | "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
600 | "requires": {
601 | "sax": ">=0.6.0",
602 | "xmlbuilder": "~11.0.0"
603 | }
604 | },
605 | "xmlbuilder": {
606 | "version": "11.0.1",
607 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
608 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
609 | },
610 | "xmldom": {
611 | "version": "0.6.0",
612 | "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.6.0.tgz",
613 | "integrity": "sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg=="
614 | },
615 | "xpath.js": {
616 | "version": "1.1.0",
617 | "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz",
618 | "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ=="
619 | }
620 | }
621 | }
622 |
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "dotenv": "^8.2.0",
4 | "tedious": "^9.0.1"
5 | },
6 | "devDependencies": {}
7 | }
8 |
--------------------------------------------------------------------------------
/api/proxies.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/proxies",
3 | "proxies": {}
4 | }
5 |
--------------------------------------------------------------------------------
/api/shared/utils.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 |
3 | var Connection = require('tedious').Connection;
4 | var Request = require('tedious').Request
5 | var TYPES = require('tedious').TYPES;
6 |
7 | const executeSQL = (context, verb, payload) => {
8 | var result = "";
9 | const paramPayload = (payload != null) ? JSON.stringify(payload) : '';
10 | //context.log(payload);
11 |
12 | const connection = new Connection({
13 | server: process.env["db_server"],
14 | authentication: {
15 | type: 'default',
16 | options: {
17 | userName: process.env["db_user"],
18 | password: process.env["db_password"],
19 | }
20 | },
21 | options: {
22 | database: process.env["db_database"],
23 | encrypt: true
24 | }
25 | });
26 |
27 | const request = new Request(`web.${verb}_todo`, (err) => {
28 | if (err) {
29 | context.log.error(err);
30 | context.res.status = 500;
31 | context.res.body = "Error executing T-SQL command";
32 | } else {
33 | context.res = {
34 | body: result
35 | }
36 | }
37 | context.done();
38 | });
39 | request.addParameter('payload', TYPES.NVarChar, paramPayload, Infinity);
40 |
41 | request.on('row', columns => {
42 | columns.forEach(column => {
43 | result += column.value;
44 | });
45 | });
46 |
47 | connection.on('connect', err => {
48 | if (err) {
49 | context.log.error(err);
50 | context.res.status = 500;
51 | context.res.body = "Error connecting to Azure SQL query";
52 | context.done();
53 | }
54 | else {
55 | connection.callProcedure(request);
56 | }
57 | });
58 |
59 | connection.connect();
60 | }
61 |
62 | exports.executeSQL = executeSQL;
--------------------------------------------------------------------------------
/api/todo/function.json:
--------------------------------------------------------------------------------
1 | {
2 | "bindings": [
3 | {
4 | "authLevel": "anonymous",
5 | "type": "httpTrigger",
6 | "direction": "in",
7 | "name": "req",
8 | "methods": [
9 | "get", "put", "post", "delete"
10 | ],
11 | "route": "todo/{id:int?}"
12 | },
13 | {
14 | "type": "http",
15 | "direction": "out",
16 | "name": "res"
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/api/todo/index.js:
--------------------------------------------------------------------------------
1 | const { executeSQL } = require('../shared/utils');
2 |
3 | todoREST = function (context, req) {
4 | const method = req.method.toLowerCase();
5 | var payload = null;
6 |
7 | switch(method) {
8 | case "get":
9 | payload = req.params.id ? { "id": req.params.id } : null;
10 | break;
11 | case "post":
12 | payload = req.body;
13 | break;
14 | case "put":
15 | payload = {
16 | "id": req.params.id,
17 | "todo": req.body
18 | };
19 | break;
20 | case "delete":
21 | payload = { "id": req.params.id };
22 | break;
23 | }
24 |
25 | executeSQL(context, method, payload)
26 | }
27 |
28 | module.exports = todoREST;
29 |
30 |
--------------------------------------------------------------------------------
/azure-deploy.arm.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "name": {
6 | "type": "string"
7 | },
8 | "location": {
9 | "type": "string"
10 | },
11 | "repositoryUrl": {
12 | "type": "string"
13 | },
14 | "branch": {
15 | "type": "string"
16 | },
17 | "repositoryToken": {
18 | "type": "securestring"
19 | },
20 | "appLocation": {
21 | "type": "string"
22 | },
23 | "apiLocation": {
24 | "type": "string"
25 | },
26 | "db_server": {
27 | "type": "string"
28 | },
29 | "db_user": {
30 | "type": "securestring"
31 | },
32 | "db_password": {
33 | "type": "securestring"
34 | },
35 | "db_database": {
36 | "type": "securestring"
37 | }
38 |
39 | },
40 | "resources": [
41 | {
42 | "apiVersion": "2021-01-15",
43 | "name": "[parameters('name')]",
44 | "type": "Microsoft.Web/staticSites",
45 | "location": "[parameters('location')]",
46 | "properties": {
47 | "repositoryUrl": "[parameters('repositoryUrl')]",
48 | "branch": "[parameters('branch')]",
49 | "repositoryToken": "[parameters('repositoryToken')]",
50 | "buildProperties": {
51 | "appLocation": "[parameters('appLocation')]",
52 | "apiLocation": "[parameters('apiLocation')]"
53 | }
54 | },
55 | "sku": {
56 | "Tier": "Free",
57 | "Name": "Free"
58 | },
59 | "resources":[
60 | {
61 | "apiVersion": "2021-01-15",
62 | "name": "appsettings",
63 | "type": "config",
64 | "location": "[parameters('location')]",
65 | "properties": {
66 | "db_server": "[parameters('db_server')]",
67 | "db_user": "[parameters('db_server')]",
68 | "db_password": "[parameters('db_server')]",
69 | "db_database": "[parameters('db_server')]"
70 | },
71 | "dependsOn": [
72 | "[resourceId('Microsoft.Web/staticSites', parameters('name'))]"
73 | ]
74 | }
75 | ]
76 | }
77 | ]
78 | }
--------------------------------------------------------------------------------
/azure-deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euo pipefail
3 |
4 | # Load values from .env file or create it if it doesn't exists
5 | FILE=".env"
6 | if [[ -f $FILE ]]; then
7 | echo "Loading from $FILE"
8 | export $(egrep "^[^#;]" $FILE | xargs -n1)
9 | else
10 | cat << EOF > .env
11 | resourceGroup=""
12 | appName=""
13 | location=""
14 |
15 | gitSource="https://github.com/Azure-Samples/azure-sql-db-todo-mvc.git"
16 | gitToken=""
17 | EOF
18 | echo "Enviroment file not detected."
19 | echo "Please configure values for your environment in the created .env file"
20 | echo "and run the script again."
21 | exit 1
22 | fi
23 |
24 | FILE="./api/.env"
25 | echo "Loading from $FILE"
26 | export $(egrep "^[^#;]" $FILE | xargs -n1)
27 |
28 | echo "Creating Resource Group...";
29 | az group create \
30 | -n $resourceGroup \
31 | -l $location
32 |
33 | echo "Deploying Static Web App...";
34 | az deployment group create \
35 | --name ToDoMVC-SWA \
36 | --resource-group $resourceGroup \
37 | --template-file azure-deploy.arm.json \
38 | --parameters \
39 | name=$appName \
40 | location=$location \
41 | repositoryToken=$gitToken \
42 | repositoryUrl=$gitSource \
43 | branch=main \
44 | appLocation="./client" \
45 | apiLocation="./api" \
46 | db_server=$db_server \
47 | db_user=$db_user \
48 | db_password=$db_password \
49 | db_database=$db_database
50 |
51 | echo "Getting Static Web App...";
52 | dhn=`az staticwebapp show -g $resourceGroup -n $appName --query "defaultHostname"`
53 | echo "Static Web App created at: $dhn";
54 |
55 | echo "Done."
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoMVC
6 |
7 |
8 |
13 |
14 |
15 |
16 |
58 |
64 |
65 |
234 |
235 |
236 |
--------------------------------------------------------------------------------
/database/create.sql:
--------------------------------------------------------------------------------
1 | create schema [web];
2 | go
3 |
4 | create user [webapp] with password = 'Super_Str0ng*P@ZZword!'
5 | go
6 |
7 | grant execute on schema::[web] to [webapp]
8 | go
9 |
10 | create sequence dbo.[global_sequence]
11 | as int
12 | start with 1
13 | increment by 1;
14 | go
15 |
16 | create table dbo.todos
17 | (
18 | id int not null primary key default (next value for [global_sequence]),
19 | todo nvarchar(100) not null,
20 | completed tinyint not null default (0)
21 | )
22 | go
23 |
24 | insert into dbo.[todos] (todo)
25 | values ('azure function nodejs sample')
26 | go
27 |
28 | /*
29 | GET
30 | Accepted Input:
31 | ''
32 | '[{"id":1}, {"id":2}]'
33 | */
34 | create or alter procedure [web].[get_todo]
35 | @payload nvarchar(max) = null
36 | as
37 |
38 | -- return all
39 | if (@payload = '' or @payload is null) begin
40 | select
41 | cast(
42 | (select
43 | id,
44 | todo as title,
45 | completed as completed
46 | from
47 | dbo.todos t
48 | for json path)
49 | as nvarchar(max)) as result;
50 | return ;
51 | end
52 |
53 | -- return the specified todos
54 | if (isjson(@payload) <> 1) begin
55 | throw 50000, 'Payload is not a valid JSON document', 16;
56 | end
57 |
58 | select
59 | cast(
60 | (select
61 | id,
62 | todo as title,
63 | completed as completed
64 | from
65 | dbo.todos t
66 | where
67 | exists (select p.id from openjson(@payload) with (id int) as p where p.id = t.id)
68 | for json path)
69 | as nvarchar(max)) as result
70 | go
71 |
72 | /*
73 | POST
74 | Accepted Input:
75 | '[{"id":1, "title":"todo title", "completed": 0}, {"id":2, "title": "another todo"}]'
76 | */
77 | create or alter procedure [web].[post_todo]
78 | @payload nvarchar(max)
79 | as
80 | if (isjson(@payload) != 1) begin
81 | throw 50000, 'Payload is not a valid JSON document', 16;
82 | end
83 |
84 | declare @ids table (id int not null);
85 |
86 | insert into dbo.todos ([todo], [completed])
87 | output inserted.id into @ids
88 | select [title], isnull([completed],0) from openjson(@payload) with
89 | (
90 | title nvarchar(100),
91 | completed bit
92 | )
93 |
94 | declare @newPayload as nvarchar(max) = (select id from @ids for json auto);
95 | exec [web].[get_todo] @newPayload
96 | go
97 |
98 | /*
99 | DELETE
100 | Accepted Input:
101 | '[{"id":1}, {"id":2}]'
102 | */
103 | create or alter procedure [web].[delete_todo]
104 | @payload nvarchar(max)
105 | as
106 | if (isjson(@payload) != 1) begin
107 | throw 50000, 'Payload is not a valid JSON document', 16;
108 | end
109 |
110 | delete t from dbo.todos t
111 | where exists (select p.id from openjson(@payload) with (id int) as p where p.id = t.id)
112 | go
113 |
114 | /*
115 | PUT
116 | Accepted Input:
117 | '[{"id":1, "todo":{"id": 10, "title": "updated title", "completed": 1 },{...}]'
118 | */
119 | create or alter procedure [web].[put_todo]
120 | @payload nvarchar(max)
121 | as
122 | if (isjson(@payload) <> 1) begin
123 | throw 50000, 'Payload is not a valid JSON document', 16;
124 | end
125 |
126 | declare @ids table (id int not null);
127 |
128 | with cte as
129 | (
130 | select
131 | id,
132 | new_id,
133 | title,
134 | completed
135 | from
136 | openjson(@payload) with
137 | (
138 | id int '$.id',
139 | todo nvarchar(max) as json
140 | )
141 | cross apply openjson(todo) with
142 | (
143 | new_id int '$.id',
144 | title nvarchar(100),
145 | completed bit
146 | )
147 | )
148 | update
149 | t
150 | set
151 | id = coalesce(c.new_id, t.id),
152 | todo = coalesce(c.title, t.todo),
153 | completed = coalesce(c.completed, t.completed)
154 | output
155 | inserted.id into @ids
156 | from
157 | dbo.[todos] t
158 | inner join
159 | cte c on t.id = c.id
160 | ;
161 |
162 | declare @newPayload as nvarchar(max) = (select id from @ids for json auto);
163 | exec [web].[get_todo] @newPayload
164 | go
--------------------------------------------------------------------------------