diff --git a/cloudformation/ecs-service.yaml b/cloudformation/ecs-service.yaml index 47536fb..3099f95 100644 --- a/cloudformation/ecs-service.yaml +++ b/cloudformation/ecs-service.yaml @@ -91,7 +91,7 @@ Resources: Cluster: Type: AWS::ECS::Cluster Properties: - ClusterName: !Join ['', [!Ref 'AWS::StackName', Cluster]] + ClusterName: !Join ["", [!Ref "AWS::StackName", Cluster]] TaskDefinition: Type: AWS::ECS::TaskDefinition @@ -99,7 +99,7 @@ Resources: DependsOn: LogGroup Properties: # Name of the task definition. Subsequent versions of the task definition are grouped together under this name. - Family: !Join ['', [!Ref 'AWS::StackName', TaskDefinition]] + Family: !Join ["", [!Ref "AWS::StackName", TaskDefinition]] # awsvpc is required for Fargate NetworkMode: awsvpc RequiresCompatibilities: @@ -119,14 +119,10 @@ Resources: - ContainerPort: !Ref ContainerPort # Define which parameters should be fetched Secrets: - - Name: 'DATABASE_URL' - ValueFrom: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${EnvironmentName}/graphql-api/DATABASE_URL' - - Name: 'COGNITO_POOL_ID' - ValueFrom: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${EnvironmentName}/cognito/ADMIN_POOL_ID' - - Name: 'INTERIORS_URL' - ValueFrom: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${EnvironmentName}/INTERIORS_URL' - - Name: 'INTERIORS_TOKEN' - ValueFrom: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${EnvironmentName}/INTERIORS_TOKEN' + - Name: "DATABASE_URL" + ValueFrom: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${EnvironmentName}/graphql-api/DATABASE_URL" + - Name: "COGNITO_POOL_ID" + ValueFrom: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${EnvironmentName}/cognito/ADMIN_POOL_ID" # Send logs to CloudWatch Logs LogConfiguration: LogDriver: awslogs @@ -139,47 +135,47 @@ Resources: ExecutionRole: Type: AWS::IAM::Role Properties: - RoleName: !Join ['', [!Ref 'AWS::StackName', ExecutionRole]] + RoleName: !Join ["", [!Ref "AWS::StackName", ExecutionRole]] AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com - Action: 'sts:AssumeRole' + Action: "sts:AssumeRole" ManagedPolicyArns: - - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' + - "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess # A role for the containers TaskRole: Type: AWS::IAM::Role Properties: - RoleName: !Join ['', [!Ref 'AWS::StackName', TaskRole]] + RoleName: !Join ["", [!Ref "AWS::StackName", TaskRole]] AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com - Action: 'sts:AssumeRole' + Action: "sts:AssumeRole" # A role needed for auto scaling AutoScalingRole: Type: AWS::IAM::Role Properties: - RoleName: !Join ['', [!Ref 'AWS::StackName', AutoScalingRole]] + RoleName: !Join ["", [!Ref "AWS::StackName", AutoScalingRole]] AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com - Action: 'sts:AssumeRole' + Action: "sts:AssumeRole" ManagedPolicyArns: - - 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole' + - "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole" ContainerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: - !Join ['', [!Ref 'AWS::StackName', ContainerSecurityGroup]] + !Join ["", [!Ref "AWS::StackName", ContainerSecurityGroup]] VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp @@ -191,7 +187,7 @@ Resources: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: - !Join ['', [!Ref 'AWS::StackName', LoadBalancerSecurityGroup]] + !Join ["", [!Ref "AWS::StackName", LoadBalancerSecurityGroup]] VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp @@ -239,7 +235,7 @@ Resources: HealthCheckTimeoutSeconds: 10 UnhealthyThresholdCount: 2 HealthyThresholdCount: 2 - Name: !Join ['', [!Ref 'AWS::StackName', Group]] + Name: !Join ["", [!Ref "AWS::StackName", Group]] Port: !Ref ContainerPort Protocol: HTTP TargetGroupAttributes: @@ -266,7 +262,7 @@ Resources: LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: 1200 - Name: !Join ['', [!Ref 'AWS::StackName', LB]] + Name: !Join ["", [!Ref "AWS::StackName", LB]] # "internal" is also an option Scheme: internet-facing SecurityGroups: @@ -278,8 +274,8 @@ Resources: DNSRecord: Type: AWS::Route53::RecordSet Properties: - HostedZoneName: !Join ['', [!Ref HostedZoneName, .]] - Name: !Join ['', [!Ref Subdomain, ., !Ref HostedZoneName, .]] + HostedZoneName: !Join ["", [!Ref HostedZoneName, .]] + Name: !Join ["", [!Ref Subdomain, ., !Ref HostedZoneName, .]] Type: A AliasTarget: DNSName: !GetAtt LoadBalancer.DNSName @@ -288,14 +284,14 @@ Resources: LogGroup: Type: AWS::Logs::LogGroup Properties: - LogGroupName: !Join ['', [/ecs/, !Ref 'AWS::StackName', TaskDefinition]] + LogGroupName: !Join ["", [/ecs/, !Ref "AWS::StackName", TaskDefinition]] AutoScalingTarget: Type: AWS::ApplicationAutoScaling::ScalableTarget Properties: MinCapacity: !Ref MinContainers MaxCapacity: !Ref MaxContainers - ResourceId: !Join ['/', [service, !Ref Cluster, !GetAtt Service.Name]] + ResourceId: !Join ["/", [service, !Ref Cluster, !GetAtt Service.Name]] ScalableDimension: ecs:service:DesiredCount ServiceNamespace: ecs # "The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that allows Application Auto Scaling to modify your scalable target." @@ -303,7 +299,7 @@ Resources: AutoScalingPolicy: Type: AWS::ApplicationAutoScaling::ScalingPolicy Properties: - PolicyName: !Join ['', [!Ref 'AWS::StackName', AutoScalingPolicy]] + PolicyName: !Join ["", [!Ref "AWS::StackName", AutoScalingPolicy]] PolicyType: TargetTrackingScaling ScalingTargetId: !Ref AutoScalingTarget TargetTrackingScalingPolicyConfiguration: @@ -331,7 +327,7 @@ Resources: PolicyDocument: Version: 2012-10-17 Statement: - - Resource: '*' + - Resource: "*" Effect: Allow Action: - logs:CreateLogGroup @@ -384,7 +380,7 @@ Resources: - s3:GetObject - s3:GetObjectVersion - s3:GetBucketVersioning - - Resource: '*' + - Resource: "*" Effect: Allow Action: - ecs:List* @@ -459,7 +455,7 @@ Resources: Type: AWS::CodePipeline::Pipeline Properties: RoleArn: !GetAtt CodePipelineServiceRole.Arn - Name: !Join ['', [!Ref 'AWS::StackName', Pipeline]] + Name: !Join ["", [!Ref "AWS::StackName", Pipeline]] ArtifactStore: Type: S3 Location: !Ref ArtifactBucket @@ -515,7 +511,7 @@ Resources: Outputs: Endpoint: Description: Endpoint - Value: !Join ['', ['https://', !Ref DNSRecord]] + Value: !Join ["", ["https://", !Ref DNSRecord]] Service: Value: Service PipelineUrl: diff --git a/docker-compose.yml b/docker-compose.yml index c82ffeb..57012ed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,8 +14,6 @@ services: environment: - COGNITO_POOL_ID=eu-west-1_3O4VfvPn7 - DATABASE_URL=${DATABASE_URL} - - INTERIOR_TOKEN=${INTERIOR_TOKEN} - - INTERIOR_URL=${INTERIOR_URL} networks: default: diff --git a/src/datasources/interior-api.ts b/src/datasources/interior-api.ts index b6b313d..51f8b8d 100644 --- a/src/datasources/interior-api.ts +++ b/src/datasources/interior-api.ts @@ -47,7 +47,7 @@ export class InteriorAPI extends BaseSQLDataSource { } getInteriorRoomNumber(roomNumberPart: string): number { - return parseInt(roomNumberPart.slice(4), 10); + return convertToPossibleType(roomNumberPart.slice(4)); } decorateRow(row: any): Interior { diff --git a/src/datasources/interiors-lambda-api.ts b/src/datasources/interiors-lambda-api.ts deleted file mode 100644 index 810d477..0000000 --- a/src/datasources/interiors-lambda-api.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { RESTDataSource } from 'apollo-datasource-rest'; -import { Product, ProductGroup } from '../types/product-types'; - -export class InteriorsLambdaAPI extends RESTDataSource { - constructor() { - super(); - this.baseURL = process.env.INTERIOR_URL; - } - - /** - * - * @param product Product with printProducts and fields - * Example usage in a resolver - * - * const product = await (dataSources.productApi).getProduct(productId); - * await (dataSources.interiorsLambdaAPI).generateNewInteriors(product); - * - */ - async generateNewInteriors(product: Product): Promise { - const found = product.printProducts.find( - (pr) => pr.groupId === ProductGroup.WALLPAPER, - ); - const isRepeating = found !== undefined; - - const body = { - token: process.env.INTERIOR_TOKEN, - image: `products/${product.id}.jpg`, - primary_image_area: [ - product.fields.focusXpoint2, - product.fields.focusYpoint2, - ], - autodetect: true, - }; - - if (isRepeating) { - body['print_file_height'] = product.fields.printFileHeight; - body['print_file_dpi'] = product.fields.printFileDpi; - } - return this.post('/batch', body); - } -} diff --git a/src/datasources/order-api.ts b/src/datasources/order-api.ts index a701053..eb61602 100644 --- a/src/datasources/order-api.ts +++ b/src/datasources/order-api.ts @@ -66,7 +66,7 @@ export class OrderAPI extends BaseSQLDataSource { .count() .first() .cache(MINUTE * 60); - return res['count'] as number; // Optimize later to return Promise + return convertToPossibleType(res['count']); // Optimize later to return Promise } async getOrders(input: Maybe): Promise> { diff --git a/src/datasources/product-api.ts b/src/datasources/product-api.ts index 84506c2..5392a40 100644 --- a/src/datasources/product-api.ts +++ b/src/datasources/product-api.ts @@ -17,7 +17,7 @@ import { ProductSizeTypes, } from '../types/product-types'; import { Maybe } from '../types/types'; -import { convertToPossibleType, nullOrNumber } from './utils'; +import { convertToPossibleType } from './utils'; const MINUTE = 60; @@ -75,40 +75,11 @@ export class ProductAPI extends BaseSQLDataSource { if (!row.fields) { return null; // 53460 for example } - // Convert to camelCase first const fields = Object.keys(row.fields).reduce((mem, key) => { - mem[camelcase(key)] = row.fields[key]; + mem[camelcase(key)] = convertToPossibleType(row.fields[key]); return mem; }, {}); - fields['artNo'] = fields['artNo']; - fields['name'] = fields['name']; - fields['copyright'] = fields['copyright'] ?? ''; - fields['batch'] = fields['batch'] ?? ''; - fields['height'] = nullOrNumber(fields['height'] ?? null); - fields['width'] = nullOrNumber(fields['width'] ?? null); - fields['printFileWidth'] = nullOrNumber(fields['printFileWidth'] ?? null); - fields['printFileHeight'] = nullOrNumber(fields['printFileHeight'] ?? null); - fields['printFileDpi'] = nullOrNumber(fields['printFileDpi'] ?? null); - fields['marginWidthMax'] = nullOrNumber(fields['marginWidthMax'] ?? null); - fields['marginWidthMin'] = nullOrNumber(fields['marginWidthMin'] ?? null); - fields['marginHeightMax'] = nullOrNumber(fields['marginHeightMax'] ?? null); - fields['marginHeightMin'] = nullOrNumber(fields['marginHeightMin'] ?? null); - fields['focusXpoint2'] = nullOrNumber(fields['focusXpoint2'] ?? null); - fields['focusYpoint2'] = nullOrNumber(fields['focusYpoint2'] ?? null); - fields['focusXpoint'] = nullOrNumber(fields['focusXpoint'] ?? null); - fields['focusYpoint'] = nullOrNumber(fields['focusYpoint'] ?? null); - fields['photowallResolution'] = nullOrNumber( - fields['photowallResolution'] ?? null, - ); - fields['canvasResolution'] = nullOrNumber( - fields['canvasResolution'] ?? null, - ); - fields['wallpaperResolution'] = nullOrNumber( - fields['wallpaperResolution'] ?? null, - ); - fields['proportionsWarning?'] = fields['proportionsWarning?'] ?? ''; - fields['imageResolution'] = nullOrNumber(fields['imageResolution'] ?? null); return fields; } @@ -133,6 +104,8 @@ export class ProductAPI extends BaseSQLDataSource { row.fields = this.getProductFields(row); row.designerId = row.designerid; row.orientation = this.getOrientation(row.fields); + row.copyright = row.copyright ?? ''; + row.batch = row.batch ?? ''; return row; } @@ -141,7 +114,7 @@ export class ProductAPI extends BaseSQLDataSource { const res = await this.cachedRaw(query) .cache(MINUTE * 60) .then((data) => data.rows); - return res[0]['count'] as number; // Optimize later to return Promise + return convertToPossibleType(res[0]['count']); // Optimize later to return Promise } async getProducts(input: ProductsFilterInput): Promise> { diff --git a/src/datasources/utils.ts b/src/datasources/utils.ts index 373aa4c..b6844ee 100644 --- a/src/datasources/utils.ts +++ b/src/datasources/utils.ts @@ -1,10 +1,8 @@ -type InputType = string | undefined | null; - -function isNumeric(n: InputType) { - return !isNaN(parseFloat(n)); +function isNumeric(n: any) { + return !isNaN(n); } -function isBoolean(n: InputType | boolean) { +function isBoolean(n: any) { if (typeof n === 'boolean') { return true; } @@ -15,13 +13,9 @@ function isBoolean(n: InputType | boolean) { return false; } -export function convertToPossibleType(n: InputType) { +export function convertToPossibleType(n: any) { if ('' === n) return ''; if (isBoolean(n)) return Boolean(n); if (isNumeric(n)) return Number(n); return n; } - -export function nullOrNumber(n: InputType) { - return n === null || '' === n ? null : Number(n); -} diff --git a/src/index.ts b/src/index.ts index 357187f..3c6ef47 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,7 +17,6 @@ import { InteriorAPI } from './datasources/interior-api'; import { verifyToken } from './cognito/cognito-client'; import { readFileSync } from 'fs'; -import { InteriorsLambdaAPI } from './datasources/interiors-lambda-api'; const path = require('path'); const typeDefs = readFileSync( path.join(__dirname, './schema.graphql'), @@ -30,7 +29,6 @@ const knexConfig = knexStringcase(dbConfig); const categoryApi = new CategoryAPI(knexConfig); const designerApi = new DesignerAPI(knexConfig); const imageServerApi = new ImageServerApi(); -const interiorsLambdaApi = new InteriorsLambdaAPI(); const interiorApi = new InteriorAPI(knexConfig, imageServerApi); const keywordApi = new KeywordAPI(knexConfig); const marketLocaleApi = new MarketLocaleAPI(knexConfig); @@ -41,7 +39,6 @@ const dataSources = () => ({ categoryApi, designerApi, interiorApi, - interiorsLambdaApi, imageServerApi, keywordApi, marketLocaleApi, diff --git a/src/resolvers/keywords-resolver.ts b/src/resolvers/keywords-resolver.ts index 82f2e69..0c36b2d 100644 --- a/src/resolvers/keywords-resolver.ts +++ b/src/resolvers/keywords-resolver.ts @@ -30,6 +30,7 @@ export const keywordsMutationTypeDefs = { name, type, ); + console.log(id); return (dataSources.keywordApi).getKeywordById(id); }, }; diff --git a/src/resolvers/products-resolver.ts b/src/resolvers/products-resolver.ts index 320563b..34b5562 100644 --- a/src/resolvers/products-resolver.ts +++ b/src/resolvers/products-resolver.ts @@ -10,7 +10,6 @@ import { KeywordAPI } from '../datasources/keyword-api'; import { Keyword } from '../types/keyword-types'; import { MarketLocaleAPI } from '../datasources/market-locale-api'; import { InteriorAPI } from '../datasources/interior-api'; -import { InteriorsLambdaAPI } from '../datasources/interiors-lambda-api'; const ProductBlacklist = { async market(parent, _args, { dataSources }) { @@ -112,13 +111,7 @@ export const productMutationTypeDefs = { productId, groupIds, ); - const product = await (dataSources.productApi).getProduct( - productId, - ); - await (( - dataSources.interiorsLambdaApi - )).generateNewInteriors(product); - return product; + return (dataSources.productApi).getProduct(productId); }, async productKeywords(_, { productId, keywordIds }, { dataSources }) { await (dataSources.keywordApi).setProductKeywords( diff --git a/src/schema.graphql b/src/schema.graphql index 1944340..13a84ad 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -267,7 +267,7 @@ type ProductFields { focusYpoint: Float focusXpoint2: Float focusYpoint2: Float - batch: String + batch: String! imageResolution: Int printFileWidth: Int printFileHeight: Int