Skip to content

Commit

Permalink
#3 wip energy-label api
Browse files Browse the repository at this point in the history
  • Loading branch information
bennobuilder committed Aug 28, 2024
1 parent d1eb069 commit 4e7e7f0
Show file tree
Hide file tree
Showing 29 changed files with 448 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const baseUrl = assertValue(
'Environment variable "API_CORE_BASE_URL" not set!'
).replace(/\/$/, '');

export const apiCoreConfig = {
export const coreApiConfig = {
baseUrl,
shopify: {
baseUrl: `${baseUrl}/v1/shopify`,
Expand Down
2 changes: 1 addition & 1 deletion apps/sfy-eu-blocks-app/app/src/environment/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './api-core.config';
export * from './app.config';
export * from './core-api.config';
export * from './shopify.config';
6 changes: 3 additions & 3 deletions apps/sfy-eu-blocks-app/app/src/shopify.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import '@shopify/shopify-app-remix/adapters/node';

import { ApiVersion, AppDistribution, shopifyApp } from '@shopify/shopify-app-remix/server';

import { apiCoreConfig, appConfig, shopifyConfig } from './environment';
import { appConfig, coreApiConfig, shopifyConfig } from './environment';

export const apiCoreSessionStorage = new ApiCoreSessionStorage(
apiCoreConfig.shopify.baseUrl,
apiCoreConfig.shopify.bearerToken,
coreApiConfig.shopify.baseUrl,
coreApiConfig.shopify.bearerToken,
appConfig.name
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const baseUrl = assertValue(
'Environment variable "API_CORE_BASE_URL" not set!'
).replace(/\/$/, '');

export const apiCoreConfig = {
export const coreApiConfig = {
baseUrl,
shopify: {
baseUrl: `${baseUrl}/v1/shopify`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './api-core.config';
export * from './app.config';
export * from './core-api.config';
export * from './shopify.config';
6 changes: 3 additions & 3 deletions apps/sfy-playground-app/app/src/shopify.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import '@shopify/shopify-app-remix/adapters/node';

import { ApiVersion, AppDistribution, shopifyApp } from '@shopify/shopify-app-remix/server';

import { apiCoreConfig, appConfig, shopifyConfig } from './environment';
import { appConfig, coreApiConfig, shopifyConfig } from './environment';

export const apiCoreSessionStorage = new ApiCoreSessionStorage(
apiCoreConfig.shopify.baseUrl,
apiCoreConfig.shopify.bearerToken,
coreApiConfig.shopify.baseUrl,
coreApiConfig.shopify.bearerToken,
appConfig.name
);

Expand Down
2 changes: 2 additions & 0 deletions packages/api-energy-label/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# EPREL
EPREL_API_KEY=<api-key>
5 changes: 5 additions & 0 deletions packages/api-energy-label/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @type {import('@types/eslint').Linter.BaseConfig} */
module.exports = {
root: true,
extends: [require.resolve('@blgc/config/eslint/library')]
};
33 changes: 33 additions & 0 deletions packages/api-energy-label/dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { serve } from '@hono/node-server';
import { errorHandler, invalidPathHandler } from '@repo/api-utils';
import { Hono } from 'hono';

(async () => {
// Only load .env in development and before loading the app
const nodeEnv = process.env.NODE_ENV ?? 'local';
if (nodeEnv === 'local') {
const dotenv = await import('dotenv');
dotenv.config({ path: `.env.${nodeEnv}` });
console.log(`Loaded dotenv from '.env.${nodeEnv}'.`);
}

const port = 8787;
const app = new Hono();

// Append Shopify API router
const { createApp: createShopifyRoute, logger: shopifyLogger } = await import('./src');
app.route('/v1/energy-label', createShopifyRoute());
shopifyLogger.info(
`Initialized Shopify API at http://localhost:${port.toString()}/v1/energy-label`
);

app.onError(errorHandler);
app.notFound(invalidPathHandler);

console.info(`Server is running at http://localhost:${port.toString()}`);

serve({
fetch: app.fetch,
port
});
})();
51 changes: 51 additions & 0 deletions packages/api-energy-label/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "@repo/api-energy-label",
"version": "0.0.1",
"description": "Shopify API",
"private": true,
"scripts": {
"build": "shx rm -rf dist && chmod +x ../../scripts/cli.sh && ../../scripts/cli.sh bundle",
"start:dev": "tsx watch ./dev.js",
"lint": "eslint --ext .js,.ts src/",
"clean": "shx rm -rf dist && shx rm -rf node_modules && shx rm -rf .turbo",
"install:clean": "pnpm run clean && pnpm install",
"test": "vitest run"
},
"source": "./src/index.ts",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/builder-group/shopify.git"
},
"keywords": [],
"author": "@bennobuilder",
"license": "AGPL-3.0-or-later",
"bugs": {
"url": "https://github.com/builder-group/shopify/issues"
},
"homepage": "",
"dependencies": {
"@blgc/openapi-router": "^0.0.15",
"@blgc/utils": "^0.0.17",
"eprel-client": "^0.0.5",
"feature-logger": "^0.0.21",
"hono": "^4.5.9",
"valibot": "^0.39.0",
"validation-adapters": "^0.0.9"
},
"devDependencies": {
"@blgc/config": "^0.0.22",
"@hono/node-server": "^1.12.2",
"@repo/api-utils": "workspace:*",
"@repo/types": "workspace:*",
"@types/node": "^22.5.1",
"dotenv": "^16.4.5",
"tsx": "^4.19.0"
},
"files": [
"dist",
"README.md"
]
}
20 changes: 20 additions & 0 deletions packages/api-energy-label/src/app/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { errorHandler, invalidPathHandler } from '@repo/api-utils';
import { Hono } from 'hono';
import { logger as loggerMiddleware } from 'hono/logger';

import { logger } from '../logger';
import { router } from './router';

import './routes';

export function createApp(app: Hono = new Hono()): Hono {
app.onError(errorHandler);
app.notFound(invalidPathHandler);
app.use(
loggerMiddleware((str: string, ...rest: string[]) => {
logger.info(str, rest);
})
);
app.route('/', router);
return app;
}
7 changes: 7 additions & 0 deletions packages/api-energy-label/src/app/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type energyLabelApiV1 } from '@repo/types/api';
import { Hono } from 'hono';
import { createHonoOpenApiRouter } from '@blgc/openapi-router';

export const router = new Hono();

export const openApiRouter = createHonoOpenApiRouter<energyLabelApiV1.paths>(router);
3 changes: 3 additions & 0 deletions packages/api-energy-label/src/app/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './v1.health';
import './v1.product-groups';
import './v1.products';
10 changes: 10 additions & 0 deletions packages/api-energy-label/src/app/routes/v1.health.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { openApiRouter } from '../router';

openApiRouter.get('/health', {
handler: (c) => {
return c.json({
message: 'App is up and running',
status: 'Up'
});
}
});
60 changes: 60 additions & 0 deletions packages/api-energy-label/src/app/routes/v1.product-groups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { RequestError } from 'eprel-client';
import * as v from 'valibot';
import { vValidator } from 'validation-adapters/valibot';
import { AppError } from '@blgc/openapi-router';
import { extractErrorData } from '@blgc/utils';

import { eprelClient } from '../../eprel-client';
import { openApiRouter } from '../router';

openApiRouter.get('/product-groups', {
handler: async (c) => {
try {
const productGroups = await eprelClient.getProductGroups();
return c.json(productGroups);
} catch (e) {
if (e instanceof RequestError) {
throw new AppError('#ERR_EPREL_API', e.status, {
description: e.message
});
}
throw new AppError('#ERR_INTERNAL', 500, { description: extractErrorData(e).message });
}
}
});

openApiRouter.get('/product-groups/{productGroup}/products', {
pathValidator: vValidator(
v.object({
productGroup: v.pipe(v.string(), v.nonEmpty())
})
),
queryValidator: vValidator(
v.object({
model: v.pipe(v.string(), v.nonEmpty())
})
),
handler: async (c) => {
const { productGroup } = c.req.valid('param');
const { model } = c.req.valid('query');

try {
const { hits = [] } = await eprelClient.getModelsInProductGroup(productGroup, {
filter: {
modelIdentifier: model
}
});
if (hits.length === 0) {
throw new AppError('#ERR_NOT_FOUND', 404);
}
return c.json(hits[0]);
} catch (e) {
if (e instanceof RequestError) {
throw new AppError('#ERR_EPREL_API', e.status, {
description: e.message
});
}
throw new AppError('#ERR_INTERNAL', 500, { description: extractErrorData(e).message });
}
}
});
98 changes: 98 additions & 0 deletions packages/api-energy-label/src/app/routes/v1.products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { RequestError } from 'eprel-client';
import * as v from 'valibot';
import { vValidator } from 'validation-adapters/valibot';
import { AppError } from '@blgc/openapi-router';
import { extractErrorData } from '@blgc/utils';

import { eprelClient } from '../../eprel-client';
import { openApiRouter } from '../router';

const LanguageEnum = {
BG: 'BG',
CS: 'CS',
DA: 'DA',
DE: 'DE',
ET: 'ET',
EL: 'EL',
EN: 'EN',
ES: 'ES',
FR: 'FR',
GA: 'GA',
HR: 'HR',
IT: 'IT',
LV: 'LV',
LT: 'LT',
HU: 'HU',
MT: 'MT',
NL: 'NL',
PL: 'PL',
PT: 'PT',
RO: 'RO',
SK: 'SK',
SL: 'SL',
FI: 'FI',
SV: 'SV'
} as const;

openApiRouter.get('/products/{registrationNumber}/labels', {
pathValidator: vValidator(
v.object({
registrationNumber: v.pipe(v.string(), v.nonEmpty())
})
),
handler: async (c) => {
const { registrationNumber } = c.req.valid('param');

try {
// TODO: Construct addresses manually.. the URL pattern should be always the same
const { address } = await eprelClient.getProductLabels(registrationNumber, {
noRedirect: true
});
if (address == null) {
throw new AppError('#ERR_NOT_FOUND', 404);
}
return c.json([address]);
} catch (e) {
if (e instanceof RequestError) {
throw new AppError('#ERR_EPREL_API', e.status, {
description: e.message
});
}
throw new AppError('#ERR_INTERNAL', 500, { description: extractErrorData(e).message });
}
}
});

openApiRouter.get('/products/{registrationNumber}/sheets', {
pathValidator: vValidator(
v.object({
registrationNumber: v.pipe(v.string(), v.nonEmpty())
})
),
queryValidator: vValidator(
v.object({
language: v.optional(v.enum(LanguageEnum))
})
),
handler: async (c) => {
const { registrationNumber } = c.req.valid('param');

try {
// TODO: Construct addresses manually.. the URL pattern should be always the same
const { address } = await eprelClient.getProductFiches(registrationNumber, {
noRedirect: true
});
if (address == null) {
throw new AppError('#ERR_NOT_FOUND', 404);
}
return c.json([address]);
} catch (e) {
if (e instanceof RequestError) {
throw new AppError('#ERR_EPREL_API', e.status, {
description: e.message
});
}
throw new AppError('#ERR_INTERNAL', 500, { description: extractErrorData(e).message });
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const appConfig = {
environment: process.env.NODE_ENV ?? 'local',
port: process.env.APP_PORT ?? 9000,
packageVersion: process.env.npm_package_version
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { assertValue } from '@blgc/utils';

export const eprelApiConfig = {
apiKey: assertValue(process.env.EPREL_API_KEY, 'Environment variable "EPREL_API_KEY" not set!')
};
2 changes: 2 additions & 0 deletions packages/api-energy-label/src/environment/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './app.config';
export * from './eprel-api.config';
1 change: 1 addition & 0 deletions packages/api-energy-label/src/environment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './config';
5 changes: 5 additions & 0 deletions packages/api-energy-label/src/eprel-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createEPRELClient } from 'eprel-client';

import { eprelApiConfig } from './environment';

export const eprelClient = createEPRELClient({ apiKey: eprelApiConfig.apiKey });
3 changes: 3 additions & 0 deletions packages/api-energy-label/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './app';
export * from './environment';
export * from './logger';
8 changes: 8 additions & 0 deletions packages/api-energy-label/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createLogger, LOG_LEVEL, withPrefix } from 'feature-logger';

export const logger = withPrefix(
createLogger({
level: LOG_LEVEL.INFO
}),
'[@repo/api-energy-label]'
);
Loading

0 comments on commit 4e7e7f0

Please sign in to comment.