Repository: n8n-io/n8n
File: packages/cli/src/credentials/credentials.service.ts (TypeScript)

Code before:
		if (
			!(await userHasScopes(user, ['credential:update'], false, { credentialId: credential.id }))
		) {
			mergedCredentials.data = decryptedData;
		}
	}

	/**
	 * Create a new credential in user's account and return it along the scopes
	 * If a projectId is send, then it also binds the credential to that specific project
	 */
	async createUnmanagedCredential(dto: CreateCredentialDto, user: User) {
		return await this.createCredential({ ...dto, isManaged: false }, user);
	}

	/**
	 * Used to check credential data for creating a new credential.
	 * TODO: consider refactoring enable using this for both creating and updating, right now only used for creation
	 * (likely only affects the validateExternalSecretsPermissions call)
	 */
	checkCredentialData(type: string, data: ICredentialDataDecryptedObject, user: User) {
		// check mandatory fields are present
		const credentialProperties = this.credentialsHelper.getCredentialsProperties(type);
		for (const property of credentialProperties) {
			/*
			 * displyaOpyions is possibly undefined, which causes displayParameter to default `true`, which is expected behaviur for UI
			 * however, missing `displayOptions` should not default to true for validation purposes as credentials can be overridden
			 * without displayOptions being explicitly set on every credential definition
			 */
			if (
				property.required &&
				property.displayOptions !== undefined &&
				displayParameter(data, property, null, null)
			) {
				// Check if value is present in data, if not, check if default value exists
				const value = data[property.name];
				const hasDefault =
					property.default !== undefined && property.default !== null && property.default !== '';
				if ((value === undefined || value === null || value === '') && !hasDefault) {
					throw new BadRequestError(
						`The field "${property.name}" is mandatory for credentials of type "${type}"`,
					);
				}
			}
		}
		validateExternalSecretsPermissions(user, data);
		this.validateOAuthCredentialUrls(type, data);
	}

	/**
	 * Validates that OAuth credential URL fields (authUrl, accessTokenUrl, etc.) use http/https only.

Review comment (suggestion):
```suggestion
			 * displayOpyions is possibly undefined, which causes displayParameter to default `true`, which is expected behavior for UI
```

Code after:
		if (
			!(await userHasScopes(user, ['credential:update'], false, { credentialId: credential.id }))
		) {
			mergedCredentials.data = decryptedData;
		}
	}

	/**
	 * Create a new credential in user's account and return it along the scopes
	 * If a projectId is send, then it also binds the credential to that specific project
	 */
	async createUnmanagedCredential(dto: CreateCredentialDto, user: User) {
		return await this.createCredential({ ...dto, isManaged: false }, user);
	}

	/**
	 * Used to check credential data for creating a new credential.
	 * TODO: consider refactoring enable using this for both creating and updating, right now only used for creation
	 * (likely only affects the validateExternalSecretsPermissions call)
	 */
	checkCredentialData(type: string, data: ICredentialDataDecryptedObject, user: User) {
		// check mandatory fields are present
		const credentialProperties = this.credentialsHelper.getCredentialsProperties(type);
		for (const property of credentialProperties) {
			/*
			 * displayOpyions is possibly undefined, which causes displayParameter to default `true`, which is expected behavior for UI
			 * however, missing `displayOptions` should not default to true for validation purposes as credentials can be overridden
			 * without displayOptions being explicitly set on every credential definition
			 */
			if (
				property.required &&
				property.displayOptions !== undefined &&
				displayParameter(data, property, null, null)
			) {
				// Check if value is present in data, if not, check if default value exists
				const value = data[property.name];
				const hasDefault =
					property.default !== undefined && property.default !== null && property.default !== '';
				if ((value === undefined || value === null || value === '') && !hasDefault) {
					throw new BadRequestError(
						`The field "${property.name}" is mandatory for credentials of type "${type}"`,
					);
				}
			}
		}
		validateExternalSecretsPermissions(user, data);
		this.validateOAuthCredentialUrls(type, data);
	}

	/**
	 * Validates that OAuth credential URL fields (authUrl, accessTokenUrl, etc.) use http/https only.