File upload functionality for own interiors (#39)

This commit is contained in:
Niklas Fondberg
2021-09-13 13:38:53 +02:00
committed by GitHub
parent 0a4be2b081
commit 84bed9a7ce
15 changed files with 2275 additions and 4 deletions
+10
View File
@@ -17,3 +17,13 @@ export const dbConfig = {
},
],
};
export const uploadBucket =
process.env.ENVIRONMENT_NAME === 'production'
? 'photowall-inquiries-dev'
: 'photowall-inquiries-dev';
export const imageBucket =
process.env.ENVIRONMENT_NAME === 'production'
? 'photowall-images'
: 'photowall-images-dev';
+34 -2
View File
@@ -4,10 +4,11 @@ import {
InteriorType,
} from '../types/interior-types';
import { Orientation } from '../types/product-types';
import { Maybe } from '../types/types';
import { IdNumberResult, Maybe } from '../types/types';
import { BaseSQLDataSource } from './BaseSQLDataSource';
import { ImageServerApi } from './imageserver-api';
import { convertToPossibleType } from './utils';
import * as CONFIG from '../config';
import { moveS3File } from '../s3';
const MINUTE = 60;
@@ -120,4 +121,35 @@ export class InteriorAPI extends BaseSQLDataSource {
});
});
}
// Mutations
async addOwnUploadToPrintId(
printId: number,
uploadedS3Key: string,
): Promise<IdNumberResult> {
// Get the last position entered in interiors for this printId
const positionRes: any = await this.knex
.queryBuilder()
.from('interiors')
.where({ print_id: printId })
.max('position')
.first();
const position = positionRes.max === null ? 0 : positionRes.max + 1;
// Insert the printId in interiors returning the id (important as it is used as image key name)
const res = await this.knex('interiors')
.insert({ print_id: printId, position: position })
.returning('id');
const interiorId = res[0];
// Move file
moveS3File(
CONFIG.uploadBucket,
uploadedS3Key,
CONFIG.imageBucket,
`interior-images/${interiorId}.jpg`,
);
return {
id: interiorId,
};
}
}
+31
View File
@@ -0,0 +1,31 @@
import { EXPIRES_DEFAULT_SECS, getSignedPutObjectUrl } from '../s3';
import {
SignedUploadURL,
SignedUploadURLInput,
} from '../types/file-upload-types';
import * as CONFIG from '../config';
async function getSignedFileUploadURL(
_,
args: SignedUploadURLInput,
__,
): Promise<SignedUploadURL> {
const config = args.config;
const bucket = CONFIG.uploadBucket;
const url = await getSignedPutObjectUrl(
bucket,
config.key,
config.contentType,
);
return {
url: url,
key: config.key,
bucket: bucket,
expires: EXPIRES_DEFAULT_SECS,
};
}
export const imagesQueryTypeDefs = {
signedFileUploadURL: getSignedFileUploadURL,
};
+2
View File
@@ -18,6 +18,7 @@ import {
import { DateTimeResolver, DateResolver, JSONResolver } from 'graphql-scalars';
import { interiorsQueryTypeDefs, interiorTypeDefs } from './interiors-resolver';
import { imagesQueryTypeDefs } from './file-upload-resolvers';
const resolvers = {
Query: {
...orderQueryTypeDefs,
@@ -27,6 +28,7 @@ const resolvers = {
...keywordsQueryTypeDefs,
...marketsQueryTypeDefs,
...interiorsQueryTypeDefs,
...imagesQueryTypeDefs,
},
Mutation: {
...productMutationTypeDefs,
+12
View File
@@ -4,6 +4,7 @@ import {
ProductListResult,
ProductsFilterInput,
} from '../types/product-types';
import * as CONFIG from '../config';
import { CategoryAPI } from '../datasources/category-api';
import { Category } from '../types/category-types';
import { KeywordAPI } from '../datasources/keyword-api';
@@ -11,6 +12,7 @@ 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';
import { IdNumberResult } from '../types/types';
const ProductBlacklist = {
async market(parent, _args, { dataSources }) {
@@ -127,6 +129,16 @@ export const productMutationTypeDefs = {
);
return (<ProductAPI>dataSources.productApi).getProduct(productId);
},
async addOwnInteriorToPrintProduct(
_,
{ printId, uploadedS3Key },
{ dataSources },
): Promise<IdNumberResult> {
return (<InteriorAPI>dataSources.interiorApi).addOwnUploadToPrintId(
printId,
uploadedS3Key,
);
},
async productWallpaperType(
_,
{ productId, wallpaperTypes },
+48
View File
@@ -0,0 +1,48 @@
import {
S3Client,
CopyObjectCommand,
DeleteObjectCommand,
PutObjectCommand,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
export const EXPIRES_DEFAULT_SECS = 300;
export const getSignedPutObjectUrl = (
bucket: string,
key: string,
contentType: string,
): Promise<string> => {
const s3 = new S3Client({ region: 'eu-west-1' });
return getSignedUrl(
s3,
new PutObjectCommand({
Bucket: bucket,
Key: key,
ContentType: contentType,
}),
{ expiresIn: EXPIRES_DEFAULT_SECS },
);
};
export const moveS3File = async (
fromBucket: string,
fromKey: string,
toBucket: string,
toKey: string,
): Promise<any> => {
const s3 = new S3Client({ region: 'eu-west-1' });
const copyCmd = new CopyObjectCommand({
Bucket: toBucket,
CopySource: `${fromBucket}/${fromKey}`,
Key: toKey,
});
await s3.send(copyCmd);
const deleteCmd = new DeleteObjectCommand({
Bucket: fromBucket,
Key: fromKey,
});
return s3.send(deleteCmd);
};
+33
View File
@@ -5,6 +5,17 @@ scalar DateTime
scalar Date
scalar JSON
enum CacheControlScope {
PUBLIC
PRIVATE
}
directive @cacheControl(
maxAge: Int
scope: CacheControlScope
inheritMaxAge: Boolean
) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
type Query {
orders(pagination: PaginationInput!, filter: DateFilterInput!): OrdersResult
order(id: ID!): Order
@@ -24,6 +35,13 @@ type Query {
locales: [Locale]
locale(value: String!): Locale
interiors(filter: InteriorsFilterInput): [Interior]
signedFileUploadURL(config: SignedUploadURLInput): SignedUploadURL
@cacheControl(maxAge: 0)
}
input SignedUploadURLInput {
contentType: String!
key: String!
}
input PaginationInput {
@@ -68,6 +86,13 @@ input InteriorsFilterInput {
productId: Int!
}
type SignedUploadURL {
url: String!
key: String!
bucket: String!
expires: Int!
}
type Market {
id: ID!
name: String!
@@ -394,6 +419,10 @@ input ProductBlacklistingInput {
groupIds: [Int]
}
type IdNumberResult {
id: Int!
}
type Mutation {
productInfo(productId: Int!, info: ProductInfoInput!): Product
productFocusPoint(
@@ -407,6 +436,10 @@ type Mutation {
): Product
productGroup(productId: Int!, groupIds: [Int]): Product
productKeywords(productId: Int!, keywordIds: [Int]): Product
addOwnInteriorToPrintProduct(
printId: Int!
uploadedS3Key: String!
): IdNumberResult
productWallpaperType(
productId: Int!
wallpaperTypes: [ProductWallpaperType]!
+15
View File
@@ -0,0 +1,15 @@
export interface SignedUploadURLInput {
config: SignedUploadURLInputConfig;
}
export interface SignedUploadURLInputConfig {
contentType: string;
key: string;
}
export interface SignedUploadURL {
url: string;
key: string;
bucket: string;
expires: number;
}
+4
View File
@@ -24,3 +24,7 @@ export interface PaginationResult {
offset: number;
limit: number;
}
export interface IdNumberResult {
id: number;
}