-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
284 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,7 @@ | |
"direction": "in", | ||
"name": "req", | ||
"methods": [ | ||
"get", | ||
"post" | ||
"get" | ||
] | ||
}, | ||
{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import Ajv from 'ajv'; | ||
import addFormats from 'ajv-formats'; | ||
|
||
const flightPlanSchema = { | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"type": "object", | ||
"properties": { | ||
"id": { | ||
"type": "string" | ||
}, | ||
"departureTime": { | ||
"type": "string", | ||
"format": "date-time" | ||
}, | ||
"aircraft": { | ||
"type": "string" | ||
}, | ||
"aircraftType": { | ||
"type": "string" | ||
}, | ||
"segments": { | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"route": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
}, | ||
"minItems": 2 | ||
}, | ||
"altitude": { | ||
"type": "number", | ||
"minimum": 1, | ||
"maximum": 60000 | ||
}, | ||
"remarks": { | ||
"type": "string" | ||
} | ||
}, | ||
"required": ["route", "altitude"] | ||
} | ||
} | ||
}, | ||
"required": ["departureTime", "aircraft", "aircraftType", "segments"] | ||
}; | ||
|
||
export interface ISegment { | ||
route: string[]; | ||
altitude: number; | ||
remarks?: string; | ||
} | ||
|
||
export interface IFlightPlan { | ||
id?: string; | ||
departureTime: string; | ||
aircraft: string; | ||
aircraftType: string; | ||
segments: ISegment[]; | ||
} | ||
|
||
export const validateFlightPlan = (flightPlan: IFlightPlan) => { | ||
const ajv = new Ajv(); | ||
addFormats(ajv); | ||
const validateFlightPlanSchema = ajv.compile(flightPlanSchema); | ||
if (!validateFlightPlanSchema(flightPlan)) { | ||
throw new Error(`Schema validation failed: ${validateFlightPlanSchema.errors}`); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"bindings": [ | ||
{ | ||
"authLevel": "function", | ||
"type": "httpTrigger", | ||
"direction": "in", | ||
"name": "req", | ||
"methods": [ | ||
"get", | ||
"post", | ||
"put" | ||
], | ||
"route": "flightplan/{id?}" | ||
}, | ||
{ | ||
"type": "http", | ||
"direction": "out", | ||
"name": "res" | ||
} | ||
], | ||
"scriptFile": "../dist/flightplan/index.js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { AzureFunction, Context, HttpRequest } from "@azure/functions" | ||
import { v4 as uuidv4 } from 'uuid'; | ||
import BlobStorage from "../shared/blob-storage"; | ||
import { validateFlightPlan } from "./flightplan"; | ||
|
||
const makeBlobKey = (id: string) => `${id}.json`; | ||
|
||
const flightPlanHttpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> { | ||
const blobStorage = new BlobStorage(process.env.BLOB_STORAGE_CONNECTION!, 'flightplans'); | ||
|
||
switch (req.method) { | ||
case "POST": | ||
{ | ||
let flightPlanString: string; | ||
try { | ||
const flightPlan = req.body; | ||
validateFlightPlan(flightPlan); | ||
flightPlan.id = uuidv4(); | ||
flightPlanString = JSON.stringify(flightPlan, null, 2); | ||
blobStorage.writeBlob(makeBlobKey(flightPlan.id), flightPlanString); | ||
} catch(err) { | ||
context.res = { | ||
status: 400, | ||
body: `Invalid flight plan: ${err}`, | ||
}; | ||
return; | ||
} | ||
context.res = { | ||
status: 201, | ||
body: flightPlanString, | ||
}; | ||
} | ||
break; | ||
case "PUT": | ||
{ | ||
let flightPlanBlobKeys: string[]; | ||
try { | ||
flightPlanBlobKeys = await blobStorage.listBlobs(); | ||
} catch(err) { | ||
context.res = { | ||
status: 500, | ||
body: `Error listing flight plans`, | ||
}; | ||
return; | ||
} | ||
if (!flightPlanBlobKeys.includes(makeBlobKey(context.bindingData.id))) { | ||
context.res = { | ||
status: 404, | ||
body: `Unknown flight plan ${context.bindingData.id}`, | ||
}; | ||
return; | ||
} | ||
let flightPlanString: string; | ||
try { | ||
const flightPlan = req.body; | ||
validateFlightPlan(flightPlan); | ||
flightPlan.id = context.bindingData.id; | ||
flightPlanString = JSON.stringify(flightPlan, null, 2); | ||
blobStorage.writeBlob(makeBlobKey(flightPlan.id), flightPlanString); | ||
} catch(err) { | ||
context.res = { | ||
status: 400, | ||
body: `Invalid flight plan: ${err}`, | ||
}; | ||
return; | ||
} | ||
context.res = { | ||
status: 200, | ||
body: flightPlanString, | ||
}; | ||
} | ||
break; | ||
case "GET": | ||
{ | ||
if (context.bindingData.id) { | ||
let flightPlanString: string; | ||
try { | ||
flightPlanString = await blobStorage.readBlob(makeBlobKey(context.bindingData.id)); | ||
} catch(err) { | ||
context.res = { | ||
status: 404, | ||
body: `Unknown flight plan ${context.bindingData.id}`, | ||
}; | ||
return; | ||
} | ||
context.res = { | ||
status: 200, | ||
body: flightPlanString | ||
}; | ||
} else { | ||
let flightPlanBlobKeys: string[]; | ||
try { | ||
flightPlanBlobKeys = await blobStorage.listBlobs(); | ||
} catch(err) { | ||
context.res = { | ||
status: 500, | ||
body: `Error listing flight plans`, | ||
}; | ||
return; | ||
} | ||
let flightPlans: string[] = []; | ||
for (let flightPlanBlobKey of flightPlanBlobKeys) { | ||
try { | ||
flightPlans.push(await blobStorage.readBlob(flightPlanBlobKey)); | ||
} catch(err) { | ||
context.res = { | ||
status: 500, | ||
body: `Error reading flight plan ${flightPlanBlobKey}`, | ||
}; | ||
return; | ||
} | ||
} | ||
context.res = { | ||
status: 200, | ||
body: `[${flightPlans.join(',\n')}]` | ||
}; | ||
} | ||
} | ||
break; | ||
default: | ||
context.res = { | ||
status: 400, | ||
body: "Invalid HTTP method", | ||
}; | ||
break; | ||
} | ||
}; | ||
|
||
export default flightPlanHttpTrigger; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { BlobServiceClient, ContainerClient, BlobClient } from '@azure/storage-blob'; | ||
|
||
class BlobStorage { | ||
private containerClient: ContainerClient; | ||
|
||
constructor(connectionString: string, containerName: string) { | ||
const serviceClient = BlobServiceClient.fromConnectionString(connectionString); | ||
this.containerClient = serviceClient.getContainerClient(containerName); | ||
} | ||
|
||
async listBlobs(): Promise<string[]> { | ||
let blobNames: string[] = []; | ||
for await (const blob of this.containerClient.listBlobsFlat()) { | ||
blobNames.push(blob.name); | ||
} | ||
return blobNames; | ||
} | ||
|
||
async readBlob(key: string): Promise<string> { | ||
const blobClient = this.containerClient.getBlobClient(key); | ||
const downloadBlockBlobResponse = await blobClient.download(); | ||
if (!downloadBlockBlobResponse.readableStreamBody) { | ||
throw new Error('No blob stream found'); | ||
} | ||
const content = await this.streamToString(downloadBlockBlobResponse.readableStreamBody); | ||
return content; | ||
} | ||
|
||
async writeBlob(key: string, value: string): Promise<void> { | ||
const blobClient = this.containerClient.getBlockBlobClient(key); | ||
await blobClient.upload(value, value.length); | ||
} | ||
|
||
async deleteBlob(key: string): Promise<void> { | ||
const blobClient = this.containerClient.getBlobClient(key); | ||
await blobClient.delete(); | ||
} | ||
|
||
private async streamToString(readableStream: NodeJS.ReadableStream): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
const chunks: string[] = []; | ||
readableStream.on('data', (data) => { | ||
chunks.push(data.toString()); | ||
}); | ||
readableStream.on('end', () => { | ||
resolve(chunks.join('')); | ||
}); | ||
readableStream.on('error', reject); | ||
}); | ||
} | ||
} | ||
|
||
export default BlobStorage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,7 @@ | |
"direction": "in", | ||
"name": "req", | ||
"methods": [ | ||
"get", | ||
"post" | ||
"get" | ||
] | ||
}, | ||
{ | ||
|