-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36 from dimitrov-d/master
Cloud functions SDK module
- Loading branch information
Showing
19 changed files
with
704 additions
and
94 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
82 changes: 82 additions & 0 deletions
82
packages/cli/src/modules/cloud-functions/cloud-functions.commands.ts
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,82 @@ | ||
import { Command } from 'commander'; | ||
import { | ||
createCloudFunction, | ||
listCloudFunctions, | ||
getCloudFunction, | ||
createCloudFunctionJob, | ||
setCloudFunctionEnvironment, | ||
listCloudFunctionJobs, | ||
deleteCloudFunctionJob, | ||
} from './cloud-functions.service'; | ||
import { addPaginationOptions } from '../../lib/options'; | ||
|
||
export function createCloudFunctionsCommands(cli: Command) { | ||
const cloudFunctions = cli | ||
.command('cloud-functions') | ||
.description('Commands for managing cloud functions on Apillon platform'); | ||
|
||
// CLOUD FUNCTIONS | ||
const listCloudFunctionsCommand = cloudFunctions | ||
.command('list') | ||
.description('List all cloud functions') | ||
.action(async function () { | ||
await listCloudFunctions(this.optsWithGlobals()); | ||
}); | ||
addPaginationOptions(listCloudFunctionsCommand); | ||
|
||
cloudFunctions | ||
.command('get') | ||
.description('Get details of a specific cloud function') | ||
.requiredOption('--uuid <cloud-function-uuid>', 'Cloud Function UUID') | ||
.action(async function () { | ||
await getCloudFunction(this.optsWithGlobals()); | ||
}); | ||
|
||
cloudFunctions | ||
.command('create') | ||
.description('Create a new cloud function') | ||
.requiredOption('--name <name>', 'Name of the cloud function') | ||
.option('--description <description>', 'Description of the cloud function') | ||
.action(async function () { | ||
await createCloudFunction(this.optsWithGlobals()); | ||
}); | ||
|
||
// JOBS | ||
cloudFunctions | ||
.command('create-job') | ||
.description('Create a job for a cloud function from a script file') | ||
.requiredOption('--uuid <cloud-function-uuid>', 'Cloud Function UUID') | ||
.requiredOption('--name <job-name>', 'Name of the job') | ||
.requiredOption('--script <path>', 'Path to the script file') | ||
.action(async function () { | ||
await createCloudFunctionJob(this.optsWithGlobals()); | ||
}); | ||
|
||
cloudFunctions | ||
.command('set-environment') | ||
.description('Set environment variables for a cloud function') | ||
.requiredOption('--uuid <cloud-function-uuid>', 'Cloud Function UUID') | ||
.requiredOption( | ||
'--variables <variables>', | ||
'Environment variables in key=value format, separated by commas', | ||
) | ||
.action(async function () { | ||
await setCloudFunctionEnvironment(this.optsWithGlobals()); | ||
}); | ||
|
||
cloudFunctions | ||
.command('list-jobs') | ||
.description('List all jobs for a specific cloud function') | ||
.requiredOption('--uuid <cloud-function-uuid>', 'Cloud Function UUID') | ||
.action(async function () { | ||
await listCloudFunctionJobs(this.optsWithGlobals()); | ||
}); | ||
|
||
cloudFunctions | ||
.command('delete-job') | ||
.description('Delete a job from a cloud function') | ||
.requiredOption('-j, --job-uuid <job-uuid>', 'Job UUID to delete') | ||
.action(async function () { | ||
await deleteCloudFunctionJob(this.optsWithGlobals()); | ||
}); | ||
} |
123 changes: 123 additions & 0 deletions
123
packages/cli/src/modules/cloud-functions/cloud-functions.service.ts
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,123 @@ | ||
/* eslint-disable security/detect-non-literal-fs-filename */ | ||
import { CloudFunctions } from '@apillon/sdk'; | ||
import { readFileSync } from 'fs'; | ||
import { GlobalOptions } from '../../lib/types'; | ||
import { paginate } from '../../lib/options'; | ||
import { withErrorHandler } from '../../lib/utils'; | ||
import { Storage } from '@apillon/sdk'; | ||
|
||
export async function listCloudFunctions(optsWithGlobals: GlobalOptions) { | ||
await withErrorHandler(async () => { | ||
const data = await new CloudFunctions(optsWithGlobals).listCloudFunctions({ | ||
...paginate(optsWithGlobals), | ||
}); | ||
console.log(data.items.map((d) => d.serialize())); | ||
}); | ||
} | ||
|
||
export async function getCloudFunction(optsWithGlobals: GlobalOptions) { | ||
await withErrorHandler(async () => { | ||
const cloudFunction = await new CloudFunctions(optsWithGlobals) | ||
.cloudFunction(optsWithGlobals.uuid) | ||
.get(); | ||
|
||
cloudFunction.jobs = cloudFunction.jobs.map((job) => job.serialize()); | ||
console.info(cloudFunction.serialize()); | ||
}); | ||
} | ||
|
||
export async function createCloudFunction(optsWithGlobals: GlobalOptions) { | ||
await withErrorHandler(async () => { | ||
const cloudFunctions = new CloudFunctions(optsWithGlobals); | ||
const data = await cloudFunctions.createCloudFunction({ | ||
name: optsWithGlobals.name, | ||
description: optsWithGlobals.description, | ||
}); | ||
if (data) { | ||
console.log(data.serialize()); | ||
console.log('Cloud function created successfully!'); | ||
} | ||
}); | ||
} | ||
|
||
export async function createCloudFunctionJob(optsWithGlobals: GlobalOptions) { | ||
await withErrorHandler(async () => { | ||
let scriptContent: string; | ||
const script = optsWithGlobals.script; | ||
|
||
if (!script.endsWith('.js')) { | ||
return console.error('The script file must have a .js extension.'); | ||
} | ||
|
||
try { | ||
scriptContent = readFileSync(script, 'utf-8'); | ||
} catch (e) { | ||
return e.code === 'ENOENT' | ||
? console.error(`Error: Script file not found (${script}).`) | ||
: console.error(e); | ||
} | ||
|
||
const cloudFunction = await new CloudFunctions(optsWithGlobals) | ||
.cloudFunction(optsWithGlobals.uuid) | ||
.get(); | ||
|
||
// Upload the script to IPFS | ||
const files = await new Storage(optsWithGlobals) | ||
.bucket(cloudFunction.bucketUuid) | ||
.uploadFiles( | ||
[ | ||
{ | ||
fileName: script.split('/').pop(), | ||
content: Buffer.from(scriptContent, 'utf-8'), | ||
contentType: 'application/javascript', | ||
}, | ||
], | ||
{ awaitCid: true }, | ||
); | ||
|
||
const job = await cloudFunction.createJob({ | ||
name: optsWithGlobals.name, | ||
scriptCid: files[0].CID, | ||
}); | ||
|
||
console.log(job.serialize()); | ||
console.log('Cloud function job created successfully!'); | ||
}); | ||
} | ||
|
||
export async function setCloudFunctionEnvironment( | ||
optsWithGlobals: GlobalOptions, | ||
) { | ||
await withErrorHandler(async () => { | ||
const variables = optsWithGlobals.variables.split(',').map((v) => { | ||
const [key, value] = v.split('='); | ||
return { key, value }; | ||
}); | ||
|
||
await new CloudFunctions(optsWithGlobals) | ||
.cloudFunction(optsWithGlobals.uuid) | ||
.setEnvironment(variables); | ||
|
||
console.log('Environment variables set successfully!'); | ||
}); | ||
} | ||
|
||
export async function listCloudFunctionJobs(optsWithGlobals: GlobalOptions) { | ||
await withErrorHandler(async () => { | ||
const data = await new CloudFunctions(optsWithGlobals) | ||
.cloudFunction(optsWithGlobals.uuid) | ||
.get(); | ||
|
||
console.log(data.jobs.map((job) => job.serialize())); | ||
}); | ||
} | ||
|
||
export async function deleteCloudFunctionJob(optsWithGlobals: GlobalOptions) { | ||
await withErrorHandler(async () => { | ||
await new CloudFunctions(optsWithGlobals) | ||
.cloudFunctionJob(optsWithGlobals.jobUuid) | ||
.delete(); | ||
|
||
console.log('Cloud function job deleted successfully!'); | ||
}); | ||
} |
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
54 changes: 54 additions & 0 deletions
54
packages/sdk/src/modules/cloud-functions/cloud-function-job.ts
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,54 @@ | ||
import { ApillonApi } from '../../lib/apillon-api'; | ||
import { ApillonModel } from '../../lib/apillon'; | ||
import { ApillonLogger } from '../../lib/apillon-logger'; | ||
import { JobStatus } from '../../types/cloud-functions'; | ||
|
||
export class CloudFunctionJob extends ApillonModel { | ||
/** | ||
* Unique identifier of the cloud function. | ||
*/ | ||
public functionUuid: string = null; | ||
|
||
/** | ||
* Name of the job. | ||
*/ | ||
public name: string = null; | ||
|
||
/** | ||
* CID of the script to be executed by the job. | ||
*/ | ||
public scriptCid: string = null; | ||
|
||
/** | ||
* Number of processors to use for the job. | ||
*/ | ||
public slots: number = null; | ||
|
||
/** | ||
* Status of the job. | ||
*/ | ||
public jobStatus: JobStatus = null; | ||
|
||
constructor(uuid: string, data?: Partial<CloudFunctionJob>) { | ||
super(uuid); | ||
this.API_PREFIX = `/cloud-functions/jobs/${uuid}`; | ||
this.populate(data); | ||
} | ||
|
||
/** | ||
* Deletes a specific job. | ||
* @returns {Promise<void>} | ||
*/ | ||
public async delete(): Promise<void> { | ||
await ApillonApi.delete<void>(this.API_PREFIX); | ||
ApillonLogger.log(`Job with UUID: ${this.uuid} successfully deleted`); | ||
} | ||
|
||
protected override serializeFilter(key: string, value: any) { | ||
const serialized = super.serializeFilter(key, value); | ||
const enums = { | ||
jobStatus: JobStatus[value], | ||
}; | ||
return Object.keys(enums).includes(key) ? enums[key] : serialized; | ||
} | ||
} |
Oops, something went wrong.