adapté à la CLEAN
This commit is contained in:
parent
f540d03c1d
commit
31671d4ec0
4
api.http
4
api.http
@ -12,5 +12,7 @@ POST {{url}}/locations
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Bourg-Achard"
|
||||
"name": "Bourg-Achard 2",
|
||||
"lat": 49.443232,
|
||||
"lon": 1.099971
|
||||
}
|
||||
6
jest.config.js
Normal file
6
jest.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
testEnvironment: "node",
|
||||
transform: {
|
||||
"^.+\\.tsx?$": "ts-jest",
|
||||
},
|
||||
};
|
||||
7181
package-lock.json
generated
7181
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "ts-node src/index.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "jest"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
@ -13,7 +13,10 @@
|
||||
"@types/node-fetch": "^2.6.1",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"ts-node": "^10.5.0",
|
||||
"typescript": "^4.5.5"
|
||||
"typescript": "^4.5.5",
|
||||
"@types/jest": "27.4.1",
|
||||
"jest": "27.5.1",
|
||||
"ts-jest": "27.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^7.5.0",
|
||||
|
||||
67
solid/employee.ts
Normal file
67
solid/employee.ts
Normal file
@ -0,0 +1,67 @@
|
||||
class Employee_ {
|
||||
// Nom, prénom, etc...
|
||||
|
||||
public reportHours() {}
|
||||
public calculatePay() {}
|
||||
public save() {}
|
||||
}
|
||||
|
||||
class Employee {
|
||||
public constructor(private email: string) {}
|
||||
// Nom, prénom, etc...
|
||||
}
|
||||
|
||||
class PayCalculator {
|
||||
calculatePay(employee: Employee) {}
|
||||
}
|
||||
|
||||
class HoursReporter {
|
||||
reportHours(employee: Employee) {}
|
||||
}
|
||||
|
||||
class EmployeeSaver {
|
||||
save(employee: Employee) {}
|
||||
}
|
||||
|
||||
interface EmployeeRepository {
|
||||
save(employee: Employee);
|
||||
}
|
||||
|
||||
class PostgresRepository implements EmployeeRepository {
|
||||
save(employee: Employee) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
function registerEmployee(repository: EmployeeRepository, email: string) {
|
||||
// Validation mot de passe, email, unicité
|
||||
const empl = new Employee(email);
|
||||
|
||||
repository.save(empl);
|
||||
}
|
||||
|
||||
// Dans mes tests unitaires
|
||||
|
||||
class InMemoryRepository implements EmployeeRepository {
|
||||
constructor(public employees: Employee[] = []) {}
|
||||
|
||||
save(employee: Employee) {
|
||||
this.employees.push(employee);
|
||||
}
|
||||
}
|
||||
|
||||
function TestRegisterUser() {
|
||||
const repository = new InMemoryRepository();
|
||||
|
||||
registerEmployee(repository, "un@email.com");
|
||||
|
||||
// assert(repository.employees.length) == 1
|
||||
}
|
||||
|
||||
function TestRegisterUserDuplicateEmail() {
|
||||
const repository = new InMemoryRepository([new Employee("un@email.com")]);
|
||||
|
||||
registerEmployee(repository, "un@email.com");
|
||||
|
||||
// assert erreur levée car email dupliqué
|
||||
}
|
||||
@ -28,7 +28,7 @@ class Square implements Shape {
|
||||
}
|
||||
|
||||
class AreaCalculator {
|
||||
constructor(private readonly shapes: Shape[]) {}
|
||||
constructor(protected readonly shapes: Shape[]) {}
|
||||
|
||||
public sum(): number {
|
||||
return this.shapes.reduce((total, shape) => total + shape.area(), 0);
|
||||
@ -38,7 +38,7 @@ class AreaCalculator {
|
||||
class VolumeCalculator extends AreaCalculator {
|
||||
public sum(): number {
|
||||
// logique de calcul, on retournerait la somme des volumes
|
||||
return 0;
|
||||
return this.shapes.reduce((total, shape) => total + shape.volume(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
interface ReaderAndWriter extends Writer, Reader {}
|
||||
|
||||
interface File {
|
||||
write(data: any): void;
|
||||
read(): any;
|
||||
}
|
||||
|
||||
interface Writer {
|
||||
write(data: any): void;
|
||||
}
|
||||
|
||||
10
src/clean/adapters/fastifyPresenter.ts
Normal file
10
src/clean/adapters/fastifyPresenter.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { FastifyReply } from "fastify";
|
||||
import Presenter from "../usecases/presenter";
|
||||
|
||||
export default class FastifyPresenter<T> implements Presenter<T> {
|
||||
constructor(private readonly res: FastifyReply) {}
|
||||
|
||||
show(result: T): void {
|
||||
this.res.status(200).send(result);
|
||||
}
|
||||
}
|
||||
14
src/clean/adapters/inMemoryLocationsRepository.ts
Normal file
14
src/clean/adapters/inMemoryLocationsRepository.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import LocationsRepository from "../entities/locationsRepository";
|
||||
import Location from "../entities/location";
|
||||
|
||||
export class InMemoryLocationRepository implements LocationsRepository {
|
||||
constructor(public locations: Location[] = []) {}
|
||||
|
||||
getAll(): Location[] {
|
||||
return this.locations;
|
||||
}
|
||||
|
||||
save(location: Location): void {
|
||||
this.locations.push(location);
|
||||
}
|
||||
}
|
||||
22
src/clean/adapters/openWeatherService.ts
Normal file
22
src/clean/adapters/openWeatherService.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import fetch from "node-fetch";
|
||||
import WeatherService, { Forecast } from "../entities/weatherService";
|
||||
|
||||
export default class OpenWeatherService implements WeatherService {
|
||||
constructor(
|
||||
private readonly appid: string,
|
||||
private readonly units: string = "metric"
|
||||
) {}
|
||||
|
||||
async getForecastFor(lat: number, lon: number): Promise<Forecast> {
|
||||
const { weather, main } = await fetch(
|
||||
`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${this.appid}&units=${this.units}`
|
||||
).then((r) => r.json());
|
||||
|
||||
return {
|
||||
temperature: main.temp,
|
||||
min: main.temp_min,
|
||||
max: main.temp_max,
|
||||
weather: weather[0].main,
|
||||
};
|
||||
}
|
||||
}
|
||||
29
src/clean/adapters/sqliteLocationsRepository.ts
Normal file
29
src/clean/adapters/sqliteLocationsRepository.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Database } from "better-sqlite3";
|
||||
import Location from "../entities/location";
|
||||
import LocationsRepository from "../entities/locationsRepository";
|
||||
|
||||
export default class SqliteLocationsRepository implements LocationsRepository {
|
||||
constructor(private readonly db: Database) {}
|
||||
|
||||
getAll(): Location[] {
|
||||
return this.db
|
||||
.prepare("SELECT id, name, lat, lon FROM locations")
|
||||
.all()
|
||||
.map((locationData) => {
|
||||
return Object.setPrototypeOf(locationData, Location);
|
||||
});
|
||||
}
|
||||
|
||||
save(location: Location): void {
|
||||
this.db
|
||||
.prepare(
|
||||
"INSERT INTO locations (id, name, lat, lon) VALUES (@id, @name, @lat, @lon)"
|
||||
)
|
||||
.run({
|
||||
id: location.id,
|
||||
name: location.name,
|
||||
lat: location.lat,
|
||||
lon: location.lon,
|
||||
});
|
||||
}
|
||||
}
|
||||
16
src/clean/entities/location.test.ts
Normal file
16
src/clean/entities/location.test.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import Location from "./location";
|
||||
|
||||
describe("entity location", () => {
|
||||
it("can be created", () => {
|
||||
const name = "Bourg-Achard";
|
||||
const lat = 49.443232;
|
||||
const lon = 1.099971;
|
||||
|
||||
const location = new Location(name, lat, lon);
|
||||
|
||||
expect(location.id).not.toBeUndefined();
|
||||
expect(location.name).toEqual(name);
|
||||
expect(location.lat).toEqual(lat);
|
||||
expect(location.lon).toEqual(lon);
|
||||
});
|
||||
});
|
||||
13
src/clean/entities/location.ts
Normal file
13
src/clean/entities/location.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
export default class Location {
|
||||
public id: string;
|
||||
|
||||
public constructor(
|
||||
public name: string,
|
||||
public lat: number,
|
||||
public lon: number
|
||||
) {
|
||||
this.id = uuid();
|
||||
}
|
||||
}
|
||||
6
src/clean/entities/locationsRepository.ts
Normal file
6
src/clean/entities/locationsRepository.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import Location from "./location";
|
||||
|
||||
export default interface LocationsRepository {
|
||||
getAll(): Location[];
|
||||
save(location: Location): void; // On pourrait séparer lecture / écriture
|
||||
}
|
||||
10
src/clean/entities/weatherService.ts
Normal file
10
src/clean/entities/weatherService.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export type Forecast = {
|
||||
weather: string;
|
||||
temperature: number;
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
|
||||
export default interface WeatherService {
|
||||
getForecastFor(lat: number, lon: number): Promise<Forecast>;
|
||||
}
|
||||
33
src/clean/usecases/createLocation.test.ts
Normal file
33
src/clean/usecases/createLocation.test.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { InMemoryLocationRepository } from "../adapters/inMemoryLocationsRepository";
|
||||
import Location from "../entities/location";
|
||||
import createLocation, { CreateLocationData } from "./createLocation";
|
||||
import Presenter from "./presenter";
|
||||
|
||||
export class DummyPresenter<T> implements Presenter<T> {
|
||||
constructor(public result?: T) {}
|
||||
|
||||
show(result: T): void {
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
|
||||
describe("use case createLocation", () => {
|
||||
it("persist a location", () => {
|
||||
const cmd: CreateLocationData = {
|
||||
name: "Bourg-Achard",
|
||||
lat: 49.443232,
|
||||
lon: 1.099971,
|
||||
};
|
||||
const presenter = new DummyPresenter<Location>();
|
||||
const repository = new InMemoryLocationRepository();
|
||||
|
||||
createLocation(repository, presenter, cmd);
|
||||
|
||||
expect(repository.locations).toHaveLength(1);
|
||||
const newLocation = repository.locations[0];
|
||||
|
||||
expect(newLocation.name).toEqual(cmd.name);
|
||||
expect(newLocation.lat).toEqual(cmd.lat);
|
||||
expect(newLocation.lon).toEqual(cmd.lon);
|
||||
});
|
||||
});
|
||||
72
src/clean/usecases/createLocation.ts
Normal file
72
src/clean/usecases/createLocation.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import Location from "../entities/location";
|
||||
import LocationsRepository from "../entities/locationsRepository";
|
||||
import Presenter from "./presenter";
|
||||
|
||||
// On pourrait l'injecter dans le use case
|
||||
// export interface CreateLocationDataValidator {
|
||||
// validate(cmd: CreateLocationData): void;
|
||||
// }
|
||||
|
||||
export type CreateLocationData = {
|
||||
name: string;
|
||||
lat: number;
|
||||
lon: number;
|
||||
};
|
||||
|
||||
// interface UseCase<TCommand> {
|
||||
// execute(cmd: TCommand): void;
|
||||
// }
|
||||
|
||||
// class CreateLocationUseCase implements UseCase<CreateLocationData> {
|
||||
// public constructor(private readonly locationRepository: LocationRepository) {}
|
||||
|
||||
// execute(cmd: CreateLocationData): void {
|
||||
// // FIXME: Valider les entrées utilisateurs
|
||||
// if (typeof cmd !== "object") {
|
||||
// throw new Error("should be an object");
|
||||
// }
|
||||
|
||||
// const location = new Location(cmd.name, cmd.lat, cmd.lon);
|
||||
|
||||
// this.locationRepository.save(location);
|
||||
// }
|
||||
// }
|
||||
|
||||
// const db = new Database("monfichier.db")
|
||||
// const sqliteRepository = new SqliteLocationRepository(db);
|
||||
// const uc = new CreateLocationUseCase(sqliteRepository)
|
||||
|
||||
// uc.execute({
|
||||
|
||||
// })
|
||||
|
||||
// createLocation(sqliteRepository, {
|
||||
|
||||
// });
|
||||
|
||||
export default function createLocation(
|
||||
locationRepository: LocationsRepository,
|
||||
presenter: Presenter<Location>,
|
||||
cmd: CreateLocationData
|
||||
) {
|
||||
// FIXME: Valider les entrées utilisateurs
|
||||
if (typeof cmd !== "object") {
|
||||
throw new Error("should be an object");
|
||||
}
|
||||
|
||||
const location = new Location(cmd.name, cmd.lat, cmd.lon);
|
||||
|
||||
locationRepository.save(location);
|
||||
presenter.show(location);
|
||||
}
|
||||
|
||||
// export function createLocation_(
|
||||
// locationRepository: LocationsRepository,
|
||||
// cmd: CreateLocationData
|
||||
// ): Location {
|
||||
// const location = new Location(cmd.name, cmd.lat, cmd.lon);
|
||||
|
||||
// locationRepository.save(location);
|
||||
|
||||
// return location;
|
||||
// }
|
||||
48
src/clean/usecases/getForecasts.test.ts
Normal file
48
src/clean/usecases/getForecasts.test.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { InMemoryLocationRepository } from "../adapters/inMemoryLocationsRepository";
|
||||
import Location from "../entities/location";
|
||||
import WeatherService, { Forecast } from "../entities/weatherService";
|
||||
import { DummyPresenter } from "./createLocation.test";
|
||||
import getForecasts, { GetForecastsResult } from "./getForecasts";
|
||||
|
||||
class DummyWeatherService implements WeatherService {
|
||||
getForecastFor(lat: number, lon: number): Promise<Forecast> {
|
||||
return Promise.resolve({
|
||||
weather: "Sunny",
|
||||
temperature: 12,
|
||||
min: 10,
|
||||
max: 16,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe("use case getForecasts", () => {
|
||||
it("retrieve all locations forecasts", async () => {
|
||||
const repository = new InMemoryLocationRepository([
|
||||
new Location("Rouen", 49.443232, 1.099971),
|
||||
new Location("Bourg-Achard", 49.35322, 0.81623),
|
||||
]);
|
||||
const presenter = new DummyPresenter<GetForecastsResult[]>();
|
||||
const weatherService = new DummyWeatherService();
|
||||
|
||||
await getForecasts(presenter, repository, weatherService);
|
||||
|
||||
expect(presenter.result).not.toBeUndefined();
|
||||
expect(presenter.result).toHaveLength(2);
|
||||
|
||||
let forecast = presenter.result![0];
|
||||
|
||||
expect(forecast.location.name).toEqual("Rouen");
|
||||
expect(forecast.forecast.max).toEqual(16);
|
||||
expect(forecast.forecast.min).toEqual(10);
|
||||
expect(forecast.forecast.temperature).toEqual(12);
|
||||
expect(forecast.forecast.weather).toEqual("Sunny");
|
||||
|
||||
forecast = presenter.result![1];
|
||||
|
||||
expect(forecast.location.name).toEqual("Bourg-Achard");
|
||||
expect(forecast.forecast.max).toEqual(16);
|
||||
expect(forecast.forecast.min).toEqual(10);
|
||||
expect(forecast.forecast.temperature).toEqual(12);
|
||||
expect(forecast.forecast.weather).toEqual("Sunny");
|
||||
});
|
||||
});
|
||||
32
src/clean/usecases/getForecasts.ts
Normal file
32
src/clean/usecases/getForecasts.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import Location from "../entities/location";
|
||||
import LocationsRepository from "../entities/locationsRepository";
|
||||
import WeatherService, { Forecast } from "../entities/weatherService";
|
||||
import Presenter from "./presenter";
|
||||
|
||||
export type GetForecastsResult = {
|
||||
location: Location;
|
||||
forecast: Forecast;
|
||||
};
|
||||
|
||||
export default async function getForecasts(
|
||||
presenter: Presenter<GetForecastsResult[]>,
|
||||
repository: LocationsRepository,
|
||||
weatherService: WeatherService
|
||||
) {
|
||||
const locations = repository.getAll();
|
||||
const forecasts = await Promise.all(
|
||||
locations.map(async (location) => {
|
||||
const forecast = await weatherService.getForecastFor(
|
||||
location.lat,
|
||||
location.lon
|
||||
);
|
||||
|
||||
return {
|
||||
location,
|
||||
forecast,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
presenter.show(forecasts);
|
||||
}
|
||||
24
src/clean/usecases/locationService.ts
Normal file
24
src/clean/usecases/locationService.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import LocationsRepository from "../entities/locationsRepository";
|
||||
import WeatherService from "../entities/weatherService";
|
||||
import { CreateLocationData } from "./createLocation";
|
||||
|
||||
export default class LocationService {
|
||||
constructor(
|
||||
private readonly repository: LocationsRepository,
|
||||
private readonly weatherService: WeatherService
|
||||
) {}
|
||||
|
||||
createLocation(data: CreateLocationData) {
|
||||
// TODO
|
||||
// const location = new Location()
|
||||
// this.repository.save(location);
|
||||
}
|
||||
|
||||
getLocations() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
getForecasts() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
3
src/clean/usecases/presenter.ts
Normal file
3
src/clean/usecases/presenter.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default interface Presenter<T> {
|
||||
show(result: T): void;
|
||||
}
|
||||
19
src/clean/usecases/showLocations.test.ts
Normal file
19
src/clean/usecases/showLocations.test.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { InMemoryLocationRepository } from "../adapters/inMemoryLocationsRepository";
|
||||
import Location from "../entities/location";
|
||||
import { DummyPresenter } from "./createLocation.test";
|
||||
import showLocations from "./showLocations";
|
||||
|
||||
describe("use case showsLocations", () => {
|
||||
it("show every locations", () => {
|
||||
const repository = new InMemoryLocationRepository([
|
||||
new Location("Rouen", 49.443232, 1.099971),
|
||||
new Location("Bourg-Achard", 49.35322, 0.81623),
|
||||
]);
|
||||
const presenter = new DummyPresenter<Location[]>();
|
||||
|
||||
showLocations(repository, presenter);
|
||||
|
||||
expect(presenter.result).not.toBeUndefined();
|
||||
expect(presenter.result).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
11
src/clean/usecases/showLocations.ts
Normal file
11
src/clean/usecases/showLocations.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import Location from "../entities/location";
|
||||
import LocationsRepository from "../entities/locationsRepository";
|
||||
import Presenter from "./presenter";
|
||||
|
||||
export default function showLocations(
|
||||
repository: LocationsRepository,
|
||||
presenter: Presenter<Location[]>
|
||||
) {
|
||||
const locations = repository.getAll();
|
||||
presenter.show(locations);
|
||||
}
|
||||
54
src/index.old.ts
Normal file
54
src/index.old.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import fastify from "fastify";
|
||||
import Database from "better-sqlite3";
|
||||
import fetch from "node-fetch";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
const db = new Database("weathery.db");
|
||||
const APPID = "d6f0f985f37372e9824c68a52662cc23";
|
||||
|
||||
const app = fastify({
|
||||
logger: true,
|
||||
});
|
||||
|
||||
app.post("/locations", async (req) => {
|
||||
const payload: any = req.body;
|
||||
const location = {
|
||||
id: uuid(),
|
||||
name: payload.name,
|
||||
};
|
||||
|
||||
db.prepare("INSERT INTO locations (id, name) VALUES (@id, @name)").run(
|
||||
location
|
||||
);
|
||||
|
||||
return location;
|
||||
});
|
||||
|
||||
app.get("/locations", async () => {
|
||||
const locations = db.prepare("SELECT * FROM locations").all();
|
||||
|
||||
return { locations };
|
||||
});
|
||||
|
||||
app.get("/", async () => {
|
||||
const locations = db.prepare("SELECT * FROM locations").all();
|
||||
|
||||
const weathers = await Promise.all(
|
||||
locations.map(async (location) => {
|
||||
const { lat, lon } = location;
|
||||
const { weather, main: properties } = await fetch(
|
||||
`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${APPID}&units=metric`
|
||||
).then((r) => r.json());
|
||||
|
||||
return {
|
||||
...location,
|
||||
weather,
|
||||
properties,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return weathers;
|
||||
});
|
||||
|
||||
app.listen(8080);
|
||||
64
src/index.ts
64
src/index.ts
@ -1,7 +1,13 @@
|
||||
import fastify from "fastify";
|
||||
import showLocations from "./clean/usecases/showLocations";
|
||||
import LocationsRepository from "./clean/entities/locationsRepository";
|
||||
import FastifyPresenter from "./clean/adapters/fastifyPresenter";
|
||||
import SqliteLocationsRepository from "./clean/adapters/sqliteLocationsRepository";
|
||||
import Database from "better-sqlite3";
|
||||
import fetch from "node-fetch";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import createLocation from "./clean/usecases/createLocation";
|
||||
import getForecasts from "./clean/usecases/getForecasts";
|
||||
import OpenWeatherService from "./clean/adapters/openWeatherService";
|
||||
import WeatherService from "./clean/entities/weatherService";
|
||||
|
||||
const db = new Database("weathery.db");
|
||||
const APPID = "d6f0f985f37372e9824c68a52662cc23";
|
||||
@ -10,45 +16,33 @@ const app = fastify({
|
||||
logger: true,
|
||||
});
|
||||
|
||||
app.post("/locations", async (req) => {
|
||||
const payload: any = req.body;
|
||||
const location = {
|
||||
id: uuid(),
|
||||
name: payload.name,
|
||||
};
|
||||
// const locationsRepository = new InMemoryLocationRepository([
|
||||
// new Location("Rouen", 49.443232, 1.099971),
|
||||
// new Location("Bourg-Achard", 49.35322, 0.81623),
|
||||
// ]);
|
||||
const locationsRepository: LocationsRepository = new SqliteLocationsRepository(
|
||||
db
|
||||
);
|
||||
const weatherService: WeatherService = new OpenWeatherService(APPID);
|
||||
|
||||
db.prepare("INSERT INTO locations (id, name) VALUES (@id, @name)").run(
|
||||
location
|
||||
);
|
||||
|
||||
return location;
|
||||
app.get("/locations", async (_, res) => {
|
||||
showLocations(locationsRepository, new FastifyPresenter(res));
|
||||
});
|
||||
|
||||
app.get("/locations", async () => {
|
||||
const locations = db.prepare("SELECT * FROM locations").all();
|
||||
|
||||
return { locations };
|
||||
app.get("/", async (_, res) => {
|
||||
await getForecasts(
|
||||
new FastifyPresenter(res),
|
||||
locationsRepository,
|
||||
weatherService
|
||||
);
|
||||
});
|
||||
|
||||
app.get("/", async () => {
|
||||
const locations = db.prepare("SELECT * FROM locations").all();
|
||||
|
||||
const weathers = await Promise.all(
|
||||
locations.map(async (location) => {
|
||||
const { lat, lon } = location;
|
||||
const { weather, main: properties } = await fetch(
|
||||
`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${APPID}&units=metric`
|
||||
).then((r) => r.json());
|
||||
|
||||
return {
|
||||
...location,
|
||||
weather,
|
||||
properties,
|
||||
};
|
||||
})
|
||||
app.post("/locations", async (req, res) => {
|
||||
createLocation(
|
||||
locationsRepository,
|
||||
new FastifyPresenter(res),
|
||||
req.body as any
|
||||
);
|
||||
|
||||
return weathers;
|
||||
});
|
||||
|
||||
app.listen(8080);
|
||||
|
||||
BIN
weathery.db
BIN
weathery.db
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user