Browse Source

Merge branch 'feature/identity-transfer' into dev

master
anastasijasavov 3 years ago
parent
commit
5cffa86509

+ 1
- 1
GrpcShared/DTO/Search/SearchRequest.cs View File

namespace GrpcShared.DTO.Search namespace GrpcShared.DTO.Search
{ {
[ProtoContract] [ProtoContract]
public class SearchRequest : TokenMessage
public class SearchRequest : SessionMessage
{ {
[ProtoMember(1)] [ProtoMember(1)]

+ 29
- 0
GrpcShared/DTO/SessionMessage.cs View File

using GrpcShared.DTO.Search;
using GrpcShared.DTO.TopItem;
using GrpcShared.DTO.Track.MultipleTrack;
using GrpcShared.DTO.Track.SaveTracks;
using GrpcShared.DTO.Track.SingleTrack;
using GrpcShared.DTO.TrackByID;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GrpcShared.DTO
{
[ProtoContract]
[ProtoInclude(5, typeof(TopItemRequest))]
[ProtoInclude(6, typeof(SingleTrackRequest))]
[ProtoInclude(7, typeof(SearchRequest))]
[ProtoInclude(8, typeof(MultipleTrackRequest))]
[ProtoInclude(9, typeof(SaveTracksRequest))]
[ProtoInclude(10, typeof(TrackRequest))]
public class SessionMessage
{
[ProtoMember(1)]
public string? UserId { get; set; }
}
}

+ 1
- 6
GrpcShared/DTO/TokenMessage.cs View File



namespace GrpcShared.DTO namespace GrpcShared.DTO
{ {
[ProtoInclude(5, typeof(TopItemRequest))]
[ProtoInclude(6, typeof(SingleTrackRequest))]
[ProtoInclude(7, typeof(SearchRequest))]
[ProtoInclude(8,typeof(MultipleTrackRequest))]
[ProtoInclude(9,typeof(SaveTracksRequest))]
[ProtoInclude(10,typeof(TrackRequest))]


[ProtoContract(SkipConstructor = true)] [ProtoContract(SkipConstructor = true)]
public class TokenMessage public class TokenMessage

+ 1
- 1
GrpcShared/DTO/TopItem/TopItemRequest.cs View File

namespace GrpcShared.DTO.TopItem namespace GrpcShared.DTO.TopItem
{ {
[ProtoContract] [ProtoContract]
public class TopItemRequest : TokenMessage
public class TopItemRequest : SessionMessage
{ {


[ProtoMember(1)] [ProtoMember(1)]

+ 1
- 1
GrpcShared/DTO/Track/MultipleTrack/MultipleTrackRequest.cs View File

namespace GrpcShared.DTO.Track.MultipleTrack namespace GrpcShared.DTO.Track.MultipleTrack
{ {
[ProtoContract] [ProtoContract]
public class MultipleTrackRequest:TokenMessage
public class MultipleTrackRequest: SessionMessage
{ {
[ProtoMember(1)] [ProtoMember(1)]
public List<string>? Ids { get; set; } public List<string>? Ids { get; set; }

+ 1
- 1
GrpcShared/DTO/Track/SaveTracks/SaveTracksRequest.cs View File

namespace GrpcShared.DTO.Track.SaveTracks namespace GrpcShared.DTO.Track.SaveTracks
{ {
[ProtoContract] [ProtoContract]
public class SaveTracksRequest : TokenMessage
public class SaveTracksRequest : SessionMessage
{ {
[ProtoMember(1)] [ProtoMember(1)]
public List<string>? Ids { get; set; } public List<string>? Ids { get; set; }

+ 1
- 1
GrpcShared/DTO/Track/SingleTrack/SingleTrackRequest.cs View File

namespace GrpcShared.DTO.Track.SingleTrack namespace GrpcShared.DTO.Track.SingleTrack
{ {
[ProtoContract] [ProtoContract]
public class SingleTrackRequest : TokenMessage
public class SingleTrackRequest : SessionMessage
{ {
[ProtoMember(1)] [ProtoMember(1)]
public string? Id { get; set; } public string? Id { get; set; }

+ 1
- 1
GrpcShared/DTO/Track/TrackByID/TrackRequest.cs View File

namespace GrpcShared.DTO.TrackByID namespace GrpcShared.DTO.TrackByID
{ {
[ProtoContract] [ProtoContract]
public class TrackRequest:TokenMessage
public class TrackRequest: SessionMessage
{ {
[ProtoMember(1)] [ProtoMember(1)]
[JsonProperty("id")] [JsonProperty("id")]

+ 3
- 2
GrpcShared/Interfaces/IAuthService.cs View File

using GrpcShared.DTO; using GrpcShared.DTO;
using GrpcShared.DTO.Auth; using GrpcShared.DTO.Auth;
using GrpcShared.DTO.Db;
using GrpcShared.DTO.User; using GrpcShared.DTO.User;
using ProtoBuf.Grpc.Configuration; using ProtoBuf.Grpc.Configuration;


{ {
Task<TokenResponse> GetAccessToken(TokenRequest code); Task<TokenResponse> GetAccessToken(TokenRequest code);
Task<CodeRequest> GetAuthParams(); Task<CodeRequest> GetAuthParams();
Task<UserInfoResponse> GetUserInfo(TokenMessage token);
Task<RefreshTokenResponse> RefreshAccessToken(TokenMessage msg);
Task<UserInfoResponse> GetUserInfo(UserResponse token);
Task<RefreshTokenResponse> RefreshAccessToken(UserResponse msg);
//Task<ClientSecrets> GetClientSecrets(); //Task<ClientSecrets> GetClientSecrets();
} }
} }

+ 1
- 1
GrpcShared/Interfaces/IStatsService.cs View File

[Service] [Service]
public interface IStatsService public interface IStatsService
{ {
Task<CurrentTrackResponse> GetCurrentlyPlayingTrack(TokenMessage token);
Task<CurrentTrackResponse> GetCurrentlyPlayingTrack(SessionMessage message);
Task<TopItemResponse> GetTopItems(TopItemRequest request); Task<TopItemResponse> GetTopItems(TopItemRequest request);
} }
} }

+ 0
- 1
IdentityProvider/IdentityProvider.csproj View File

</ItemGroup> </ItemGroup>


<ItemGroup> <ItemGroup>
<ProjectReference Include="..\gRPCServer\SpotifyService.csproj" />
<ProjectReference Include="..\GrpcShared\GrpcShared.csproj" /> <ProjectReference Include="..\GrpcShared\GrpcShared.csproj" />
<ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" /> <ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" />
</ItemGroup> </ItemGroup>

+ 4
- 4
IdentityProvider/Models/SpotifyDbConfig.cs View File

{ {
public class SpotifyDbConfig public class SpotifyDbConfig
{ {
public string ConnectionString { get; set; } = null!;
public string DatabaseName { get; set; } = null!;
public string UserCollection { get; set; } = null!;
public string TracksCollection { get; set; } = null!;
public string ConnectionString { get; set; } = "mongodb://127.0.0.1:27017";
public string DatabaseName { get; set; } = "spotifyDb";
public string UserCollection { get; set; } = "Users";
public string TracksCollection { get; set; } = "Tracks";
} }
} }

+ 21
- 21
IdentityProvider/Program.cs View File

using GrpcShared; using GrpcShared;
using SpotifyService.Services;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using ProtoBuf.Grpc.Server; using ProtoBuf.Grpc.Server;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
#if DEBUG #if DEBUG


builder.WebHost.ConfigureKestrel(options =>
{
options.ListenLocalhost(5050, o => o.Protocols =
HttpProtocols.Http2);
options.ListenLocalhost(5051, o => o.Protocols =
HttpProtocols.Http1AndHttp2);
});
//builder.WebHost.ConfigureKestrel(options =>
//{
// options.ListenLocalhost(5050, o => o.Protocols =
// HttpProtocols.Http2);
// options.ListenLocalhost(5051, o => o.Protocols =
// HttpProtocols.Http1AndHttp2);
//});


#endif #endif
builder.Services.AddHttpClient("HttpClient", c =>
{
c.BaseAddress = new Uri(SpotifyService.GLOBALS.SPOTIFYURL);
c.DefaultRequestHeaders.Add("Accept", SpotifyService.GLOBALS.MEDIATYPE);
});

builder.Services.AddOptions(); builder.Services.AddOptions();
builder.Services.AddSingleton<SpotifyDbConfig>();
// Additional configuration is required to successfully run gRPC on macOS. // Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
builder.Services.AddControllersWithViews(); builder.Services.AddControllersWithViews();
//call spotify api //call spotify api
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();


builder.Services.Configure<CodeRequest>(
builder.Configuration.GetSection("AuthParams"));
builder.Services.AddSingleton<SpotifyDbConfig>();


builder.Services.Configure<SpotifyDbConfig>( builder.Services.Configure<SpotifyDbConfig>(
builder.Configuration.GetSection("SpotifyDb")); builder.Configuration.GetSection("SpotifyDb"));


//builder.Services.AddHttpClient("HttpClient", c =>
//{
// c.BaseAddress = new Uri(builder.Configuration.GetSection("SpotifyConfig:SpotifyURL").Value.ToString());
// c.DefaultRequestHeaders.Add("Accept", builder.Configuration.GetSection("SpotifyConfig:MediaType").Value.ToString());

//});

var app = builder.Build(); var app = builder.Build();


// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
app.MapControllers(); app.MapControllers();


//app.MapGrpcService<WeatherService>(); //app.MapGrpcService<WeatherService>();
app.MapGrpcService<AuthService>().EnableGrpcWeb();
app.MapGrpcService<TrackService>().EnableGrpcWeb();
app.MapGrpcService<StatsService>().EnableGrpcWeb();
app.MapGrpcService<IdentityService>().EnableGrpcWeb();
//app.MapGrpcService<AuthService>().EnableGrpcWeb();
//app.MapGrpcService<TrackService>().EnableGrpcWeb();
//app.MapGrpcService<StatsService>().EnableGrpcWeb();
//app.MapGrpcService<IdentityService>().EnableGrpcWeb();


app.MapCodeFirstGrpcReflectionService(); app.MapCodeFirstGrpcReflectionService();



+ 8
- 7
IdentityProvider/Properties/launchSettings.json View File

"profiles": { "profiles": {
"IIS Express": { "IIS Express": {
"commandName": "IISExpress", "commandName": "IISExpress",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
},
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
}
}, },
"IdentityProvider": {
"SpotifyService": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:5001;http://localhost:5000", "applicationUrl": "https://localhost:5001;http://localhost:5000",
"dotnetRunMessages": "true"
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
} }
} }
} }

+ 2
- 1
IdentityProvider/Services/IdentityService.cs View File

return new UserResponse return new UserResponse
{ {
Id = user.Id, Id = user.Id,
Token = user.Token
Token = user.Token,
RefreshToken = user.RefreshToken
}; };
else return new UserResponse(); else return new UserResponse();
} }

+ 5
- 7
IdentityProvider/appsettings.json View File

"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel": { "Kestrel": {
"EndpointDefaults": { "EndpointDefaults": {
"Protocols": "Http2"
"Protocols": "Http1AndHttp2"
} }
}, },
"AuthParams": {
"ClientId": "83e1d09876b049c4bb1953185a4b3bfb",
"RedirectURI": "https://localhost:44342/callback",
"Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read user-read-private",
"ClientSecret": "ea752433d0774fad87fab5c1ee788c8d"
},
"SpotifyDb": { "SpotifyDb": {
"ConnectionString": "mongodb://127.0.0.1:27017", "ConnectionString": "mongodb://127.0.0.1:27017",
"DatabaseName": "spotifyDb", "DatabaseName": "spotifyDb",
"UserCollection": "Users", "UserCollection": "Users",
"TracksCollection": "Tracks" "TracksCollection": "Tracks"
} }
//"SpotifyConfig": {
// "SpotifyURL": "https://api.spotify.com/v1/",
// "MediaType": "application/json"
//}
} }

+ 12
- 26
NemAnCore/Pages/FetchData.razor View File

@page "/search" @page "/search"
@using Grpc.Core @using Grpc.Core
@using GrpcShared.DTO @using GrpcShared.DTO
@using GrpcShared.DTO.Db
@using GrpcShared.DTO.Search @using GrpcShared.DTO.Search
@using GrpcShared.DTO.Track.MultipleTrack @using GrpcShared.DTO.Track.MultipleTrack
@using GrpcShared.DTO.Track.SingleTrack @using GrpcShared.DTO.Track.SingleTrack


var userInfo = await localStorage.GetItemAsync<string>("user_info"); var userInfo = await localStorage.GetItemAsync<string>("user_info");


var user = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage
{
Id = userInfo
});

TokenMessage tokenM = new TokenMessage { Token = user.Token, RefreshToken = user.RefreshToken };

SearchRequest request = new() { Query = "aitch", Type = "track", Token = user.Token };

try
{
SearchResponse searchResponse = await SearchService.GetListSearchAsync(request);

if (searchResponse.ResponseMsg == System.Net.HttpStatusCode.Unauthorized)
{
string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, tokenM, user, localStorage, identityService);
}
}
catch (RpcException e)
{
if (e.StatusCode == StatusCode.Cancelled)
{
return;
}
throw;
}
UserResponse userResponse = await identityService.GetTokenByIdAsync(new DbRequestMessage { Id = userInfo });
//var user = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage
// {
// Id = userInfo
// });

//TokenMessage tokenM = new TokenMessage { Token = user.Token, RefreshToken = user.RefreshToken };

SearchRequest request = new() { Query = "aitch", Type = "track", UserId = userInfo };

SearchResponse searchResponse = await SearchService.GetListSearchAsync(request);


} }



+ 10
- 41
NemAnCore/Pages/Home.razor View File

tokenS = userResponse.Token; tokenS = userResponse.Token;
refreshT = userResponse.RefreshToken; refreshT = userResponse.RefreshToken;
} }
else {
else
{
await localStorage.RemoveItemAsync("user_info"); await localStorage.RemoveItemAsync("user_info");
NavigationMgr.NavigateTo("/"); NavigationMgr.NavigateTo("/");
} }
} }


//tokenS = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; //tokenS = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN";
TokenMessage token = new() { Token = tokenS, RefreshToken = refreshT }; TokenMessage token = new() { Token = tokenS, RefreshToken = refreshT };
UserResponse user = new() { Id = userId, RefreshToken = refreshT }; UserResponse user = new() { Id = userId, RefreshToken = refreshT };
try
{
track = await spotifyService.GetCurrentlyPlayingTrack(token);

//if token expired, refresh it
if (track.ResponseMsg == System.Net.HttpStatusCode.Unauthorized)
{
string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, token, user, localStorage, identityService);
tokenS = tempToken == null ? tokenS : tempToken;
//if refreshed token is null, that means that refresh token was invalid, so you should redirect to login
}
}
catch (RpcException e)
{
if (e.StatusCode == StatusCode.Cancelled)
{
return;
}
throw;
}
track = await spotifyService.GetCurrentlyPlayingTrack(new SessionMessage { UserId = userId });




//napravi komponentu koja ce da prikazuje sta trenutno slusas i passuj joj parametre //napravi komponentu koja ce da prikazuje sta trenutno slusas i passuj joj parametre


//var trackById = await trackService.GetById(new GrpcShared.DTO.TrackByID.TrackRequest { TrackID = "4fy1A2WBTPX55mUI16TQXa", Token = tokenS }); //var trackById = await trackService.GetById(new GrpcShared.DTO.TrackByID.TrackRequest { TrackID = "4fy1A2WBTPX55mUI16TQXa", Token = tokenS });
try
{
var items = await spotifyService.GetTopItems(new GrpcShared.DTO.TopItem.TopItemRequest { Token = tokenS, IsTracks = false, Offset = 5 });

if (items.ResponseMsg == System.Net.HttpStatusCode.Unauthorized)
{
string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, token, user, localStorage, identityService);
tokenS = tempToken == null ? tokenS : tempToken;
}
}
catch (RpcException e)
{
if (e.StatusCode == StatusCode.Cancelled)
{
return;
}
throw;
}

var items = await spotifyService.GetTopItems(new GrpcShared.DTO.TopItem.TopItemRequest { UserId = userId, IsTracks = false, Offset = 5 });


await identityService.SaveTrackAsync(new GrpcShared.DTO.Db.SaveTrackRequest await identityService.SaveTrackAsync(new GrpcShared.DTO.Db.SaveTrackRequest
{ {
TrackId = track.Item.Id, TrackId = track.Item.Id,

+ 10
- 10
NemAnCore/Services/AuthClientService.cs View File

using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Blazored.LocalStorage; using Blazored.LocalStorage;
using GrpcShared.DTO.Db;


namespace NemAnBlazor.Services namespace NemAnBlazor.Services
{ {
return await _serviceClient.GetAuthParams(); return await _serviceClient.GetAuthParams();
} }


public async Task<UserInfoResponse> GetUserInfo(TokenMessage token)
public async Task<UserInfoResponse> GetUserInfo(UserResponse token)
{ {
return await _serviceClient.GetUserInfo(token); return await _serviceClient.GetUserInfo(token);
} }
//return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); //return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
await Task.Delay(500); await Task.Delay(500);


string token = await _localStorage.GetItemAsync<string>("token");
string refreshT = await _localStorage.GetItemAsync<string>("refresh_token");

string userId = await _localStorage.GetItemAsync<string>("user_info");
//token = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; //token = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN";
if (token == null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
if (userId == null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));


var userInfo = await _serviceClient.GetUserInfo(new TokenMessage { Token = token, RefreshToken = refreshT });
// var userInfo = await _serviceClient.GetUserInfo(new UserResponse { Token = token, RefreshToken = refreshT });


List<Claim> claims = new(); List<Claim> claims = new();


claims.Add(new Claim("email", userInfo.Email!));
claims.Add(new Claim("id", userInfo.Id!));
claims.Add(new Claim("name", userInfo.DisplayName!));
//claims.Add(new Claim("email", userInfo.Email!));
claims.Add(new Claim("id", userId!));
//claims.Add(new Claim("name", userInfo.DisplayName!));


ClaimsIdentity identity = new(claims, "jwt"); ClaimsIdentity identity = new(claims, "jwt");
//ClaimsIdentity identity = new(); //ClaimsIdentity identity = new();
return state; return state;
} }


public async Task<RefreshTokenResponse> RefreshAccessToken(TokenMessage token)
public async Task<RefreshTokenResponse> RefreshAccessToken(UserResponse token)
{ {
return await _serviceClient.RefreshAccessToken(token); return await _serviceClient.RefreshAccessToken(token);
} }

+ 3
- 2
NemAnCore/Services/Interfaces/IAuthClientService.cs View File

using GrpcShared; using GrpcShared;
using GrpcShared.DTO; using GrpcShared.DTO;
using GrpcShared.DTO.Auth; using GrpcShared.DTO.Auth;
using GrpcShared.DTO.Db;
using GrpcShared.DTO.User; using GrpcShared.DTO.User;


namespace NemAnBlazor.Services.Interfaces namespace NemAnBlazor.Services.Interfaces
{ {
Task<TokenResponse> GetAccessToken(TokenRequest tokenRequest); Task<TokenResponse> GetAccessToken(TokenRequest tokenRequest);
Task<CodeRequest> GetAuthParams(); Task<CodeRequest> GetAuthParams();
Task<UserInfoResponse> GetUserInfo(TokenMessage token);
Task<RefreshTokenResponse> RefreshAccessToken(TokenMessage token);
Task<UserInfoResponse> GetUserInfo(UserResponse token);
Task<RefreshTokenResponse> RefreshAccessToken(UserResponse token);
} }
} }

+ 1
- 1
NemAnCore/Services/Interfaces/IStatsClientService.cs View File

{ {
public interface IStatsClientService public interface IStatsClientService
{ {
Task<CurrentTrackResponse> GetCurrentlyPlayingTrack(TokenMessage token);
Task<CurrentTrackResponse> GetCurrentlyPlayingTrack(SessionMessage message);
Task<TopItemResponse> GetTopItems(TopItemRequest request); Task<TopItemResponse> GetTopItems(TopItemRequest request);
} }
} }

+ 2
- 2
NemAnCore/Services/StatsClientService.cs View File

{ {
_serviceClient = channel.CreateGrpcService<IStatsService>(); _serviceClient = channel.CreateGrpcService<IStatsService>();
} }
public async Task<CurrentTrackResponse> GetCurrentlyPlayingTrack(TokenMessage token)
public async Task<CurrentTrackResponse> GetCurrentlyPlayingTrack(SessionMessage message)
{ {
return await _serviceClient.GetCurrentlyPlayingTrack(token);
return await _serviceClient.GetCurrentlyPlayingTrack(message);
} }


public async Task<TopItemResponse> GetTopItems(TopItemRequest request) public async Task<TopItemResponse> GetTopItems(TopItemRequest request)

+ 80
- 15
gRPCServer/HttpUtils/HttpUtils.cs View File

using Grpc.Net.Client;
using Grpc.Core;
using Grpc.Net.Client;
using GrpcShared.DTO.Db;
using GrpcShared.Interfaces;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using NemAnBlazor.Services.Interfaces;
using Newtonsoft.Json; using Newtonsoft.Json;


namespace SpotifyService.HttpUtils namespace SpotifyService.HttpUtils
{ {
public static class HttpUtils<T>
public static class HttpUtils<T> where T : new()
{ {
public static async Task<T> GetData(HttpClient client, string url, string token)
public static async Task<T> GetData
(IHttpClientFactory _httpClientFactory,
string url,
string userId,
IIdentityService identityService,
IAuthService authService)

{ {
//add header
client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + token);
try
{
var client = _httpClientFactory.CreateClient("HttpClient");

var userResponse = await identityService.GetTokenByIdAsync(new DbRequestMessage { Id = userId });


//add header
client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + userResponse.Token);

//get request
var req = await client.GetAsync(url);


//get request
var req = await client.GetAsync(url);
//read response
var response = JsonConvert.DeserializeObject<T>(await req.Content.ReadAsStringAsync())!;


//read response
var response = JsonConvert.DeserializeObject<T>(await req.Content.ReadAsStringAsync())!;


return response;
if (req.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
string newToken = await SpotifyHelper.TryRefreshToken(authService, userResponse, identityService);
if (newToken != null)
{
client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + newToken);
req = await client.GetAsync(url);
response = JsonConvert.DeserializeObject<T>(await req.Content.ReadAsStringAsync())!;
}

//ako to ne radi to znaci da je refresh token isteko, treba da se refreshuje
}
return response;
}
catch (RpcException e)
{
if (e.StatusCode == StatusCode.Cancelled)
{
//vrati message sa status kodom?
return new T();
}
throw;
}



} }
public static async Task PutData(HttpClient client, string url, string token)
public static async Task PutData(HttpClient client, string url, string userId, IIdentityService identityService, IAuthService authService)
{ {
//add header
client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + token);
try
{
var tokenMessage = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage { Id = userId });
//add header
client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + tokenMessage.Token);

//get request
var responseMessage = await client.PutAsync(url, null);

if(responseMessage.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
string newToken = await SpotifyHelper.TryRefreshToken(authService, tokenMessage, identityService);
if (newToken != null)
{
responseMessage = await client.PutAsync(url, null);
}
}
}
catch (RpcException e)
{
if (e.StatusCode == StatusCode.Cancelled)
{
//vrati message sa status kodom?
return;
}
throw;
}


//get request
await client.PutAsync(url, null);


} }



NemAnCore/SpotifyHelper.cs → gRPCServer/HttpUtils/SpotifyHelper.cs View File

using Blazored.LocalStorage;
using GrpcShared.DTO;
using GrpcShared.DTO;
using GrpcShared.DTO.Db; using GrpcShared.DTO.Db;
using GrpcShared.Interfaces;
using NemAnBlazor.Services.Interfaces; using NemAnBlazor.Services.Interfaces;


namespace NemAnBlazor
namespace SpotifyService
{ {
public static class SpotifyHelper public static class SpotifyHelper
{ {
public static async Task<string?> TryRefreshToken public static async Task<string?> TryRefreshToken
(IAuthClientService authService,
TokenMessage msg,UserResponse user,
ILocalStorageService localStorage,
IIdentityClientService identityService)
(IAuthService authService,
UserResponse user,
IIdentityService identityService)
{ {
var refreshResponse = await authService.RefreshAccessToken(msg);
var refreshResponse = await authService.RefreshAccessToken(user);


if (refreshResponse.AccessToken != null) if (refreshResponse.AccessToken != null)
{ {

+ 12
- 4
gRPCServer/Program.cs View File

using Polly; using Polly;
using Polly.Extensions.Http; using Polly.Extensions.Http;
using GrpcShared.DTO.Auth; using GrpcShared.DTO.Auth;
using IdentityProvider.Services;


var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);


#if DEBUG #if DEBUG
/*
builder.WebHost.ConfigureKestrel(options => builder.WebHost.ConfigureKestrel(options =>
{ {
options.ListenLocalhost(5050, o => o.Protocols = options.ListenLocalhost(5050, o => o.Protocols =
options.ListenLocalhost(5051, o => o.Protocols = options.ListenLocalhost(5051, o => o.Protocols =
HttpProtocols.Http1AndHttp2); HttpProtocols.Http1AndHttp2);
}); });
*/
#endif #endif


// Add services to the container. // Add services to the container.
builder.Services.AddGrpc(); builder.Services.AddGrpc();
builder.Services.AddCodeFirstGrpc(); builder.Services.AddCodeFirstGrpc();
builder.Services.AddCodeFirstGrpcReflection(); builder.Services.AddCodeFirstGrpcReflection();
builder.Services.Configure<CodeRequest>(builder.Configuration.GetSection("AuthParams"));

builder.Services.AddScoped<IIdentityService, IdentityService>();
builder.Services.AddScoped<IAuthService, AuthService>();


var app = builder.Build(); var app = builder.Build();
builder.Services.Configure<CodeRequest>(builder.Configuration.GetSection("AuthParams"));


app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();
app.MapControllers(); app.MapControllers();


//app.MapGrpcService<AuthService>(); //app.MapGrpcService<AuthService>();
//app.MapGrpcService<AuthService>().EnableGrpcWeb();
app.MapGrpcService<AuthService>().EnableGrpcWeb();
app.MapGrpcService<TrackService>().EnableGrpcWeb();
app.MapGrpcService<StatsService>().EnableGrpcWeb();
app.MapGrpcService<IdentityService>().EnableGrpcWeb();




app.MapCodeFirstGrpcReflectionService(); app.MapCodeFirstGrpcReflectionService();

+ 3
- 2
gRPCServer/Services/AuthService.cs View File

using GrpcShared; using GrpcShared;
using GrpcShared.DTO; using GrpcShared.DTO;
using GrpcShared.DTO.Auth; using GrpcShared.DTO.Auth;
using GrpcShared.DTO.Db;
using GrpcShared.DTO.User; using GrpcShared.DTO.User;
using GrpcShared.Interfaces; using GrpcShared.Interfaces;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
return await Task.FromResult(authParams); return await Task.FromResult(authParams);
} }


public async Task<UserInfoResponse> GetUserInfo(TokenMessage 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"


} }


public async Task<RefreshTokenResponse> RefreshAccessToken(TokenMessage tokenM)
public async Task<RefreshTokenResponse> RefreshAccessToken(UserResponse tokenM)
{ {
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");

+ 10
- 8
gRPCServer/Services/StatsService.cs View File

public class StatsService : IStatsService public class StatsService : IStatsService
{ {
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;

public StatsService(IHttpClientFactory httpClientFactory)
private IIdentityService _identityService;
private IAuthService _authService;
public StatsService(IHttpClientFactory httpClientFactory, IIdentityService identityService, IAuthService authService)
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_identityService = identityService;
_authService = authService;
} }


public async Task<CurrentTrackResponse> GetCurrentlyPlayingTrack(TokenMessage token)

public async Task<CurrentTrackResponse> GetCurrentlyPlayingTrack(SessionMessage message)
{ {
var client = _httpClientFactory.CreateClient("HttpClient");
string url = "me/player/currently-playing"; string url = "me/player/currently-playing";
var response = await HttpUtils<CurrentTrackResponse>.GetData(client, url, token.Token!);
var response = await HttpUtils<CurrentTrackResponse>.GetData(_httpClientFactory, url, message.UserId, _identityService, _authService);
return response; return response;


public async Task<TopItemResponse> GetTopItems(TopItemRequest request) public async Task<TopItemResponse> GetTopItems(TopItemRequest request)
{ {
//https://api.spotify.com/v1/me/top/albums?limit=10&offset=5 //https://api.spotify.com/v1/me/top/albums?limit=10&offset=5
var client = _httpClientFactory.CreateClient("HttpClient");


//URL PARAMS //URL PARAMS
string url = "me/top/"; string url = "me/top/";
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(client, url, request.Token!);
return await HttpUtils<TopItemResponse>.GetData(_httpClientFactory, url, request.UserId!, _identityService, _authService);


} }
} }

+ 13
- 13
gRPCServer/Services/TrackService.cs View File

public class TrackService : ITrackService public class TrackService : ITrackService
{ {
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
public TrackService(IHttpClientFactory httpClientFactory)
private IIdentityService _identityService;
private IAuthService _authService;

public TrackService(IHttpClientFactory httpClientFactory, IIdentityService identityService, IAuthService authService)
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_identityService = identityService;
_authService = authService;
} }
public async Task<SearchResponse> ListSearchAsync(SearchRequest request) public async Task<SearchResponse> ListSearchAsync(SearchRequest request)
{ {


var client = _httpClientFactory.CreateClient("HttpClient");

string url = $"search?q={request.Query}&type={request.Type}"; string url = $"search?q={request.Query}&type={request.Type}";


return await HttpUtils.HttpUtils<SearchResponse>.GetData(client, url, request.Token!);
return await HttpUtils.HttpUtils<SearchResponse>.GetData(_httpClientFactory, url, request.UserId!, _identityService, _authService);




} }
public async Task<SingleTrackResponse> ListSingleTrackAsync(SingleTrackRequest request) public async Task<SingleTrackResponse> ListSingleTrackAsync(SingleTrackRequest request)
{ {
var client = _httpClientFactory.CreateClient("HttpClient");
string url = $"audio-features/{request.Id}"; string url = $"audio-features/{request.Id}";
return await HttpUtils.HttpUtils<SingleTrackResponse>.GetData(client, url, request.Token!);
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 client = _httpClientFactory.CreateClient("HttpClient");

var param = new Dictionary<string, List<string>>(); var param = new Dictionary<string, List<string>>();
param["ids"] = request.Ids!; param["ids"] = request.Ids!;


var query = UriUtil(param); var query = UriUtil(param);
string url = $"audio-features{query}"; string url = $"audio-features{query}";


return await HttpUtils.HttpUtils<MultipleTrackResponse>.GetData(client,url,request.Token!);
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.Token!);
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)
} }
public async Task<TrackResponse> GetById(TrackRequest request) public async Task<TrackResponse> GetById(TrackRequest request)
{ {
var client = _httpClientFactory.CreateClient("HttpClient");

string url = $"tracks/{request.TrackID}"; string url = $"tracks/{request.TrackID}";


return await HttpUtils.HttpUtils<TrackResponse>.GetData(client, url, request.Token!);
return await HttpUtils.HttpUtils<TrackResponse>.GetData(_httpClientFactory, url, request.UserId!, _identityService, _authService);
} }
} }
} }

+ 1
- 0
gRPCServer/SpotifyService.csproj View File



<ItemGroup> <ItemGroup>
<ProjectReference Include="..\GrpcShared\GrpcShared.csproj" /> <ProjectReference Include="..\GrpcShared\GrpcShared.csproj" />
<ProjectReference Include="..\IdentityProvider\IdentityProvider.csproj" />
<ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" /> <ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" />
</ItemGroup> </ItemGroup>



Loading…
Cancel
Save