Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions src/m365/file/commands/convert/convert-pdf.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import { telemetry } from '../../../../telemetry.js';
import { pid } from '../../../../utils/pid.js';
import { session } from '../../../../utils/session.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import { z } from 'zod';
import commands from '../../commands.js';
import command from './convert-pdf.js';

describe(commands.CONVERT_PDF, () => {
let log: string[];
let logger: Logger;
let commandInfo: CommandInfo;
let commandOptionsSchema: z.ZodTypeAny;
let unlinkSyncStub: sinon.SinonStub;
const mockPdfFile = 'pdf';
let pdfConvertResponseStream: PassThrough;
Expand All @@ -32,6 +34,7 @@ describe(commands.CONVERT_PDF, () => {
auth.connection.active = true;
unlinkSyncStub = sinon.stub(fs, 'unlinkSync').returns();
commandInfo = cli.getCommandInfo(command);
commandOptionsSchema = commandInfo.command.getSchemaToParse()!;
});

beforeEach(() => {
Expand Down Expand Up @@ -1749,25 +1752,25 @@ describe(commands.CONVERT_PDF, () => {

it(`fails validation if the specified local source file doesn't exist`, async () => {
sinon.stub(fs, 'existsSync').callsFake(() => false);
const actual = await command.validate({ options: { sourceFile: 'file.docx', targetFile: 'file.pdf' } }, commandInfo);
assert.notStrictEqual(actual, true);
const actual = commandOptionsSchema.safeParse({ sourceFile: 'file.docx', targetFile: 'file.pdf' });
assert.strictEqual(actual.success, false);
});

it(`fails validation if another file exists at the path specified in the target file`, async () => {
sinon.stub(fs, 'existsSync').callsFake(() => true);
const actual = await command.validate({ options: { sourceFile: 'file.docx', targetFile: 'file.pdf' } }, commandInfo);
assert.notStrictEqual(actual, true);
const actual = commandOptionsSchema.safeParse({ sourceFile: 'file.docx', targetFile: 'file.pdf' });
assert.strictEqual(actual.success, false);
});

it(`passes validation if the source file is a URL`, async () => {
sinon.stub(fs, 'existsSync').callsFake(() => false);
const actual = await command.validate({ options: { sourceFile: 'https://contoso.sharepoint.com/Shared Documents/file.docx', targetFile: 'file.pdf' } }, commandInfo);
assert.strictEqual(actual, true);
const actual = commandOptionsSchema.safeParse({ sourceFile: 'https://contoso.sharepoint.com/Shared Documents/file.docx', targetFile: 'file.pdf' });
assert.strictEqual(actual.success, true);
});

it(`passes validation if the target file is a URL`, async () => {
sinon.stub(fs, 'existsSync').callsFake(() => true);
const actual = await command.validate({ options: { sourceFile: 'file.docx', targetFile: 'https://contoso.sharepoint.com/Shared Documents/file.pdf' } }, commandInfo);
assert.strictEqual(actual, true);
const actual = commandOptionsSchema.safeParse({ sourceFile: 'file.docx', targetFile: 'https://contoso.sharepoint.com/Shared Documents/file.pdf' });
assert.strictEqual(actual.success, true);
});
});
63 changes: 21 additions & 42 deletions src/m365/file/commands/convert/convert-pdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,27 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
import { v4 } from 'uuid';
import { z } from 'zod';
import auth from '../../../../Auth.js';
import { Logger } from '../../../../cli/Logger.js';
import { CommandError } from '../../../../Command.js';
import GlobalOptions from '../../../../GlobalOptions.js';
import { CommandError, globalOptionsZod } from '../../../../Command.js';
import request, { CliRequestOptions } from '../../../../request.js';
import { accessToken } from '../../../../utils/accessToken.js';
import GraphCommand from '../../../base/GraphCommand.js';
import commands from '../../commands.js';

export const options = z.strictObject({
...globalOptionsZod.shape,
sourceFile: z.string().alias('s'),
targetFile: z.string().alias('t')
});

declare type Options = z.infer<typeof options>;

interface CommandArgs {
options: Options;
}

interface Options extends GlobalOptions {
sourceFile: string;
targetFile: string;
}

class FileConvertPdfCommand extends GraphCommand {
// Graph's drive item URL of the source file
private sourceFileGraphUrl?: string;
Expand All @@ -33,42 +36,18 @@ class FileConvertPdfCommand extends GraphCommand {
return 'Converts the specified file to PDF using Microsoft Graph';
}

constructor() {
super();

this.#initOptions();
this.#initValidators();
public get schema(): z.ZodType | undefined {
return options;
}

#initOptions(): void {
this.options.unshift(
{
option: '-s, --sourceFile <sourceFile>'
},
{
option: '-t, --targetFile <targetFile>'
}
);
}

#initValidators(): void {
this.validators.push(
async (args: CommandArgs) => {
if (!args.options.sourceFile.toLowerCase().startsWith('https://') &&
!fs.existsSync(args.options.sourceFile)) {
// assume local path
return `Specified source file ${args.options.sourceFile} doesn't exist`;
}

if (!args.options.targetFile.toLowerCase().startsWith('https://') &&
fs.existsSync(args.options.targetFile)) {
// assume local path
return `Another file found at ${args.options.targetFile}`;
}

return true;
}
);
public getRefinedSchema(schema: typeof options): z.ZodObject<any> | undefined {
return schema
.refine(options => options.sourceFile.toLowerCase().startsWith('https://') || fs.existsSync(options.sourceFile), {
error: e => `Specified source file ${(e.input as Options).sourceFile} doesn't exist`
})
.refine(options => options.targetFile.toLowerCase().startsWith('https://') || !fs.existsSync(options.targetFile), {
error: e => `Another file found at ${(e.input as Options).targetFile}`
});
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
Expand Down Expand Up @@ -467,4 +446,4 @@ class FileConvertPdfCommand extends GraphCommand {
}
}

export default new FileConvertPdfCommand();
export default new FileConvertPdfCommand();
31 changes: 19 additions & 12 deletions src/m365/file/commands/file-add.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import { telemetry } from '../../../telemetry.js';
import { pid } from '../../../utils/pid.js';
import { session } from '../../../utils/session.js';
import { sinonUtil } from '../../../utils/sinonUtil.js';
import { z } from 'zod';
import commands from '../commands.js';
import command from './file-add.js';

describe(commands.ADD, () => {
let log: string[];
let logger: Logger;
let commandInfo: CommandInfo;
let commandOptionsSchema: z.ZodTypeAny;

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
Expand All @@ -26,6 +28,7 @@ describe(commands.ADD, () => {
sinon.stub(session, 'getId').returns('');
auth.connection.active = true;
commandInfo = cli.getCommandInfo(command);
commandOptionsSchema = commandInfo.command.getSchemaToParse()!;
});

beforeEach(() => {
Expand Down Expand Up @@ -943,25 +946,29 @@ describe(commands.ADD, () => {

it(`fails validation if the specified local source file doesn't exist`, async () => {
sinon.stub(fs, 'existsSync').returns(false);
const actual = await command.validate({ options: { filePath: 'file.pdf', folderUrl: 'https://contoso.sharepoint.com/Shared Documents' } }, commandInfo);
assert.notStrictEqual(actual, true);
const actual = commandOptionsSchema.safeParse({ filePath: 'file.pdf', folderUrl: 'https://contoso.sharepoint.com/Shared Documents' });
assert.strictEqual(actual.success, false);
});

it(`fails validation if the specified folderUrl is invalid`, async () => {
sinon.stub(fs, 'existsSync').returns(true);
const actual = commandOptionsSchema.safeParse({ filePath: 'file.pdf', folderUrl: '/' });
assert.strictEqual(actual.success, false);
});

it(`fails validation if the specified siteUrl is invalid`, async () => {
sinon.stub(fs, 'existsSync').returns(true);
const actual = await command.validate({
options: {
filePath: 'file.pdf',
folderUrl: 'https://contoso.sharepoint.com/Shared Documents',
siteUrl: '/'
}
}, commandInfo);
assert.notStrictEqual(actual, true);
const actual = commandOptionsSchema.safeParse({
filePath: 'file.pdf',
folderUrl: 'https://contoso.sharepoint.com/Shared Documents',
siteUrl: '/'
});
assert.strictEqual(actual.success, false);
});

it(`passes validation if the target file is a URL`, async () => {
sinon.stub(fs, 'existsSync').returns(true);
const actual = await command.validate({ options: { filePath: 'file.pdf', folderUrl: 'https://contoso.sharepoint.com/Shared Documents' } }, commandInfo);
assert.strictEqual(actual, true);
const actual = commandOptionsSchema.safeParse({ filePath: 'file.pdf', folderUrl: 'https://contoso.sharepoint.com/Shared Documents' });
assert.strictEqual(actual.success, true);
});
});
75 changes: 30 additions & 45 deletions src/m365/file/commands/file-add.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import fs from 'fs';
import path from 'path';
import { z } from 'zod';
import { Logger } from '../../../cli/Logger.js';
import GlobalOptions from '../../../GlobalOptions.js';
import { globalOptionsZod } from '../../../Command.js';
import request, { CliRequestOptions } from '../../../request.js';
import { validation } from '../../../utils/validation.js';
import GraphCommand from '../../base/GraphCommand.js';
import commands from '../commands.js';

export const options = z.strictObject({
...globalOptionsZod.shape,
folderUrl: z.string()
.refine(url => validation.isValidSharePointUrl(url) === true, {
error: e => `'${e.input}' is not a valid SharePoint Online site URL.`
})
.alias('u'),
filePath: z.string().alias('p'),
siteUrl: z.string().optional()
});

declare type Options = z.infer<typeof options>;

interface CommandArgs {
options: Options;
}

interface Options extends GlobalOptions {
folderUrl: string;
filePath: string;
siteUrl?: string;
}

class FileAddCommand extends GraphCommand {
public get name(): string {
return commands.ADD;
Expand All @@ -26,47 +34,24 @@ class FileAddCommand extends GraphCommand {
return 'Uploads file to the specified site';
}

constructor() {
super();

this.#initTelemetry();
this.#initOptions();
this.#initValidators();
}

#initTelemetry(): void {
this.telemetry.push((args: CommandArgs) => {
Object.assign(this.telemetryProperties, {
siteUrl: typeof args.options.siteUrl !== 'undefined'
});
});
}

#initOptions(): void {
this.options.unshift(
{ option: '-u, --folderUrl <folderUrl>' },
{ option: '-p, --filePath <filePath>' },
{ option: '--siteUrl [siteUrl]' }
);
public get schema(): z.ZodType | undefined {
return options;
}

#initValidators(): void {
this.validators.push(
async (args: CommandArgs) => {
if (!fs.existsSync(args.options.filePath)) {
return `Specified source file ${args.options.sourceFile} doesn't exist`;
public getRefinedSchema(schema: typeof options): z.ZodObject<any> | undefined {
return schema
.refine(options => fs.existsSync(options.filePath), {
error: e => `Specified source file ${(e.input as Options).filePath} doesn't exist`
})
.refine(options => {
if (options.siteUrl) {
return validation.isValidSharePointUrl(options.siteUrl) === true;
}

if (args.options.siteUrl) {
const isValidSiteUrl = validation.isValidSharePointUrl(args.options.siteUrl);
if (isValidSiteUrl !== true) {
return isValidSiteUrl;
}
}

return validation.isValidSharePointUrl(args.options.folderUrl);
}
);
return true;
}, {
error: e => `'${(e.input as Options).siteUrl}' is not a valid SharePoint Online site URL.`
});
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
Expand Down Expand Up @@ -279,4 +264,4 @@ class FileAddCommand extends GraphCommand {
}
}

export default new FileAddCommand();
export default new FileAddCommand();
Loading
Loading