Add fococoffee endpoints

This commit is contained in:
2024-04-05 16:43:44 -06:00
parent 6084054590
commit e07f34137d
8 changed files with 224 additions and 0 deletions

View File

@@ -23,6 +23,7 @@ import { RedirectsModule } from './redirects/redirects.module';
import { FileModule } from './file/file.module'; import { FileModule } from './file/file.module';
import { FocoLiveModule } from './foco-live/foco-live.module'; import { FocoLiveModule } from './foco-live/foco-live.module';
import { PowModule } from './pow/pow.module'; import { PowModule } from './pow/pow.module';
import { FocoCoffeeModule } from './fococoffee/fococoffee.module';
@Module({ @Module({
imports: [ imports: [
@@ -80,6 +81,7 @@ import { PowModule } from './pow/pow.module';
FileModule, FileModule,
FocoLiveModule, FocoLiveModule,
PowModule, PowModule,
FocoCoffeeModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService],

View File

@@ -0,0 +1,16 @@
import { Injectable } from '@nestjs/common';
import { ShopifyProduct, parseShopifyProduct } from './shopifyUtils';
import axios from 'axios';
@Injectable()
export class BindleService {
public readonly shopifyUrl = 'https://bindlecoffee.com/products.json'
public async fetchProducts(): Promise<ShopifyProduct<Date>[]> {
const response = await axios.get(this.shopifyUrl)
if (response.status !== 200) {
throw new Error('Failed to fetch products')
}
return response.data.products.map(parseShopifyProduct);
}
}

View File

@@ -0,0 +1,48 @@
import { Controller, Get } from '@nestjs/common';
import { HarbingerService } from './harbinger.service';
import { FocoCoffeeService } from './fococoffee.service';
import { LimaService } from './lima.service';
import { BindleService } from './bindle.service';
import { ApiResponse, ApiTags } from '@nestjs/swagger';
@Controller('fococoffee')
@ApiTags('fococoffee')
export class FocoCoffeeController {
constructor(
private readonly focoCoffeeService: FocoCoffeeService,
private readonly harbingerService: HarbingerService,
private readonly limaService: LimaService,
private readonly bindleService: BindleService
) { }
@Get('harbinger/products')
@ApiResponse({ status: 200, description: 'Returns the list of Harbinger products' })
async getHarbingerProducts() {
return this.harbingerService.fetchProducts()
}
@Get('lima/products')
@ApiResponse({ status: 200, description: 'Returns the list of Lima products' })
async getLimaProducts() {
return this.limaService.fetchProducts()
}
@Get('bindle/products')
@ApiResponse({ status: 200, description: 'Returns the list of Bindle products' })
async getBindleProducts() {
return this.bindleService.fetchProducts()
}
@Get('beans')
@ApiResponse({ status: 200, description: 'Returns the list of coffee bean products from all suppliers' })
async getBeans() {
return this.focoCoffeeService.getBeans()
}
@Get('products')
@ApiResponse({ status: 200, description: 'Returns the list of all products from all suppliers' })
async getAllProducts() {
return this.focoCoffeeService.getAllProducts()
}
}

View File

@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { FocoCoffeeController } from './fococoffee.controller';
import { FocoCoffeeService } from './fococoffee.service';
import { HarbingerService } from './harbinger.service';
import { LimaService } from './lima.service';
import { BindleService } from './bindle.service';
@Module({
imports: [],
controllers: [FocoCoffeeController],
providers: [FocoCoffeeService, HarbingerService, LimaService, BindleService]
})
export class FocoCoffeeModule { }

View File

@@ -0,0 +1,39 @@
import { Injectable } from '@nestjs/common';
import { LimaService } from './lima.service';
import { HarbingerService } from './harbinger.service';
import { BindleService } from './bindle.service';
@Injectable()
export class FocoCoffeeService {
constructor(
private readonly limaService: LimaService,
private readonly harbingerService: HarbingerService,
private readonly bindleService: BindleService
) { }
public async getAllProducts() {
const limaProducts = await this.limaService.fetchProducts()
const harbingerProducts = await this.harbingerService.fetchProducts()
const bindleProducts = await this.bindleService.fetchProducts()
return {
lima: limaProducts,
harbinger: harbingerProducts,
bindle: bindleProducts
}
}
public async getBeans() {
const limaBeans = await this.limaService.fetchProducts()
const harbingerBeans = await this.harbingerService.fetchProducts()
const bindleBeans = await this.bindleService.fetchProducts()
return {
lima: limaBeans
.filter(p => p.tags.includes("Coffee"))
.filter(p => !p.title.toLocaleLowerCase().includes("subscription"))
.filter(p => p.handle !== "lima-sample-pack")
,
harbinger: harbingerBeans.filter(p => p.product_type === "Packaged Coffee"),
bindle: bindleBeans.filter(p => p.product_type === "Coffee, Roasted")
}
}
}

View File

@@ -0,0 +1,16 @@
import { Injectable } from '@nestjs/common';
import { ShopifyProduct, parseShopifyProduct } from './shopifyUtils';
import axios from 'axios';
@Injectable()
export class HarbingerService {
public readonly shopifyUrl = 'https://harbingercoffee.com/products.json'
public async fetchProducts(): Promise<ShopifyProduct<Date>[]> {
const response = await axios.get(this.shopifyUrl)
if (response.status !== 200) {
throw new Error('Failed to fetch products')
}
return response.data.products.map(parseShopifyProduct);
}
}

View File

@@ -0,0 +1,16 @@
import { Injectable } from '@nestjs/common';
import { ShopifyProduct, parseShopifyProduct } from './shopifyUtils';
import axios from 'axios';
@Injectable()
export class LimaService {
public readonly shopifyUrl = 'https://www.limacoffeeroasters.com/products.json'
public async fetchProducts(): Promise<ShopifyProduct<Date>[]> {
const response = await axios.get(this.shopifyUrl)
if (response.status !== 200) {
throw new Error('Failed to fetch products')
}
return response.data.products.map(parseShopifyProduct);
}
}

View File

@@ -0,0 +1,74 @@
import { pipe } from "ramda";
export interface ShopifyVariant<T> {
id: number;
title: string;
price: string;
sku: string;
position: number;
option1?: string;
option2?: string;
option3?: string;
taxable: boolean;
grams: number;
available: boolean;
created_at: T;
updated_at: T;
}
export interface ShopifyImage<T> {
id: number;
created_at: T;
position: number;
updated_at: T;
product_id: number;
variant_ids: number[];
src: string;
width: number;
height: number;
}
export interface ShopifyOption {
name: string;
position: number;
values: string[];
}
export interface ShopifyProduct<T> {
id: number;
title: string;
handle: string;
body_html: string;
published_at: T;
created_at: T;
vendor: string;
product_type: string;
updated_at: T;
tags: string[];
variants: ShopifyVariant<T>[];
images: ShopifyImage<T>[];
options: ShopifyOption[];
}
export const parseShopifyProductDates = (productIn: ShopifyProduct<string | Date>): ShopifyProduct<Date> => {
return {
...productIn,
published_at: new Date(productIn.published_at),
created_at: new Date(productIn.created_at),
updated_at: new Date(productIn.updated_at),
variants: productIn.variants.map((variant) => ({
...variant,
created_at: new Date(variant.created_at),
updated_at: new Date(variant.updated_at),
})),
images: productIn.images.map((image) => ({
...image,
created_at: new Date(image.created_at),
updated_at: new Date(image.updated_at),
})),
};
}
export const parseShopifyProduct = pipe(parseShopifyProductDates);