Skip to content

Commit

Permalink
feat: support '--data-api-location' in 'swa init' (#735)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjk7989 authored Jul 27, 2023
1 parent eb125dd commit 1fff64f
Show file tree
Hide file tree
Showing 14 changed files with 221 additions and 5 deletions.
3 changes: 2 additions & 1 deletion e2e/detect.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe("framework detection", () => {
- samples/api/node (Node.js)
- samples/api/node-ts (Node.js, TypeScript)
- samples/api/python (Python)
Detected app folders (62):
Detected app folders (63):
- samples/app/angular (Angular)
- samples/app/angular-scully (Angular, Scully)
- samples/app/astro (Astro)
Expand Down Expand Up @@ -106,6 +106,7 @@ describe("framework detection", () => {
- samples/app/vuepress/docs (VuePress)
- samples/app/wintersmith (Wintersmith)
- samples/app/zola (Zola)
- samples/db/static-mssql/src (Static HTML)
- samples/ssr/angular-universal (Angular)
- samples/ssr/nextjs (React, Next.js)
- samples/ssr/remix (Remix)
Expand Down
2 changes: 2 additions & 0 deletions e2e/samples/db/static-mssql/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
/test-results/
3 changes: 3 additions & 0 deletions e2e/samples/db/static-mssql/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["ms-azuretools.vscode-azurefunctions"]
}
12 changes: 12 additions & 0 deletions e2e/samples/db/static-mssql/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Node Functions",
"type": "node",
"request": "attach",
"port": 9229,
"preLaunchTask": "func: host start"
}
]
}
8 changes: 8 additions & 0 deletions e2e/samples/db/static-mssql/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"azureFunctions.deploySubpath": "api",
"azureFunctions.postDeployTask": "npm install (functions)",
"azureFunctions.projectLanguage": "JavaScript",
"azureFunctions.projectRuntime": "~4",
"debug.internalConsoleOptions": "neverOpen",
"azureFunctions.preDeployTask": "npm prune (functions)"
}
33 changes: 33 additions & 0 deletions e2e/samples/db/static-mssql/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "func",
"label": "func: host start",
"command": "host start",
"problemMatcher": "$func-node-watch",
"isBackground": true,
"dependsOn": "npm install (functions)",
"options": {
"cwd": "${workspaceFolder}/api"
}
},
{
"type": "shell",
"label": "npm install (functions)",
"command": "npm install",
"options": {
"cwd": "${workspaceFolder}/api"
}
},
{
"type": "shell",
"label": "npm prune (functions)",
"command": "npm prune --production",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/api"
}
}
]
}
7 changes: 7 additions & 0 deletions e2e/samples/db/static-mssql/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "vanilla-basic",
"version": "1.0.0",
"scripts": {
"start": "sirv ./src public --cors --single --no-clear --port 8000"
}
}
7 changes: 7 additions & 0 deletions e2e/samples/db/static-mssql/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Vanilla JavaScript App

[Azure Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/overview) allows you to easily build JavaScript apps in minutes. Use this repo with the [quickstart](https://docs.microsoft.com/azure/static-web-apps/getting-started?tabs=vanilla-javascript) to build and customize a new static site.

This repo is used as a starter for a _very basic_ HTML web application using no front-end frameworks.

This repo has a dev container. This means if you open it inside a [GitHub Codespace](https://github.com/features/codespaces), or using [VS Code with the remote containers extension](https://code.visualstudio.com/docs/remote/containers), it will be opened inside a container with all the dependencies already installed.
34 changes: 34 additions & 0 deletions e2e/samples/db/static-mssql/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Static Web Apps Database Connections</title>
</head>

<body>
<div id="result"></div>
<script>
async function list() {
const endpoint = '/data-api/rest/Person';
const response = await fetch(endpoint);
const data = await response.json();
console.table(data.value);
// get the result div element
const result = document.getElementById('result');
// create a pre element to show the formatted data
const pre = document.createElement('pre');
// use JSON.stringify to format the data with indentation and line breaks
pre.textContent = JSON.stringify(data.value, null, 2);
// append the pre element to the result div
result.appendChild(pre);
}

// call the list function when the window loads
window.onload = list;
</script>
</body>

</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"$schema": "https://github.com/Azure/data-api-builder/releases/latest/download/dab.draft.schema.json",
"data-source": {
"database-type": "mssql",
"options": {
"set-session-context": false
},
"connection-string": "@env('DATABASE_CONNECTION_STRING')"
},
"runtime": {
"rest": {
"enabled": true,
"path": "/rest"
},
"graphql": {
"allow-introspection": true,
"enabled": true,
"path": "/graphql"
},
"host": {
"mode": "production",
"cors": {
"origins": ["http://localhost:4280"],
"allow-credentials": false
},
"authentication": {
"provider": "StaticWebApps"
}
}
},
"entities": {
"Person": {
"source": "dbo.MyTestPersonTable",
"permissions": [
{
"actions": ["*"],
"role": "anonymous"
}
]
}
}
}
29 changes: 27 additions & 2 deletions src/cli/commands/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
swaCliConfigFilename,
writeConfigFile,
} from "../../../core/utils";
import { detectProjectFolders, generateConfiguration, isDescendantPath } from "../../../core/frameworks";
import { detectDbConfigFiles, detectProjectFolders, generateConfiguration, isDescendantPath } from "../../../core/frameworks";
import { getChoicesForApiLanguage } from "../../../core/functions-versions";

export async function init(options: SWACLIConfig, showHints: boolean = true) {
Expand Down Expand Up @@ -82,9 +82,25 @@ export async function init(options: SWACLIConfig, showHints: boolean = true) {
api = detectedFolders.api[0];
}

const dbConfigFiles = await detectDbConfigFiles();
let dataApi: DetectedDbConfigFolder | undefined = dbConfigFiles[0];
if (dbConfigFiles.length > 1) {
logger.silly(`More than one (${dbConfigFiles.length}) data api folders found`);

const response = await promptOrUseDefault(disablePrompts, {
type: "select",
name: "dataApi",
message: "Which data api folder do you want to use?",
choices: dbConfigFiles.map((file) => ({ title: file.rootPath, value: file })),
initial: 0,
});

dataApi = typeof response.dataApi === "number" ? dbConfigFiles[response.dataApi] : response.dataApi;
}

let projectConfig;
try {
projectConfig = await generateConfiguration(app, api);
projectConfig = await generateConfiguration(app, api, dataApi);
} catch (error) {
logger.error(`Cannot generate your project configuration:`);
logger.error(error as Error, true);
Expand Down Expand Up @@ -138,6 +154,7 @@ function convertToCliConfig(config: FrameworkConfig): SWACLIConfig {
appLocation: config.appLocation,
apiLocation: config.apiLocation,
outputLocation: config.outputLocation,
dataApiLocation: config.dataApiLocation,
apiLanguage: config.apiLanguage,
apiVersion: config.apiVersion,
appBuildCommand: config.appBuildCommand,
Expand Down Expand Up @@ -196,6 +213,13 @@ async function promptConfigSettings(disablePrompts: boolean, detectedConfig: Fra
message: "What's your API version? (optional)",
choices: (prev) => getChoicesForApiLanguage(prev),
},
{
type: "text",
name: "dataApiLocation",
message: "What's your data API location? (optional)",
initial: detectedConfig.dataApiLocation,
format: trimValue,
},
{
type: "text",
name: "appBuildCommand",
Expand Down Expand Up @@ -244,6 +268,7 @@ function printFrameworkConfig(config: FrameworkConfig) {
logger.log(`- API location: ${chalk.green(config.apiLocation ?? "")}`);
logger.log(`- API language: ${chalk.green(config.apiLanguage ?? "")}`);
logger.log(`- API version: ${chalk.green(config.apiVersion ?? "")}`);
logger.log(`- Data API location: ${chalk.green(config.dataApiLocation ?? "")}`);
logger.log(`- App build command: ${chalk.green(config.appBuildCommand ?? "")}`);
logger.log(`- API build command: ${chalk.green(config.apiBuildCommand ?? "")}`);
logger.log(`- App dev server command: ${chalk.green(config.appDevserverCommand ?? "")}`);
Expand Down
14 changes: 13 additions & 1 deletion src/core/frameworks/detect.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { convertToUnixPaths } from "../../jest.helpers.";
import { detectProjectFolders, formatDetectedFolders, generateConfiguration } from "./detect";
import { detectDbConfigFiles, detectProjectFolders, formatDetectedFolders, generateConfiguration } from "./detect";

describe("framework detection", () => {
describe("detectProjectFolders()", () => {
Expand Down Expand Up @@ -51,5 +51,17 @@ describe("framework detection", () => {
outputLocation: ".",
});
});

it("should generate expected configuration for app static-mssql", async () => {
const { app, api } = await detectProjectFolders("e2e/samples/db/static-mssql");
const dataApi = await detectDbConfigFiles("e2e/samples/db/static-mssql");
const config = convertToUnixPaths(await generateConfiguration(app[0], api[0], dataApi[0]));
expect(config).toEqual({
dataApiLocation: "e2e/samples/db/static-mssql/swa-db-connections",
appLocation: "e2e/samples/db/static-mssql/src",
name: "Static HTML, data API: mssql",
outputLocation: ".",
});
});
});
});
27 changes: 26 additions & 1 deletion src/core/frameworks/detect.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { promises as fs } from "fs";
import globrex from "globrex";
import path from "path";
import { DATA_API_BUILDER_DEFAULT_CONFIG_FILE_NAME } from "../constants";
import { DEFAULT_CONFIG } from "../../config";
import { hasSpaces, logger, removeTrailingPathSep, safeReadFile, safeReadJson } from "../utils";
import { apiFrameworks, appFrameworks } from "./frameworks";

const packageJsonFile = "package.json";

export async function generateConfiguration(app?: DetectedFolder, api?: DetectedFolder): Promise<FrameworkConfig> {
export async function generateConfiguration(app?: DetectedFolder, api?: DetectedFolder, dataApi?: DetectedDbConfigFolder): Promise<FrameworkConfig> {
let config: FrameworkConfig = {
appLocation: DEFAULT_CONFIG.appLocation!,
outputLocation: DEFAULT_CONFIG.outputLocation!,
Expand Down Expand Up @@ -44,6 +45,12 @@ export async function generateConfiguration(app?: DetectedFolder, api?: Detected
config.apiLocation = removeTrailingPathSep(api.rootPath);
}

if (dataApi) {
name += name ? ", " : "No app frameworks detected, ";
name += `data API: ${dataApi.databaseType}`;
config.dataApiLocation = dataApi.rootPath;
}

const appRootPath = app && removeTrailingPathSep(app.rootPath);
if (appRootPath && config.appBuildCommand && appRootPath !== config.appLocation) {
// If the final app location is not the same as the detected root path of the app,
Expand Down Expand Up @@ -109,6 +116,24 @@ async function computePath(basePath: string, additionalPath?: string): Promise<s
return basePath;
}

export async function detectDbConfigFiles(projectPath: string = "."): Promise<DetectedDbConfigFolder[]> {
// Detect all the "staticwebapp.database.config.json" with valid "database-type"
const projectFiles = await getFiles(projectPath);
const dbConfigFilePaths = projectFiles.filter((f) => f.endsWith(DATA_API_BUILDER_DEFAULT_CONFIG_FILE_NAME));
let dbConfigFiles = await Promise.all(
dbConfigFilePaths.map(async (f) => {
const contains = await safeReadJson(f);
let result: DetectedDbConfigFolder = {
rootPath: path.dirname(f),
databaseType: contains?.["data-source"]?.["database-type"],
};
return result;
})
);
dbConfigFiles = dbConfigFiles.filter((f) => f.databaseType !== undefined);
return dbConfigFiles;
}

export async function detectProjectFolders(projectPath: string = "."): Promise<DetectionResult> {
const projectFiles = await getFiles(projectPath);
const apiFrameworks = await detectApiFrameworks(projectFiles);
Expand Down
5 changes: 5 additions & 0 deletions src/core/frameworks/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ declare interface DetectedFolder {
frameworks: DetectedFramework[];
}

declare interface DetectedDbConfigFolder {
rootPath: string;
databaseType: string;
}

declare interface DetectedFramework extends FrameworkDefinition {
rootPaths: string[];
}
Expand Down

0 comments on commit 1fff64f

Please sign in to comment.