mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
Worked on credentials, tests and fields for both resources
This commit is contained in:
parent
6307ac162f
commit
541f289466
|
@ -33,12 +33,27 @@ export class AzureCosmosDbSharedKeyApi implements ICredentialType {
|
|||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Database',
|
||||
name: 'database',
|
||||
description: 'Database name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Base URL',
|
||||
name: 'baseUrl',
|
||||
type: 'hidden',
|
||||
default: '=https://{{ $self["account"] }}.documents.azure.com/dbs/{{ $self["database"] }}',
|
||||
},
|
||||
];
|
||||
|
||||
async authenticate(
|
||||
credentials: ICredentialDataDecryptedObject,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
console.log('Authenticate invoked with requestOptions:', requestOptions);
|
||||
|
||||
if (requestOptions.qs) {
|
||||
for (const [key, value] of Object.entries(requestOptions.qs)) {
|
||||
if (value === undefined) {
|
||||
|
@ -52,7 +67,7 @@ export class AzureCosmosDbSharedKeyApi implements ICredentialType {
|
|||
requestOptions.headers = {
|
||||
...requestOptions.headers,
|
||||
'x-ms-date': date,
|
||||
'x-ms-version': '2020-04-08',
|
||||
'x-ms-version': '2018-12-31',
|
||||
};
|
||||
|
||||
if (credentials.sessionToken) {
|
||||
|
@ -60,40 +75,16 @@ export class AzureCosmosDbSharedKeyApi implements ICredentialType {
|
|||
}
|
||||
|
||||
let resourceType = '';
|
||||
let resourceLink = '';
|
||||
if (requestOptions.body && typeof requestOptions.body === 'object') {
|
||||
const isCollectionRequest = 'colls' in requestOptions.body;
|
||||
const isDocumentRequest = 'docs' in requestOptions.body;
|
||||
const resourceLink = requestOptions.url;
|
||||
|
||||
if (isCollectionRequest) {
|
||||
resourceType = 'dbs';
|
||||
resourceLink = `dbs/${credentials.database}/colls`;
|
||||
} else if (isDocumentRequest) {
|
||||
resourceType = 'colls';
|
||||
const collId = requestOptions.qs?.collId || '';
|
||||
if (!collId) {
|
||||
throw new ApplicationError('Collection ID (collId) is required for document requests.');
|
||||
}
|
||||
resourceLink = `dbs/${credentials.database}/colls/${collId}/docs`;
|
||||
}
|
||||
} else if (requestOptions.qs && typeof requestOptions.qs === 'object') {
|
||||
const queryType = requestOptions.qs.queryType;
|
||||
|
||||
if (queryType === 'colls') {
|
||||
resourceType = 'dbs';
|
||||
resourceLink = `dbs/${credentials.database}/colls`;
|
||||
} else if (queryType === 'docs') {
|
||||
resourceType = 'colls';
|
||||
const collId = requestOptions.qs.collId || '';
|
||||
if (!collId) {
|
||||
throw new ApplicationError('Collection ID (collId) is required for document queries.');
|
||||
}
|
||||
resourceLink = `dbs/${credentials.database}/colls/${collId}/docs`;
|
||||
}
|
||||
if (resourceLink.includes('/colls')) {
|
||||
resourceType = 'colls';
|
||||
} else if (resourceLink.includes('/docs')) {
|
||||
resourceType = 'docs';
|
||||
} else if (resourceLink.includes('/dbs')) {
|
||||
resourceType = 'dbs';
|
||||
} else {
|
||||
throw new ApplicationError(
|
||||
'Invalid requestOptions: Either body or query string (qs) is required.',
|
||||
);
|
||||
throw new ApplicationError('Unable to determine resourceType');
|
||||
}
|
||||
|
||||
if (requestOptions.method) {
|
||||
|
@ -105,9 +96,11 @@ export class AzureCosmosDbSharedKeyApi implements ICredentialType {
|
|||
credentials.key as string,
|
||||
);
|
||||
|
||||
requestOptions.headers.authorization = authToken;
|
||||
requestOptions.headers.Authorization = authToken;
|
||||
}
|
||||
|
||||
console.log('Final requestOptions headers:', requestOptions.headers);
|
||||
|
||||
return requestOptions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { NodeConnectionType } from 'n8n-workflow';
|
|||
|
||||
import { containerFields, containerOperations } from './descriptions/ContainerDescription';
|
||||
import { itemFields, itemOperations } from './descriptions/ItemDescription';
|
||||
import { searchCollections, searchDatabases } from './GenericFunctions';
|
||||
import { searchCollections } from './GenericFunctions';
|
||||
|
||||
export class AzureCosmosDb implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
@ -34,9 +34,10 @@ export class AzureCosmosDb implements INodeType {
|
|||
},
|
||||
],
|
||||
requestDefaults: {
|
||||
baseURL: '=https://{$credentials.account}.documents.azure.com',
|
||||
baseURL: '={{$credentials.baseUrl}}',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
properties: [
|
||||
|
@ -79,7 +80,6 @@ export class AzureCosmosDb implements INodeType {
|
|||
methods = {
|
||||
listSearch: {
|
||||
searchCollections,
|
||||
searchDatabases,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import type {
|
|||
INodeListSearchResult,
|
||||
} from 'n8n-workflow';
|
||||
import { ApplicationError } from 'n8n-workflow';
|
||||
import * as querystring from 'querystring';
|
||||
|
||||
// export const HeaderConstants = {
|
||||
// // Required
|
||||
|
@ -49,7 +48,6 @@ export function getAuthorizationTokenUsingMasterKey(
|
|||
masterKey: string,
|
||||
): string {
|
||||
const key = Buffer.from(masterKey, 'base64');
|
||||
|
||||
const payload =
|
||||
`${verb.toLowerCase()}\n` +
|
||||
`${resourceType.toLowerCase()}\n` +
|
||||
|
@ -60,7 +58,7 @@ export function getAuthorizationTokenUsingMasterKey(
|
|||
const hmacSha256 = crypto.createHmac('sha256', key);
|
||||
const hashPayload = hmacSha256.update(payload, 'utf8').digest('base64');
|
||||
|
||||
const authorizationString = querystring.escape(`type=master&ver=1.0&sig=${hashPayload}`);
|
||||
const authorizationString = `type=master&ver=1.0&sig=${hashPayload}`;
|
||||
|
||||
return authorizationString;
|
||||
}
|
||||
|
@ -134,6 +132,10 @@ export async function azureCosmosDbRequest(
|
|||
const requestOptions: IHttpRequestOptions = {
|
||||
...opts,
|
||||
baseURL: `https://${databaseAccount}.documents.azure.com`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json: true,
|
||||
};
|
||||
|
||||
|
@ -150,6 +152,8 @@ export async function azureCosmosDbRequest(
|
|||
};
|
||||
|
||||
try {
|
||||
console.log('Final Request Options before Request:', requestOptions);
|
||||
|
||||
return (await this.helpers.requestWithAuthentication.call(
|
||||
this,
|
||||
'azureCosmosDbSharedKeyApi',
|
||||
|
@ -188,25 +192,9 @@ export async function searchCollections(
|
|||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const dbId = this.getNodeParameter('dbId') as string;
|
||||
if (!dbId) {
|
||||
throw new ApplicationError('Database ID is required');
|
||||
}
|
||||
|
||||
const credentials = await this.getCredentials('azureCosmosDbSharedKeyApi');
|
||||
const databaseAccount = credentials?.account;
|
||||
|
||||
if (!databaseAccount) {
|
||||
throw new ApplicationError('Account name not found in credentials!', { level: 'error' });
|
||||
}
|
||||
|
||||
const opts: IHttpRequestOptions = {
|
||||
method: 'GET',
|
||||
url: `/dbs/${dbId}/colls`,
|
||||
baseURL: `https://${databaseAccount}.documents.azure.com`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
url: '/colls',
|
||||
};
|
||||
|
||||
const responseData: IDataObject = await azureCosmosDbRequest.call(this, opts);
|
||||
|
@ -233,38 +221,36 @@ export async function searchCollections(
|
|||
};
|
||||
}
|
||||
|
||||
export async function searchDatabases(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const opts: IHttpRequestOptions = {
|
||||
method: 'GET',
|
||||
url: '/dbs',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
// export async function searchDatabases(
|
||||
// this: ILoadOptionsFunctions,
|
||||
// filter?: string,
|
||||
// ): Promise<INodeListSearchResult> {
|
||||
|
||||
const responseData: IDataObject = await azureCosmosDbRequest.call(this, opts);
|
||||
// const opts: IHttpRequestOptions = {
|
||||
// method: 'GET',
|
||||
// url: '/dbs',
|
||||
// };
|
||||
|
||||
const responseBody = responseData as {
|
||||
Databases: IDataObject[];
|
||||
};
|
||||
const databases = responseBody.Databases;
|
||||
// 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: [] };
|
||||
}
|
||||
// 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));
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
// return {
|
||||
// results,
|
||||
// };
|
||||
// }
|
||||
|
|
|
@ -20,7 +20,7 @@ export const containerOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'POST',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls',
|
||||
url: '/colls',
|
||||
},
|
||||
},
|
||||
action: 'Create container',
|
||||
|
@ -33,7 +33,7 @@ export const containerOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'DELETE',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls/{{ $parameter["collId"] }}',
|
||||
url: '=/colls/{{ $parameter["collId"] }}',
|
||||
},
|
||||
},
|
||||
action: 'Delete container',
|
||||
|
@ -46,7 +46,7 @@ export const containerOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'GET',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls/{{ $parameter["collId"] }}',
|
||||
url: '=/colls/{{ $parameter["collId"] }}',
|
||||
},
|
||||
},
|
||||
action: 'Get container',
|
||||
|
@ -59,7 +59,7 @@ export const containerOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'GET',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls',
|
||||
url: '/colls',
|
||||
},
|
||||
},
|
||||
action: 'Get many containers',
|
||||
|
@ -70,50 +70,6 @@ export const containerOperations: INodeProperties[] = [
|
|||
];
|
||||
|
||||
export const createFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['container'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
|
@ -185,6 +141,45 @@ export const createFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Max RU/s (for Autoscale)',
|
||||
name: 'maxThroughput',
|
||||
type: 'number',
|
||||
default: 1000,
|
||||
description: 'The user specified autoscale max RU/s',
|
||||
displayOptions: {
|
||||
show: {
|
||||
offerThroughput: [undefined],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
type: 'query',
|
||||
property: 'x-ms-cosmos-offer-autopilot-settings',
|
||||
value: '={{"{"maxThroughput": " + $value + "}"}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Max RU/s (for Manual Throughput)',
|
||||
name: 'offerThroughput',
|
||||
type: 'number',
|
||||
default: 400,
|
||||
description:
|
||||
'The user specified manual throughput (RU/s) for the collection expressed in units of 100 request units per second',
|
||||
displayOptions: {
|
||||
show: {
|
||||
maxThroughput: [undefined],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
type: 'query',
|
||||
property: 'x-ms-offer-throughput',
|
||||
value: '={{$value}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'Add Option',
|
||||
type: 'collection',
|
||||
|
@ -192,50 +187,6 @@ export const createFields: INodeProperties[] = [
|
|||
];
|
||||
|
||||
export const getFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['container'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Container ID',
|
||||
name: 'collId',
|
||||
|
@ -282,98 +233,9 @@ export const getFields: INodeProperties[] = [
|
|||
},
|
||||
];
|
||||
|
||||
export const getAllFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['container'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
export const getAllFields: INodeProperties[] = [];
|
||||
|
||||
export const deleteFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['container'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Container ID',
|
||||
name: 'collId',
|
||||
|
|
|
@ -22,7 +22,7 @@ export const itemOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'POST',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls/{{ $parameter["collId"] }}/docs',
|
||||
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
||||
},
|
||||
},
|
||||
action: 'Create item',
|
||||
|
@ -35,7 +35,7 @@ export const itemOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'DELETE',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
||||
url: '=/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
||||
},
|
||||
},
|
||||
action: 'Delete item',
|
||||
|
@ -45,10 +45,20 @@ export const itemOperations: INodeProperties[] = [
|
|||
value: 'get',
|
||||
description: 'Retrieve an item',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
return requestOptions;
|
||||
},
|
||||
],
|
||||
},
|
||||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'GET',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
||||
url: '=/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
||||
},
|
||||
},
|
||||
action: 'Get item',
|
||||
|
@ -67,7 +77,7 @@ export const itemOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'GET',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls/{{ $parameter["collId"] }}/docs',
|
||||
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
||||
},
|
||||
},
|
||||
action: 'Get many items',
|
||||
|
@ -80,7 +90,7 @@ export const itemOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'POST',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls/{{ $parameter["collId"] }}/docs',
|
||||
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
||||
headers: {
|
||||
'Content-Type': 'application/query+json',
|
||||
'x-ms-documentdb-isquery': 'True',
|
||||
|
@ -97,7 +107,7 @@ export const itemOperations: INodeProperties[] = [
|
|||
request: {
|
||||
ignoreHttpStatusErrors: true,
|
||||
method: 'PATCH',
|
||||
url: '=/dbs/{{ $parameter["dbId"] }}/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
||||
url: '=/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
||||
},
|
||||
},
|
||||
action: 'Update item',
|
||||
|
@ -108,50 +118,6 @@ export const itemOperations: INodeProperties[] = [
|
|||
];
|
||||
|
||||
export const createFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['item'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Collection ID',
|
||||
name: 'collId',
|
||||
|
@ -196,28 +162,28 @@ export const createFields: INodeProperties[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. AndersenFamily',
|
||||
description: "Item's ID",
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['item'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'id',
|
||||
value: '={{$value}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
// {
|
||||
// displayName: 'ID',
|
||||
// name: 'id',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// placeholder: 'e.g. AndersenFamily',
|
||||
// description: "Item's ID",
|
||||
// required: true,
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// resource: ['item'],
|
||||
// operation: ['create'],
|
||||
// },
|
||||
// },
|
||||
// routing: {
|
||||
// send: {
|
||||
// type: 'body',
|
||||
// property: 'id',
|
||||
// value: '={{$value}}',
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
{
|
||||
displayName: 'Custom Properties',
|
||||
name: 'customProperties',
|
||||
|
@ -235,58 +201,13 @@ export const createFields: INodeProperties[] = [
|
|||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: '',
|
||||
value: '={{$value}}',
|
||||
value: '={{ $json["id"] ? Object.assign({ id: $json["id"] }, $value) : $value }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const deleteFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['item'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Collection ID',
|
||||
name: 'collId',
|
||||
|
@ -349,50 +270,6 @@ export const deleteFields: INodeProperties[] = [
|
|||
];
|
||||
|
||||
export const getFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['item'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Collection ID',
|
||||
name: 'collId',
|
||||
|
@ -451,61 +328,10 @@ export const getFields: INodeProperties[] = [
|
|||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'id',
|
||||
value: '={{$value}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const getAllFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['item'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Collection ID',
|
||||
name: 'collId',
|
||||
|
@ -561,18 +387,6 @@ export const getAllFields: INodeProperties[] = [
|
|||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
return requestOptions;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
|
@ -603,50 +417,6 @@ export const getAllFields: INodeProperties[] = [
|
|||
];
|
||||
|
||||
export const queryFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['item'],
|
||||
operation: ['query'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Collection ID',
|
||||
name: 'collId',
|
||||
|
@ -723,6 +493,12 @@ export const queryFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['item'],
|
||||
operation: ['query'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'parameters',
|
||||
|
@ -756,50 +532,6 @@ export const queryFields: INodeProperties[] = [
|
|||
];
|
||||
|
||||
export const updateFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'dbId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Select the database you want to use',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['item'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From list',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'searchDatabases',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By Name',
|
||||
name: 'databaseName',
|
||||
type: 'string',
|
||||
hint: 'Enter the database name',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '^[\\w+=,.@-]+$',
|
||||
errorMessage: 'The database name must follow the allowed pattern.',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. UsersDB',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Collection ID',
|
||||
name: 'collId',
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
import { azureCosmosDbRequest } from '../GenericFunctions';
|
||||
|
||||
describe('GenericFunctions - azureCosmosDbRequest', () => {
|
||||
let mockContext: any;
|
||||
let mockRequestWithAuthentication: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequestWithAuthentication = jest.fn();
|
||||
mockContext = {
|
||||
helpers: {
|
||||
requestWithAuthentication: mockRequestWithAuthentication,
|
||||
},
|
||||
getCredentials: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
test('should make a successful request with correct options', async () => {
|
||||
mockRequestWithAuthentication.mockResolvedValueOnce({ success: true });
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
|
||||
const requestOptions = {
|
||||
method: 'GET' as const,
|
||||
url: '/example-endpoint',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await azureCosmosDbRequest.call(mockContext, requestOptions);
|
||||
|
||||
expect(mockRequestWithAuthentication).toHaveBeenCalledWith(
|
||||
'azureCosmosDbSharedKeyApi',
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
baseURL: 'https://us-east-1.documents.azure.com',
|
||||
url: '/example-endpoint',
|
||||
headers: expect.objectContaining({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
json: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result).toEqual({ success: true });
|
||||
});
|
||||
|
||||
test('should throw an error if account is missing in credentials', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({});
|
||||
|
||||
const requestOptions = {
|
||||
method: 'GET' as const,
|
||||
url: '/example-endpoint',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
|
||||
'Database account not found in credentials!',
|
||||
);
|
||||
|
||||
expect(mockRequestWithAuthentication).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should throw a descriptive error for invalid credentials (403)', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
mockRequestWithAuthentication.mockRejectedValueOnce({
|
||||
statusCode: 403,
|
||||
response: {
|
||||
body: {
|
||||
message: 'The security token included in the request is invalid.',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const requestOptions = {
|
||||
method: 'GET' as const,
|
||||
url: '/example-endpoint',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
|
||||
'The Cosmos DB credentials are not valid!',
|
||||
);
|
||||
|
||||
expect(mockRequestWithAuthentication).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should throw a descriptive error for invalid request signature (403)', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
mockRequestWithAuthentication.mockRejectedValueOnce({
|
||||
statusCode: 403,
|
||||
response: {
|
||||
body: {
|
||||
message: 'The request signature we calculated does not match the signature you provided',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const requestOptions = {
|
||||
method: 'GET' as const,
|
||||
url: '/example-endpoint',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
|
||||
'The Cosmos DB credentials are not valid!',
|
||||
);
|
||||
|
||||
expect(mockRequestWithAuthentication).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should throw an error for resource not found (404)', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
mockRequestWithAuthentication.mockRejectedValueOnce({
|
||||
statusCode: 404,
|
||||
response: {
|
||||
body: {
|
||||
message: 'The specified resource does not exist.',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const requestOptions = {
|
||||
method: 'GET' as const,
|
||||
url: '/example-endpoint',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
|
||||
'The requested resource was not found!',
|
||||
);
|
||||
|
||||
expect(mockRequestWithAuthentication).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should throw a generic error for unexpected response', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
mockRequestWithAuthentication.mockRejectedValueOnce({
|
||||
statusCode: 500,
|
||||
message: 'Internal Server Error',
|
||||
});
|
||||
|
||||
const requestOptions = {
|
||||
method: 'GET' as const,
|
||||
url: '/example-endpoint',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
|
||||
'Cosmos DB error response [500]: Internal Server Error',
|
||||
);
|
||||
|
||||
expect(mockRequestWithAuthentication).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should handle unexpected error structures', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
mockRequestWithAuthentication.mockRejectedValueOnce({
|
||||
cause: { error: { message: 'Unexpected failure' } },
|
||||
});
|
||||
|
||||
const requestOptions = {
|
||||
method: 'GET' as const,
|
||||
url: '/example-endpoint',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(azureCosmosDbRequest.call(mockContext, requestOptions)).rejects.toThrow(
|
||||
'Cosmos DB error response [undefined]: Unexpected failure',
|
||||
);
|
||||
|
||||
expect(mockRequestWithAuthentication).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
import { handlePagination } from '../GenericFunctions';
|
||||
|
||||
describe('GenericFunctions - handlePagination', () => {
|
||||
let mockContext: any;
|
||||
let mockMakeRoutingRequest: jest.Mock;
|
||||
let resultOptions: any;
|
||||
|
||||
beforeEach(() => {
|
||||
mockMakeRoutingRequest = jest.fn();
|
||||
mockContext = {
|
||||
makeRoutingRequest: mockMakeRoutingRequest,
|
||||
getNodeParameter: jest.fn(),
|
||||
};
|
||||
|
||||
resultOptions = {
|
||||
maxResults: 60,
|
||||
options: { body: {} },
|
||||
};
|
||||
});
|
||||
|
||||
test('should aggregate results and handle pagination when returnAll is true', async () => {
|
||||
mockMakeRoutingRequest
|
||||
.mockResolvedValueOnce([
|
||||
{ id: 1 },
|
||||
{ id: 2 },
|
||||
{ headers: { 'x-ms-continuation': 'token-1' } },
|
||||
])
|
||||
.mockResolvedValueOnce([{ id: 3 }, { id: 4 }, { headers: {} }]);
|
||||
|
||||
mockContext.getNodeParameter.mockImplementation((param: string) => {
|
||||
if (param === 'returnAll') return true;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const result = await handlePagination.call(mockContext, resultOptions);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ json: { id: 1 } },
|
||||
{ json: { id: 2 } },
|
||||
{ json: { id: 3 } },
|
||||
{ json: { id: 4 } },
|
||||
]);
|
||||
|
||||
expect(mockMakeRoutingRequest).toHaveBeenCalledTimes(2);
|
||||
expect(resultOptions.options.headers).toEqual({
|
||||
'x-ms-continuation': 'token-1',
|
||||
});
|
||||
});
|
||||
|
||||
test('should stop pagination after reaching limit when returnAll is false', async () => {
|
||||
mockMakeRoutingRequest
|
||||
.mockResolvedValueOnce([
|
||||
{ id: 1 },
|
||||
{ id: 2 },
|
||||
{ headers: { 'x-ms-continuation': 'token-1' } },
|
||||
])
|
||||
.mockResolvedValueOnce([{ id: 3 }, { id: 4 }, { headers: {} }]);
|
||||
|
||||
mockContext.getNodeParameter.mockImplementation((param: string) => {
|
||||
if (param === 'returnAll') return false;
|
||||
if (param === 'limit') return 3;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const result = await handlePagination.call(mockContext, resultOptions);
|
||||
|
||||
expect(result).toEqual([{ json: { id: 1 } }, { json: { id: 2 } }, { json: { id: 3 } }]);
|
||||
|
||||
expect(mockMakeRoutingRequest).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('should handle cases with no continuation token gracefully', async () => {
|
||||
mockMakeRoutingRequest.mockResolvedValueOnce([{ id: 1 }, { id: 2 }]);
|
||||
|
||||
mockContext.getNodeParameter.mockImplementation((param: string) => {
|
||||
if (param === 'returnAll') return true;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const result = await handlePagination.call(mockContext, resultOptions);
|
||||
|
||||
expect(result).toEqual([{ json: { id: 1 } }, { json: { id: 2 } }]);
|
||||
|
||||
expect(mockMakeRoutingRequest).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should respect the limit even if fewer results are available', async () => {
|
||||
mockMakeRoutingRequest.mockResolvedValueOnce([{ id: 1 }, { id: 2 }]);
|
||||
|
||||
mockContext.getNodeParameter.mockImplementation((param: string) => {
|
||||
if (param === 'returnAll') return false;
|
||||
if (param === 'limit') return 5;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const result = await handlePagination.call(mockContext, resultOptions);
|
||||
|
||||
expect(result).toEqual([{ json: { id: 1 } }, { json: { id: 2 } }]);
|
||||
|
||||
expect(mockMakeRoutingRequest).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should break the loop if no results are returned', async () => {
|
||||
mockMakeRoutingRequest.mockResolvedValueOnce([]);
|
||||
|
||||
mockContext.getNodeParameter.mockImplementation((param: string) => {
|
||||
if (param === 'returnAll') return true;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const result = await handlePagination.call(mockContext, resultOptions);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(mockMakeRoutingRequest).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,122 @@
|
|||
import type { ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
|
||||
import { searchCollections } from '../GenericFunctions';
|
||||
|
||||
describe('GenericFunctions - searchCollections', () => {
|
||||
const mockRequestWithAuthentication = jest.fn();
|
||||
|
||||
const mockContext = {
|
||||
helpers: {
|
||||
requestWithAuthentication: mockRequestWithAuthentication,
|
||||
},
|
||||
getNodeParameter: jest.fn(),
|
||||
getCredentials: jest.fn(),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should make a GET request to fetch collections and return results', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
|
||||
mockRequestWithAuthentication.mockResolvedValueOnce({
|
||||
DocumentCollections: [{ id: 'Collection1' }, { id: 'Collection2' }],
|
||||
});
|
||||
|
||||
const response = await searchCollections.call(mockContext);
|
||||
|
||||
expect(mockRequestWithAuthentication).toHaveBeenCalledWith(
|
||||
'azureCosmosDbSharedKeyApi',
|
||||
expect.objectContaining({
|
||||
baseURL: 'https://us-east-1.documents.azure.com',
|
||||
method: 'GET',
|
||||
url: '/colls',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
json: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(response).toEqual({
|
||||
results: [
|
||||
{ name: 'Collection1', value: 'Collection1' },
|
||||
{ name: 'Collection2', value: 'Collection2' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter collections by the provided filter string', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
|
||||
mockRequestWithAuthentication.mockResolvedValueOnce({
|
||||
DocumentCollections: [{ id: 'Test-Col-1' }, { id: 'Prod-Col-1' }],
|
||||
});
|
||||
|
||||
const response = await searchCollections.call(mockContext, 'Test');
|
||||
|
||||
expect(mockRequestWithAuthentication).toHaveBeenCalledWith(
|
||||
'azureCosmosDbSharedKeyApi',
|
||||
expect.objectContaining({
|
||||
baseURL: 'https://us-east-1.documents.azure.com',
|
||||
method: 'GET',
|
||||
url: '/colls',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
json: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(response).toEqual({
|
||||
results: [{ name: 'Test-Col-1', value: 'Test-Col-1' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort collections alphabetically by name', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
(mockContext.getNodeParameter as jest.Mock).mockReturnValueOnce('db-id-1');
|
||||
|
||||
mockRequestWithAuthentication.mockResolvedValueOnce({
|
||||
DocumentCollections: [{ id: 'z-col' }, { id: 'a-col' }, { id: 'm-col' }],
|
||||
});
|
||||
|
||||
const response = await searchCollections.call(mockContext);
|
||||
|
||||
expect(response).toEqual({
|
||||
results: [
|
||||
{ name: 'a-col', value: 'a-col' },
|
||||
{ name: 'm-col', value: 'm-col' },
|
||||
{ name: 'z-col', value: 'z-col' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty results when no collections are returned', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
(mockContext.getNodeParameter as jest.Mock).mockReturnValueOnce('db-id-1');
|
||||
|
||||
mockRequestWithAuthentication.mockResolvedValueOnce({
|
||||
DocumentCollections: [],
|
||||
});
|
||||
|
||||
const response = await searchCollections.call(mockContext);
|
||||
|
||||
expect(response).toEqual({ results: [] });
|
||||
});
|
||||
|
||||
it('should handle missing Collections property', async () => {
|
||||
(mockContext.getCredentials as jest.Mock).mockResolvedValueOnce({ account: 'us-east-1' });
|
||||
(mockContext.getNodeParameter as jest.Mock).mockReturnValueOnce('db-id-1');
|
||||
|
||||
mockRequestWithAuthentication.mockResolvedValueOnce({
|
||||
unexpectedkey: 'value',
|
||||
});
|
||||
const response = await searchCollections.call(mockContext);
|
||||
|
||||
expect(response).toEqual({ results: [] });
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue