| public class DbRequestMessage | public class DbRequestMessage | ||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public string Id { get; set; } | |||||
| public string? Id { get; set; } | |||||
| } | } | ||||
| } | } |
| public class SaveTrackRequest : TrackResponse | public class SaveTrackRequest : TrackResponse | ||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public string UserId { get; set; } | |||||
| public string? UserId { get; set; } | |||||
| } | } | ||||
| } | } |
| public class TrackResponse | public class TrackResponse | ||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public string Id { get; set; } | |||||
| public string? Id { get; set; } | |||||
| [ProtoMember(2)] | [ProtoMember(2)] | ||||
| public string Title { get; set; } | |||||
| public string? Title { get; set; } | |||||
| [ProtoMember(3)] | [ProtoMember(3)] | ||||
| public string Artist { get; set; } | |||||
| public string? Artist { get; set; } | |||||
| [ProtoMember(4)] | [ProtoMember(4)] | ||||
| public string Album { get; set; } | |||||
| public string? Album { get; set; } | |||||
| [ProtoMember(5)] | [ProtoMember(5)] | ||||
| public string TrackId { get; set; } | |||||
| public string? TrackId { get; set; } | |||||
| } | } | ||||
| } | } |
| public class UserResponse | public class UserResponse | ||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public string Id { get; set; } | |||||
| public string? Id { get; set; } | |||||
| [ProtoMember(2)] | [ProtoMember(2)] | ||||
| public string Token { get; set; } | |||||
| public string? Token { get; set; } | |||||
| [ProtoMember(3)] | [ProtoMember(3)] | ||||
| public string RefreshToken{ get; set; } | |||||
| public string? RefreshToken{ get; set; } | |||||
| } | } | ||||
| } | } |
| public class VoidMessage | public class VoidMessage | ||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public string InsertedId { get; set; } | |||||
| public string? InsertedId { get; set; } | |||||
| } | } | ||||
| } | } |
| { | { | ||||
| public static class GLOBALS | public static class GLOBALS | ||||
| { | { | ||||
| public const String SPOTIFYURL = "https://api.spotify.com/v1/"; | |||||
| public const String MEDIATYPE = "application/json"; | |||||
| public const string SPOTIFYURL = "https://api.spotify.com/v1/"; | |||||
| public const string MEDIATYPE = "application/json"; | |||||
| } | } | ||||
| } | } |
| { | { | ||||
| [BsonId] | [BsonId] | ||||
| [BsonRepresentation(BsonType.ObjectId)] | [BsonRepresentation(BsonType.ObjectId)] | ||||
| public string Id { get; set; } | |||||
| public string Title { get; set; } | |||||
| public string Artist { get; set; } | |||||
| public string Album { get; set; } | |||||
| public string UserId { get; set; } | |||||
| public string TrackId { get; set; } | |||||
| public string? Id { get; set; } | |||||
| public string? Title { get; set; } | |||||
| public string? Artist { get; set; } | |||||
| public string? Album { get; set; } | |||||
| public string? UserId { get; set; } | |||||
| public string? TrackId { get; set; } | |||||
| } | } | ||||
| } | } |
| { | { | ||||
| [BsonId] | [BsonId] | ||||
| [BsonRepresentation(BsonType.ObjectId)] | [BsonRepresentation(BsonType.ObjectId)] | ||||
| public string Id { get; set; } | |||||
| public string Token { get; set; } | |||||
| public string RefreshToken { get; set; } | |||||
| public string? Id { get; set; } | |||||
| public string? Token { get; set; } | |||||
| public string? RefreshToken { get; set; } | |||||
| } | } | ||||
| } | } |
| if (userResponse != null) | if (userResponse != null) | ||||
| { | { | ||||
| tokenS = userResponse.Token; | |||||
| refreshT = userResponse.RefreshToken; | |||||
| tokenS = userResponse.Token!; | |||||
| refreshT = userResponse.RefreshToken!; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| await identityService.SaveTrackAsync(new GrpcShared.DTO.Db.SaveTrackRequest | await identityService.SaveTrackAsync(new GrpcShared.DTO.Db.SaveTrackRequest | ||||
| { | { | ||||
| TrackId = track.Item.Id, | |||||
| Title = track.Item.Name, | |||||
| Album = track.Item.Album.Name, | |||||
| Artist = track.Item.Artists[0].Name, | |||||
| UserId = userId | |||||
| TrackId = track.Item!.Id!, | |||||
| Title = track.Item!.Name!, | |||||
| Album = track.Item.Album!.Name!, | |||||
| Artist = track.Item.Artists![0].Name!, | |||||
| UserId = userId! | |||||
| }); | }); | ||||
| //var resp = await identityService.ListUsersAsync(new VoidMessage()); | //var resp = await identityService.ListUsersAsync(new VoidMessage()); | ||||
| var tokenFromDb = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage | var tokenFromDb = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage | ||||
| { | { | ||||
| Id = userId | |||||
| Id = userId! | |||||
| }); | }); | ||||
| var trackByUser = await identityService.GetTrackByUserAsync(new GrpcShared.DTO.Db.DbRequestMessage | var trackByUser = await identityService.GetTrackByUserAsync(new GrpcShared.DTO.Db.DbRequestMessage | ||||
| { | { | ||||
| Id = userId | |||||
| Id = userId! | |||||
| }); | }); | ||||
| //await identityService.DeleteTrackAsync(new GrpcShared.DTO.Db.DbRequestMessage | //await identityService.DeleteTrackAsync(new GrpcShared.DTO.Db.DbRequestMessage | ||||
| // { | // { | ||||
| await identityService.SaveTrackAsync(new GrpcShared.DTO.Db.SaveTrackRequest | await identityService.SaveTrackAsync(new GrpcShared.DTO.Db.SaveTrackRequest | ||||
| { | { | ||||
| TrackId = track.Item.Id, | |||||
| Title = track.Item.Name, | |||||
| Album = track.Item.Album.Name, | |||||
| Artist = track.Item.Artists[0].Name, | |||||
| UserId = userId | |||||
| TrackId = track.Item.Id!, | |||||
| Title = track.Item.Name!, | |||||
| Album = track.Item.Album.Name!, | |||||
| Artist = track.Item.Artists[0].Name!, | |||||
| UserId = userId! | |||||
| }); | }); | ||||
| //find id from local storage | //find id from local storage | ||||
| //await identityService.DeleteUserAsync(new GrpcShared.DTO.Db.DbRequestMessage | //await identityService.DeleteUserAsync(new GrpcShared.DTO.Db.DbRequestMessage |
| @code { | @code { | ||||
| private string? message; | |||||
| protected override async Task OnInitializedAsync() | protected override async Task OnInitializedAsync() | ||||
| { | { |
| public class Worker : BackgroundService | public class Worker : BackgroundService | ||||
| { | { | ||||
| private readonly ILogger<Worker> _logger; | private readonly ILogger<Worker> _logger; | ||||
| private HttpClient _httpClient; | |||||
| private HttpClient? _httpClient; | |||||
| public Worker(ILogger<Worker> logger) | public Worker(ILogger<Worker> logger) | ||||
| { | { | ||||
| _logger = logger; | _logger = logger; |
| if (req.StatusCode == System.Net.HttpStatusCode.Unauthorized) | if (req.StatusCode == System.Net.HttpStatusCode.Unauthorized) | ||||
| { | { | ||||
| string newToken = await SpotifyHelper.TryRefreshToken(authService, userResponse, identityService); | |||||
| string? newToken = await TryRefreshToken(authService, userResponse, identityService); | |||||
| if (newToken != null) | if (newToken != null) | ||||
| { | { | ||||
| client.DefaultRequestHeaders.Remove(HeaderNames.Authorization); | |||||
| client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + newToken); | client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + newToken); | ||||
| req = await client.GetAsync(url); | req = await client.GetAsync(url); | ||||
| response = JsonConvert.DeserializeObject<T>(await req.Content.ReadAsStringAsync())!; | response = JsonConvert.DeserializeObject<T>(await req.Content.ReadAsStringAsync())!; | ||||
| } | } | ||||
| public static async Task PutData(HttpClient client, string url, string userId, IIdentityService identityService, IAuthService authService) | |||||
| public static async Task PutData(HttpClient client, | |||||
| string url, | |||||
| string userId, | |||||
| IIdentityService identityService, | |||||
| IAuthService authService) | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| var tokenMessage = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage { Id = userId }); | |||||
| var tokenMessage = await identityService.GetTokenByIdAsync(new DbRequestMessage { Id = userId }); | |||||
| //add header | //add header | ||||
| client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + tokenMessage.Token); | client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + tokenMessage.Token); | ||||
| //get request | //get request | ||||
| var responseMessage = await client.PutAsync(url, null); | var responseMessage = await client.PutAsync(url, null); | ||||
| if(responseMessage.StatusCode == System.Net.HttpStatusCode.Unauthorized) | |||||
| if (responseMessage.StatusCode == System.Net.HttpStatusCode.Unauthorized) | |||||
| { | { | ||||
| string newToken = await SpotifyHelper.TryRefreshToken(authService, tokenMessage, identityService); | |||||
| string? newToken = await TryRefreshToken(authService, tokenMessage, identityService); | |||||
| if (newToken != null) | if (newToken != null) | ||||
| { | { | ||||
| client.DefaultRequestHeaders.Remove(HeaderNames.Authorization); | |||||
| client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + newToken); | |||||
| responseMessage = await client.PutAsync(url, null); | responseMessage = await client.PutAsync(url, null); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| public static async Task<string?> TryRefreshToken( | |||||
| IAuthService authService, | |||||
| UserResponse user, | |||||
| IIdentityService identityService) | |||||
| { | |||||
| var refreshResponse = await authService.RefreshAccessToken(user); | |||||
| if (refreshResponse.AccessToken != null) | |||||
| { | |||||
| await identityService.UpdateTokenAsync(new UserResponse | |||||
| { | |||||
| Id = user.Id, | |||||
| Token = refreshResponse.AccessToken, | |||||
| RefreshToken = user.RefreshToken | |||||
| }); | |||||
| return refreshResponse.AccessToken; | |||||
| } | |||||
| else return null; | |||||
| } | |||||
| } | } | ||||
| } | } |
| using GrpcShared.DTO; | |||||
| using GrpcShared.DTO.Db; | |||||
| using GrpcShared.Interfaces; | |||||
| using NemAnBlazor.Services.Interfaces; | |||||
| namespace SpotifyService | |||||
| { | |||||
| public static class SpotifyHelper | |||||
| { | |||||
| public static async Task<string?> TryRefreshToken | |||||
| (IAuthService authService, | |||||
| UserResponse user, | |||||
| IIdentityService identityService) | |||||
| { | |||||
| var refreshResponse = await authService.RefreshAccessToken(user); | |||||
| if (refreshResponse.AccessToken != null) | |||||
| { | |||||
| await identityService.UpdateTokenAsync(new UserResponse | |||||
| { | |||||
| Id = user.Id, | |||||
| Token = refreshResponse.AccessToken, | |||||
| RefreshToken = user.RefreshToken | |||||
| }); | |||||
| return refreshResponse.AccessToken; | |||||
| } | |||||
| else return null; | |||||
| } | |||||
| } | |||||
| } |
| //using IdentityProvider.Protos.AuthService; | //using IdentityProvider.Protos.AuthService; | ||||
| using Blazored.LocalStorage; | using Blazored.LocalStorage; | ||||
| using Grpc.Core; | |||||
| using Grpc.Net.Client; | using Grpc.Net.Client; | ||||
| using GrpcShared; | using GrpcShared; | ||||
| using GrpcShared.DTO; | using GrpcShared.DTO; | ||||
| //AUTHORIZATION HEADER | //AUTHORIZATION HEADER | ||||
| http.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Basic " + Convert.ToBase64String(contentType)); | http.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Basic " + Convert.ToBase64String(contentType)); | ||||
| ////ACCEPT HEADER | |||||
| //http.DefaultRequestHeaders.Accept.Add( | |||||
| // new MediaTypeWithQualityHeaderValue("application/json")); | |||||
| //BODY PARAMS | //BODY PARAMS | ||||
| var requestBody = new Dictionary<string, string>(); | var requestBody = new Dictionary<string, string>(); | ||||
| requestBody["grant_type"] = tokenRequest.GrantType; | requestBody["grant_type"] = tokenRequest.GrantType; | ||||
| requestBody["code"] = tokenRequest.Code!; | requestBody["code"] = tokenRequest.Code!; | ||||
| requestBody["redirect_uri"] = tokenRequest.RedirectUri!; | requestBody["redirect_uri"] = tokenRequest.RedirectUri!; | ||||
| //REQUEST | |||||
| var response = await http.PostAsync(url, new FormUrlEncodedContent(requestBody)); | |||||
| try | |||||
| { | |||||
| //REQUEST | |||||
| var response = await http.PostAsync(url, new FormUrlEncodedContent(requestBody)); | |||||
| var contents = JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync())!; | |||||
| return contents; | |||||
| } | |||||
| catch (RpcException e) | |||||
| { | |||||
| if (e.StatusCode == StatusCode.Cancelled) | |||||
| { | |||||
| return new TokenResponse(); | |||||
| } | |||||
| throw; | |||||
| } | |||||
| var contents = JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync()); | |||||
| return contents; | |||||
| } | } | ||||
| public async Task<UserInfoResponse> GetUserInfo(UserResponse tokenM) | public async Task<UserInfoResponse> GetUserInfo(UserResponse tokenM) | ||||
| { | { | ||||
| // expired token example "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN" | // expired token example "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN" | ||||
| var http = _httpClientFactory.CreateClient("HttpClient"); | var http = _httpClientFactory.CreateClient("HttpClient"); | ||||
| http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenM.Token!); | http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenM.Token!); | ||||
| //make this a method in http utils | //make this a method in http utils | ||||
| if (response.StatusCode == HttpStatusCode.Unauthorized) | if (response.StatusCode == HttpStatusCode.Unauthorized) | ||||
| { | { | ||||
| //refresh the token | //refresh the token | ||||
| var refreshResponse = await RefreshAccessToken(tokenM); | var refreshResponse = await RefreshAccessToken(tokenM); | ||||
| http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", refreshResponse.AccessToken); | http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", refreshResponse.AccessToken); | ||||
| response = await http.GetAsync("me"); | response = await http.GetAsync("me"); | ||||
| } | } | ||||
| //var headerError = response.Headers.WwwAuthenticate.; | //var headerError = response.Headers.WwwAuthenticate.; | ||||
| var userInfo = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync())!; | var userInfo = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync())!; | ||||
| userInfo.ResponseMsg = response.StatusCode; | userInfo.ResponseMsg = response.StatusCode; | ||||
| return userInfo; | return userInfo; | ||||
| } | } | ||||
| { | { | ||||
| var client = _httpClientFactory.CreateClient("HttpClient"); | var client = _httpClientFactory.CreateClient("HttpClient"); | ||||
| client.BaseAddress = new Uri("https://accounts.spotify.com/api/token"); | client.BaseAddress = new Uri("https://accounts.spotify.com/api/token"); | ||||
| //BODY PARAMS | //BODY PARAMS | ||||
| var requestBody = new Dictionary<string, string>(); | var requestBody = new Dictionary<string, string>(); | ||||
| requestBody["refresh_token"] = tokenM.RefreshToken!; | requestBody["refresh_token"] = tokenM.RefreshToken!; | ||||
| //REQUEST | //REQUEST | ||||
| var response = await client.PostAsync("https://accounts.spotify.com/api/token", new FormUrlEncodedContent(requestBody)); | var response = await client.PostAsync("https://accounts.spotify.com/api/token", new FormUrlEncodedContent(requestBody)); | ||||
| if (response.StatusCode == HttpStatusCode.Unauthorized) | |||||
| { | |||||
| //delete tokens from localstorage | |||||
| //redirect to login | |||||
| } | |||||
| var contents = JsonConvert.DeserializeObject<RefreshTokenResponse>(await response.Content.ReadAsStringAsync())!; | var contents = JsonConvert.DeserializeObject<RefreshTokenResponse>(await response.Content.ReadAsStringAsync())!; | ||||
| return contents; | return contents; | ||||
| } | } |
| { | { | ||||
| string url = "me/player/currently-playing"; | string url = "me/player/currently-playing"; | ||||
| var response = await HttpUtils<CurrentTrackResponse>.GetData(_httpClientFactory, url, message.UserId, _identityService, _authService); | |||||
| var response = await HttpUtils<CurrentTrackResponse> | |||||
| .GetData(_httpClientFactory, | |||||
| url, | |||||
| message.UserId!, | |||||
| _identityService, | |||||
| _authService); | |||||
| return response; | return response; | ||||
| if (request.Limit == null && request.Offset != null) url += $"?offset={request.Offset}"; | if (request.Limit == null && request.Offset != null) url += $"?offset={request.Offset}"; | ||||
| else url += request.Offset == null ? "" : $"&offset={request.Offset}"; | else url += request.Offset == null ? "" : $"&offset={request.Offset}"; | ||||
| return await HttpUtils<TopItemResponse>.GetData(_httpClientFactory, url, request.UserId!, _identityService, _authService); | |||||
| return await HttpUtils<TopItemResponse> | |||||
| .GetData(_httpClientFactory, | |||||
| url, | |||||
| request.UserId!, | |||||
| _identityService, | |||||
| _authService); | |||||
| } | } | ||||
| } | } |
| string url = $"search?q={request.Query}&type={request.Type}"; | string url = $"search?q={request.Query}&type={request.Type}"; | ||||
| return await HttpUtils.HttpUtils<SearchResponse>.GetData(_httpClientFactory, url, request.UserId!, _identityService, _authService); | |||||
| return await HttpUtils.HttpUtils<SearchResponse> | |||||
| .GetData(_httpClientFactory, | |||||
| url, | |||||
| request.UserId!, | |||||
| _identityService, | |||||
| _authService); | |||||
| } | } | ||||
| { | { | ||||
| string url = $"audio-features/{request.Id}"; | string url = $"audio-features/{request.Id}"; | ||||
| return await HttpUtils.HttpUtils<SingleTrackResponse>.GetData(_httpClientFactory, url, request.UserId!, _identityService,_authService); | |||||
| return await HttpUtils.HttpUtils<SingleTrackResponse> | |||||
| .GetData(_httpClientFactory, | |||||
| url, | |||||
| request.UserId!, | |||||
| _identityService, | |||||
| _authService); | |||||
| } | } | ||||
| public async Task<MultipleTrackResponse> ListMultipleTrackAsync(MultipleTrackRequest request) | public async Task<MultipleTrackResponse> ListMultipleTrackAsync(MultipleTrackRequest request) | ||||
| { | { | ||||
| var query = UriUtil(param); | var query = UriUtil(param); | ||||
| string url = $"audio-features{query}"; | string url = $"audio-features{query}"; | ||||
| return await HttpUtils.HttpUtils<MultipleTrackResponse>.GetData(_httpClientFactory, url, request.UserId, _identityService, _authService); | |||||
| return await HttpUtils.HttpUtils<MultipleTrackResponse> | |||||
| .GetData(_httpClientFactory, | |||||
| url, | |||||
| request.UserId!, | |||||
| _identityService, | |||||
| _authService); | |||||
| } | } | ||||
| public async Task SaveTracks(SaveTracksRequest request) | public async Task SaveTracks(SaveTracksRequest request) | ||||
| { | { | ||||
| string url = $"me/tracks/{query}"; | string url = $"me/tracks/{query}"; | ||||
| //the response type has nothing to do with the method, it's there so that the method can be called | //the response type has nothing to do with the method, it's there so that the method can be called | ||||
| await HttpUtils.HttpUtils<StatusCodeMessage>.PutData(client, url, request.UserId!, _identityService, _authService); | |||||
| await HttpUtils.HttpUtils<StatusCodeMessage> | |||||
| .PutData(client, | |||||
| url, | |||||
| request.UserId!, | |||||
| _identityService, | |||||
| _authService); | |||||
| } | } | ||||
| public static string UriUtil(Dictionary<string, List<string>> param) | public static string UriUtil(Dictionary<string, List<string>> param) | ||||
| string url = $"tracks/{request.TrackID}"; | string url = $"tracks/{request.TrackID}"; | ||||
| return await HttpUtils.HttpUtils<TrackResponse>.GetData(_httpClientFactory, url, request.UserId!, _identityService, _authService); | |||||
| return await HttpUtils.HttpUtils<TrackResponse> | |||||
| .GetData(_httpClientFactory, | |||||
| url, | |||||
| request.UserId!, | |||||
| _identityService, | |||||
| _authService); | |||||
| } | } | ||||
| } | } | ||||
| } | } |