Part II
First Name: | Azat |
Last Name: | Davliatshin |
Experience: | 9 years in JS developmentLife Science & Healthcare |
Expertise: | FE, BE, Clouds, Mobile, Legacy |
Criminal Records: | Had to force push changes into production branch |
import { json2csv } from 'json-2-csv';
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
const urlAPI = "...";
const tokenAPI = "...";
const options = {
headers: '' // use tokenAPI
};
const fetchResponse = await fetch(urlAPI, options);
const data = fetchResponse.json();
const csv = await converter.json2csv(data);
const client = new S3Client({});
const command = new PutObjectCommand({});
await client.send(command);
QA deals with change and the cost in time or money of making a change, including the extent to which this modification affects other functions or quality attributes.
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import { fetchData } from './fetch-data';
import { transformToCSV } from "./transform-to-csv";
import { putCSVtoS3 } from "./put-csv-to-s3";
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
const data = await fetchData();
const csv = await transformToCSV(data);
const response = await putCSVtoS3(csv);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch(error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify(error),
};
}
};
// put-csv-to-s3.ts
import {
PutObjectCommand,
S3Client,
type PutObjectCommandOutput,
} from "@aws-sdk/client-s3";
const client = new S3Client({});
export const putCSVtoS3 = async (
csv: string
): Promise<PutObjectCommandOutput> => {
const command = new PutObjectCommand({
Bucket: "test-bucket",
Key: `data_${(new Date()).getTime()}.csv`,
Body: csv,
});
return client.send(command);
};
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import { fetchData } from './fetch-data';
import { transformToCSV } from "./transform-to-csv";
import { putCSVtoS3 } from "./put-csv-to-s3";
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
const data = await fetchData();
const csv = await transformToCSV(data);
const response = await putCSVtoS3(csv);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch(error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// put-csv-to-s3.ts
import {
PutObjectCommand,
S3Client,
type PutObjectCommandOutput,
} from "@aws-sdk/client-s3";
const client = new S3Client({});
export const putCSVtoS3 = async (
csv: string
): Promise<PutObjectCommandOutput> => {
try {
const command = new PutObjectCommand({
Bucket: "test-bucket",
Key: `data_${(new Date()).getTime()}.csv`,
Body: csv,
});
return await client.send(command);
} catch (error) {
console.info("Store CSV file to S3 operation is failed");
throw error;
}
};
// put-csv-to-s3.ts
import {
PutObjectCommand,
S3Client,
type PutObjectCommandOutput,
} from "@aws-sdk/client-s3";
// THERE MIGHT BE HARDCODED REGION
const client = new S3Client({});
export const putCSVtoS3 = async (
csv: string
): Promise<PutObjectCommandOutput> => {
try {
const command = new PutObjectCommand({
// HARDCODED!
Bucket: "test-bucket",
// HARDCODED!
Key: `data_${(new Date()).getTime()}.csv`,
Body: csv,
});
return await client.send(command);
} catch (error) {
console.info("Store CSV file to S3 operation is failed");
throw error;
}
};
// put-csv-to-s3.ts
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
// THERE MIGHT BE HARDCODED REGION
const client = new S3Client({});
export const putCSVtoS3 = async (csv: string) => {
try {
const command = new PutObjectCommand({
Bucket: process.env.BUCKET_NAME,
Key: `${process.env.BUCKET_KEY}_${(new Date()).getTime()}.csv`,
Body: csv,
});
return client.send(command);
} catch (error) {
// TO CONSIDER: CARRY OUT ERROR MESSAGES (OPTIONAL)
console.info("Store CSV file to S3 operation is failed");
throw error;
}
};
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import { fetchData } from './fetch-data';
import { transformToCSV } from "./transform-to-csv";
import { putCSVtoS3 } from "./put-csv-to-s3";
import { getConfig } from './get-config';
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
const config = await getConfig();
const data = await fetchData(config.apiURL, config.apiToken);
const csv = await transformToCSV(data, config.separator);
const response = await putCSVtoS3(
csv,
config.bucketName,
config.bucketKey
);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch(error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// put-csv-to-s3.ts
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { type Config } from './get-config';
const client = new S3Client({});
export const putCSVtoS3 = async (
csv: string,
bucketName: string,
bucketKey: string
) => {
try {
const command = new PutObjectCommand({
Bucket: bucketName,
Key: bucketKey,
Body: csv,
});
return await client.send(command);
} catch (error) {
console.info("Store CSV file to S3 operation is failed");
throw error;
}
};
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import { fetchData } from './fetch-data';
import { transformToCSV } from "./transform-to-csv";
import { putCSVtoS3 } from "./put-csv-to-s3";
import { getConfig } from './get-config';
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
const config = await getConfig();
const data = await fetchData(config.apiURL, config.apiToken);
const csv = await transformToCSV(data, config.separator);
const response = await putCSVtoS3(
csv,
config.bucketName,
config.bucketKey
);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch(error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import * as API from "@api";
import { convert } from "@converter";
import { putToStorage } from "@storage";
import { getConfig } from '@config';
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
const config = await getConfig();
const data = await API.get(config);
const transformedData = await convert(data, config);
const response = await putToStorage(transformedData, config);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch(error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import * as API from "@api";
import { convert } from "@converter";
import { putToStorage } from "@storage";
import { getConfig } from '@config';
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
const config = await getConfig();
const data = await API.get(config);
const transformedData = await convert(data, config);
const response = await putToStorage(transformedData, config);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch(error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import * as API from "@api";
import { convert } from "@converter";
import { putToStorage } from "@storage";
import { getConfig } from '@config';
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
const config = await getConfig();
const data = await API.get(config);
const transformedData = await convert(data, config);
const response = await putToStorage(transformedData, config);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch(error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// @storage/index.ts
import { putObjectToS3 } from './s3-realization';
//import { somethingElse } from './something-else';
import { type Config } from '@config';
export const putToStorage = async (data: string, config: Config) => {
try {
return await putObjectToS3(
data,
config.bucketName,
config.bucketKey,
);
// in the case of dynamic changes, use if-else/switch
} catch(error) {
console.info("Error inside 'putToStorage' function");
throw error;
}
};
Inversion of Control (IoC) is a design principle in which a software component is designed to receive its dependencies from an external source, rather than creating them itself.
Dependency injection is a programming technique that makes a class independent of its dependencies. It achieves that by decoupling the usage of an object from its creation. This helps you to follow SOLID's dependency inversion and single responsibility principles.
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import { createContainer } from './inversify.container';
import { TOKENS, type IConductorProcessor } from '@infrastructure';
export const handler = async (
event: APIGatewayProxyEvent,
): Promise<APIGatewayProxyResult> => {
try {
// there might be params
const container = await createContainer();
const conductorProcessor =
container.get<IConductorProcessor>(TOKENS.CONDUCTOR_PROCESSOR);
await conductorProcessor.processData();
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch (error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import { createContainer } from './inversify.container';
import { TOKENS, type IConductorProcessor } from '@infrastructure';
export const handler = async (
event: APIGatewayProxyEvent,
): Promise<APIGatewayProxyResult> => {
try {
// there might be params
const container = await createContainer();
const conductorProcessor =
container.get<IConductorProcessor>(TOKENS.CONDUCTOR_PROCESSOR);
await conductorProcessor.processData();
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch (error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// inversify.config.ts
import "reflect-metadata";
import { Container } from "inversify";
import {
TOKENS,
getConfig,
type IConductorProcessor,
type IConfigProvider,
type IAPIProvider,
type IConverterProvider,
type IStorageProvider,
} from "@infrastructure";
import { ConductorProcessor } from "@processors";
import {
ConfigProvider,
APIProvider,
ConverterProvider,
StorageProvider
} from "@providers";
export const createContainer = (): Container => {
const container = new Container({ defaultScope: "Singleton" });
// processors
container
.bind<IConductorProcessor>(TOKENS.CONDUCTOR)
.to(ConductorProcessor);
const config = await getConfig();
// providers
container
.bind<IConfigProvider>(TOKENS.CONFIG)
.tValue(config);
container
.bind<IAPIProvider>(TOKENS.API)
.to(APIProvider);
container
.bind<IConverterProvider>(TOKENS.CONVERTER)
.to(ConverterProvider);
container
.bind<IStorageProvider>(TOKENS.STORAGE)
.to(StorageProvider);
return container;
};
// inversify.config.ts
import "reflect-metadata";
import { Container } from "inversify";
import {
TOKENS,
getConfig,
type IConductorProcessor,
type IConfigProvider,
type IAPIProvider,
type IConverterProvider,
type IStorageProvider,
} from "@infrastructure";
import { ConductorProcessor } from "@processors";
import {
ConfigProvider,
APIProvider,
ConverterProvider,
StorageProvider
} from "@providers";
export const createContainer = (): Container => {
const container = new Container({ defaultScope: "Singleton" });
// processors
container
.bind<IConductorProcessor>(TOKENS.CONDUCTOR)
.to(ConductorProcessor);
const config = await getConfig();
// providers
container
.bind<IConfigProvider>(TOKENS.CONFIG)
.toValue(config);
container
.bind<IAPIProvider>(TOKENS.API)
.to(APIProvider);
container
.bind<IConverterProvider>(TOKENS.CONVERTER)
.to(ConverterProvider);
container
.bind<IStorageProvider>(TOKENS.STORAGE)
.to(StorageProvider);
return container;
};
// inversify.config.ts
import "reflect-metadata";
import { Container } from "inversify";
import {
TOKENS,
getConfig,
type IConductorProcessor,
type IConfigProvider,
type IAPIProvider,
type IConverterProvider,
type IStorageProvider,
} from "@infrastructure";
import { ConductorProcessor } from "@processors";
import {
ConfigProvider,
APIProvider,
ConverterProvider,
StorageProvider
} from "@providers";
export const createContainer = (): Container => {
const container = new Container({ defaultScope: "Singleton" });
// processors
container
.bind<IConductorProcessor>(TOKENS.CONDUCTOR)
.to(ConductorProcessor);
const config = await getConfig();
// providers
container
.bind<IConfigProvider>(TOKENS.CONFIG)
.toValue(config);
container
.bind<IAPIProvider>(TOKENS.API)
.to(APIProvider);
container
.bind<IConverterProvider>(TOKENS.CONVERTER)
.to(ConverterProvider);
container
.bind<IStorageProvider>(TOKENS.STORAGE)
.to(StorageProvider);
return container;
};
// @processors/conductorProcessor.ts
import { inject, injectable } from "inversify";
@injectable()
export class ConductorProcessor implements IConductorProcessor {
constructor(
@inject(TOKENS.API)
private readonly _apiProvider: IAPIProvider,
@inject(TOKENS.CONVERTER)
private readonly _converterProvider: IConverterProvider,
@inject(TOKENS.STORAGE)
private readonly _storageProvider: IStorageProvider,
) {}
public async processData(): Promise<void> {
try {
const data = await this._apiProvider.getData();
const csv = await this._converterProvider.convertData(data);
await this._storageProvider(csv);
} catch(error) {
console.info("[ConductorProcessor][processData] error");
throw error;
}
}
}
// index.ts
import {
type APIGatewayProxyEvent,
type APIGatewayProxyResult
} from 'aws-lambda';
import { createContainer } from './inversify.container';
import { TOKENS, type IConductorProcessor } from '@infrastructure';
export const handler = async (
event: APIGatewayProxyEvent,
): Promise<APIGatewayProxyResult> => {
try {
// there might be params
const container = await createContainer();
const conductorProcessor =
container.get<IConductorProcessor>(TOKENS.CONDUCTOR_PROCESSOR);
await conductorProcessor.processData();
return {
statusCode: 200,
body: JSON.stringify({
message: 'Success!'
}),
};
} catch (error) {
console.error(JSON.stringify(error, null, 2));
return {
statusCode: 500,
body: JSON.stringify({
message: 'Failure!',
...error
}),
};
}
};
// inversify.config.ts
import "reflect-metadata";
import { Container } from "inversify";
import {
TOKENS,
getConfig,
type IConductorProcessor,
type IConfigProvider,
type IAPIProvider,
type IConverterProvider,
type IStorageProvider,
} from "@infrastructure";
import { ConductorProcessor } from "@processors";
import {
ConfigProvider,
APIProvider,
ConverterProvider,
StorageProvider
} from "@providers";
export const createContainer = (): Container => {
const container = new Container({ defaultScope: "Singleton" });
// processors
container
.bind<IConductorProcessor>(TOKENS.CONDUCTOR)
.to(ConductorProcessor);
const config = await getConfig();
// providers
container
.bind<IConfigProvider>(TOKENS.CONFIG)
.toValue(config);
container
.bind<IAPIProvider>(TOKENS.API)
.to(APIProvider);
container
.bind<IConverterProvider>(TOKENS.CONVERTER)
.to(ConverterProvider);
container
.bind<IStorageProvider>(TOKENS.STORAGE)
.to(StorageProvider);
return container;
};
Part I
Part II
Telegram