Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • Jw_sonar_backup
  • add_route_assignments
  • add_route_user
  • assignment_filter
  • bedran_exercise-list
  • exercise_list_filter
  • interactive-mode-preference
  • jw_sonar
  • main
  • move-to-esm-only
  • v6.0.0
  • 2.0.0
  • 2.1.0
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 3.1.0
  • 3.1.1
  • 3.1.2
  • 3.2.0
  • 3.2.2
  • 3.2.3
  • 3.3.0
  • 3.4.1
  • 3.4.2
  • 3.5.0
  • 4.0.0
  • 4.0.1
  • 4.1.0
  • 4.1.1
  • 4.2.0
  • 5.0.0
  • 6.0.0-dev
  • Latest
  • Pre-alpha
  • v1.0.1
36 results

Target

Select target project
  • Dojo_Project_Nguyen/ui/dojocli
  • dojo_project/projects/ui/dojocli
  • tom.andrivet/dojocli
  • orestis.malaspin/dojocli
4 results
Select Git revision
  • Jw_sonar_backup
  • add_route_assignments
  • add_route_user
  • assignment_filter
  • bedran_exercise-list
  • exercise_list_filter
  • interactive-mode-preference
  • jw_sonar
  • main
  • move-to-esm-only
  • v6.0.0
  • 2.0.0
  • 2.1.0
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 3.1.0
  • 3.1.1
  • 3.1.2
  • 3.2.0
  • 3.2.2
  • 3.2.3
  • 3.3.0
  • 3.4.1
  • 3.4.2
  • 3.5.0
  • 4.0.0
  • 4.0.1
  • 4.1.0
  • 4.1.1
  • 4.2.0
  • 5.0.0
  • 6.0.0-dev
  • Latest
  • Pre-alpha
  • v1.0.1
36 results
Show changes
Showing
with 709 additions and 136 deletions
import AssignmentPublishUnpublishCommandBase from './AssignmentPublishUnpublishCommandBase'; import AssignmentPublishUnpublishCommandBase from './AssignmentPublishUnpublishCommandBase.js';
class AssignmentUnpublishCommand extends AssignmentPublishUnpublishCommandBase { class AssignmentUnpublishCommand extends AssignmentPublishUnpublishCommandBase {
......
import CommanderCommand from '../../../CommanderCommand'; import CommanderCommand from '../../../CommanderCommand.js';
import AssignmentCorrectionLinkCommand from './subcommands/AssignmentCorrectionLinkCommand'; import AssignmentCorrectionLinkCommand from './subcommands/AssignmentCorrectionLinkCommand.js';
import AssignmentCorrectionUpdateCommand from './subcommands/AssignmentCorrectionUpdateCommand'; import AssignmentCorrectionUpdateCommand from './subcommands/AssignmentCorrectionUpdateCommand.js';
import AssignmentCorrectionUnlinkCommand from './subcommands/AssignmentCorrectionUnlinkCommand.js';
class AssignmentCorrectionCommand extends CommanderCommand { class AssignmentCorrectionCommand extends CommanderCommand {
...@@ -8,15 +9,18 @@ class AssignmentCorrectionCommand extends CommanderCommand { ...@@ -8,15 +9,18 @@ class AssignmentCorrectionCommand extends CommanderCommand {
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('manage corrections of an assignment'); .description('manage corrections of an assignment');
} }
protected defineSubCommands() { protected defineSubCommands() {
AssignmentCorrectionLinkCommand.registerOnCommand(this.command); AssignmentCorrectionLinkCommand.registerOnCommand(this.command);
AssignmentCorrectionUpdateCommand.registerOnCommand(this.command); AssignmentCorrectionUpdateCommand.registerOnCommand(this.command);
AssignmentCorrectionUnlinkCommand.registerOnCommand(this.command);
} }
protected async commandAction(): Promise<void> { } protected async commandAction(): Promise<void> {
// No action
}
} }
......
import AssignmentCorrectionLinkUpdateCommand from './AssignmentCorrectionLinkUpdateCommand'; import AssignmentCorrectionLinkUpdateCommand from './AssignmentCorrectionLinkUpdateCommand.js';
class AssignmentCorrectionLinkCommand extends AssignmentCorrectionLinkUpdateCommand { class AssignmentCorrectionLinkCommand extends AssignmentCorrectionLinkUpdateCommand {
......
import CommanderCommand from '../../../../CommanderCommand'; import CommanderCommand from '../../../../CommanderCommand.js';
import chalk from 'chalk';
import ora from 'ora'; import ora from 'ora';
import DojoBackendManager from '../../../../../managers/DojoBackendManager'; import DojoBackendManager from '../../../../../managers/DojoBackendManager.js';
import SessionManager from '../../../../../managers/SessionManager'; import Assignment from '../../../../../sharedByClients/models/Assignment.js';
import Assignment from '../../../../../sharedByClients/models/Assignment'; import TextStyle from '../../../../../types/TextStyle.js';
import GlobalHelper from '../../../../../helpers/GlobalHelper.js';
abstract class AssignmentCorrectionLinkUpdateCommand extends CommanderCommand { abstract class AssignmentCorrectionLinkUpdateCommand extends CommanderCommand {
...@@ -11,34 +11,31 @@ abstract class AssignmentCorrectionLinkUpdateCommand extends CommanderCommand { ...@@ -11,34 +11,31 @@ abstract class AssignmentCorrectionLinkUpdateCommand extends CommanderCommand {
protected defineCommand() { protected defineCommand() {
this.command this.command
.description(this.isUpdate ? 'update a correction of an assignment' : 'link an exercise repo as a correction for an assignment') .description(this.isUpdate ? 'update a correction of an assignment' : 'link an exercise repo as a correction for an assignment')
.argument('<string>', 'id or url of the exercise that is the correction') .argument('<string>', 'id or url of the exercise that is the correction')
.requiredOption('-a, --assignment <string>', 'id or url of the assignment of the correction') .requiredOption('-a, --assignment <string>', 'id or url of the assignment of the correction')
.action(this.commandAction.bind(this)); .option('-c, --commit <string>', 'specific commit to link as correction (default: last commit)')
.option('-d, --description <string>', 'description of the correction (limited to 80 characters, default: empty)')
.action(this.commandAction.bind(this));
} }
protected async commandAction(exerciseIdOrUrl: string, options: { assignment: string }): Promise<void> { protected async commandAction(exerciseIdOrUrl: string, options: { assignment: string, commit?: string, description?: string }): Promise<void> {
let assignment!: Assignment | undefined; let assignment!: Assignment | undefined;
// Check access // Check requirements
{ {
console.log(chalk.cyan('Please wait while we check access...')); if ( options.description && options.description.length > 80 ) {
ora('Description is limited to 80 characters').start().fail();
const assignmentGetSpinner: ora.Ora = ora('Checking if assignment exists').start();
assignment = await DojoBackendManager.getAssignment(options.assignment);
if ( !assignment ) {
assignmentGetSpinner.fail(`The assignment doesn't exists`);
return; return;
} }
assignmentGetSpinner.succeed(`The assignment exists`); }
const assignmentAccessSpinner: ora.Ora = ora('Checking assignment access').start(); // Check access
if ( assignment.staff.find(staff => staff.id === SessionManager.profile?.id) === undefined ) { {
assignmentAccessSpinner.fail(`You are not in the staff of the assignment`); assignment = await GlobalHelper.checkAssignmentCorrectionAccess(options.assignment);
if ( !assignment ) {
return; return;
} }
assignmentAccessSpinner.succeed(`You are in the staff of the assignment`);
const assignmentPublishedSpinner: ora.Ora = ora('Checking assignment').start(); const assignmentPublishedSpinner: ora.Ora = ora('Checking assignment').start();
...@@ -51,9 +48,9 @@ abstract class AssignmentCorrectionLinkUpdateCommand extends CommanderCommand { ...@@ -51,9 +48,9 @@ abstract class AssignmentCorrectionLinkUpdateCommand extends CommanderCommand {
// Link the exercise // Link the exercise
{ {
console.log(chalk.cyan('Please wait while we link the exercise...')); console.log(TextStyle.BLOCK('Please wait while we link the exercise...'));
await DojoBackendManager.linkUpdateCorrection(exerciseIdOrUrl, assignment, this.isUpdate); await DojoBackendManager.linkUpdateCorrection(exerciseIdOrUrl, assignment, options.commit, options.description, this.isUpdate);
} }
} }
} }
......
import Assignment from '../../../../../sharedByClients/models/Assignment.js';
import TextStyle from '../../../../../types/TextStyle.js';
import DojoBackendManager from '../../../../../managers/DojoBackendManager.js';
import CommanderCommand from '../../../../CommanderCommand.js';
import GlobalHelper from '../../../../../helpers/GlobalHelper.js';
class AssignmentCorrectionLinkCommand extends CommanderCommand {
protected commandName: string = 'unlink';
protected defineCommand() {
this.command
.description('remove a correction of an assignment')
.argument('<string>', 'id or url of the exercise that is the correction')
.requiredOption('-a, --assignment <string>', 'id or url of the assignment of the correction')
.action(this.commandAction.bind(this));
}
protected async commandAction(exerciseIdOrUrl: string, options: { assignment: string }): Promise<void> {
let assignment!: Assignment | undefined;
// Check access
{
assignment = await GlobalHelper.checkAssignmentCorrectionAccess(options.assignment);
if ( !assignment ) {
return;
}
}
// Link the exercise
{
console.log(TextStyle.BLOCK('Please wait while we unlink the exercise...'));
await DojoBackendManager.unlinkCorrection(exerciseIdOrUrl, assignment);
}
}
}
export default new AssignmentCorrectionLinkCommand();
\ No newline at end of file
import AssignmentCorrectionLinkUpdateCommand from './AssignmentCorrectionLinkUpdateCommand'; import AssignmentCorrectionLinkUpdateCommand from './AssignmentCorrectionLinkUpdateCommand.js';
class AssignmentCorrectionUpdateCommand extends AssignmentCorrectionLinkUpdateCommand { class AssignmentCorrectionUpdateCommand extends AssignmentCorrectionLinkUpdateCommand {
......
import CommanderCommand from '../CommanderCommand'; import CommanderCommand from '../CommanderCommand.js';
import SessionTestCommand from './subcommands/SessionTestCommand'; import SessionTestCommand from './subcommands/AuthTestCommand.js';
import SessionLoginCommand from './subcommands/SessionLoginCommand'; import SessionLoginCommand from './subcommands/AuthLoginCommand.js';
import SessionLogoutCommand from './subcommands/SessionLogoutCommand'; import SessionLogoutCommand from './subcommands/AuthLogoutCommand.js';
class SessionCommand extends CommanderCommand { class AuthCommand extends CommanderCommand {
protected commandName: string = 'session'; protected commandName: string = 'auth';
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('manage Dojo and Gitlab sessions'); .description('manage Dojo and Gitlab sessions');
} }
protected defineSubCommands() { protected defineSubCommands() {
...@@ -18,8 +18,10 @@ class SessionCommand extends CommanderCommand { ...@@ -18,8 +18,10 @@ class SessionCommand extends CommanderCommand {
SessionTestCommand.registerOnCommand(this.command); SessionTestCommand.registerOnCommand(this.command);
} }
protected async commandAction(): Promise<void> { } protected async commandAction(): Promise<void> {
// No action
}
} }
export default new SessionCommand(); export default AuthCommand;
\ No newline at end of file \ No newline at end of file
import AuthCommand from './AuthCommand.js';
import { CommandOptions } from 'commander';
class SessionCommand extends AuthCommand {
protected commandName: string = 'session';
protected options: CommandOptions = {
hidden: true
};
}
export default new SessionCommand();
\ No newline at end of file
import chalk from 'chalk'; import CommanderCommand from '../../CommanderCommand.js';
import CommanderCommand from '../../CommanderCommand'; import SessionManager from '../../../managers/SessionManager.js';
import SessionManager from '../../../managers/SessionManager'; import TextStyle from '../../../types/TextStyle.js';
class SessionLoginCommand extends CommanderCommand { class AuthLoginCommand extends CommanderCommand {
protected commandName: string = 'login'; protected commandName: string = 'login';
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('login to Dojo') .description('login to Dojo')
.option('-c, --cli', 'proceed to the login in headless mode (do not try to open web browser).') .option('-c, --cli', 'proceed to the login in headless mode (do not try to open web browser).')
.action(this.commandAction.bind(this)); .action(this.commandAction.bind(this));
} }
protected async commandAction(options: { cli: boolean }): Promise<void> { protected async commandAction(options: { cli: boolean }): Promise<void> {
try { try {
console.log(chalk.cyan('Please wait while we login you into Dojo...')); console.log(TextStyle.BLOCK('Please wait while we login you into Dojo...'));
await SessionManager.login(options.cli); await SessionManager.login(options.cli);
} catch ( error ) { /* empty */ } } catch ( error ) { /* empty */ }
} }
} }
export default new SessionLoginCommand(); export default new AuthLoginCommand();
\ No newline at end of file \ No newline at end of file
import CommanderCommand from '../../CommanderCommand'; import CommanderCommand from '../../CommanderCommand.js';
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import ora from 'ora'; import ora from 'ora';
import SessionManager from '../../../managers/SessionManager'; import SessionManager from '../../../managers/SessionManager.js';
class SessionLogoutCommand extends CommanderCommand { class AuthLogoutCommand extends CommanderCommand {
protected commandName: string = 'logout'; protected commandName: string = 'logout';
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('logout of Dojo') .description('logout of Dojo')
.option('-f, --force', 'attempt to logout without prompting for confirmation') .option('-f, --force', 'attempt to logout without prompting for confirmation')
.action(this.commandAction.bind(this)); .action(this.commandAction.bind(this));
} }
protected async commandAction(options: { force: boolean }): Promise<void> { protected async commandAction(options: { force: boolean }): Promise<void> {
...@@ -35,4 +35,4 @@ class SessionLogoutCommand extends CommanderCommand { ...@@ -35,4 +35,4 @@ class SessionLogoutCommand extends CommanderCommand {
} }
export default new SessionLogoutCommand(); export default new AuthLogoutCommand();
\ No newline at end of file \ No newline at end of file
import CommanderCommand from '../../CommanderCommand'; import CommanderCommand from '../../CommanderCommand.js';
import SessionManager from '../../../managers/SessionManager'; import SessionManager from '../../../managers/SessionManager.js';
import GitlabManager from '../../../managers/GitlabManager'; import Config from '../../../config/Config';
class SessionTestCommand extends CommanderCommand { class AuthTestCommand extends CommanderCommand {
protected commandName: string = 'test'; protected commandName: string = 'test';
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('test availability of registered Dojo API and Gitlab API sessions') .description('test availability of registered Dojo API and Gitlab API sessions')
.action(this.commandAction.bind(this)); .action(this.commandAction.bind(this));
} }
protected async commandAction(): Promise<void> { protected async commandAction(): Promise<void> {
await SessionManager.testSession(); await SessionManager.testSession();
await GitlabManager.testToken(); await Config.gitlabManager.testToken();
} }
} }
export default new SessionTestCommand(); export default new AuthTestCommand();
\ No newline at end of file \ No newline at end of file
import CommanderCommand from '../CommanderCommand.js';
import CompletionCreateUpdateCommand from './subcommands/CompletionCreateUpdateCommand.js';
import CompletionGetCommand from './subcommands/CompletionGetCommand.js';
import CompletionScriptCommand from './subcommands/CompletionScriptCommand.js';
class CompletionCommand extends CommanderCommand {
protected commandName: string = 'completion';
protected defineCommand() {
this.command
.description('generate completions for bash, fish, or zsh');
}
protected defineSubCommands() {
CompletionCreateUpdateCommand.registerOnCommand(this.command);
CompletionGetCommand.registerOnCommand(this.command);
CompletionScriptCommand.registerOnCommand(this.command);
}
protected async commandAction(): Promise<void> {
// No action
}
}
export default new CompletionCommand();
\ No newline at end of file
import CommanderCommand from '../../CommanderCommand.js';
import { Option } from 'commander';
import { generateFishCompletion, getRoot, tryRenameFile, updateRcFile } from '../../../helpers/AutoCompletionHelper.js';
import os, { homedir } from 'os';
import path from 'path';
import ora from 'ora';
import fs from 'fs-extra';
import TextStyle from '../../../types/TextStyle.js';
class CompletionCreateUpdateCommand extends CommanderCommand {
protected commandName: string = 'create';
protected aliasNames: Array<string> = [ 'update' ];
protected defineCommand() {
this.command.description('generate shell completion')
.addOption(new Option('-s, --shell <shell>', 'shell type').choices([ 'bash', 'zsh', 'fish' ]).makeOptionMandatory(true))
.addOption(new Option('-f, --file <filename>', '(only for fish shell)').implies({ shell: 'fish' }))
.addOption(new Option('-y, --force', 'don\'t ask for file overwrite confirmation (only for fish shell)').implies({ shell: 'fish' }))
.action(this.commandAction.bind(this));
}
private bash() {
const completionCommand = `
# Added by DojoCLI
eval "$(dojo completion script bash)"
`;
updateRcFile('bash', path.join(os.homedir(), '.bashrc'), completionCommand);
}
private zsh() {
const completionCommand = `
# Added by DojoCLI
source <(dojo completion script zsh)
`;
updateRcFile('zsh', path.join(homedir(), '.zshrc'), completionCommand);
}
/* The completion command must do the following:
- if a file is provided:
- if force is not enabled:
- check if the file exists:
- if it exists, prompt the user that it will be erased
- if ok is given write the file and prompt that a backup has been created
- else create the file containing the completion
- else
- if force is not enabled:
- check if the default file exists:
- if it exists, prompt the user that it will be erased:
- if ok is given write the file and prompt that a backup has been created
- else
- create the file containing the completion
*/
private async fish(options: { file: string, force: boolean }) {
const filePath = path.resolve(options.file ?? path.join(os.homedir(), '.config/fish/completions/dojo.fish'));
const showInstructions = !!options.file;
if ( !(await tryRenameFile(filePath, options.force)) ) { // means renaming was interrupted
return;
}
const spinner: ora.Ora = ora(`Writing fish completion in ${ filePath }...`).start();
try {
fs.mkdirsSync(path.dirname(filePath));
fs.writeFileSync(filePath, generateFishCompletion(getRoot(this.command)));
spinner.succeed(`Fish completion successfully written in ${ filePath }.`);
if ( showInstructions ) {
const cpCommand = ` cp -i ${ filePath } ~/.config/fish/completions # interactive cp to avoid accidents `;
console.log(`
The easiest way to install the completion is to copy the ${ TextStyle.CODE(filePath) } into the ${ TextStyle.CODE('~/.config/fish/completions') } directory.
${ TextStyle.CODE(cpCommand) }`);
}
} catch ( error ) {
spinner.fail(`Fish completion error: ${ error }.`);
}
}
protected async commandAction(options: { shell: 'bash' | 'zsh' | 'fish', file: string, force: boolean }): Promise<void> {
switch ( options.shell ) {
case 'bash':
this.bash();
break;
case 'zsh':
this.zsh();
break;
case 'fish':
await this.fish(options);
break;
default:
console.error('Unsupported shell.');
break;
}
}
}
export default new CompletionCreateUpdateCommand();
\ No newline at end of file
import CommanderCommand from '../../CommanderCommand.js';
import { Command, CommandOptions, Option } from 'commander';
import * as AutoCompletionHelper from '../../../helpers/AutoCompletionHelper.js';
type CompletionProposal = { name: string, description: string };
class CompletionGetCommand extends CommanderCommand {
protected commandName: string = 'get';
protected options: CommandOptions = {
hidden: true
};
protected defineCommand() {
this.command.description('generate completion options for a given command')
.addOption(new Option('-s, --shell <shell>', 'shell completion result format').choices([ 'bash', 'zsh' ]))
.argument('<commands...>', 'command chain to complete')
.action(this.commandAction.bind(this));
}
private completion(commandsChain: Array<string>, displayFunction: (completionProposals: Array<CompletionProposal>) => void) {
const command = AutoCompletionHelper.getCommandFromChain(AutoCompletionHelper.getRoot(this.command), commandsChain.slice(1));
if ( command ) {
const commands = command.commands.filter(cmd => !(cmd as Command & { _hidden: boolean })._hidden);
const options = command.options.filter(option => !option.hidden);
displayFunction([ ...commands.flatMap(cmd => [ {
name : cmd.name(),
description: cmd.description()
}, ...cmd.aliases().map(alias => ({
name : alias,
description: cmd.description()
})) ]), ...options.flatMap(option => [ {
name : option.long,
description: option.description
}, {
name : option.short,
description: option.description
} ]) ].filter(proposal => proposal.name) as Array<CompletionProposal>);
}
}
private bashCompletion(commandsChain: Array<string>) {
this.completion(commandsChain, (completionProposals: Array<CompletionProposal>) => console.log(completionProposals.map(proposal => proposal.name).join(' ')));
}
private zshCompletion(commandsChain: Array<string>) {
this.completion(commandsChain, (completionProposals: Array<CompletionProposal>) => completionProposals.forEach(proposal => console.log(`${ proposal.name }:${ proposal.description }`)));
}
protected async commandAction(commandsChain: Array<string>, options: { shell: 'bash' | 'zsh' }): Promise<void> {
switch ( options.shell ) {
case 'bash':
this.bashCompletion(commandsChain);
break;
case 'zsh':
this.zshCompletion(commandsChain);
break;
default:
console.error('Unsupported shell completion format');
break;
}
}
}
export default new CompletionGetCommand();
\ No newline at end of file
import CommanderCommand from '../../CommanderCommand.js';
import { Argument } from 'commander';
class CompletionScriptCommand extends CommanderCommand {
protected commandName: string = 'script';
protected defineCommand() {
this.command.description('generate shell script completion')
.addArgument(new Argument('<shell>', 'shell completion format').choices([ 'bash', 'zsh' ]))
.action(this.commandAction.bind(this));
}
private bashCompletionScript() {
console.log(`
#/usr/bin/env bash
###-begin-dojo-completions-###
#
# dojo command completion script for bash
#
# Installation: dojo completion bash
#
function _dojo_completions()
{
latest="\${COMP_WORDS[$COMP_CWORD]}"
words=$(dojo completion get --shell bash \${COMP_WORDS[@]})
COMPREPLY=($(compgen -W "$words" -- $latest))
return 0
}
complete -F _dojo_completions dojo
###-end-dojo-completions-###
`);
}
private zshCompletionScript() {
console.log(`
#compdef dojo
###-begin-dojo-completions-###
#
# dojo command completion script for zsh
#
# Installation: dojo completion zsh
#
_dojo_completions()
{
local reply
local si=$IFS
IFS=$'
' reply=($(dojo completion get --shell zsh \${words[@]}))
IFS=$si
_describe 'values' reply
}
compdef _dojo_completions dojo
###-end-dojo-completions-###
`);
}
protected async commandAction(shell: 'bash' | 'zsh'): Promise<void> {
switch ( shell ) {
case 'bash':
this.bashCompletionScript();
break;
case 'zsh':
this.zshCompletionScript();
break;
default:
console.error('Unsupported shell completion format');
break;
}
}
}
export default new CompletionScriptCommand();
\ No newline at end of file
import CommanderCommand from '../CommanderCommand'; import CommanderCommand from '../CommanderCommand.js';
import ExerciseCreateCommand from './subcommands/ExerciseCreateCommand'; import ExerciseCreateCommand from './subcommands/ExerciseCreateCommand.js';
import ExerciseRunCommand from './subcommands/ExerciseRunCommand'; import ExerciseRunCommand from './subcommands/ExerciseRunCommand.js';
import ExerciseCorrectionCommand from './subcommands/ExerciseCorrectionCommand'; import ExerciseCorrectionCommand from './subcommands/ExerciseCorrectionCommand.js';
import ExerciseDeleteCommand from './subcommands/ExerciseDeleteCommand';
import ExerciseSearchCommand from './subcommands/ExerciseSearchCommand';
import ExerciseInfoCommand from './subcommands/ExerciseInfoCommand';
class ExerciseCommand extends CommanderCommand { class ExerciseCommand extends CommanderCommand {
...@@ -9,16 +12,23 @@ class ExerciseCommand extends CommanderCommand { ...@@ -9,16 +12,23 @@ class ExerciseCommand extends CommanderCommand {
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('manage an exercise'); .description('manage an exercise');
} }
protected defineSubCommands() { protected defineSubCommands() {
ExerciseCreateCommand.registerOnCommand(this.command); ExerciseCreateCommand.registerOnCommand(this.command);
ExerciseRunCommand.registerOnCommand(this.command); ExerciseRunCommand.registerOnCommand(this.command);
ExerciseDeleteCommand.registerOnCommand(this.command);
ExerciseCorrectionCommand.registerOnCommand(this.command); ExerciseCorrectionCommand.registerOnCommand(this.command);
ExerciseSearchCommand.registerOnCommand(this.command);
ExerciseInfoCommand.registerOnCommand(this.command);
// ExerciseResultCommand.registerOnCommand(this.command);
// ExerciseSummaryCommand.registerOnCommand(this.command);
} }
protected async commandAction(): Promise<void> { } protected async commandAction(): Promise<void> {
// No action
}
} }
......
import CommanderCommand from '../../CommanderCommand'; import CommanderCommand from '../../CommanderCommand.js';
import ora from 'ora'; import ora from 'ora';
import DojoBackendManager from '../../../managers/DojoBackendManager'; import DojoBackendManager from '../../../managers/DojoBackendManager.js';
import Config from '../../../config/Config'; import Assignment from '../../../sharedByClients/models/Assignment.js';
import Assignment from '../../../sharedByClients/models/Assignment';
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import open from 'open'; import open from 'open';
import chalk from 'chalk'; import chalk from 'chalk';
import TextStyle from '../../../types/TextStyle.js';
import Config from '../../../config/Config';
type CorrectionResume = { name: string, value: string } type CorrectionResume = { name: string, value: string }
...@@ -16,9 +17,9 @@ class ExerciseCorrectionCommand extends CommanderCommand { ...@@ -16,9 +17,9 @@ class ExerciseCorrectionCommand extends CommanderCommand {
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('link an exercise repo as a correction for an assignment') .description('list corrections of an assignment')
.requiredOption('-a, --assignment <string>', 'id or url of the assignment of the correction') .requiredOption('-a, --assignment <string>', 'id or url of the assignment')
.action(this.commandAction.bind(this)); .action(this.commandAction.bind(this));
} }
protected async commandAction(options: { assignment: string }): Promise<void> { protected async commandAction(options: { assignment: string }): Promise<void> {
...@@ -33,15 +34,16 @@ class ExerciseCorrectionCommand extends CommanderCommand { ...@@ -33,15 +34,16 @@ class ExerciseCorrectionCommand extends CommanderCommand {
Config.interactiveMode ? await this.showCorrectionsInteractive(assignment, assignmentGetSpinner) : this.showCorrections(assignment, assignmentGetSpinner); Config.interactiveMode ? await this.showCorrectionsInteractive(assignment, assignmentGetSpinner) : this.showCorrections(assignment, assignmentGetSpinner);
} else { } else {
assignmentGetSpinner.fail(`The assignment doesn't have any corrections yet`); assignmentGetSpinner.fail(`The assignment doesn't have any corrections yet`);
return;
} }
} }
private getCorrections(assignment: Assignment): Array<CorrectionResume> { private getCorrections(assignment: Assignment): Array<CorrectionResume> {
return assignment.corrections.map(correction => { return assignment.corrections.map(correction => {
const authors = correction.name.replace(correction.assignmentName, '').split('-')[2].trim();
return { return {
name : correction.name.replace(correction.assignmentName, '').split('-')[2].trim(), name : (correction.correctionDescription && correction.correctionDescription !== '' ? `${ correction.correctionDescription } (${ authors })` : authors),
value: correction.correctionCommit!.web_url?.replace('/commit/', '/tree/') ?? '' value: correction.correctionCommit?.web_url?.replace('/commit/', '/tree/') ?? ''
}; };
}); });
} }
...@@ -66,9 +68,9 @@ class ExerciseCorrectionCommand extends CommanderCommand { ...@@ -66,9 +68,9 @@ class ExerciseCorrectionCommand extends CommanderCommand {
default: false default: false
})).correctionUrl; })).correctionUrl;
console.log(chalk.green(correctionUrl)); console.log(TextStyle.URL(correctionUrl));
open(correctionUrl).then(); await open(correctionUrl);
} }
} }
......
import CommanderCommand from '../../CommanderCommand'; import CommanderCommand from '../../CommanderCommand.js';
import chalk from 'chalk'; import ora from 'ora';
import GitlabManager from '../../../managers/GitlabManager'; import DojoBackendManager from '../../../managers/DojoBackendManager.js';
import GitlabUser from '../../../shared/types/Gitlab/GitlabUser'; import AccessesHelper from '../../../helpers/AccessesHelper.js';
import ora from 'ora'; import Assignment from '../../../sharedByClients/models/Assignment.js';
import DojoBackendManager from '../../../managers/DojoBackendManager'; import Exercise from '../../../sharedByClients/models/Exercise.js';
import AccessesHelper from '../../../helpers/AccessesHelper'; import * as Gitlab from '@gitbeaker/rest';
import Assignment from '../../../sharedByClients/models/Assignment'; import TextStyle from '../../../types/TextStyle.js';
import Exercise from '../../../sharedByClients/models/Exercise'; import inquirer from 'inquirer';
import Config from '../../../config/Config';
import ClientsSharedConfig from '../../../sharedByClients/config/ClientsSharedConfig';
import { Option } from 'commander';
import ExerciseHelper from '../../../helpers/Dojo/ExerciseHelper';
type CommandOptions = { assignment: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean, force?: boolean };
class ExerciseCreateCommand extends CommanderCommand { class ExerciseCreateCommand extends CommanderCommand {
protected commandName: string = 'create'; protected commandName: string = 'create';
private members!: Array<Gitlab.UserSchema> | undefined;
private assignment!: (Assignment & { myExercises?: Array<Exercise> }) | undefined;
private exercise!: Exercise;
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('create a new exercise from an assignment') .description('create a new exercise from an assignment')
.requiredOption('-a, --assignment <value>', 'assignment source (Dojo assignment ID, Dojo assignment name or Gitlab assignment URL)') .requiredOption('-a, --assignment <value>', 'assignment source (Dojo assignment ID, Dojo assignment name or Gitlab assignment URL)')
.option('-i, --members_id <ids...>', 'list of gitlab members ids (group\'s student) to add to the repository') .option('-i, --members_id <ids...>', 'list of gitlab members ids (group\'s student) to add to the repository')
.option('-u, --members_username <usernames...>', 'list of gitlab members username (group\'s student) to add to the repository') .option('-u, --members_username <usernames...>', 'list of gitlab members username (group\'s student) to add to the repository')
.option('-c, --clone [string]', 'automatically clone the repository (SSH required) in the specified directory (this will create a subdirectory with the assignment name)') .option('-c, --clone [string]', 'automatically clone the repository (SSH required) in the specified directory (this will create a subdirectory with the assignment name)')
.action(this.commandAction.bind(this)); .addOption(new Option('-f, --force', 'don\'t ask for choice if there are already exercises for this assignment and try to create a new one anyway'))
.action(this.commandAction.bind(this));
} }
protected async commandAction(options: { assignment: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean }): Promise<void> { private async dataRetrieval(options: CommandOptions) {
let members!: Array<GitlabUser> | false; console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
let assignment!: Assignment | undefined;
let exercise!: Exercise;
// Check access and retrieve data await AccessesHelper.checkStudent();
{
console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
if ( !await AccessesHelper.checkStudent() ) { this.members = await Config.gitlabManager.fetchMembers(options);
return; if ( !this.members ) {
} throw new Error();
}
members = await GitlabManager.fetchMembers(options); ora('Checking assignment:').start().info();
if ( !members ) { const assignmentGetSpinner: ora.Ora = ora({
return; text : 'Checking if assignment exists',
indent: 4
}).start();
this.assignment = await DojoBackendManager.getAssignment(options.assignment, true);
if ( !this.assignment ) {
assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`);
throw new Error();
}
assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`);
const assignmentPublishedSpinner: ora.Ora = ora({
text : 'Checking if assignment is published',
indent: 4
}).start();
if ( !this.assignment.published ) {
assignmentPublishedSpinner.fail(`Assignment "${ this.assignment.name }" isn't published`);
throw new Error();
}
assignmentPublishedSpinner.succeed(`Assignment "${ this.assignment.name }" is published`);
if ( this.assignment.myExercises && this.assignment.myExercises.length > 0 ) {
const oraInfo = (message: string, indent: number = 12) => {
ora({
text : message,
indent: indent
}).start().info();
};
oraInfo(TextStyle.WARNING(`You already created ${ this.assignment.myExercises.length } exercise${ this.assignment.myExercises.length > 1 ? 's' : '' } for this assignment${ this.assignment.myExercises.length >= ClientsSharedConfig.exercise.maxPerAssignment ? ' (which is the limit)' : '' }:`), 4);
type CreationChoice = { delete: string | false, deleteAll: boolean, create: boolean };
const presets: Array<{ name: string, value: CreationChoice } | inquirer.Separator> = [];
for ( const exercise of this.assignment.myExercises ) {
exercise.assignment = this.assignment;
await ExerciseHelper.displayDetails(exercise, false);
presets.push({
name : `Delete "${ exercise.name }" and create a new one.`,
value: {
delete : exercise.id,
deleteAll: false,
create : true
}
});
} }
ora('Checking assignment:').start().info(); if ( !options.force ) {
const assignmentGetSpinner: ora.Ora = ora({ presets.push(new inquirer.Separator());
text : 'Checking if assignment exists',
indent: 4 if ( this.assignment.myExercises.length > 1 ) {
}).start(); presets.push({
assignment = await DojoBackendManager.getAssignment(options.assignment); name : `Delete all existing exercises for this assignment and create a new one.`,
if ( !assignment ) { value: {
assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`); delete : false,
return; deleteAll: true,
} create : true
assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`); }
}, new inquirer.Separator());
const assignmentPublishedSpinner: ora.Ora = ora({ }
text : 'Checking if assignment is published',
indent: 4 if ( this.assignment.myExercises.length < ClientsSharedConfig.exercise.maxPerAssignment ) {
}).start(); presets.push({
if ( !assignment.published ) { name : `Create a new one`,
assignmentPublishedSpinner.fail(`Assignment "${ assignment.name }" isn't published`); value: {
return; delete : false,
deleteAll: false,
create : true
}
}, new inquirer.Separator());
}
presets.push({
name : `Cancel`,
value: {
delete : false,
deleteAll: false,
create : false
}
});
const creationChoice: CreationChoice = (await inquirer.prompt({
name : 'creationChoice',
message : `You already created ${ this.assignment.myExercises.length } exercise${ this.assignment.myExercises.length > 1 ? 's' : '' }${ this.assignment.myExercises.length >= ClientsSharedConfig.exercise.maxPerAssignment ? ' (which is the limit)' : '' } for this assignment (see above). What do you want to do?`,
type : 'list',
pageSize: 1000,
choices : presets
})).creationChoice;
if ( creationChoice.delete || creationChoice.deleteAll ) {
console.log(TextStyle.BLOCK(`Please wait while we are deleting the exercise${ creationChoice.deleteAll ? 's' : '' }...`));
if ( creationChoice.delete ) {
await DojoBackendManager.deleteExercise(creationChoice.delete);
} else if ( creationChoice.deleteAll ) {
for ( const exercise of this.assignment.myExercises ) {
await DojoBackendManager.deleteExercise(exercise.id);
}
}
}
if ( !creationChoice.create ) {
throw new Error();
}
} }
assignmentPublishedSpinner.succeed(`Assignment "${ assignment.name }" is published`);
} }
}
//Create the exercise private async createExercise() {
{ console.log(TextStyle.BLOCK('Please wait while we are creating the exercise (approximately 10 seconds)...'));
console.log(chalk.cyan('Please wait while we are creating the exercise (approximately 10 seconds)...'));
try {
exercise = await DojoBackendManager.createExercise((assignment as Assignment).name, members);
const oraInfo = (message: string) => {
ora({
text : message,
indent: 4
}).start().info();
};
oraInfo(`${ chalk.magenta('Id:') } ${ exercise.id }`);
oraInfo(`${ chalk.magenta('Name:') } ${ exercise.name }`);
oraInfo(`${ chalk.magenta('Web URL:') } ${ exercise.gitlabCreationInfo.web_url }`);
oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ exercise.gitlabCreationInfo.http_url_to_repo }`);
oraInfo(`${ chalk.magenta('SSH Repo:') } ${ exercise.gitlabCreationInfo.ssh_url_to_repo }`);
} catch ( error ) {
return;
}
}
// Clone the repository this.exercise = await DojoBackendManager.createExercise(this.assignment!.name, this.members!);
{
if ( options.clone ) {
console.log(chalk.cyan('Please wait while we are cloning the repository...'));
await GitlabManager.cloneRepository(options.clone, exercise.gitlabCreationInfo.ssh_url_to_repo, `DojoExercise - ${ exercise.assignmentName }`, true, 0); await ExerciseHelper.displayDetails(this.exercise);
} }
}
protected async commandAction(options: CommandOptions): Promise<void> {
try {
await this.dataRetrieval(options);
await this.createExercise();
await ExerciseHelper.clone(this.exercise, options.clone ?? false);
} catch ( e ) { /* Do nothing */ }
} }
} }
......
import CommanderCommand from '../../CommanderCommand';
import AccessesHelper from '../../../helpers/AccessesHelper';
import TextStyle from '../../../types/TextStyle';
import ExerciseHelper from '../../../helpers/Dojo/ExerciseHelper';
class ExerciseDeleteCommand extends CommanderCommand {
protected commandName: string = 'delete';
protected defineCommand(): void {
this.command
.description('delete an exercise')
.argument('id or url', 'id or url of the exercise')
.action(this.commandAction.bind(this));
}
private async dataRetrieval() {
console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
await AccessesHelper.checkStudent();
}
protected async commandAction(exerciseIdOrUrl: string): Promise<void> {
try {
await this.dataRetrieval();
await ExerciseHelper.delete(exerciseIdOrUrl);
} catch ( e ) { /* Do nothing */ }
}
}
export default new ExerciseDeleteCommand();
import CommanderCommand from '../../CommanderCommand';
import AccessesHelper from '../../../helpers/AccessesHelper';
import TextStyle from '../../../types/TextStyle';
import ExerciseHelper from '../../../helpers/Dojo/ExerciseHelper';
import Exercise from '../../../sharedByClients/models/Exercise';
import DojoBackendManager from '../../../managers/DojoBackendManager';
import ora from 'ora';
import Config from '../../../config/Config';
class ExerciseInfoCommand extends CommanderCommand {
protected commandName: string = 'info';
protected defineCommand(): void {
this.command
.description('delete an exercise')
.argument('id or url', 'id or url of the exercise')
.action(this.commandAction.bind(this));
}
private async dataRetrieval(exerciseIdOrUrl: string): Promise<Exercise> {
console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
await AccessesHelper.checkStudent();
// Fetch exercise
const exercisesGetSpinner: ora.Ora = ora({
text : `Checking exercise`,
indent: 4
}).start();
const exercise = await DojoBackendManager.getExercise(exerciseIdOrUrl);
if ( !exercise ) {
exercisesGetSpinner.fail(`Exercise not found`);
throw new Error();
}
exercisesGetSpinner.succeed(`Exercise fetched successfully`);
return exercise;
}
protected async commandAction(exerciseIdOrUrl: string): Promise<void> {
try {
const exercise = await this.dataRetrieval(exerciseIdOrUrl);
return ExerciseHelper.displayDetails(exercise, Config.interactiveMode);
} catch ( e ) { /* Do nothing */ }
}
}
export default new ExerciseInfoCommand();