Skip to content

Commit

Permalink
Feature/eng 866 integrate sites apis with cli sites commands (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
uditdc authored Jun 20, 2023
1 parent 18885e7 commit a84ddd9
Show file tree
Hide file tree
Showing 11 changed files with 374 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/commands/function/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const deleteFunction = async (data: any) => {
console.log(Chalk.yellow(`Deleting ${functionName} ...`))
console.log('')

const { data } = await consoleClient.get(`/api/modules/mine`, {})
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
const functions = data.docs ? data.docs : []

// Sort all matching functions by name and select the last matching function
Expand Down
2 changes: 1 addition & 1 deletion src/commands/function/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const deployFunction = async (functionName: string, functionData: any, options:

// Find all matching functions, warn users if they are overwriting a deployed function
try {
const { data } = await consoleClient.get(`/api/modules/mine`, {})
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
const functions = data.docs ? data.docs : []

// Sort all matching functions by name and select the last matching function
Expand Down
2 changes: 1 addition & 1 deletion src/commands/function/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { logger } from "../../lib/logger"

export const run = async () => {
try {
const { data } = await consoleClient.get(`/api/modules/mine`, {})
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
const functions = data.docs ? data.docs : []

logger.log('List of Functions:')
Expand Down
2 changes: 1 addition & 1 deletion src/commands/function/stop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const stopFunction = async (data: any) => {
console.log(Chalk.yellow(`Stopping ${functionName} ...`))
console.log('')

const { data } = await consoleClient.get(`/api/modules/mine`, {})
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
const functions = data.docs ? data.docs : []

// Sort all matching functions by name and select the last matching function
Expand Down
2 changes: 1 addition & 1 deletion src/commands/function/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const updateFunction = async (data: any) => {

// Find all matching functions, warn users if they are updating a function that is not deployed
try {
const { data } = await consoleClient.get(`/api/modules/mine`, {})
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
const functions = data.docs ? data.docs : []

// Sort all matching functions by name and select the last matching function
Expand Down
13 changes: 1 addition & 12 deletions src/commands/sites/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,4 @@ export const run = async (options: {
logger.error('Failed to build site.', error.message)
return
}
}

const dynamicImport = async (path: string) => {
console.log('dynamic import')

try {
const module = await import(path)
return module.default
} catch (err) {
return console.error(err)
}
};
}
85 changes: 85 additions & 0 deletions src/commands/sites/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import Chalk from "chalk"
import { parseBlsConfig } from "../../lib/blsConfig"
import { consoleClient } from "../../lib/http"
import { logger } from "../../lib/logger"
import { normalizeFunctionName } from "../../lib/strings"

interface DeleteCommandOptions {
target: string
}

/**
* Entry function for bls site delete
*
* @param options
*/
export const run = async (options: DeleteCommandOptions) => {
try {
if (options.target) {
await deleteSite({ name: options.target })
} else {
const { name: configName } = parseBlsConfig()
await deleteSite({ name: configName })
}
} catch (error: any) {
logger.error('Failed to delete site.', error.message)
}
}

/**
*
* @param data
* @returns
*/
const deleteSite = async (data: any) => {
const { name: functionName } = data

let matchingFunction = null
let internalFunctionId = null

// Find all matching functions, warn users if they are updating a function that is not deployed
try {
console.log(Chalk.yellow(`Deleting ${functionName} ...`))
console.log('')

const { data } = await consoleClient.get(`/api/sites?limit=999`, {})
const functions = data.docs ? data.docs : []

// Sort all matching functions by name and select the last matching function
// TODO: Ensure all functions have unique names under a user's scope
const matchingFunctions = functions.filter((f: any) =>
normalizeFunctionName(f.functionName) === normalizeFunctionName(functionName))

if (matchingFunctions && matchingFunctions.length > 0) {
matchingFunction = matchingFunctions[matchingFunctions.length - 1]
internalFunctionId = matchingFunction._id
}

// If a function does not exist, request the user to deploy that function first
if (!matchingFunction) {
throw new Error('Site not found.')
}
} catch (error: any) {
logger.error('Failed to retrive deployed sites.', error.message)
return
}

// Delete the site
try {
if (!internalFunctionId || !matchingFunction)
throw new Error('Unable to retrive site ID.')

const { data } = await consoleClient.delete(`/api/sites/${internalFunctionId}`)

if (!data) throw new Error("")

console.log(
Chalk.green(
`Successfully deleted site ${functionName}!`
)
)
} catch (error: any) {
logger.error('Failed to delete site.', error.message)
return
}
}
132 changes: 132 additions & 0 deletions src/commands/sites/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import Chalk from "chalk"
import { run as runPublish } from "./publish"
import { basename, resolve } from "path"
import { consoleClient } from "../../lib/http"
import promptFnDeploy from "../../prompts/function/deploy"
import { parseBlsConfig } from "../../lib/blsConfig"
import { logger } from "../../lib/logger"
import { normalizeFunctionName, slugify } from "../../lib/strings"

interface DeployCommandOptions {
name?: string
path?: string
yes?: boolean
}

/**
* Entry function for bls function deploy
*
* @param options
*/
export const run = (options: DeployCommandOptions) => {
try {
const {
name: configName
} = parseBlsConfig()

const {
name = configName || basename(resolve(process.cwd())),
path = process.cwd()
} = options

runPublish({
debug: false,
name,
path,
publishCallback: (data: any) => deployFunction(slugify(name), data, options),
rebuild: true,
})
} catch (error: any) {
logger.error('Failed to deploy site.', error.message)
}
}

/**
* Helper to deploy a bls site via CLI
*
* 1. Publish package and retrive ipfs site id
* 2. Get list of user sites
* 3. Decide whether to update or create a new site (based on site name), bail if deploying same data
* 4. Call new or update API with site config parameters
* 5. Run deploy
*
* @param data
* @returns
*/
const deployFunction = async (functionName: string, functionData: any, options: DeployCommandOptions) => {
const { cid: functionId } = functionData
let matchingFunction = null
let internalFunctionId = null

// Find all matching functions, warn users if they are overwriting a deployed function
try {
const { data } = await consoleClient.get(`/api/sites?limit=999`, {})
const functions = data.docs ? data.docs : []

// Sort all matching functions by name and select the last matching function
// TODO: Ensure all functions have unique names under a user's scope
const matchingFunctions = functions.filter((f: any) =>
normalizeFunctionName(f.functionName) === normalizeFunctionName(functionName))

if (matchingFunctions && matchingFunctions.length > 0) {
matchingFunction = matchingFunctions[matchingFunctions.length - 1]
internalFunctionId = matchingFunction._id
}

// If a function exists and has been deployed, request a user's confirmation
if (matchingFunction && matchingFunction.status === 'deployed' && !options.yes) {
const { confirm } = await promptFnDeploy({ name: matchingFunction.functionName })

if (!confirm) {
throw new Error("Cancelled by user, aborting deployment.")
}
}
} catch (error: any) {
logger.error('Failed to retrive deployed sites.', error.message)
return
}

// Create or update site
try {
let response

if (!internalFunctionId) {
response = await consoleClient.post(`/api/sites`, { functionId, functionName })
} else {
response = await consoleClient.patch(
`/api/sites/${internalFunctionId}`,
{ functionId, functionName, status: 'deploying' }
)
}

if (!internalFunctionId && response.data && response.data._id) internalFunctionId = response.data._id
} catch (error: any) {
logger.error('Failed to update site metadata.', error.message)
return
}

// Deploy Site
try {
if (!internalFunctionId) throw new Error('Unable to retrive site ID')
console.log(Chalk.yellow(`Deploying ${functionName} ...`))

const { data } = await consoleClient.put(`/api/sites/${internalFunctionId}/deploy`, {
functionId: functionId
})

if (!!data.err) {
console.log(Chalk.red(`Deployment unsuccessful, ${data.message}`))
} else {
console.log(
Chalk.green(
`Successfully deployed ${functionName} with id ${functionId}`
)
)
}
} catch (error: any) {
logger.error('Failed to deploy site.', error.message)
return
}

return
}
44 changes: 44 additions & 0 deletions src/commands/sites/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import type { Argv } from "yargs"
import { run as runInit } from "./init"
import { run as runList } from "./list"
import { run as runBuild } from "./build"
import { run as runPreview } from "./preview"
import { run as runDelete } from "./delete"
import { run as runDeploy } from "./deploy"

export function sitesCli(yargs: Argv) {
yargs
.usage('bls sites [subcommand]')
.demandOption('experimental')

yargs.command(
'list',
'Lists your deployed blockless sites',
() => { },
() => {
runList()
}
)

yargs.command(
'init [name]',
'Initializes a blockless site project with a given name and template',
Expand All @@ -29,6 +41,22 @@ export function sitesCli(yargs: Argv) {
}
)

yargs.command(
'delete [target]',
'Undeploys and deletes a site from the network',
(yargs) => {
return yargs
.positional('target', {
description: 'The name of the site to delete (Defaults to the working directory)',
type: 'string',
default: undefined
})
},
(argv) => {
runDelete(argv as any)
}
)

yargs.command(
'build [path]',
'Builds and creates a wasm archive of a static site',
Expand Down Expand Up @@ -67,4 +95,20 @@ export function sitesCli(yargs: Argv) {
runPreview(argv)
}
)

yargs.command(
'deploy [path]',
'Deploys a static site on Blockless',
(yargs) => {
return yargs
.positional('path', {
description: 'Set the path to the static site project',
type: 'string',
default: undefined
})
},
(argv) => {
runDeploy(argv as any)
}
)
}
31 changes: 31 additions & 0 deletions src/commands/sites/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Chalk from "chalk"
import { consoleClient } from "../../lib/http"
import { logger } from "../../lib/logger"

export const run = async () => {
try {
const { data } = await consoleClient.get(`/api/sites?limit=999`, {})
const sites = data.docs ? data.docs : []

logger.log('List of Sites:')
logger.log('-----------------------------------')

if (sites && sites.length > 0) {
sites.forEach && sites.forEach((f: any) => {
logger.log('')
logger.log(`${Chalk.blue('Name:')} ${f.functionName}`)
logger.log(`${Chalk.blue('CID:')} ${f.functionId}`)
logger.log(`${Chalk.blue('Status:')} ${f.status === 'stopped' ? Chalk.red(f.status) : f.status === 'deployed' ? Chalk.green(f.status) : f.status}`)
})

logger.log('')
logger.log(`Total Sites: ${sites.length}`)
} else {
logger.log('')
logger.log('You have no sites.')
}
} catch (error: any) {
logger.error('Failed to retrieve site list.', error.message)
return
}
}
Loading

0 comments on commit a84ddd9

Please sign in to comment.