Added tests and changed name

This commit is contained in:
Adina Totorean 2025-01-31 10:07:38 +02:00
parent 541f289466
commit 0a57678acc
10 changed files with 66 additions and 91 deletions

View file

@ -6,14 +6,17 @@ import {
type INodeProperties, type INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { getAuthorizationTokenUsingMasterKey } from '../nodes/Microsoft/AzureCosmosDB/GenericFunctions'; import {
getAuthorizationTokenUsingMasterKey,
HeaderConstants,
} from '../nodes/Microsoft/CosmosDB/GenericFunctions';
export class AzureCosmosDbSharedKeyApi implements ICredentialType { export class MicrosoftCosmosDbSharedKeyApi implements ICredentialType {
name = 'azureCosmosDbSharedKeyApi'; name = 'microsoftCosmosDbSharedKeyApi';
displayName = 'Azure Cosmos DB API'; displayName = 'Azure Cosmos DB API';
documentationUrl = 'azureCosmosDb'; documentationUrl = 'microsoftCosmosDb';
properties: INodeProperties[] = [ properties: INodeProperties[] = [
{ {
@ -75,8 +78,9 @@ export class AzureCosmosDbSharedKeyApi implements ICredentialType {
} }
let resourceType = ''; let resourceType = '';
const resourceLink = requestOptions.url; const resourceLink = '/dbs/first_database_1' + requestOptions.url;
console.log('Link', resourceLink);
if (resourceLink.includes('/colls')) { if (resourceLink.includes('/colls')) {
resourceType = 'colls'; resourceType = 'colls';
} else if (resourceLink.includes('/docs')) { } else if (resourceLink.includes('/docs')) {
@ -86,6 +90,7 @@ export class AzureCosmosDbSharedKeyApi implements ICredentialType {
} else { } else {
throw new ApplicationError('Unable to determine resourceType'); throw new ApplicationError('Unable to determine resourceType');
} }
console.log('Type', resourceType);
if (requestOptions.method) { if (requestOptions.method) {
const authToken = getAuthorizationTokenUsingMasterKey( const authToken = getAuthorizationTokenUsingMasterKey(
@ -96,10 +101,10 @@ export class AzureCosmosDbSharedKeyApi implements ICredentialType {
credentials.key as string, credentials.key as string,
); );
requestOptions.headers.Authorization = authToken; requestOptions.headers[HeaderConstants.AUTHORIZATION] = authToken;
} }
console.log('Final requestOptions headers:', requestOptions.headers); console.log('Final requestOptions:', requestOptions);
return requestOptions; return requestOptions;
} }

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -7,8 +7,8 @@ import { searchCollections } from './GenericFunctions';
export class AzureCosmosDb implements INodeType { export class AzureCosmosDb implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
displayName: 'Azure Cosmos DB', displayName: 'Cosmos DB',
name: 'azureCosmosDb', name: 'cosmosDb',
icon: { icon: {
light: 'file:CosmosDB.svg', light: 'file:CosmosDB.svg',
dark: 'file:CosmosDB.svg', dark: 'file:CosmosDB.svg',
@ -24,7 +24,7 @@ export class AzureCosmosDb implements INodeType {
outputs: [NodeConnectionType.Main], outputs: [NodeConnectionType.Main],
credentials: [ credentials: [
{ {
name: 'azureCosmosDbSharedKeyApi', name: 'microsoftCosmosDbSharedKeyApi',
required: true, required: true,
displayOptions: { displayOptions: {
show: { show: {

View file

@ -11,34 +11,34 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { ApplicationError } from 'n8n-workflow'; import { ApplicationError } from 'n8n-workflow';
// export const HeaderConstants = { export const HeaderConstants = {
// // Required // Required
// AUTHORIZATION: 'Authorization', AUTHORIZATION: 'Authorization',
// CONTENT_TYPE: 'Content-Type', CONTENT_TYPE: 'Content-Type',
// X_MS_DATE: 'x-ms-date', X_MS_DATE: 'x-ms-date',
// X_MS_VERSION: 'x-ms-version', X_MS_VERSION: 'x-ms-version',
// //Required - for session consistency only //Required - for session consistency only
// X_MS_SESSION_TOKEN: 'x-ms-session-token', X_MS_SESSION_TOKEN: 'x-ms-session-token',
// // Optional // Optional
// IF_MATCH: 'If-Match', IF_MATCH: 'If-Match',
// IF_NONE_MATCH: 'If-None-Match', IF_NONE_MATCH: 'If-None-Match',
// IF_MODIFIED_SINCE: 'If-Modified-Since', IF_MODIFIED_SINCE: 'If-Modified-Since',
// USER_AGENT: 'User-Agent', USER_AGENT: 'User-Agent',
// X_MS_ACTIVITY_ID: 'x-ms-activity-id', X_MS_ACTIVITY_ID: 'x-ms-activity-id',
// X_MS_CONSISTENCY_LEVEL: 'x-ms-consistency-level', X_MS_CONSISTENCY_LEVEL: 'x-ms-consistency-level',
// X_MS_CONTINUATION: 'x-ms-continuation', X_MS_CONTINUATION: 'x-ms-continuation',
// X_MS_MAX_ITEM_COUNT: 'x-ms-max-item-count', X_MS_MAX_ITEM_COUNT: 'x-ms-max-item-count',
// X_MS_DOCUMENTDB_PARTITIONKEY: 'x-ms-documentdb-partitionkey', X_MS_DOCUMENTDB_PARTITIONKEY: 'x-ms-documentdb-partitionkey',
// X_MS_DOCUMENTDB_ISQUERY: 'x-ms-documentdb-isquery', X_MS_DOCUMENTDB_ISQUERY: 'x-ms-documentdb-isquery',
// X_MS_DOCUMENTDB_QUERY_ENABLECROSSPARTITION: 'x-ms-documentdb-query-enablecrosspartition', X_MS_DOCUMENTDB_QUERY_ENABLECROSSPARTITION: 'x-ms-documentdb-query-enablecrosspartition',
// A_IM: 'A-IM', A_IM: 'A-IM',
// X_MS_DOCUMENTDB_PARTITIONKEYRANGEID: 'x-ms-documentdb-partitionkeyrangeid', X_MS_DOCUMENTDB_PARTITIONKEYRANGEID: 'x-ms-documentdb-partitionkeyrangeid',
// X_MS_COSMOS_ALLOW_TENTATIVE_WRITES: 'x-ms-cosmos-allow-tentative-writes', X_MS_COSMOS_ALLOW_TENTATIVE_WRITES: 'x-ms-cosmos-allow-tentative-writes',
// PREFIX_FOR_STORAGE: 'x-ms-', PREFIX_FOR_STORAGE: 'x-ms-',
// }; };
export function getAuthorizationTokenUsingMasterKey( export function getAuthorizationTokenUsingMasterKey(
verb: string, verb: string,
@ -52,15 +52,13 @@ export function getAuthorizationTokenUsingMasterKey(
`${verb.toLowerCase()}\n` + `${verb.toLowerCase()}\n` +
`${resourceType.toLowerCase()}\n` + `${resourceType.toLowerCase()}\n` +
`${resourceLink}\n` + `${resourceLink}\n` +
`${date.toLowerCase()}\n` + `${date}\n` +
'\n'; '\n';
const hmacSha256 = crypto.createHmac('sha256', key); const hmacSha256 = crypto.createHmac('sha256', key);
const hashPayload = hmacSha256.update(payload, 'utf8').digest('base64'); const hashPayload = hmacSha256.update(payload, 'utf8').digest('base64');
const authorizationString = `type=master&ver=1.0&sig=${hashPayload}`; return encodeURIComponent('type=master&ver=1.0&sig=' + hashPayload);
return authorizationString;
} }
export async function handlePagination( export async function handlePagination(
@ -118,11 +116,11 @@ export async function handlePagination(
return aggregatedResult.map((result) => ({ json: result })); return aggregatedResult.map((result) => ({ json: result }));
} }
export async function azureCosmosDbRequest( export async function microsoftCosmosDbRequest(
this: ILoadOptionsFunctions, this: ILoadOptionsFunctions,
opts: IHttpRequestOptions, opts: IHttpRequestOptions,
): Promise<IDataObject> { ): Promise<IDataObject> {
const credentials = await this.getCredentials('azureCosmosDbSharedKeyApi'); const credentials = await this.getCredentials('microsoftCosmosDbSharedKeyApi');
const databaseAccount = credentials?.account; const databaseAccount = credentials?.account;
if (!databaseAccount) { if (!databaseAccount) {
@ -131,7 +129,7 @@ export async function azureCosmosDbRequest(
const requestOptions: IHttpRequestOptions = { const requestOptions: IHttpRequestOptions = {
...opts, ...opts,
baseURL: `https://${databaseAccount}.documents.azure.com`, baseURL: `${credentials.baseUrl}`,
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -156,7 +154,7 @@ export async function azureCosmosDbRequest(
return (await this.helpers.requestWithAuthentication.call( return (await this.helpers.requestWithAuthentication.call(
this, this,
'azureCosmosDbSharedKeyApi', 'microsoftCosmosDbSharedKeyApi',
requestOptions, requestOptions,
)) as IDataObject; )) as IDataObject;
} catch (error) { } catch (error) {
@ -197,7 +195,7 @@ export async function searchCollections(
url: '/colls', url: '/colls',
}; };
const responseData: IDataObject = await azureCosmosDbRequest.call(this, opts); const responseData: IDataObject = await microsoftCosmosDbRequest.call(this, opts);
const responseBody = responseData as { const responseBody = responseData as {
DocumentCollections: IDataObject[]; DocumentCollections: IDataObject[];
@ -220,37 +218,3 @@ export async function searchCollections(
results, results,
}; };
} }
// export async function searchDatabases(
// this: ILoadOptionsFunctions,
// filter?: string,
// ): Promise<INodeListSearchResult> {
// const opts: IHttpRequestOptions = {
// method: 'GET',
// url: '/dbs',
// };
// const responseData: IDataObject = await azureCosmosDbRequest.call(this, opts);
// console.log('Got this response', responseData)
// const responseBody = responseData as {
// Databases: IDataObject[];
// };
// const databases = responseBody.Databases;
// if (!databases) {
// return { results: [] };
// }
// const results: INodeListSearchItems[] = databases
// .map((database) => ({
// name: String(database.id),
// value: String(database.id),
// }))
// .filter((database) => !filter || database.name.includes(filter))
// .sort((a, b) => a.name.localeCompare(b.name));
// return {
// results,
// };
// }

View file

@ -1,6 +1,6 @@
import { azureCosmosDbRequest } from '../GenericFunctions'; import { microsoftCosmosDbRequest } from '../GenericFunctions';
describe('GenericFunctions - azureCosmosDbRequest', () => { describe('GenericFunctions - microsoftCosmosDbRequest', () => {
let mockContext: any; let mockContext: any;
let mockRequestWithAuthentication: jest.Mock; let mockRequestWithAuthentication: jest.Mock;
@ -17,6 +17,12 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
test('should make a successful request with correct options', async () => { test('should make a successful request with correct options', async () => {
mockRequestWithAuthentication.mockResolvedValueOnce({ success: true }); mockRequestWithAuthentication.mockResolvedValueOnce({ success: true });
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' }); (mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({
database: 'first_database_1',
});
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({
baseUrl: 'https://us-east-1.documents.azure.com/dbs',
});
const requestOptions = { const requestOptions = {
method: 'GET' as const, method: 'GET' as const,
@ -26,13 +32,13 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
}, },
}; };
const result = await azureCosmosDbRequest.call(mockContext, requestOptions); const result = await microsoftCosmosDbRequest.call(mockContext, requestOptions);
expect(mockRequestWithAuthentication).toHaveBeenCalledWith( expect(mockRequestWithAuthentication).toHaveBeenCalledWith(
'azureCosmosDbSharedKeyApi', 'microsoftCosmosDbSharedKeyApi',
expect.objectContaining({ expect.objectContaining({
method: 'GET', method: 'GET',
baseURL: 'https://us-east-1.documents.azure.com', baseURL: 'https://us-east-1.documents.azure.com/dbs/first_database_1',
url: '/example-endpoint', url: '/example-endpoint',
headers: expect.objectContaining({ headers: expect.objectContaining({
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -55,7 +61,7 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
}, },
}; };
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow( await expect(microsoftCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
'Database account not found in credentials!', 'Database account not found in credentials!',
); );
@ -81,7 +87,7 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
}, },
}; };
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow( await expect(microsoftCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
'The Cosmos DB credentials are not valid!', 'The Cosmos DB credentials are not valid!',
); );
@ -107,7 +113,7 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
}, },
}; };
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow( await expect(microsoftCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
'The Cosmos DB credentials are not valid!', 'The Cosmos DB credentials are not valid!',
); );
@ -133,7 +139,7 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
}, },
}; };
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow( await expect(microsoftCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
'The requested resource was not found!', 'The requested resource was not found!',
); );
@ -155,7 +161,7 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
}, },
}; };
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow( await expect(microsoftCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
'Cosmos DB error response [500]: Internal Server Error', 'Cosmos DB error response [500]: Internal Server Error',
); );
@ -176,7 +182,7 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
}, },
}; };
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow( await expect(microsoftCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
'Cosmos DB error response [undefined]: Unexpected failure', 'Cosmos DB error response [undefined]: Unexpected failure',
); );

View file

@ -39,7 +39,6 @@
"dist/credentials/AutomizyApi.credentials.js", "dist/credentials/AutomizyApi.credentials.js",
"dist/credentials/AutopilotApi.credentials.js", "dist/credentials/AutopilotApi.credentials.js",
"dist/credentials/Aws.credentials.js", "dist/credentials/Aws.credentials.js",
"dist/credentials/AzureCosmosDbSharedKeyApi.credentials.js",
"dist/credentials/BambooHrApi.credentials.js", "dist/credentials/BambooHrApi.credentials.js",
"dist/credentials/BannerbearApi.credentials.js", "dist/credentials/BannerbearApi.credentials.js",
"dist/credentials/BaserowApi.credentials.js", "dist/credentials/BaserowApi.credentials.js",
@ -218,6 +217,7 @@
"dist/credentials/MetabaseApi.credentials.js", "dist/credentials/MetabaseApi.credentials.js",
"dist/credentials/MessageBirdApi.credentials.js", "dist/credentials/MessageBirdApi.credentials.js",
"dist/credentials/MetabaseApi.credentials.js", "dist/credentials/MetabaseApi.credentials.js",
"dist/credentials/MicrosoftCosmosDbSharedKeyApi.credentials.js",
"dist/credentials/MicrosoftDynamicsOAuth2Api.credentials.js", "dist/credentials/MicrosoftDynamicsOAuth2Api.credentials.js",
"dist/credentials/MicrosoftEntraOAuth2Api.credentials.js", "dist/credentials/MicrosoftEntraOAuth2Api.credentials.js",
"dist/credentials/MicrosoftExcelOAuth2Api.credentials.js", "dist/credentials/MicrosoftExcelOAuth2Api.credentials.js",
@ -626,7 +626,7 @@
"dist/nodes/Merge/Merge.node.js", "dist/nodes/Merge/Merge.node.js",
"dist/nodes/MessageBird/MessageBird.node.js", "dist/nodes/MessageBird/MessageBird.node.js",
"dist/nodes/Metabase/Metabase.node.js", "dist/nodes/Metabase/Metabase.node.js",
"dist/nodes/Microsoft/AzureCosmosDB/AzureCosmosDb.node.js", "dist/nodes/Microsoft/CosmosDB/CosmosDb.node.js",
"dist/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.js", "dist/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.js",
"dist/nodes/Microsoft/Entra/MicrosoftEntra.node.js", "dist/nodes/Microsoft/Entra/MicrosoftEntra.node.js",
"dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js", "dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js",