UNPKG

@pnp/cli-microsoft365

Version:

Manage Microsoft 365 and SharePoint Framework projects on any platform

177 lines • 8.88 kB
import { z } from 'zod'; import { globalOptionsZod } from '../../../../Command.js'; import request from '../../../../request.js'; import { validation } from '../../../../utils/validation.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; const allowedTlsVersions = ['v1_0', 'v1_1', 'v1_2', 'v1_3']; export const options = z.strictObject({ ...globalOptionsZod.shape, resource: z.string().alias('r'), notificationUrl: z.string().alias('u'), changeTypes: z.string().alias('c'), expirationDateTime: z.string().optional().alias('e'), clientState: z.string().optional().alias('s'), lifecycleNotificationUrl: z.string().optional(), notificationUrlAppId: z.string().optional(), latestTLSVersion: z.enum(allowedTlsVersions).optional(), withResourceData: z.boolean().optional(), encryptionCertificate: z.string().optional(), encryptionCertificateId: z.string().optional() }); const DEFAULT_EXPIRATION_DELAY_IN_MINUTES_PER_RESOURCE_TYPE = { // User, group, other directory resources 4230 minutes (under 3 days) "users": 4230, "groups": 4230, // Mail 4230 minutes (under 3 days) "/messages": 4230, // Calendar 4230 minutes (under 3 days) "/events": 4230, // Contacts 4230 minutes (under 3 days) "/contacts": 4230, // Group conversations 4230 minutes (under 3 days) "/conversations": 4230, // Drive root items 4230 minutes (under 3 days) "/drive/root": 4230, // Security alerts 43200 minutes (under 30 days) "security/alerts": 43200 }; const DEFAULT_EXPIRATION_DELAY_IN_MINUTES = 4230; const SAFE_MINUTES_DELTA = 1; class GraphSubscriptionAddCommand extends GraphCommand { get name() { return commands.SUBSCRIPTION_ADD; } get description() { return 'Creates a Microsoft Graph subscription'; } get schema() { return options; } getRefinedSchema(schema) { return schema .refine(options => { const url = options.notificationUrl.toLowerCase(); return url.startsWith('https://') || url.startsWith('eventhub:https://') || url.startsWith('eventgrid:?azuresubscriptionid='); }, { error: e => `The specified notification URL '${e.input.notificationUrl}' does not start with either 'https://' or 'EventHub:https://' or 'EventGrid:?azuresubscriptionid='`, path: ['notificationUrl'] }) .refine(options => this.isValidChangeTypes(options.changeTypes), { error: `The specified changeTypes are invalid. Valid options are 'created', 'updated' and 'deleted'`, path: ['changeTypes'] }) .refine(options => !options.expirationDateTime || validation.isValidISODateTime(options.expirationDateTime), { error: 'The expirationDateTime is not a valid ISO date string', path: ['expirationDateTime'] }) .refine(options => !options.clientState || options.clientState.length <= 128, { error: 'The clientState value exceeds the maximum length of 128 characters', path: ['clientState'] }) .refine(options => { if (!options.lifecycleNotificationUrl) { return true; } const url = options.lifecycleNotificationUrl.toLowerCase(); return url.startsWith('https://') || url.startsWith('eventhub:https://') || url.startsWith('eventgrid:?azuresubscriptionid='); }, { error: e => `The lifecycle notification URL '${e.input.lifecycleNotificationUrl}' does not start with either 'https://' or 'EventHub:https://' or 'EventGrid:?azuresubscriptionid='`, path: ['lifecycleNotificationUrl'] }) .refine(options => !options.withResourceData || options.encryptionCertificate, { error: `The 'encryptionCertificate' options is required to include the changed resource data`, path: ['encryptionCertificate'] }) .refine(options => !options.withResourceData || options.encryptionCertificateId, { error: `The 'encryptionCertificateId' options is required to include the changed resource data`, path: ['encryptionCertificateId'] }) .refine(options => !options.notificationUrlAppId || validation.isValidGuid(options.notificationUrlAppId), { error: e => `${e.input.notificationUrlAppId} is not a valid GUID for the 'notificationUrlAppId'`, path: ['notificationUrlAppId'] }); } async commandAction(logger, args) { const data = { changeType: args.options.changeTypes, resource: args.options.resource, notificationUrl: args.options.notificationUrl, expirationDateTime: await this.getExpirationDateTimeOrDefault(logger, args), clientState: args.options.clientState, includeResourceData: args.options.withResourceData, encryptionCertificate: args.options.encryptionCertificate, encryptionCertificateId: args.options.encryptionCertificateId, lifecycleNotificationUrl: args.options.lifecycleNotificationUrl, notificationUrlAppId: args.options.notificationUrlAppId, latestSupportedTlsVersion: args.options.latestTLSVersion }; const requestOptions = { url: `${this.resource}/v1.0/subscriptions`, headers: { accept: 'application/json;odata.metadata=none', 'content-type': 'application/json' }, data, responseType: 'json' }; try { const res = await request.post(requestOptions); await logger.log(res); } catch (err) { this.handleRejectedODataJsonPromise(err); } } async getExpirationDateTimeOrDefault(logger, args) { if (args.options.expirationDateTime) { if (this.debug) { await logger.logToStderr(`Expiration date time is specified (${args.options.expirationDateTime}).`); } return args.options.expirationDateTime; } if (this.debug) { await logger.logToStderr(`Expiration date time is not specified. Will try to get appropriate maximum value`); } const fromNow = (minutes) => { // convert minutes in milliseconds return new Date(Date.now() + (minutes * 60000)); }; const expirationDelayPerResource = DEFAULT_EXPIRATION_DELAY_IN_MINUTES_PER_RESOURCE_TYPE; for (const resource in expirationDelayPerResource) { if (args.options.resource.indexOf(resource) < 0) { continue; } const resolvedExpirationDelay = expirationDelayPerResource[resource]; // Compute the actual expirationDateTime argument from now const actualExpiration = fromNow(resolvedExpirationDelay - SAFE_MINUTES_DELTA); const actualExpirationIsoString = actualExpiration.toISOString(); if (this.debug) { await logger.logToStderr(`Matching resource in default values '${args.options.resource}' => '${resource}'`); await logger.logToStderr(`Resolved expiration delay: ${resolvedExpirationDelay} (safe delta: ${SAFE_MINUTES_DELTA})`); await logger.logToStderr(`Actual expiration date time: ${actualExpirationIsoString}`); } if (this.verbose) { await logger.logToStderr(`An expiration maximum delay is resolved for the resource '${args.options.resource}' : ${resolvedExpirationDelay} minutes.`); } return actualExpirationIsoString; } // If an resource specific expiration has not been found, return a default expiration delay if (this.verbose) { await logger.logToStderr(`An expiration maximum delay couldn't be resolved for the resource '${args.options.resource}'. Will use generic default value: ${DEFAULT_EXPIRATION_DELAY_IN_MINUTES} minutes.`); } const actualExpiration = fromNow(DEFAULT_EXPIRATION_DELAY_IN_MINUTES - SAFE_MINUTES_DELTA); const actualExpirationIsoString = actualExpiration.toISOString(); if (this.debug) { await logger.logToStderr(`Actual expiration date time: ${actualExpirationIsoString}`); } return actualExpirationIsoString; } isValidChangeTypes(changeTypes) { const validChangeTypes = ["created", "updated", "deleted"]; const invalidChangesTypes = changeTypes.split(",").filter(c => validChangeTypes.indexOf(c.trim()) < 0); return invalidChangesTypes.length === 0; } } export default new GraphSubscriptionAddCommand(); //# sourceMappingURL=subscription-add.js.map