add a new API route and getSpots
continuous-integration/drone/push Build is passing Details

pull/19/head
Emre KARTAL 1 year ago
parent f761028031
commit bc612f98dd

@ -1,4 +1,3 @@
import IController from './interfaces/IController';
import { Router, Request, Response, NextFunction } from 'express';
import HttpException from '../exception/HttpException';

@ -35,6 +35,7 @@ class UserController implements IController {
this.login
);
this.router.get(`${this.path}`, authenticator, this.getUser);
this.router.get(`${this.path}s`, authenticator, this.getUsers);
this.router.delete(`${this.path}`, authenticator, this.deleteUser);
this.router.get(`${this.path}/nextTo`, authenticator, this.getUserNext);
this.router.delete(`${this.path}/musics/:id`, authenticator, this.deleteMusic);
@ -134,6 +135,27 @@ class UserController implements IController {
res.status(200).send({ data: req.user });
};
private getUsers = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
const userIds = req.query.ids as string;
if (!userIds) {
return res.status(400).json({ message: 'Please provide user ids' });
}
const userIdArray = userIds.split('&');
try {
const users = await this.userService.getUsers(userIdArray);
res.json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
private deleteUser = async (
req: Request,
res: Response,
@ -202,13 +224,13 @@ class UserController implements IController {
): Promise<Response | void> => {
try {
const { _id } = req.user;
const { idMusic, idUser } = req.body;
if (!idMusic || !idUser) {
return res.status(400).json({ error: 'idMusic and idUser are required fields.' });
const { musicId, userId } = req.body;
if (!musicId || !userId) {
return res.status(400).json({ error: 'musicId and userId are required fields.' });
}
const music: IMusic = {
idMusic,
idUser,
musicId,
userId,
date: new Date(),
};
await this.userService.addMusic(_id, music);

@ -34,8 +34,8 @@ const userSchema = new Schema({
},
musics_likes: {
type: [{
idMusic: String,
idUser: String,
musicId: String,
userId: String,
date: Date
}],
default: []

@ -1,15 +1,17 @@
import { Document } from 'mongoose';
export class UserLocation {
_id: string;
userId: string;
musicId : string;
latitude : number;
longitude: number;
constructor(userId: string, musicId : string,latitude: number, longitude: number){
musicId: string;
distance: number;
date: Date;
constructor(id: string, userId: string, musicId: string, distance: number, date: Date) {
this._id = id;
this.userId = userId;
this.musicId = musicId;
this.latitude = latitude;
this.longitude = longitude;
this.distance = distance;
this.date = date;
}
}
@ -18,4 +20,5 @@ export class Location extends Document {
musicId: string;
latitude: number;
longitude: number;
updatedAt: Date;
}

@ -1,5 +1,5 @@
export interface IMusic {
idMusic: string;
idUser: string;
musicId: string;
userId: string;
date: Date;
}

@ -0,0 +1,5 @@
export interface IPerson {
_id: string;
name: string;
image: string;
}

@ -17,17 +17,14 @@ class LocationService {
}
let usersLocation: UserLocation[] = [];
snapshot.forEach(doc => {
usersLocation.push(new UserLocation(doc.userId, doc.musicId, doc.latitude, doc.longitude));
});
const listUser: Record<string, string> = {};
usersLocation.forEach(user => {
const distance = this.distanceBetween(latitude, longitude, user.latitude, user.longitude);
if (distance <= 100) {
listUser[user.userId] = user.musicId;
snapshot.forEach(location => {
const distance = this.distanceBetween(latitude, longitude, location.latitude, location.longitude);
if (distance <= 1000) {
usersLocation.push(new UserLocation(location._id, location.userId, location.musicId, Math.ceil(distance / 200) * 200, location.updatedAt));
}
});
return { listUser };
return { data: usersLocation };
}
private distanceBetween(lat1: number, lon1: number, lat2: number, lon2: number): number {
@ -48,7 +45,7 @@ class LocationService {
dist = Math.acos(dist);
dist = dist * 180 / Math.PI;
dist = dist * 60 * 1.1515;
dist = dist * 1.609344;
dist = dist * 1.609344 * 1000;
return dist;
}

@ -3,7 +3,7 @@ import User from '../models/User';
import Token from '../models/Token';
export const createToken = (user: User): string => {
return jwt.sign({ id: user._id }, "dave" as jwt.Secret, {
return jwt.sign({ id: user._id }, process.env.SECRET_JWT as jwt.Secret, {
expiresIn: '1d',
});
};
@ -14,7 +14,7 @@ export const verifyToken = async (
return new Promise((resolve, reject) => {
jwt.verify(
token,
"dave" as jwt.Secret,
process.env.SECRET_JWT as jwt.Secret,
(err, payload) => {
if (err) return reject(err);
resolve(payload as Token);

@ -2,6 +2,8 @@ import { IMusic } from "../models/Music";
import LocationSchema from "../database/LocationSchema";
import UserSchema from "../database/UserSchema";
import token from "./TokenService";
import { IPerson } from "../models/Person";
import mongoose from "mongoose";
class UserService {
private user = UserSchema;
@ -56,6 +58,23 @@ class UserService {
}
}
public async getUsers(
ids: string[]
): Promise<IPerson[] | Error> {
try {
const validIds = ids.filter(id => mongoose.Types.ObjectId.isValid(id));
if (validIds.length === 0) {
return [];
}
return await this.user.find({ _id: { $in: validIds } })
.select('_id name image')
} catch (error: any) {
throw new Error(error.message);
}
}
public async addMusic(userId: string, music: IMusic): Promise<string | Error> {
try {
return await this.user.findByIdAndUpdate(userId, {

@ -5,16 +5,16 @@ import Animated, { interpolate, useAnimatedStyle, useSharedValue, withRepeat, wi
export default function Loading() {
const size = 100
const progresse = useSharedValue(1);
const progress = useSharedValue(1);
useEffect(() => {
progresse.value = withRepeat(withTiming(0.01, { duration: 750 }), -1, true);
}, [progresse]);
progress.value = withRepeat(withTiming(0.01, { duration: 750 }), -1, true);
}, [progress]);
const breatheStyleSquare = useAnimatedStyle(() => {
const borderRange = interpolate
(progresse.value,
(progress.value,
[0, 1],
[(size + 20), (size)],
);
@ -26,7 +26,6 @@ export default function Loading() {
borderWidth: size / 10,
borderColor: "#F80404",
shadowColor: "#F40C1C",
//shadowOffset: { width: 0, height: 0 },
shadowOpacity: 1,
shadowRadius: 10,
};

@ -3,7 +3,7 @@ export class Person {
private _name: string;
public image: string;
constructor(id: string, idSpotify: string, name: string, email: string, creationDate: Date, image: string) {
constructor(id: string, name: string, image: string) {
this._id = id;
this._name = name;
this.image = image;
@ -13,7 +13,15 @@ export class Person {
return this._id;
}
set id(value: string) {
this._id = value;
}
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
}

@ -1,22 +1,31 @@
import Music from "./Music";
import { Person } from "./Person";
export class Spot {
private _id: string;
private _user: string;
private _music: Music;
private _date: Date;
constructor(userId: string, music: Music, date: Date) {
this._user = userId;
constructor(id: string, user: string, music: Music, date: Date) {
this._id = id;
this._user = user;
this._music = music;
this._date = date;
}
get userSpotifyId(): string {
get id(): string {
return this._id;
}
set id(value: string) {
this._id = value;
}
get user(): string {
return this._user;
}
set userSpotifyId(value: string) {
set user(value: string) {
this._user = value;
}

@ -2,6 +2,6 @@ import { Spot } from "../Spot";
export class SpotMapper {
public static toModel(spot: any): Spot {
return new Spot(spot.idUser, spot.music, new Date(spot.date));
return new Spot(spot._id, spot.userId, spot.music, new Date(spot.date));
}
}

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { View, StyleSheet, Platform, Alert } from 'react-native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer } from '@react-navigation/native';
@ -15,17 +15,23 @@ import { colorsLight } from '../constants/colorsLight';
import { getUserCurrentMusic } from '../redux/thunk/appThunk';
import { logout } from '../redux/thunk/authThunk';
import { setAccessError, setErrorEmptyMusic } from '../redux/actions/userActions';
import * as Location from 'expo-location';
import { getSpotList } from '../redux/thunk/spotThunk';
import Music from '../models/Music';
export default function HomeNavigation() {
//@ts-ignore
const favoritesMusicLength = useSelector(state => state.appReducer.nbAddedFavoritesMusic);
const favoritesMusicLength = useSelector(state => state.appReducer.nbAddedFavoriteMusic);
//@ts-ignore
const accessError = useSelector(state => state.userReducer.accessError);
//@ts-ignore
const errorEmptyMusic = useSelector(state => state.userReducer.errorEmptyMusic);
// @ts-ignore
const isDark = useSelector(state => state.userReducer.dark);
// @ts-ignore
const currentMusic: Music = useSelector(state => state.appReducer.userCurrentMusic);
const [locationPermission, setLocationPermission] = useState(false);
const style = isDark ? colorsDark : colorsLight;
const BottomTabNavigator = createBottomTabNavigator();
const MyTheme = {
@ -40,11 +46,49 @@ export default function HomeNavigation() {
const dispatch = useDispatch();
const requestLocationPermission = async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
alert(
"Oups ! Il semble que l'accès à votre localisation soit désactivé. Pour découvrir la musique des personnes autour de vous, veuillez autoriser l'accès à la localisation dans les paramètres de votre appareil."
);
} else {
setLocationPermission(true);
}
}
useEffect(() => {
requestLocationPermission();
}, []);
useEffect(() => {
//@ts-ignore
dispatch(getUserCurrentMusic());
const getSpots = async () => {
//@ts-ignore
dispatch(getUserCurrentMusic());
}
getSpots();
const interval = setInterval(getSpots, 30000);
return () => {
clearInterval(interval);
};
}, []);
useEffect(() => {
getSpots();
}, [currentMusic]);
const getSpots = async () => {
if (currentMusic && locationPermission) {
const location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Low,
});
//@ts-ignore
dispatch(getSpotList(location.coords.longitude, location.coords.latitude, currentMusic._id));
}
}
useEffect(() => {
if (accessError) {
Alert.alert(

@ -1,5 +1,5 @@
import Music from "../../model/Music";
import { Spot } from "../../model/Spot";
import Music from "../../models/Music";
import { Spot } from "../../models/Spot";
import { favoritesTypes } from "../types/favoritesTypes";
import { spotifyTypes } from "../types/spotifyTypes";

@ -1,5 +1,6 @@
import { Spot } from "../../model/Spot";
import { Spot } from "../../models/Spot";
import { spotTypes } from "../types/spotTypes";
import { userTypes } from "../types/userTypes";
export const setSpotList = (spotList: Spot[]) => {
return {
@ -13,11 +14,4 @@ export const removeFromSpotList = (spot: Spot) => {
type: spotTypes.REMOVE_SPOT,
payload: spot
}
}
export const addSpotListMock = (spotList: Spot[]) => {
return {
type: spotTypes.ADD_SPOT_MOCK,
payload: spotList,
};
}

@ -1,4 +1,4 @@
import { User } from "../../model/User";
import { User } from "../../models/User";
import { userTypes } from "../types/userTypes";
export interface LoginCredentials {

@ -1,13 +1,14 @@
import { Spot } from "../../model/Spot";
import { Spot } from "../../models/Spot";
import { favoritesTypes } from "../types/favoritesTypes";
import { spotifyTypes } from "../types/spotifyTypes";
import { spotTypes } from "../types/spotTypes";
import { userTypes } from "../types/userTypes";
const initialState = {
spot: [] as Spot[],
favoriteMusic: [] as Spot[],
userCurrentMusic: null,
nbAddedFavoritesMusic: 0
nbAddedFavoriteMusic: 0
}
const appReducer = (state = initialState, action: any) => {
@ -16,14 +17,16 @@ const appReducer = (state = initialState, action: any) => {
return { ...state, favoriteMusic: action.payload };
case spotTypes.FETCH_SPOT:
const uniqueSpots = action.payload.filter((spot: Spot) => {
return !state.spot.some((s) => s.userSpotifyId === spot.userSpotifyId && s.music.id === spot.music.id);
return !state.spot.some((s) => s.user === spot.user && s.music.id === spot.music.id);
});
const updatedSpotList = [...uniqueSpots, ...state.spot];
return { ...state, spot: updatedSpotList };
case spotTypes.REMOVE_SPOT:
return { ...state, spot: state.spot.filter((spot) => spot.userSpotifyId !== action.payload.userSpotifyId && spot.music.id !== action.payload.music.id) };
return { ...state, spot: state.spot.filter((spot) => spot.user !== action.payload.user && spot.music.id !== action.payload.music.id) };
case spotifyTypes.GET_USER_CURRENT_MUSIC:
return { ...state, userCurrentMusic: action.payload };
case userTypes.USER_LOGOUT:
return { ...state, spot: [], favoriteMusic: [], userCurrentMusic: null, nbAddedFavoriteMusic: 0 };
default:
return state;
}

@ -1,5 +1,5 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { User } from "../../model/User";
import { User } from "../../models/User";
import { userTypes } from "../types/userTypes";
const initialState = {

@ -1,11 +1,11 @@
import axios from "axios";
import * as SecureStore from 'expo-secure-store';
import { Spot } from "../../model/Spot";
import { Spot } from "../../models/Spot";
import configs from "../../constants/config";
import { MusicServiceProvider } from "../../model/MusicServiceProvider";
import { MusicServiceProvider } from "../../models/MusicServiceProvider";
import { setFavoriteMusic, setUserCurrentMusic } from "../actions/appActions";
import { setAccessError, setErrorEmptyMusic } from "../actions/userActions";
import { SpotMapper } from "../../model/mapper/SpotMapper";
import { SpotMapper } from "../../models/mapper/SpotMapper";
export const getUserCurrentMusic = () => {
//@ts-ignore
@ -59,12 +59,12 @@ export const getFavoriteMusic = () => {
{ headers }
)
const musicIds = resp.data.musics.map((music: any) => music.idMusic);
const musicIds = resp.data.musics.map((music: any) => music.musicId);
const musics = await MusicServiceProvider.musicService.getMusicsWithIds(musicIds);
const result = resp.data.musics
.filter((music: any) => musics.some((m: any) => m.id === music.idMusic))
.filter((music: any) => musics.some((m: any) => m.id === music.musicId))
.map((music: any) => {
const matchingMusic = musics.find((m: any) => m.id === music.idMusic);
const matchingMusic = musics.find((m: any) => m.id === music.musicId);
return {
...music,
music: matchingMusic,

@ -2,8 +2,8 @@ import axios from "axios";
import configs from "../../constants/config";
import { LoginCredentials, RegisterCredentials, restoreToken, userLogin, userLogout, setErrorLogin, setErrorSignup, setErrorNetwork } from "../actions/userActions";
import * as SecureStore from 'expo-secure-store';
import { UserMapper } from "../../model/mapper/UserMapper";
import { MusicServiceProvider } from "../../model/MusicServiceProvider";
import { UserMapper } from "../../models/mapper/UserMapper";
import { MusicServiceProvider } from "../../models/MusicServiceProvider";
const keyRemember = 'rememberUser';

@ -1,9 +1,49 @@
import axios from "axios";
const key = 'userToken';
import configs from "../../constants/config";
import * as SecureStore from 'expo-secure-store';
import qs from "qs";
import { setSpotList } from "../actions/spotActions";
import { MusicServiceProvider } from "../../models/MusicServiceProvider";
import { SpotMapper } from "../../models/mapper/SpotMapper";
export const getSpotList = () => {
export const getSpotList = (longitude: string, latitude: string, music: string) => {
//@ts-ignore
return async dispatch => {
try {
let token: string | null = await SecureStore.getItemAsync(configs.key);
const body: Record<string, string | boolean | number | (string | boolean | number)[]> = {
longitude: longitude,
latitude: latitude,
currentMusic: music
}
const resp = await axios({
url: configs.API_URL + '/user/nextTo?' + qs.stringify(body),
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
},
});
const musicIds = resp.data.data.map((music: any) => music.musicId);
const musics = await MusicServiceProvider.musicService.getMusicsWithIds(musicIds);
const result = resp.data.data
.filter((spot: any) => musics.some((m: any) => m.id === spot.musicId))
.map((spot: any) => {
const matchingMusic = musics.find((m: any) => m.id === spot.musicId);
return {
...spot,
music: matchingMusic,
};
});
dispatch(setSpotList(result.map((item: any) => SpotMapper.toModel(item))));
} catch (error: any) {
switch (error.response.status) {
default:
console.error("Error retrieving spots : " + error);
break;
}
}
}
}

@ -2,7 +2,7 @@ import axios from "axios";
import configs from "../../constants/config";
import { setDarkMode, setErrorNetwork, setErrorUpdateMessage, userLogin } from "../actions/userActions";
import * as SecureStore from 'expo-secure-store';
import { UserMapper } from "../../model/mapper/UserMapper";
import { UserMapper } from "../../models/mapper/UserMapper";
export const darkMode = (value: boolean) => {
//@ts-ignore

@ -1,5 +1,4 @@
export const spotTypes = {
FETCH_SPOT: 'FETCH_SPOT',
ADD_SPOT_MOCK: 'ADD_SPOT_MOCK',
REMOVE_SPOT: 'REMOVE_SPOT'
}

@ -4,12 +4,12 @@ import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle,
import { Audio } from 'expo-av';
import { useEffect, useState } from "react";
import normalize from '../components/Normalize';
import Music from "../model/Music";
import Music from "../models/Music";
import { LinearGradient } from "expo-linear-gradient";
import { MusicServiceProvider } from "../model/MusicServiceProvider";
import { MusicServiceProvider } from "../models/MusicServiceProvider";
import { SimilarMusic } from "../components/SimilarMusicComponent";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import Artist from "../model/Artist";
import Artist from "../models/Artist";
import { BlurView } from 'expo-blur';
const halfPi = Math.PI / 2;

@ -9,7 +9,7 @@ import { useSelector, useDispatch } from 'react-redux';
import { colorsDark } from '../constants/colorsDark';
import { colorsLight } from '../constants/colorsLight';
import { getFavoriteMusic } from '../redux/thunk/appThunk';
import { Spot } from '../model/Spot';
import { Spot } from '../models/Spot';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
export default function FavoriteScreen() {

@ -1,6 +1,6 @@
import React, { useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { View, StyleSheet, Text, Image, TouchableWithoutFeedback, Keyboard, TouchableOpacity, SafeAreaView } from 'react-native';
import { View, StyleSheet, Text, Image, TouchableWithoutFeedback, Keyboard, TouchableOpacity, SafeAreaView, Alert } from 'react-native';
import { Svg, Path } from 'react-native-svg';
import { useNavigation } from "@react-navigation/native";
import { useDispatch, useSelector } from 'react-redux';
@ -11,8 +11,9 @@ import { logout } from '../redux/thunk/authThunk';
import { darkMode } from '../redux/thunk/userThunk';
import { colorsDark } from '../constants/colorsDark';
import { colorsLight } from '../constants/colorsLight';
import { User } from '../model/User';
import { User } from '../models/User';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import * as Location from 'expo-location';
// @ts-ignore
const DismissKeyboard = ({ children }) => (
@ -38,7 +39,7 @@ export default function SettingScreen() {
// @ts-ignore
const isDark = useSelector(state => state.userReducer.dark);
const [locationPermission, setLocationPermission] = useState(false);
const style = isDark ? colorsDark : colorsLight;
async function ChangeDarkMode() {
@ -48,6 +49,19 @@ export default function SettingScreen() {
dispatch(darkMode(JSON.parse(newValue)))
}
const checkLocationPermission = async () => {
const { status } = await Location.getForegroundPermissionsAsync();
if (status !== 'granted') {
setLocationPermission(false);
} else {
setLocationPermission(true);
}
}
useEffect(() => {
checkLocationPermission();
}, []);
const [isCheckedNotif, setIsCheckedNotif] = useState(false);
const toggleNotif =
@ -58,20 +72,21 @@ export default function SettingScreen() {
dispatch(logout());
}
const [isCheckedLocalisation, setIsCheckedLocalisation] = useState(false);
const toggleLocalisation =
() => setIsCheckedLocalisation(value => !value);
() => {
Alert.alert(
'Localisation',
"Pour changer la permission de localisation, veuillez aller dans les paramètres de votre téléphone et ajuster les autorisations pour l'application."
);
};
const insets = useSafeAreaInsets();
const styles = StyleSheet.create({
mainSafeArea: {
flex: 1,
backgroundColor: style.body,
paddingTop: insets.top
},
container: {
marginTop: 30,
@ -323,7 +338,7 @@ export default function SettingScreen() {
</View>
<View style={styles.lastOptionView}>
<Text style={styles.textOption}>Localisation</Text>
<Switch style={{ marginBottom: normalize(10), marginRight: 20 }} value={isCheckedLocalisation} onValueChange={toggleLocalisation} />
<Switch style={{ marginBottom: normalize(10), marginRight: 20 }} value={locationPermission} onValueChange={toggleLocalisation} />
</View>
</View>
</View>

@ -10,12 +10,13 @@ import Lotties from '../assets/lottie/Lottie';
import Loading from '../components/LoadingComponent';
import { useNavigation } from '@react-navigation/native';
import { useDispatch, useSelector } from 'react-redux';
import { Spot } from '../model/Spot';
import { Spot } from '../models/Spot';
import { removeFromSpotList, setSpotList } from '../redux/actions/spotActions';
import { MusicServiceProvider } from '../models/MusicServiceProvider';
export default function SpotScreen() {
//@ts-ignore
const spotReducer = useSelector(state => state.appReducer.spot)
const spotReducer: Spot[] = useSelector(state => state.appReducer.spot)
const [cards, setCards] = useState<Spot[]>(spotReducer);
const [currentCard, setcurrentCard] = useState(cards[cards.length - 1]);
@ -33,8 +34,8 @@ export default function SpotScreen() {
removeSpots(currentCard);
}
else if (direction === 'down') {
addMockSpots();
console.log('Swiped down');
MusicServiceProvider.musicService.addToPlaylist(currentCard.music.id);
removeSpots(currentCard);
}
};
@ -69,11 +70,10 @@ export default function SpotScreen() {
// @ts-ignore
navigator.navigate("Detail", { "music": card.music })
};
return (
<View style={{
flex: 1,
}}>
<>
{cards.length > 0 ? (
<>
<ImageBackground blurRadius={7}
@ -96,7 +96,7 @@ export default function SpotScreen() {
left: wWidht / 9,
top: normalize(87),
color: "#FFFFFF",
fontSize: normalize(currentCard.music.name),
fontSize: normalize(40),
fontWeight: "800",
}}>{currentCard.music.name}</Text>
<Text
@ -149,11 +149,11 @@ export default function SpotScreen() {
<View style={{ position: "absolute" }}>
<Loading />
</View>
<Text style={{ color: "grey", fontWeight: "400", textAlign: "center", top: 100 }}>Vous avez explorer toutes les spot autour de vous.
<Text style={{ color: "grey", fontWeight: "400", textAlign: "center", top: "10%" }}>Vous avez explorer toutes les spot autour de vous.
{"\n"}Continuer dans discoverie pour découvrir de nouvelles music basées sur vos gouts musicaux.</Text>
</View>)
}
</View>
</>
);
};

@ -2,6 +2,9 @@ import Music from "../models/Music";
import IMusicService from "./musics/interfaces/IMusicService";
export default class EmptyMusicService implements IMusicService {
getImageArtistWithId(idArtist: string): Promise<string | null> {
throw new Error("Method not implemented.");
}
getMusicById(id: string): Promise<Music> {
throw new Error("Method not implemented.");
}

@ -1,4 +1,4 @@
import Music from "../../../model/Music";
import Music from "../../../models/Music";
export default interface IMusicService {
getMusicById(id: string): Promise<Music>;

@ -1,8 +1,8 @@
import axios from "axios";
import Music from "../../../model/Music";
import Music from "../../../models/Music";
import IMusicService from "../interfaces/IMusicService";
import TokenSpotify from "./TokenSpotify";
import MusicMapper from "../../../model/mapper/MusicMapper";
import MusicMapper from "../../../models/mapper/MusicMapper";
export default class SpotifyService implements IMusicService {
private readonly API_URL = "https://api.spotify.com/v1";

Loading…
Cancel
Save