canvaslms-cli 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * File upload utilities for Canvas
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const axios = require('axios');
7
+ const FormData = require('form-data');
8
+ const { makeCanvasRequest } = require('./api-client');
9
+
10
+ /**
11
+ * Upload single file to Canvas and return the file ID
12
+ */
13
+ async function uploadSingleFileToCanvas(courseId, assignmentId, filePath) {
14
+ try {
15
+ // Check if file exists
16
+ if (!fs.existsSync(filePath)) {
17
+ throw new Error(`File not found: ${filePath}`);
18
+ }
19
+
20
+ const fileName = require('path').basename(filePath);
21
+ const fileContent = fs.readFileSync(filePath);
22
+
23
+ // Step 1: Get upload URL from Canvas
24
+ const uploadParams = [
25
+ `name=${encodeURIComponent(fileName)}`,
26
+ `size=${fileContent.length}`,
27
+ 'parent_folder_path=/assignments'
28
+ ];
29
+
30
+ const uploadData = await makeCanvasRequest('post', `courses/${courseId}/assignments/${assignmentId}/submissions/self/files`, uploadParams);
31
+
32
+ if (!uploadData.upload_url) {
33
+ throw new Error('Failed to get upload URL from Canvas');
34
+ }
35
+
36
+ // Step 2: Upload file to the provided URL
37
+ const form = new FormData();
38
+
39
+ // Add all the required fields from Canvas response
40
+ Object.keys(uploadData.upload_params).forEach(key => {
41
+ form.append(key, uploadData.upload_params[key]);
42
+ });
43
+
44
+ // Add the file
45
+ form.append('file', fileContent, fileName);
46
+
47
+ const uploadResponse = await axios.post(uploadData.upload_url, form, {
48
+ headers: form.getHeaders(),
49
+ maxRedirects: 0,
50
+ validateStatus: (status) => status < 400
51
+ });
52
+
53
+ // Return the file ID for later submission
54
+ return uploadData.id || uploadResponse.data.id;
55
+
56
+ } catch (error) {
57
+ throw new Error(`Failed to upload file ${filePath}: ${error.message}`);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Submit assignment with uploaded files
63
+ */
64
+ async function submitAssignmentWithFiles(courseId, assignmentId, fileIds) {
65
+ const submissionData = {
66
+ submission: {
67
+ submission_type: 'online_upload',
68
+ file_ids: fileIds
69
+ }
70
+ };
71
+
72
+ return await makeCanvasRequest('post', `courses/${courseId}/assignments/${assignmentId}/submissions`, [], JSON.stringify(submissionData));
73
+ }
74
+
75
+ module.exports = {
76
+ uploadSingleFileToCanvas,
77
+ submitAssignmentWithFiles
78
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Interactive prompt utilities
3
+ */
4
+
5
+ const readline = require('readline');
6
+
7
+ /**
8
+ * Create readline interface for user input
9
+ */
10
+ function createReadlineInterface() {
11
+ return readline.createInterface({
12
+ input: process.stdin,
13
+ output: process.stdout
14
+ });
15
+ }
16
+
17
+ /**
18
+ * Prompt user for input
19
+ */
20
+ function askQuestion(rl, question) {
21
+ return new Promise((resolve) => {
22
+ rl.question(question, (answer) => {
23
+ resolve(answer.trim());
24
+ });
25
+ });
26
+ }
27
+
28
+ module.exports = {
29
+ createReadlineInterface,
30
+ askQuestion
31
+ };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "canvaslms-cli",
3
+ "version": "1.1.0",
4
+ "description": "A command line tool for interacting with Canvas LMS API",
5
+ "keywords": [
6
+ "canvas",
7
+ "cli",
8
+ "api",
9
+ "instructure",
10
+ "lms",
11
+ "education",
12
+ "canvas-lms"
13
+ ],
14
+ "homepage": "https://github.com/caphefalumi/Canvas-CLI#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/caphefalumi/Canvas-CLI/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/caphefalumi/Canvas-CLI.git"
21
+ },
22
+ "license": "MIT",
23
+ "author": {
24
+ "name": "Your Name",
25
+ "email": "your.email@example.com"
26
+ },
27
+ "type": "commonjs",
28
+ "main": "src/index.js",
29
+ "bin": {
30
+ "canvas": "src/index.js"
31
+ },
32
+ "files": [
33
+ "src/",
34
+ "lib/",
35
+ "commands/",
36
+ "README.md",
37
+ "CHANGELOG.md"
38
+ ],
39
+ "scripts": {
40
+ "start": "node src/index.js",
41
+ "dev": "node src/index.js",
42
+ "test": "echo \"Error: no test specified\" && exit 1",
43
+ "lint": "echo \"Add linting here\"",
44
+ "format": "echo \"Add formatting here\"",
45
+ "setup": "powershell -ExecutionPolicy Bypass -File setup.ps1",
46
+ "postinstall": "echo \"Canvas CLI installed successfully! Run 'canvas config' to get started.\""
47
+ },
48
+ "dependencies": {
49
+ "axios": "^1.6.0",
50
+ "commander": "^11.1.0",
51
+ "form-data": "^4.0.3"
52
+ },
53
+ "devDependencies": {
54
+ "eslint": "^8.0.0",
55
+ "prettier": "^3.0.0"
56
+ },
57
+ "engines": {
58
+ "node": ">=14.0.0",
59
+ "npm": ">=6.0.0"
60
+ },
61
+ "preferGlobal": true,
62
+ "os": [
63
+ "win32",
64
+ "darwin",
65
+ "linux"
66
+ ]
67
+ }
package/src/index.js ADDED
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Canvas CLI - A command line tool for interacting with Canvas API
5
+ *
6
+ * @author Canvas CLI Team
7
+ * @version 1.0.0
8
+ */
9
+
10
+ const { Command } = require('commander');
11
+
12
+ // Import command handlers
13
+ const { listCourses } = require('../commands/list');
14
+ const { showConfig, setupConfig, editConfig, showConfigPath, deleteConfigFile } = require('../commands/config');
15
+ const { listAssignments } = require('../commands/assignments');
16
+ const { showGrades } = require('../commands/grades');
17
+ const { showAnnouncements } = require('../commands/announcements');
18
+ const { showProfile } = require('../commands/profile');
19
+ const { submitAssignment } = require('../commands/submit');
20
+ const { createQueryHandler } = require('../commands/api');
21
+ const { requireConfig } = require('../lib/config-validator');
22
+
23
+ const program = new Command();
24
+
25
+ // Setup CLI program
26
+ program
27
+ .name('canvas')
28
+ .description('Canvas API Command Line Tool')
29
+ .version('1.1.0');
30
+
31
+ // Raw API commands
32
+ function createQueryCommand(method) {
33
+ return program
34
+ .command(method)
35
+ .alias(method === 'query' ? 'q' : method.charAt(0))
36
+ .argument('<endpoint>', 'Canvas API endpoint to query')
37
+ .option('-q, --query <param>', 'Query parameter (can be used multiple times)', [])
38
+ .option('-d, --data <data>', 'Request body (JSON string or @filename)')
39
+ .description(`${method.toUpperCase()} request to Canvas API`)
40
+ .action(requireConfig(createQueryHandler(method)));
41
+ }
42
+
43
+ // Create raw API commands
44
+ createQueryCommand('get');
45
+ createQueryCommand('post');
46
+ createQueryCommand('put');
47
+ createQueryCommand('delete');
48
+ createQueryCommand('query');
49
+
50
+ // List command to show enrolled courses
51
+ program
52
+ .command('list')
53
+ .alias('l')
54
+ .description('List starred courses (default) or all courses with -a')
55
+ .option('-a, --all', 'Show all enrolled courses instead of just starred ones')
56
+ .option('-v, --verbose', 'Show detailed course information')
57
+ .action(requireConfig(listCourses));
58
+
59
+ // Config command with subcommands
60
+ const configCommand = program
61
+ .command('config')
62
+ .description('Manage Canvas CLI configuration')
63
+ .action(showConfig); // Default action when no subcommand is provided
64
+
65
+ configCommand
66
+ .command('show')
67
+ .alias('status')
68
+ .description('Show current configuration')
69
+ .action(showConfig);
70
+
71
+ configCommand
72
+ .command('setup')
73
+ .alias('init')
74
+ .description('Interactive configuration setup')
75
+ .action(setupConfig);
76
+
77
+ configCommand
78
+ .command('edit')
79
+ .alias('update')
80
+ .description('Edit existing configuration')
81
+ .action(editConfig);
82
+
83
+ configCommand
84
+ .command('path')
85
+ .description('Show configuration file path')
86
+ .action(showConfigPath);
87
+
88
+ configCommand
89
+ .command('delete')
90
+ .alias('remove')
91
+ .description('Delete configuration file')
92
+ .action(deleteConfigFile);
93
+
94
+ // Assignments command to show assignments for a course
95
+ program
96
+ .command('assignments')
97
+ .alias('assign')
98
+ .description('List assignments for a specific course')
99
+ .argument('<course-id>', 'Course ID to get assignments from')
100
+ .option('-v, --verbose', 'Show detailed assignment information')
101
+ .option('-s, --submitted', 'Only show submitted assignments')
102
+ .option('-p, --pending', 'Only show pending assignments')
103
+ .action(requireConfig(listAssignments));
104
+
105
+ // Grades command to show grades
106
+ program
107
+ .command('grades')
108
+ .alias('grade')
109
+ .description('Show grades for all courses or a specific course')
110
+ .argument('[course-id]', 'Optional course ID to get grades for specific course')
111
+ .option('-v, --verbose', 'Show detailed grade information')
112
+ .action(requireConfig(showGrades));
113
+
114
+ // Announcements command
115
+ program
116
+ .command('announcements')
117
+ .alias('announce')
118
+ .description('Show recent announcements')
119
+ .argument('[course-id]', 'Optional course ID to get announcements for specific course')
120
+ .option('-l, --limit <number>', 'Number of announcements to show', '5')
121
+ .action(requireConfig(showAnnouncements));
122
+
123
+ // Profile command
124
+ program
125
+ .command('profile')
126
+ .alias('me')
127
+ .description('Show current user profile information')
128
+ .option('-v, --verbose', 'Show detailed profile information')
129
+ .action(requireConfig(showProfile));
130
+
131
+ // Submit command for interactive assignment submission
132
+ program
133
+ .command('submit')
134
+ .alias('sub')
135
+ .description('Interactively submit one or multiple files to an assignment')
136
+ .option('-c, --course <course-id>', 'Skip course selection and use specific course ID')
137
+ .option('-a, --assignment <assignment-id>', 'Skip assignment selection and use specific assignment ID')
138
+ .option('-f, --file <file-path>', 'Skip file selection and use specific file path')
139
+ .action(requireConfig(submitAssignment));
140
+
141
+ // Parse command line arguments
142
+ program.parse();