Skip to content

Commit

Permalink
Merge branch 'main' into feat-legacy-models-import
Browse files Browse the repository at this point in the history
  • Loading branch information
solaris007 committed Dec 12, 2024
2 parents 20f275f + 6fd8c68 commit 14bd887
Show file tree
Hide file tree
Showing 17 changed files with 304 additions and 635 deletions.
726 changes: 96 additions & 630 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions packages/spacecat-shared-data-access/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# [@adobe/spacecat-shared-data-access-v1.59.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.58.2...@adobe/spacecat-shared-data-access-v1.59.0) (2024-12-11)


### Features

* **data-access:** get site candidates ([#486](https://github.com/adobe/spacecat-shared/issues/486)) ([f45032d](https://github.com/adobe/spacecat-shared/commit/f45032dbcd3ea1e8cdfc2066f523b4a1690b5da4))

# [@adobe/spacecat-shared-data-access-v1.58.2](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.58.1...@adobe/spacecat-shared-data-access-v1.58.2) (2024-12-07)


Expand Down
4 changes: 2 additions & 2 deletions packages/spacecat-shared-data-access/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/spacecat-shared-data-access",
"version": "1.58.2",
"version": "1.59.0",
"description": "Shared modules of the Spacecat Services - Data Access",
"type": "module",
"engines": {
Expand Down Expand Up @@ -34,7 +34,7 @@
"access": "public"
},
"dependencies": {
"@adobe/spacecat-shared-dynamo": "1.3.50",
"@adobe/spacecat-shared-dynamo": "1.4.0",
"@adobe/spacecat-shared-utils": "1.23.1",
"@aws-sdk/client-dynamodb": "3.705.0",
"@aws-sdk/lib-dynamodb": "3.705.0",
Expand Down
1 change: 1 addition & 0 deletions packages/spacecat-shared-data-access/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ export interface DataAccess {
) => Promise<ApiKey | null>;

// site candidate functions
getSiteCandidates: () => Promise<SiteCandidate[]>;
getSiteCandidateByBaseURL: (baseURL: string) => Promise<SiteCandidate>;
upsertSiteCandidate: (siteCandidateDate: object) => Promise<SiteCandidate>;
siteCandidateExists: (baseURL: string) => Promise<boolean>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ import { isObject } from '@adobe/spacecat-shared-utils';
import { createSiteCandidate } from '../../models/site-candidate.js';
import { SiteCandidateDto } from '../../dto/site-candidate.js';

/**
* Retrieves all site candidates.
*
* @param {DynamoDbClient} dynamoClient - The DynamoDB client.
* @param {DataAccessConfig} config - The data access config.
* @returns {Promise<Readonly<Site>[]>} A promise that resolves to an array of all site candidates.
*/
export const getSiteCandidates = async (dynamoClient, config) => {
const dynamoItems = await dynamoClient.scan({
TableName: config.tableNameSites,
});

return dynamoItems.map((dynamoItem) => SiteCandidateDto.fromDynamoItem(dynamoItem));
};

/**
* Checks if a site candidate exists in site candidates table using base url
* @param {DynamoDbClient} dynamoClient - The DynamoDB client.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
*/

import {
getSiteCandidates,
getSiteCandidateByBaseURL,
upsertSiteCandidate,
exists,
updateSiteCandidate,
} from './accessPatterns.js';

export const siteCandidateFunctions = (dynamoClient, config, log) => ({
getSiteCandidates: () => getSiteCandidates(
dynamoClient,
config,
),
getSiteCandidateByBaseURL: (baseURL) => getSiteCandidateByBaseURL(
dynamoClient,
config,
Expand Down
5 changes: 5 additions & 0 deletions packages/spacecat-shared-data-access/test/it/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,11 @@ describe('Legacy Data Model IT', async () => {
expect(latestAuditAfterRemoval).to.be.null;
});

it('get all site candidates', async () => {
const siteCandidates = await dataAccess.getSiteCandidates();
expect(siteCandidates.length).to.equal(10);
});

it('verify a previously added site candidate exists', async () => {
const exists = await dataAccess.siteCandidateExists('https://example0.com');
expect(exists).to.be.true;
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ describe('Data Access Object Tests', () => {
];

const siteCandidateFunctions = [
'getSiteCandidates',
'getSiteCandidateByBaseURL',
'upsertSiteCandidate',
'siteCandidateExists',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ describe('Site Candidate Access Pattern Tests', () => {

const exportedFunctions = siteCandidateFunctions(mockDynamoClient, TEST_DA_CONFIG, mockLog);

it('exports upsertSiteCandidate function', () => {
expect(exportedFunctions).to.have.property('getSiteCandidates');
expect(exportedFunctions.getSiteCandidates).to.be.a('function');
});

it('exports upsertSiteCandidate function', () => {
expect(exportedFunctions).to.have.property('upsertSiteCandidate');
expect(exportedFunctions.upsertSiteCandidate).to.be.a('function');
Expand All @@ -63,11 +68,13 @@ describe('Site Candidate Access Pattern Tests', () => {
let mockDynamoClient;
let mockLog = {};
let exportedFunctions;
const siteCandidates = [{ baseURL: 'https://site1.com' }, { baseURL: 'https://site2.com' }];

beforeEach(() => {
mockDynamoClient = {
getItem: sinon.stub().returns(Promise.resolve(null)),
putItem: sinon.stub().returns(Promise.resolve()),
scan: sinon.stub().resolves(siteCandidates),
};

mockLog = {
Expand All @@ -77,6 +84,13 @@ describe('Site Candidate Access Pattern Tests', () => {
exportedFunctions = siteCandidateFunctions(mockDynamoClient, TEST_DA_CONFIG, mockLog);
});

it('returns all sitecandidates', async () => {
const result = await exportedFunctions.getSiteCandidates();

expect(result[0].getBaseURL()).to.equal(siteCandidates[0].baseURL);
expect(result[1].getBaseURL()).to.equal(siteCandidates[1].baseURL);
});

it('returns the site candidate by base url', async () => {
const siteCandidateData = { baseURL: 'https://existingsite.com', status: SITE_CANDIDATE_STATUS.PENDING };
mockDynamoClient.getItem.returns(Promise.resolve(siteCandidateData));
Expand Down
7 changes: 7 additions & 0 deletions packages/spacecat-shared-dynamo/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# [@adobe/spacecat-shared-dynamo-v1.4.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-dynamo-v1.3.51...@adobe/spacecat-shared-dynamo-v1.4.0) (2024-12-11)


### Features

* **dynamo:** scan support ([#485](https://github.com/adobe/spacecat-shared/issues/485)) ([3d32b1e](https://github.com/adobe/spacecat-shared/commit/3d32b1e8d2796e8a6a0f1c057845eb63f27eff39))

# [@adobe/spacecat-shared-dynamo-v1.3.51](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-dynamo-v1.3.50...@adobe/spacecat-shared-dynamo-v1.3.51) (2024-12-07)


Expand Down
2 changes: 1 addition & 1 deletion packages/spacecat-shared-dynamo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/spacecat-shared-dynamo",
"version": "1.3.51",
"version": "1.4.0",
"description": "Shared modules of the Spacecat Services - DynamoDB client",
"type": "module",
"engines": {
Expand Down
3 changes: 2 additions & 1 deletion packages/spacecat-shared-dynamo/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

import { DynamoDB } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, QueryCommandInput } from '@aws-sdk/lib-dynamodb';
import { DynamoDBDocumentClient, QueryCommandInput, ScanCommandInput } from '@aws-sdk/lib-dynamodb';

export declare interface Logger {
error(message: string, ...args: unknown[]): void;
Expand All @@ -24,6 +24,7 @@ export declare interface DynamoDbKey {
}

export declare interface DynamoDbClient {
scan(originalParams: ScanCommandInput): Promise<object[]>;
query(originalParams: QueryCommandInput): Promise<object[]>;
getItem(tableName: string, key: DynamoDbKey): Promise<object>;
putItem(tableName: string, item: object): Promise<{ message: string }>;
Expand Down
2 changes: 2 additions & 0 deletions packages/spacecat-shared-dynamo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import AWSXray from 'aws-xray-sdk';
import { DynamoDB } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';

import scan from './modules/scan.js';
import query from './modules/query.js';
import getItem from './modules/getItem.js';
import putItem from './modules/putItem.js';
Expand All @@ -37,6 +38,7 @@ const createClient = (
},
}),
) => ({
scan: (params) => scan(docClient, params, log),
query: (params) => query(docClient, params, log),
getItem: (tableName, key) => getItem(docClient, tableName, key, log),
putItem: (tableName, item) => putItem(docClient, tableName, item, log),
Expand Down
62 changes: 62 additions & 0 deletions packages/spacecat-shared-dynamo/src/modules/scan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import { performance } from 'perf_hooks';

/**
* Scans DynamoDB and automatically handles pagination to retrieve all items.
*
* @param {DynamoDBDocumentClient} docClient - The AWS SDK DynamoDB Document client instance.
* @param {Object} originalParams - The parameters for the DynamoDB scan.
* @param {Logger} log - The logging object, defaults to console.
* @returns {Promise<Array>} A promise that resolves to an array of items retrieved from DynamoDB.
* @throws {Error} Throws an error if the DynamoDB scan operation fails.
*/
async function scan(docClient, originalParams, log = console) {
let items = [];
const params = { ...originalParams };

let totalTime = 0;
let paginationCount = 0;

try {
let data;
if (params.Limit && params.Limit <= 1) {
const result = await docClient.scan(params);
return result.Items;
}
do {
const startTime = performance.now();

// eslint-disable-next-line no-await-in-loop
data = await docClient.scan(params);

const endTime = performance.now(); // End timing
const duration = endTime - startTime;
totalTime += duration;
paginationCount += 1;

log.info(`Pagination ${paginationCount} scan time: ${duration.toFixed(2)} ms`);

items = items.concat(data.Items);
params.ExclusiveStartKey = data.LastEvaluatedKey;
} while (data.LastEvaluatedKey);
} catch (error) {
log.error('DB Scan Error:', error);
throw error;
}

log.info(`Total scan time: ${totalTime.toFixed(2)} ms with ${paginationCount} paginations for scan: ${JSON.stringify(params)}`);

return items;
}

export default scan;
8 changes: 7 additions & 1 deletion packages/spacecat-shared-dynamo/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ describe('createClient', () => {
docClient = DynamoDBDocumentClient.from(dbClient);
});

it('should create a DynamoDB client with scan method', () => {
const client = createClient(console, dbClient, docClient);
expect(client).to.have.property('scan');
expect(client.query).to.be.a('function');
});

it('should create a DynamoDB client with query method', () => {
const client = createClient(console, dbClient, docClient);
expect(client).to.have.property('query');
Expand All @@ -51,6 +57,6 @@ describe('createClient', () => {

it('should use default parameters if none are provided', () => {
const client = createClient();
expect(client).to.have.all.keys('query', 'getItem', 'putItem', 'removeItem');
expect(client).to.have.all.keys('scan', 'query', 'getItem', 'putItem', 'removeItem');
});
});
77 changes: 77 additions & 0 deletions packages/spacecat-shared-dynamo/test/modules/scan.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

/* eslint-env mocha */

import { expect } from 'chai';
import { createClient } from '../../src/index.js';

describe('scan', () => {
const scanParams = {
TableName: 'TestTable',
};
const scanParamsWithLimit = {
TableName: 'TestTable',
Limit: 1,
};

let dynamoDbClient;
let mockDocClient;

beforeEach(() => {
mockDocClient = {
scan: async (params) => {
if (params.Limit) {
return { Items: ['item1'] };
}
// Check if LastEvaluatedKey is provided and simulate pagination
if (params.ExclusiveStartKey === 'key2') {
return { Items: ['item3'], LastEvaluatedKey: undefined };
} else {
return { Items: ['item1', 'item2'], LastEvaluatedKey: 'key2' };
}
},
};

dynamoDbClient = createClient(console, undefined, mockDocClient);
});

it('scans items from the database', async () => {
const result = await dynamoDbClient.scan(scanParams);
expect(result).to.be.an('array');
});

it('scans items from the database with pagination', async () => {
const result = await dynamoDbClient.scan(scanParams);
expect(result).to.have.lengthOf(3);
expect(result).to.deep.equal(['item1', 'item2', 'item3']);
});

it('scans items from the database with limit', async () => {
const result = await dynamoDbClient.scan(scanParamsWithLimit);
expect(result).to.have.lengthOf(1);
expect(result).to.deep.equal(['item1']);
});

it('handles errors in scan', async () => {
mockDocClient.scan = async () => {
throw new Error('Scan failed');
};

try {
await dynamoDbClient.scan(scanParams);
expect.fail('scanDb did not throw as expected');
} catch (error) {
expect(error.message).to.equal('Scan failed');
}
});
});

0 comments on commit 14bd887

Please sign in to comment.