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

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 {
description: INodeTypeDescription = {
displayName: 'Azure Cosmos DB',
name: 'azureCosmosDb',
displayName: 'Cosmos DB',
name: 'cosmosDb',
icon: {
light: 'file:CosmosDB.svg',
dark: 'file:CosmosDB.svg',
@ -24,7 +24,7 @@ export class AzureCosmosDb implements INodeType {
outputs: [NodeConnectionType.Main],
credentials: [
{
name: 'azureCosmosDbSharedKeyApi',
name: 'microsoftCosmosDbSharedKeyApi',
required: true,
displayOptions: {
show: {

View file

@ -11,34 +11,34 @@ import type {
} from 'n8n-workflow';
import { ApplicationError } from 'n8n-workflow';
// export const HeaderConstants = {
// // Required
// AUTHORIZATION: 'Authorization',
// CONTENT_TYPE: 'Content-Type',
// X_MS_DATE: 'x-ms-date',
// X_MS_VERSION: 'x-ms-version',
export const HeaderConstants = {
// Required
AUTHORIZATION: 'Authorization',
CONTENT_TYPE: 'Content-Type',
X_MS_DATE: 'x-ms-date',
X_MS_VERSION: 'x-ms-version',
// //Required - for session consistency only
// X_MS_SESSION_TOKEN: 'x-ms-session-token',
//Required - for session consistency only
X_MS_SESSION_TOKEN: 'x-ms-session-token',
// // Optional
// IF_MATCH: 'If-Match',
// IF_NONE_MATCH: 'If-None-Match',
// IF_MODIFIED_SINCE: 'If-Modified-Since',
// USER_AGENT: 'User-Agent',
// X_MS_ACTIVITY_ID: 'x-ms-activity-id',
// X_MS_CONSISTENCY_LEVEL: 'x-ms-consistency-level',
// X_MS_CONTINUATION: 'x-ms-continuation',
// X_MS_MAX_ITEM_COUNT: 'x-ms-max-item-count',
// X_MS_DOCUMENTDB_PARTITIONKEY: 'x-ms-documentdb-partitionkey',
// X_MS_DOCUMENTDB_ISQUERY: 'x-ms-documentdb-isquery',
// X_MS_DOCUMENTDB_QUERY_ENABLECROSSPARTITION: 'x-ms-documentdb-query-enablecrosspartition',
// A_IM: 'A-IM',
// X_MS_DOCUMENTDB_PARTITIONKEYRANGEID: 'x-ms-documentdb-partitionkeyrangeid',
// X_MS_COSMOS_ALLOW_TENTATIVE_WRITES: 'x-ms-cosmos-allow-tentative-writes',
// Optional
IF_MATCH: 'If-Match',
IF_NONE_MATCH: 'If-None-Match',
IF_MODIFIED_SINCE: 'If-Modified-Since',
USER_AGENT: 'User-Agent',
X_MS_ACTIVITY_ID: 'x-ms-activity-id',
X_MS_CONSISTENCY_LEVEL: 'x-ms-consistency-level',
X_MS_CONTINUATION: 'x-ms-continuation',
X_MS_MAX_ITEM_COUNT: 'x-ms-max-item-count',
X_MS_DOCUMENTDB_PARTITIONKEY: 'x-ms-documentdb-partitionkey',
X_MS_DOCUMENTDB_ISQUERY: 'x-ms-documentdb-isquery',
X_MS_DOCUMENTDB_QUERY_ENABLECROSSPARTITION: 'x-ms-documentdb-query-enablecrosspartition',
A_IM: 'A-IM',
X_MS_DOCUMENTDB_PARTITIONKEYRANGEID: 'x-ms-documentdb-partitionkeyrangeid',
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(
verb: string,
@ -52,15 +52,13 @@ export function getAuthorizationTokenUsingMasterKey(
`${verb.toLowerCase()}\n` +
`${resourceType.toLowerCase()}\n` +
`${resourceLink}\n` +
`${date.toLowerCase()}\n` +
`${date}\n` +
'\n';
const hmacSha256 = crypto.createHmac('sha256', key);
const hashPayload = hmacSha256.update(payload, 'utf8').digest('base64');
const authorizationString = `type=master&ver=1.0&sig=${hashPayload}`;
return authorizationString;
return encodeURIComponent('type=master&ver=1.0&sig=' + hashPayload);
}
export async function handlePagination(
@ -118,11 +116,11 @@ export async function handlePagination(
return aggregatedResult.map((result) => ({ json: result }));
}
export async function azureCosmosDbRequest(
export async function microsoftCosmosDbRequest(
this: ILoadOptionsFunctions,
opts: IHttpRequestOptions,
): Promise<IDataObject> {
const credentials = await this.getCredentials('azureCosmosDbSharedKeyApi');
const credentials = await this.getCredentials('microsoftCosmosDbSharedKeyApi');
const databaseAccount = credentials?.account;
if (!databaseAccount) {
@ -131,7 +129,7 @@ export async function azureCosmosDbRequest(
const requestOptions: IHttpRequestOptions = {
...opts,
baseURL: `https://${databaseAccount}.documents.azure.com`,
baseURL: `${credentials.baseUrl}`,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
@ -156,7 +154,7 @@ export async function azureCosmosDbRequest(
return (await this.helpers.requestWithAuthentication.call(
this,
'azureCosmosDbSharedKeyApi',
'microsoftCosmosDbSharedKeyApi',
requestOptions,
)) as IDataObject;
} catch (error) {
@ -197,7 +195,7 @@ export async function searchCollections(
url: '/colls',
};
const responseData: IDataObject = await azureCosmosDbRequest.call(this, opts);
const responseData: IDataObject = await microsoftCosmosDbRequest.call(this, opts);
const responseBody = responseData as {
DocumentCollections: IDataObject[];
@ -220,37 +218,3 @@ export async function searchCollections(
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 mockRequestWithAuthentication: jest.Mock;
@ -17,6 +17,12 @@ describe('GenericFunctions - azureCosmosDbRequest', () => {
test('should make a successful request with correct options', async () => {
mockRequestWithAuthentication.mockResolvedValueOnce({ success: true });
(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 = {
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(
'azureCosmosDbSharedKeyApi',
'microsoftCosmosDbSharedKeyApi',
expect.objectContaining({
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',
headers: expect.objectContaining({
'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!',
);
@ -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!',
);
@ -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!',
);
@ -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!',
);
@ -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',
);
@ -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',
);

View file

@ -39,7 +39,6 @@
"dist/credentials/AutomizyApi.credentials.js",
"dist/credentials/AutopilotApi.credentials.js",
"dist/credentials/Aws.credentials.js",
"dist/credentials/AzureCosmosDbSharedKeyApi.credentials.js",
"dist/credentials/BambooHrApi.credentials.js",
"dist/credentials/BannerbearApi.credentials.js",
"dist/credentials/BaserowApi.credentials.js",
@ -218,6 +217,7 @@
"dist/credentials/MetabaseApi.credentials.js",
"dist/credentials/MessageBirdApi.credentials.js",
"dist/credentials/MetabaseApi.credentials.js",
"dist/credentials/MicrosoftCosmosDbSharedKeyApi.credentials.js",
"dist/credentials/MicrosoftDynamicsOAuth2Api.credentials.js",
"dist/credentials/MicrosoftEntraOAuth2Api.credentials.js",
"dist/credentials/MicrosoftExcelOAuth2Api.credentials.js",
@ -626,7 +626,7 @@
"dist/nodes/Merge/Merge.node.js",
"dist/nodes/MessageBird/MessageBird.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/Entra/MicrosoftEntra.node.js",
"dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js",