@ -1,47 +0,0 @@ | |||||
apiVersion: v1 | |||||
kind: Service | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: frontend | |||||
name: frontend | |||||
spec: | |||||
ports: | |||||
- port: 80 | |||||
targetPort: 8080 | |||||
selector: | |||||
app: eshop | |||||
component: frontend | |||||
type: LoadBalancer | |||||
--- | |||||
apiVersion: extensions/v1beta1 | |||||
kind: Deployment | |||||
metadata: | |||||
name: frontend | |||||
spec: | |||||
template: | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: frontend | |||||
spec: | |||||
containers: | |||||
- name: nginx | |||||
image: nginx:1.13.8-alpine | |||||
imagePullPolicy: IfNotPresent | |||||
ports: | |||||
- containerPort: 8080 | |||||
lifecycle: | |||||
preStop: | |||||
exec: | |||||
command: ["/usr/sbin/nginx","-s","quit"] | |||||
volumeMounts: | |||||
- name: config | |||||
mountPath: /etc/nginx | |||||
volumes: | |||||
- name: config | |||||
configMap: | |||||
name: config-files | |||||
items: | |||||
- key: nginx-conf | |||||
path: nginx.conf |
@ -0,0 +1,31 @@ | |||||
apiVersion: v1 | |||||
kind: ConfigMap | |||||
metadata: | |||||
name: internalurls | |||||
labels: | |||||
app: eshop | |||||
data: | |||||
# Internal Services & healthchecks | |||||
basket: http://basket | |||||
basket__hc: http://basket/hc | |||||
catalog: http://catalog | |||||
catalog__hc: http://catalog/hc | |||||
identity: http://identity | |||||
identity__hc: http://identity/hc | |||||
ordering: http://ordering | |||||
ordering__hc: http://ordering/hc | |||||
marketing: http://marketing | |||||
marketing__hc: http://marketing/hc | |||||
locations: http://locations | |||||
locations__hc: http://locations/hc | |||||
payment__hc: http://payment/hc | |||||
mvc__hc: http://webmvc/hc | |||||
spa__hc: http://webspa/hc | |||||
# Aggreggators | |||||
mobileshoppingagg: http://mobileshoppingagg | |||||
webshoppingagg: http://webshoppingagg | |||||
# API GWs | |||||
apigwmm: http://ocelotapigw-mm | |||||
apigwms: http://ocelotapigw-ms | |||||
apigwwm: http://ocelotapigw-wm | |||||
apigwws: http://ocelotapigw-ws |
@ -0,0 +1,34 @@ | |||||
{ | |||||
"ReRoutes": [ | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "marketing", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/m/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "locations", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/l/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
} | |||||
], | |||||
"GlobalConfiguration": { | |||||
"RequestIdKey": "OcRequestId", | |||||
"AdministrationPath": "/administration" | |||||
} | |||||
} | |||||
@ -0,0 +1,142 @@ | |||||
{ | |||||
"ReRoutes": [ | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "catalog", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/c/{everything}", | |||||
"UpstreamHttpMethod": [ "GET" ] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "basket", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/b/{everything}", | |||||
"UpstreamHttpMethod": [], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "ordering", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/o/{everything}", | |||||
"UpstreamHttpMethod": [], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "mobileshoppingagg", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/{everything}", | |||||
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "ordering", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/orders-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "basket", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/basket-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "catalog", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/catalog-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "marketing", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/marketing-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "payment", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/payment-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "locations.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/location-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
} | |||||
], | |||||
"GlobalConfiguration": { | |||||
"RequestIdKey": "OcRequestId", | |||||
"AdministrationPath": "/administration" | |||||
} | |||||
} | |||||
@ -0,0 +1,34 @@ | |||||
{ | |||||
"ReRoutes": [ | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "marketing", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/m/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "locations", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/l/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
} | |||||
], | |||||
"GlobalConfiguration": { | |||||
"RequestIdKey": "OcRequestId", | |||||
"AdministrationPath": "/administration" | |||||
} | |||||
} | |||||
@ -0,0 +1,142 @@ | |||||
{ | |||||
"ReRoutes": [ | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "catalog", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/c/{everything}", | |||||
"UpstreamHttpMethod": [ "GET" ] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "basket", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/b/{everything}", | |||||
"UpstreamHttpMethod": [], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "ordering", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/o/{everything}", | |||||
"UpstreamHttpMethod": [], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "webshoppingagg", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/{everything}", | |||||
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "ordering", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/orders-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "basket", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/basket-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "catalog", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/catalog-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "marketing", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/marketing-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "payment", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/payment-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "locations.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/location-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
} | |||||
], | |||||
"GlobalConfiguration": { | |||||
"RequestIdKey": "OcRequestId", | |||||
"AdministrationPath": "/administration" | |||||
} | |||||
} | |||||
@ -0,0 +1,155 @@ | |||||
apiVersion: extensions/v1beta1 | |||||
kind: Deployment | |||||
metadata: | |||||
name: apigwmm | |||||
spec: | |||||
paused: true | |||||
template: | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: apigwmm | |||||
spec: | |||||
containers: | |||||
- name: apigwmm | |||||
image: eshop/ocelotapigw | |||||
imagePullPolicy: Always | |||||
env: | |||||
- name: PATH_BASE | |||||
value: /mobilemarketingapigw | |||||
- name: IdentityUrl | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: internalurls | |||||
key: identity | |||||
ports: | |||||
- containerPort: 80 | |||||
volumeMounts: | |||||
- name: config | |||||
mountPath: /app/configuration | |||||
volumes: | |||||
- name: config | |||||
configMap: | |||||
name: ocelot | |||||
items: | |||||
- key: mm | |||||
path: configuration.json | |||||
imagePullSecrets: | |||||
- name: registry-key | |||||
--- | |||||
apiVersion: extensions/v1beta1 | |||||
kind: Deployment | |||||
metadata: | |||||
name: apigwms | |||||
spec: | |||||
paused: true | |||||
template: | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: apigwms | |||||
spec: | |||||
containers: | |||||
- name: apigwms | |||||
image: eshop/ocelotapigw | |||||
imagePullPolicy: Always | |||||
env: | |||||
- name: PATH_BASE | |||||
value: /mobileshoppingapigw | |||||
- name: IdentityUrl | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: internalurls | |||||
key: identity | |||||
ports: | |||||
- containerPort: 80 | |||||
volumeMounts: | |||||
- name: config | |||||
mountPath: /app/configuration | |||||
volumes: | |||||
- name: config | |||||
configMap: | |||||
name: ocelot | |||||
items: | |||||
- key: ms | |||||
path: configuration.json | |||||
imagePullSecrets: | |||||
- name: registry-key | |||||
--- | |||||
apiVersion: extensions/v1beta1 | |||||
kind: Deployment | |||||
metadata: | |||||
name: apigwwm | |||||
spec: | |||||
paused: true | |||||
template: | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: apigwwm | |||||
spec: | |||||
containers: | |||||
- name: apigwwm | |||||
image: eshop/ocelotapigw | |||||
imagePullPolicy: Always | |||||
env: | |||||
- name: PATH_BASE | |||||
value: /webmarketingapigw | |||||
- name: IdentityUrl | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: internalurls | |||||
key: identity | |||||
ports: | |||||
- containerPort: 80 | |||||
volumeMounts: | |||||
- name: config | |||||
mountPath: /app/configuration | |||||
volumes: | |||||
- name: config | |||||
configMap: | |||||
name: ocelot | |||||
items: | |||||
- key: wm | |||||
path: configuration.json | |||||
imagePullSecrets: | |||||
- name: registry-key | |||||
--- | |||||
apiVersion: extensions/v1beta1 | |||||
kind: Deployment | |||||
metadata: | |||||
name: apigwws | |||||
spec: | |||||
paused: true | |||||
template: | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: apigwws | |||||
spec: | |||||
containers: | |||||
- name: apigwws | |||||
image: eshop/ocelotapigw | |||||
imagePullPolicy: Always | |||||
env: | |||||
- name: PATH_BASE | |||||
value: /webshoppingapigw | |||||
- name: IdentityUrl | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: internalurls | |||||
key: identity | |||||
ports: | |||||
- containerPort: 80 | |||||
volumeMounts: | |||||
- name: config | |||||
mountPath: /app/configuration | |||||
volumes: | |||||
- name: config | |||||
configMap: | |||||
name: ocelot | |||||
items: | |||||
- key: ws | |||||
path: configuration.json | |||||
imagePullSecrets: | |||||
- name: registry-key |
@ -0,0 +1,55 @@ | |||||
apiVersion: v1 | |||||
kind: Service | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: ocelotapigw-mm | |||||
name: ocelotapigw-mm | |||||
spec: | |||||
ports: | |||||
- port: 80 | |||||
selector: | |||||
app: eshop | |||||
component: apigwmm | |||||
--- | |||||
apiVersion: v1 | |||||
kind: Service | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: ocelotapigw-ms | |||||
name: ocelotapigw-ms | |||||
spec: | |||||
ports: | |||||
- port: 80 | |||||
selector: | |||||
app: eshop | |||||
component: apigwms | |||||
--- | |||||
apiVersion: v1 | |||||
kind: Service | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: ocelotapigw-wm | |||||
name: ocelotapigw-wm | |||||
spec: | |||||
ports: | |||||
- port: 80 | |||||
selector: | |||||
app: eshop | |||||
component: apigwwm | |||||
--- | |||||
apiVersion: v1 | |||||
kind: Service | |||||
metadata: | |||||
labels: | |||||
app: eshop | |||||
component: ocelotapigw-ws | |||||
name: ocelotapigw-ws | |||||
spec: | |||||
ports: | |||||
- port: 80 | |||||
selector: | |||||
app: eshop | |||||
component: apigwws |
@ -0,0 +1,19 @@ | |||||
FROM microsoft/aspnetcore:2.0 AS base | |||||
WORKDIR /app | |||||
EXPOSE 80 | |||||
FROM microsoft/aspnetcore-build:2.0 AS build | |||||
WORKDIR /src | |||||
COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ | |||||
RUN dotnet restore src/ApiGateways/ApiGw-Base/ | |||||
COPY . . | |||||
WORKDIR /src/src/ApiGateways/ApiGw-Base/ | |||||
RUN dotnet build -c Release -o /app | |||||
FROM build AS publish | |||||
RUN dotnet publish -c Release -o /app | |||||
FROM base AS final | |||||
WORKDIR /app | |||||
COPY --from=publish /app . | |||||
ENTRYPOINT ["dotnet", "OcelotApiGw.dll"] |
@ -0,0 +1,16 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<Folder Include="wwwroot\" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" /> | |||||
<PackageReference Include="Ocelot" Version="3.0.0" /> | |||||
</ItemGroup> | |||||
</Project> |
@ -0,0 +1,31 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
namespace OcelotApiGw | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static void Main(string[] args) | |||||
{ | |||||
BuildWebHost(args).Run(); | |||||
} | |||||
public static IWebHost BuildWebHost(string[] args) | |||||
{ | |||||
var builder = WebHost.CreateDefaultBuilder(args); | |||||
builder.ConfigureServices(s => s.AddSingleton(builder)) | |||||
.ConfigureAppConfiguration(ic => ic.AddJsonFile(Path.Combine("configuration", "configuration.json"))) | |||||
.UseStartup<Startup>(); | |||||
var host = builder.Build(); | |||||
return host; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
{ | |||||
"iisSettings": { | |||||
"windowsAuthentication": false, | |||||
"anonymousAuthentication": true, | |||||
"iisExpress": { | |||||
"applicationUrl": "http://localhost:56755/", | |||||
"sslPort": 0 | |||||
} | |||||
}, | |||||
"profiles": { | |||||
"IIS Express": { | |||||
"commandName": "IISExpress", | |||||
"launchBrowser": true, | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
}, | |||||
"OcelotApiGw": { | |||||
"commandName": "Project", | |||||
"launchBrowser": true, | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
}, | |||||
"applicationUrl": "http://localhost:64021/" | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,91 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using CacheManager.Core; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using Ocelot.DependencyInjection; | |||||
using Ocelot.Middleware; | |||||
namespace OcelotApiGw | |||||
{ | |||||
public class Startup | |||||
{ | |||||
private readonly IConfiguration _cfg; | |||||
public Startup(IConfiguration configuration) | |||||
{ | |||||
_cfg = configuration; | |||||
} | |||||
public void ConfigureServices(IServiceCollection services) | |||||
{ | |||||
var identityUrl = _cfg.GetValue<string>("IdentityUrl"); | |||||
var authenticationProviderKey = "IdentityApiKey"; | |||||
services.AddCors(options => | |||||
{ | |||||
options.AddPolicy("CorsPolicy", | |||||
builder => builder.AllowAnyOrigin() | |||||
.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials()); | |||||
}); | |||||
services.AddAuthentication() | |||||
.AddJwtBearer(authenticationProviderKey, x => | |||||
{ | |||||
x.Authority = identityUrl; | |||||
x.RequireHttpsMetadata = false; | |||||
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() | |||||
{ | |||||
ValidAudiences = new[] { "orders", "basket", "locations", "marketing", "mobileshoppingagg", "webshoppingagg" } | |||||
}; | |||||
x.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents() | |||||
{ | |||||
OnAuthenticationFailed = async ctx => | |||||
{ | |||||
int i = 0; | |||||
}, | |||||
OnTokenValidated = async ctx => | |||||
{ | |||||
int i = 0; | |||||
}, | |||||
OnMessageReceived = async ctx => | |||||
{ | |||||
int i = 0; | |||||
} | |||||
}; | |||||
}); | |||||
services.AddOcelot(_cfg); | |||||
} | |||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||||
{ | |||||
var pathBase = _cfg["PATH_BASE"]; | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
app.UseDeveloperExceptionPage(); | |||||
} | |||||
loggerFactory.AddConsole(_cfg.GetSection("Logging")); | |||||
app.UseCors("CorsPolicy"); | |||||
app.UseOcelot().Wait(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,10 @@ | |||||
{ | |||||
"Logging": { | |||||
"IncludeScopes": true, | |||||
"LogLevel": { | |||||
"Default": "Trace", | |||||
"System": "Information", | |||||
"Microsoft": "Information" | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,34 @@ | |||||
{ | |||||
"ReRoutes": [ | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "marketing.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/m/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "locations.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/l/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
} | |||||
], | |||||
"GlobalConfiguration": { | |||||
"RequestIdKey": "OcRequestId", | |||||
"AdministrationPath": "/administration" | |||||
} | |||||
} | |||||
@ -0,0 +1,31 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config | |||||
{ | |||||
public class UrlsConfig | |||||
{ | |||||
public class CatalogOperations | |||||
{ | |||||
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; | |||||
public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}"; | |||||
} | |||||
public class BasketOperations | |||||
{ | |||||
public static string GetItemById(string id) => $"/api/v1/basket/{id}"; | |||||
public static string UpdateBasket() => "/api/v1/basket"; | |||||
} | |||||
public class OrdersOperations | |||||
{ | |||||
public static string GetOrderDraft() => "/api/v1/orders/draft"; | |||||
} | |||||
public string Basket { get; set; } | |||||
public string Catalog { get; set; } | |||||
public string Orders { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,133 @@ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers | |||||
{ | |||||
[Route("api/v1/[controller]")] | |||||
[Authorize] | |||||
public class BasketController : Controller | |||||
{ | |||||
private readonly ICatalogService _catalog; | |||||
private readonly IBasketService _basket; | |||||
public BasketController(ICatalogService catalogService, IBasketService basketService) | |||||
{ | |||||
_catalog = catalogService; | |||||
_basket = basketService; | |||||
} | |||||
[HttpPost] | |||||
[HttpPut] | |||||
public async Task<IActionResult> UpdateAllBasket([FromBody] UpdateBasketRequest data) | |||||
{ | |||||
if (data.Items == null || !data.Items.Any()) | |||||
{ | |||||
return BadRequest("Need to pass at least one basket line"); | |||||
} | |||||
// Retrieve the current basket | |||||
var currentBasket = await _basket.GetById(data.BuyerId); | |||||
if (currentBasket == null) | |||||
{ | |||||
currentBasket = new BasketData(data.BuyerId); | |||||
} | |||||
var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId)); | |||||
var newBasket = new BasketData(data.BuyerId); | |||||
foreach (var bitem in data.Items) | |||||
{ | |||||
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId); | |||||
if (catalogItem == null) | |||||
{ | |||||
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})"); | |||||
} | |||||
newBasket.Items.Add(new BasketDataItem() | |||||
{ | |||||
Id = bitem.Id, | |||||
ProductId = catalogItem.Id.ToString(), | |||||
ProductName = catalogItem.Name, | |||||
PictureUrl = catalogItem.PictureUri, | |||||
UnitPrice = catalogItem.Price, | |||||
Quantity = bitem.Quantity | |||||
}); | |||||
} | |||||
await _basket.Update(newBasket); | |||||
return Ok(newBasket); | |||||
} | |||||
[HttpPut] | |||||
[Route("items")] | |||||
public async Task<IActionResult> UpdateQuantities([FromBody] UpdateBasketItemsRequest data) | |||||
{ | |||||
if (!data.Updates.Any()) | |||||
{ | |||||
return BadRequest("No updates sent"); | |||||
} | |||||
// Retrieve the current basket | |||||
var currentBasket = await _basket.GetById(data.BasketId); | |||||
if (currentBasket == null) | |||||
{ | |||||
return BadRequest($"Basket with id {data.BasketId} not found."); | |||||
} | |||||
// Update with new quantities | |||||
foreach (var update in data.Updates) | |||||
{ | |||||
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId); | |||||
if (basketItem == null) | |||||
{ | |||||
return BadRequest($"Basket item with id {update.BasketItemId} not found"); | |||||
} | |||||
basketItem.Quantity = update.NewQty; | |||||
} | |||||
// Save the updated basket | |||||
await _basket.Update(currentBasket); | |||||
return Ok(currentBasket); | |||||
} | |||||
[HttpPost] | |||||
[Route("items")] | |||||
public async Task<IActionResult> AddBasketItem([FromBody] AddBasketItemRequest data) | |||||
{ | |||||
if (data == null || data.Quantity == 0) | |||||
{ | |||||
return BadRequest("Invalid payload"); | |||||
} | |||||
// Step 1: Get the item from catalog | |||||
var item = await _catalog.GetCatalogItem(data.CatalogItemId); | |||||
//item.PictureUri = | |||||
// Step 2: Get current basket status | |||||
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId); | |||||
// Step 3: Merge current status with new product | |||||
currentBasket.Items.Add(new BasketDataItem() | |||||
{ | |||||
UnitPrice = item.Price, | |||||
PictureUrl = item.PictureUri, | |||||
ProductId = item.Id.ToString(), | |||||
ProductName = item.Name, | |||||
Quantity = data.Quantity, | |||||
Id = Guid.NewGuid().ToString() | |||||
}); | |||||
// Step 4: Update basket | |||||
await _basket.Update(currentBasket); | |||||
return Ok(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,18 @@ | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers | |||||
{ | |||||
[Route("")] | |||||
public class HomeController : Controller | |||||
{ | |||||
[HttpGet()] | |||||
public IActionResult Index() | |||||
{ | |||||
return new RedirectResult("~/swagger"); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,42 @@ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers | |||||
{ | |||||
[Route("api/v1/[controller]")] | |||||
[Authorize] | |||||
public class OrderController : Controller | |||||
{ | |||||
private readonly IBasketService _basketService; | |||||
private readonly IOrderApiClient _orderClient; | |||||
public OrderController(IBasketService basketService, IOrderApiClient orderClient) | |||||
{ | |||||
_basketService = basketService; | |||||
_orderClient = orderClient; | |||||
} | |||||
[Route("draft/{basketId}")] | |||||
[HttpGet] | |||||
public async Task<IActionResult> GetOrderDraft(string basketId) | |||||
{ | |||||
if (string.IsNullOrEmpty(basketId)) | |||||
{ | |||||
return BadRequest("Need a valid basketid"); | |||||
} | |||||
// Get the basket data and build a order draft based on it | |||||
var basket = await _basketService.GetById(basketId); | |||||
if (basket == null) | |||||
{ | |||||
return BadRequest($"No basket found for id {basketId}"); | |||||
} | |||||
var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket); | |||||
return Ok(orderDraft); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,19 @@ | |||||
FROM microsoft/aspnetcore:2.0.5 AS base | |||||
WORKDIR /app | |||||
EXPOSE 80 | |||||
FROM microsoft/aspnetcore-build:2.0 AS build | |||||
WORKDIR /src | |||||
COPY . . | |||||
RUN dotnet restore -nowarn:msb3202,nu1503 | |||||
WORKDIR /src/src/ApiGateways/Mobile.Bff.Shopping/aggregator | |||||
RUN dotnet build --no-restore -c Release -o /app | |||||
FROM build AS publish | |||||
RUN dotnet publish --no-restore -c Release -o /app | |||||
FROM base AS final | |||||
WORKDIR /app | |||||
COPY --from=publish /app . | |||||
ENTRYPOINT ["dotnet", "Mobile.Shopping.HttpAggregator.dll"] | |||||
@ -0,0 +1,33 @@ | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters | |||||
{ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Swashbuckle.AspNetCore.Swagger; | |||||
using Swashbuckle.AspNetCore.SwaggerGen; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
namespace Basket.API.Infrastructure.Filters | |||||
{ | |||||
public class AuthorizeCheckOperationFilter : IOperationFilter | |||||
{ | |||||
public void Apply(Operation operation, OperationFilterContext context) | |||||
{ | |||||
// Check for authorize attribute | |||||
var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType<AuthorizeAttribute>().Any() || | |||||
context.ApiDescription.ActionAttributes().OfType<AuthorizeAttribute>().Any(); | |||||
if (hasAuthorize) | |||||
{ | |||||
operation.Responses.Add("401", new Response { Description = "Unauthorized" }); | |||||
operation.Responses.Add("403", new Response { Description = "Forbidden" }); | |||||
operation.Security = new List<IDictionary<string, IEnumerable<string>>>(); | |||||
operation.Security.Add(new Dictionary<string, IEnumerable<string>> | |||||
{ | |||||
{ "oauth2", new [] { "Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator" } } | |||||
}); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||||
<AssemblyName>Mobile.Shopping.HttpAggregator</AssemblyName> | |||||
<RootNamespace>Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator</RootNamespace> | |||||
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<Folder Include="wwwroot\" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> | |||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.1.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@ -0,0 +1,20 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class AddBasketItemRequest | |||||
{ | |||||
public int CatalogItemId { get; set; } | |||||
public string BasketId { get; set; } | |||||
public int Quantity { get; set; } | |||||
public AddBasketItemRequest() | |||||
{ | |||||
Quantity = 1; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,31 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class BasketData | |||||
{ | |||||
public string BuyerId { get; set; } | |||||
public List<BasketDataItem> Items { get; set; } | |||||
public BasketData(string buyerId) | |||||
{ | |||||
BuyerId = buyerId; | |||||
Items = new List<BasketDataItem>(); | |||||
} | |||||
} | |||||
public class BasketDataItem | |||||
{ | |||||
public string Id { get; set; } | |||||
public string ProductId { get; set; } | |||||
public string ProductName { get; set; } | |||||
public decimal UnitPrice { get; set; } | |||||
public decimal OldUnitPrice { get; set; } | |||||
public int Quantity { get; set; } | |||||
public string PictureUrl { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,20 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class CatalogItem | |||||
{ | |||||
public int Id { get; set; } | |||||
public string Name { get; set; } | |||||
public decimal Price { get; set; } | |||||
public string PictureUri { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,33 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class OrderData | |||||
{ | |||||
public string OrderNumber { get; set; } | |||||
public DateTime Date { get; set; } | |||||
public string Status { get; set; } | |||||
public decimal Total { get; set; } | |||||
public string Description { get; set; } | |||||
public string City { get; set; } | |||||
public string Street { get; set; } | |||||
public string State { get; set; } | |||||
public string Country { get; set; } | |||||
public string ZipCode { get; set; } | |||||
public string CardNumber { get; set; } | |||||
public string CardHolderName { get; set; } | |||||
public bool IsDraft { get; set; } | |||||
public DateTime CardExpiration { get; set; } | |||||
public string CardExpirationShort { get; set; } | |||||
public string CardSecurityNumber { get; set; } | |||||
public int CardTypeId { get; set; } | |||||
public string Buyer { get; set; } | |||||
public List<OrderItemData> OrderItems { get; } = new List<OrderItemData>(); | |||||
} | |||||
} |
@ -0,0 +1,17 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class OrderItemData | |||||
{ | |||||
public int ProductId { get; set; } | |||||
public string ProductName { get; set; } | |||||
public decimal UnitPrice { get; set; } | |||||
public decimal Discount { get; set; } | |||||
public int Units { get; set; } | |||||
public string PictureUrl { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,31 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class UpdateBasketItemsRequest | |||||
{ | |||||
public string BasketId { get; set; } | |||||
public ICollection<UpdateBasketItemData> Updates { get; set; } | |||||
public UpdateBasketItemsRequest() | |||||
{ | |||||
Updates = new List<UpdateBasketItemData>(); | |||||
} | |||||
} | |||||
public class UpdateBasketItemData | |||||
{ | |||||
public string BasketItemId { get; set; } | |||||
public int NewQty { get; set; } | |||||
public UpdateBasketItemData() | |||||
{ | |||||
NewQty = 0; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,21 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class UpdateBasketRequest | |||||
{ | |||||
public string BuyerId { get; set; } | |||||
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; } | |||||
} | |||||
public class UpdateBasketRequestItemData | |||||
{ | |||||
public string Id { get; set; } // Basket id | |||||
public int ProductId { get; set; } // Catalog item id | |||||
public int Quantity { get; set; } // Quantity | |||||
} | |||||
} |
@ -0,0 +1,36 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static void Main(string[] args) | |||||
{ | |||||
BuildWebHost(args).Run(); | |||||
} | |||||
public static IWebHost BuildWebHost(string[] args) => | |||||
WebHost | |||||
.CreateDefaultBuilder(args) | |||||
.ConfigureAppConfiguration(cb => | |||||
{ | |||||
var sources = cb.Sources; | |||||
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource() | |||||
{ | |||||
Optional = true, | |||||
Path = "appsettings.localhost.json", | |||||
ReloadOnChange = false | |||||
}); | |||||
}) | |||||
.UseStartup<Startup>() | |||||
.Build(); | |||||
} | |||||
} |
@ -0,0 +1,29 @@ | |||||
{ | |||||
"iisSettings": { | |||||
"windowsAuthentication": false, | |||||
"anonymousAuthentication": true, | |||||
"iisExpress": { | |||||
"applicationUrl": "http://localhost:57425/", | |||||
"sslPort": 0 | |||||
} | |||||
}, | |||||
"profiles": { | |||||
"IIS Express": { | |||||
"commandName": "IISExpress", | |||||
"launchBrowser": true, | |||||
"launchUrl": "api/values", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
}, | |||||
"PurchaseForMvc": { | |||||
"commandName": "Project", | |||||
"launchBrowser": true, | |||||
"launchUrl": "api/values", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
}, | |||||
"applicationUrl": "http://localhost:61632/" | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,53 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Authentication; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | |||||
{ | |||||
public class BasketService : IBasketService | |||||
{ | |||||
private readonly IHttpClient _apiClient; | |||||
private readonly ILogger<BasketService> _logger; | |||||
private readonly UrlsConfig _urls; | |||||
private readonly IHttpContextAccessor _httpContextAccessor; | |||||
public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger<BasketService> logger, IOptionsSnapshot<UrlsConfig> config) | |||||
{ | |||||
_apiClient = httpClient; | |||||
_logger = logger; | |||||
_urls = config.Value; | |||||
_httpContextAccessor = httpContextAccessor; | |||||
} | |||||
public async Task<BasketData> GetById(string id) | |||||
{ | |||||
var token = await GetUserTokenAsync(); | |||||
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id), token); | |||||
var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null; | |||||
return basket; | |||||
} | |||||
public async Task Update(BasketData currentBasket) | |||||
{ | |||||
var token = await GetUserTokenAsync(); | |||||
var data = await _apiClient.PostAsync<BasketData>(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token); | |||||
int i = 0; | |||||
} | |||||
async Task<string> GetUserTokenAsync() | |||||
{ | |||||
var context = _httpContextAccessor.HttpContext; | |||||
return await context.GetTokenAsync("access_token"); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,43 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | |||||
{ | |||||
public class CatalogService : ICatalogService | |||||
{ | |||||
private readonly IHttpClient _apiClient; | |||||
private readonly ILogger<CatalogService> _logger; | |||||
private readonly UrlsConfig _urls; | |||||
public CatalogService(IHttpClient httpClient, ILogger<CatalogService> logger, IOptionsSnapshot<UrlsConfig> config) | |||||
{ | |||||
_apiClient = httpClient; | |||||
_logger = logger; | |||||
_urls = config.Value; | |||||
} | |||||
public async Task<CatalogItem> GetCatalogItem(int id) | |||||
{ | |||||
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); | |||||
var item = JsonConvert.DeserializeObject<CatalogItem>(data); | |||||
return item; | |||||
} | |||||
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids) | |||||
{ | |||||
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); | |||||
var item = JsonConvert.DeserializeObject<CatalogItem[]>(data); | |||||
return item; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,15 @@ | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | |||||
{ | |||||
public interface IBasketService | |||||
{ | |||||
Task<BasketData> GetById(string id); | |||||
Task Update(BasketData currentBasket); | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | |||||
{ | |||||
public interface ICatalogService | |||||
{ | |||||
Task<CatalogItem> GetCatalogItem(int id); | |||||
Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids); | |||||
} | |||||
} |
@ -0,0 +1,13 @@ | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | |||||
{ | |||||
public interface IOrderApiClient | |||||
{ | |||||
Task<OrderData> GetOrderDraftFromBasket(BasketData basket); | |||||
} | |||||
} |
@ -0,0 +1,37 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | |||||
{ | |||||
public class OrderApiClient : IOrderApiClient | |||||
{ | |||||
private readonly IHttpClient _apiClient; | |||||
private readonly ILogger<OrderApiClient> _logger; | |||||
private readonly UrlsConfig _urls; | |||||
public OrderApiClient(IHttpClient httpClient, ILogger<OrderApiClient> logger, IOptionsSnapshot<UrlsConfig> config) | |||||
{ | |||||
_apiClient = httpClient; | |||||
_logger = logger; | |||||
_urls = config.Value; | |||||
} | |||||
public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket) | |||||
{ | |||||
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); | |||||
var response = await _apiClient.PostAsync<BasketData>(url, basket); | |||||
response.EnsureSuccessStatusCode(); | |||||
var jsonResponse = await response.Content.ReadAsStringAsync(); | |||||
return JsonConvert.DeserializeObject<OrderData>(jsonResponse); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,138 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IdentityModel.Tokens.Jwt; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; | |||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; | |||||
using Swashbuckle.AspNetCore.Swagger; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator | |||||
{ | |||||
public class Startup | |||||
{ | |||||
public Startup(IConfiguration configuration) | |||||
{ | |||||
Configuration = configuration; | |||||
} | |||||
public IConfiguration Configuration { get; } | |||||
// This method gets called by the runtime. Use this method to add services to the container. | |||||
public void ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||||
services.AddSingleton<IHttpClient, StandardHttpClient>(); | |||||
services.AddTransient<ICatalogService, CatalogService>(); | |||||
services.AddTransient<IBasketService, BasketService>(); | |||||
services.AddTransient<IOrderApiClient, OrderApiClient>(); | |||||
services.AddOptions(); | |||||
services.Configure<UrlsConfig>(Configuration.GetSection("urls")); | |||||
services.AddMvc(); | |||||
services.AddSwaggerGen(options => | |||||
{ | |||||
options.DescribeAllEnumsAsStrings(); | |||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info | |||||
{ | |||||
Title = "Shopping Aggregator for Mobile Clients", | |||||
Version = "v1", | |||||
Description = "Shopping Aggregator for Mobile Clients", | |||||
TermsOfService = "Terms Of Service" | |||||
}); | |||||
options.AddSecurityDefinition("oauth2", new OAuth2Scheme | |||||
{ | |||||
Type = "oauth2", | |||||
Flow = "implicit", | |||||
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", | |||||
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", | |||||
Scopes = new Dictionary<string, string>() | |||||
{ | |||||
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" } | |||||
} | |||||
}); | |||||
options.OperationFilter<AuthorizeCheckOperationFilter>(); | |||||
}); | |||||
services.AddCors(options => | |||||
{ | |||||
options.AddPolicy("CorsPolicy", | |||||
builder => builder.AllowAnyOrigin() | |||||
.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials()); | |||||
}); | |||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); | |||||
var identityUrl = Configuration.GetValue<string>("urls:identity"); | |||||
services.AddAuthentication(options => | |||||
{ | |||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
}).AddJwtBearer(options => | |||||
{ | |||||
options.Authority = identityUrl; | |||||
options.RequireHttpsMetadata = false; | |||||
options.Audience = "mobileshoppingagg"; | |||||
options.Events = new JwtBearerEvents() | |||||
{ | |||||
OnAuthenticationFailed = async ctx => | |||||
{ | |||||
int i = 0; | |||||
}, | |||||
OnTokenValidated = async ctx => | |||||
{ | |||||
int i = 0; | |||||
} | |||||
}; | |||||
}); | |||||
} | |||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||||
{ | |||||
var pathBase = Configuration["PATH_BASE"]; | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
app.UseCors("CorsPolicy"); | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
app.UseDeveloperExceptionPage(); | |||||
} | |||||
app.UseAuthentication(); | |||||
app.UseMvc(); | |||||
app.UseSwagger().UseSwaggerUI(c => | |||||
{ | |||||
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); | |||||
c.ConfigureOAuth2("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI"); | |||||
}); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,15 @@ | |||||
{ | |||||
"Logging": { | |||||
"IncludeScopes": false, | |||||
"Debug": { | |||||
"LogLevel": { | |||||
"Default": "Warning" | |||||
} | |||||
}, | |||||
"Console": { | |||||
"LogLevel": { | |||||
"Default": "Warning" | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,8 @@ | |||||
{ | |||||
"urls": { | |||||
"basket": "http://localhost:55105", | |||||
"catalog": "http://localhost:55101", | |||||
"orders": "http://localhost:55102", | |||||
"identity": "http://localhost:55105" | |||||
} | |||||
} |
@ -0,0 +1,142 @@ | |||||
{ | |||||
"ReRoutes": [ | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "catalog.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/c/{everything}", | |||||
"UpstreamHttpMethod": [ "GET" ] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "basket.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/b/{everything}", | |||||
"UpstreamHttpMethod": [], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "ordering.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/o/{everything}", | |||||
"UpstreamHttpMethod": [], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "mobileshoppingagg", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/{everything}", | |||||
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "ordering.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/orders-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "basket.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/basket-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "catalog.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/catalog-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "marketing.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/marketing-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "payment.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/payment-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "locations.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/location-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
} | |||||
], | |||||
"GlobalConfiguration": { | |||||
"RequestIdKey": "OcRequestId", | |||||
"AdministrationPath": "/administration" | |||||
} | |||||
} | |||||
@ -0,0 +1,34 @@ | |||||
{ | |||||
"ReRoutes": [ | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "marketing.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/m/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "locations.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/l/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
} | |||||
], | |||||
"GlobalConfiguration": { | |||||
"RequestIdKey": "OcRequestId", | |||||
"AdministrationPath": "/administration" | |||||
} | |||||
} | |||||
@ -0,0 +1,31 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config | |||||
{ | |||||
public class UrlsConfig | |||||
{ | |||||
public class CatalogOperations | |||||
{ | |||||
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; | |||||
public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}"; | |||||
} | |||||
public class BasketOperations | |||||
{ | |||||
public static string GetItemById(string id) => $"/api/v1/basket/{id}"; | |||||
public static string UpdateBasket() => "/api/v1/basket"; | |||||
} | |||||
public class OrdersOperations | |||||
{ | |||||
public static string GetOrderDraft() => "/api/v1/orders/draft"; | |||||
} | |||||
public string Basket { get; set; } | |||||
public string Catalog { get; set; } | |||||
public string Orders { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,133 @@ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers | |||||
{ | |||||
[Route("api/v1/[controller]")] | |||||
[Authorize] | |||||
public class BasketController : Controller | |||||
{ | |||||
private readonly ICatalogService _catalog; | |||||
private readonly IBasketService _basket; | |||||
public BasketController(ICatalogService catalogService, IBasketService basketService) | |||||
{ | |||||
_catalog = catalogService; | |||||
_basket = basketService; | |||||
} | |||||
[HttpPost] | |||||
[HttpPut] | |||||
public async Task<IActionResult> UpdateAllBasket([FromBody] UpdateBasketRequest data) | |||||
{ | |||||
if (data.Items == null || !data.Items.Any()) | |||||
{ | |||||
return BadRequest("Need to pass at least one basket line"); | |||||
} | |||||
// Retrieve the current basket | |||||
var currentBasket = await _basket.GetById(data.BuyerId); | |||||
if (currentBasket == null) | |||||
{ | |||||
currentBasket = new BasketData(data.BuyerId); | |||||
} | |||||
var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId)); | |||||
var newBasket = new BasketData(data.BuyerId); | |||||
foreach (var bitem in data.Items) | |||||
{ | |||||
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId); | |||||
if (catalogItem == null) | |||||
{ | |||||
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})"); | |||||
} | |||||
newBasket.Items.Add(new BasketDataItem() | |||||
{ | |||||
Id = bitem.Id, | |||||
ProductId = catalogItem.Id.ToString(), | |||||
ProductName = catalogItem.Name, | |||||
PictureUrl = catalogItem.PictureUri, | |||||
UnitPrice = catalogItem.Price, | |||||
Quantity = bitem.Quantity | |||||
}); | |||||
} | |||||
await _basket.Update(newBasket); | |||||
return Ok(newBasket); | |||||
} | |||||
[HttpPut] | |||||
[Route("items")] | |||||
public async Task<IActionResult> UpdateQuantities([FromBody] UpdateBasketItemsRequest data) | |||||
{ | |||||
if (!data.Updates.Any()) | |||||
{ | |||||
return BadRequest("No updates sent"); | |||||
} | |||||
// Retrieve the current basket | |||||
var currentBasket = await _basket.GetById(data.BasketId); | |||||
if (currentBasket == null) | |||||
{ | |||||
return BadRequest($"Basket with id {data.BasketId} not found."); | |||||
} | |||||
// Update with new quantities | |||||
foreach (var update in data.Updates) | |||||
{ | |||||
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId); | |||||
if (basketItem == null) | |||||
{ | |||||
return BadRequest($"Basket item with id {update.BasketItemId} not found"); | |||||
} | |||||
basketItem.Quantity = update.NewQty; | |||||
} | |||||
// Save the updated basket | |||||
await _basket.Update(currentBasket); | |||||
return Ok(currentBasket); | |||||
} | |||||
[HttpPost] | |||||
[Route("items")] | |||||
public async Task<IActionResult> AddBasketItem([FromBody] AddBasketItemRequest data) | |||||
{ | |||||
if (data == null || data.Quantity == 0) | |||||
{ | |||||
return BadRequest("Invalid payload"); | |||||
} | |||||
// Step 1: Get the item from catalog | |||||
var item = await _catalog.GetCatalogItem(data.CatalogItemId); | |||||
//item.PictureUri = | |||||
// Step 2: Get current basket status | |||||
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId); | |||||
// Step 3: Merge current status with new product | |||||
currentBasket.Items.Add(new BasketDataItem() | |||||
{ | |||||
UnitPrice = item.Price, | |||||
PictureUrl = item.PictureUri, | |||||
ProductId = item.Id.ToString(), | |||||
ProductName = item.Name, | |||||
Quantity = data.Quantity, | |||||
Id = Guid.NewGuid().ToString() | |||||
}); | |||||
// Step 4: Update basket | |||||
await _basket.Update(currentBasket); | |||||
return Ok(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,18 @@ | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers | |||||
{ | |||||
[Route("")] | |||||
public class HomeController : Controller | |||||
{ | |||||
[HttpGet()] | |||||
public IActionResult Index() | |||||
{ | |||||
return new RedirectResult("~/swagger"); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,42 @@ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers | |||||
{ | |||||
[Route("api/v1/[controller]")] | |||||
[Authorize] | |||||
public class OrderController : Controller | |||||
{ | |||||
private readonly IBasketService _basketService; | |||||
private readonly IOrderApiClient _orderClient; | |||||
public OrderController(IBasketService basketService, IOrderApiClient orderClient) | |||||
{ | |||||
_basketService = basketService; | |||||
_orderClient = orderClient; | |||||
} | |||||
[Route("draft/{basketId}")] | |||||
[HttpGet] | |||||
public async Task<IActionResult> GetOrderDraft(string basketId) | |||||
{ | |||||
if (string.IsNullOrEmpty(basketId)) | |||||
{ | |||||
return BadRequest("Need a valid basketid"); | |||||
} | |||||
// Get the basket data and build a order draft based on it | |||||
var basket = await _basketService.GetById(basketId); | |||||
if (basket == null) | |||||
{ | |||||
return BadRequest($"No basket found for id {basketId}"); | |||||
} | |||||
var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket); | |||||
return Ok(orderDraft); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,18 @@ | |||||
FROM microsoft/aspnetcore:2.0.5 AS base | |||||
WORKDIR /app | |||||
EXPOSE 80 | |||||
FROM microsoft/aspnetcore-build:2.0 AS build | |||||
WORKDIR /src | |||||
COPY . . | |||||
RUN dotnet restore -nowarn:msb3202,nu1503 | |||||
WORKDIR /src/src/ApiGateways/Web.Bff.Shopping/aggregator | |||||
RUN dotnet build --no-restore -c Release -o /app | |||||
FROM build AS publish | |||||
RUN dotnet publish --no-restore -c Release -o /app | |||||
FROM base AS final | |||||
WORKDIR /app | |||||
COPY --from=publish /app . | |||||
ENTRYPOINT ["dotnet", "Web.Shopping.HttpAggregator.dll"] |
@ -0,0 +1,33 @@ | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters | |||||
{ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Swashbuckle.AspNetCore.Swagger; | |||||
using Swashbuckle.AspNetCore.SwaggerGen; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
namespace Basket.API.Infrastructure.Filters | |||||
{ | |||||
public class AuthorizeCheckOperationFilter : IOperationFilter | |||||
{ | |||||
public void Apply(Operation operation, OperationFilterContext context) | |||||
{ | |||||
// Check for authorize attribute | |||||
var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType<AuthorizeAttribute>().Any() || | |||||
context.ApiDescription.ActionAttributes().OfType<AuthorizeAttribute>().Any(); | |||||
if (hasAuthorize) | |||||
{ | |||||
operation.Responses.Add("401", new Response { Description = "Unauthorized" }); | |||||
operation.Responses.Add("403", new Response { Description = "Forbidden" }); | |||||
operation.Security = new List<IDictionary<string, IEnumerable<string>>>(); | |||||
operation.Security.Add(new Dictionary<string, IEnumerable<string>> | |||||
{ | |||||
{ "oauth2", new [] { "Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator" } } | |||||
}); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,20 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class AddBasketItemRequest | |||||
{ | |||||
public int CatalogItemId { get; set; } | |||||
public string BasketId { get; set; } | |||||
public int Quantity { get; set; } | |||||
public AddBasketItemRequest() | |||||
{ | |||||
Quantity = 1; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,31 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class BasketData | |||||
{ | |||||
public string BuyerId { get; set; } | |||||
public List<BasketDataItem> Items { get; set; } | |||||
public BasketData(string buyerId) | |||||
{ | |||||
BuyerId = buyerId; | |||||
Items = new List<BasketDataItem>(); | |||||
} | |||||
} | |||||
public class BasketDataItem | |||||
{ | |||||
public string Id { get; set; } | |||||
public string ProductId { get; set; } | |||||
public string ProductName { get; set; } | |||||
public decimal UnitPrice { get; set; } | |||||
public decimal OldUnitPrice { get; set; } | |||||
public int Quantity { get; set; } | |||||
public string PictureUrl { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,20 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class CatalogItem | |||||
{ | |||||
public int Id { get; set; } | |||||
public string Name { get; set; } | |||||
public decimal Price { get; set; } | |||||
public string PictureUri { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,33 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class OrderData | |||||
{ | |||||
public string OrderNumber { get; set; } | |||||
public DateTime Date { get; set; } | |||||
public string Status { get; set; } | |||||
public decimal Total { get; set; } | |||||
public string Description { get; set; } | |||||
public string City { get; set; } | |||||
public string Street { get; set; } | |||||
public string State { get; set; } | |||||
public string Country { get; set; } | |||||
public string ZipCode { get; set; } | |||||
public string CardNumber { get; set; } | |||||
public string CardHolderName { get; set; } | |||||
public bool IsDraft { get; set; } | |||||
public DateTime CardExpiration { get; set; } | |||||
public string CardExpirationShort { get; set; } | |||||
public string CardSecurityNumber { get; set; } | |||||
public int CardTypeId { get; set; } | |||||
public string Buyer { get; set; } | |||||
public List<OrderItemData> OrderItems { get; } = new List<OrderItemData>(); | |||||
} | |||||
} |
@ -0,0 +1,17 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class OrderItemData | |||||
{ | |||||
public int ProductId { get; set; } | |||||
public string ProductName { get; set; } | |||||
public decimal UnitPrice { get; set; } | |||||
public decimal Discount { get; set; } | |||||
public int Units { get; set; } | |||||
public string PictureUrl { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,31 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class UpdateBasketItemsRequest | |||||
{ | |||||
public string BasketId { get; set; } | |||||
public ICollection<UpdateBasketItemData> Updates { get; set; } | |||||
public UpdateBasketItemsRequest() | |||||
{ | |||||
Updates = new List<UpdateBasketItemData>(); | |||||
} | |||||
} | |||||
public class UpdateBasketItemData | |||||
{ | |||||
public string BasketItemId { get; set; } | |||||
public int NewQty { get; set; } | |||||
public UpdateBasketItemData() | |||||
{ | |||||
NewQty = 0; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,21 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models | |||||
{ | |||||
public class UpdateBasketRequest | |||||
{ | |||||
public string BuyerId { get; set; } | |||||
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; } | |||||
} | |||||
public class UpdateBasketRequestItemData | |||||
{ | |||||
public string Id { get; set; } // Basket id | |||||
public int ProductId { get; set; } // Catalog item id | |||||
public int Quantity { get; set; } // Quantity | |||||
} | |||||
} |
@ -0,0 +1,36 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static void Main(string[] args) | |||||
{ | |||||
BuildWebHost(args).Run(); | |||||
} | |||||
public static IWebHost BuildWebHost(string[] args) => | |||||
WebHost | |||||
.CreateDefaultBuilder(args) | |||||
.ConfigureAppConfiguration(cb => | |||||
{ | |||||
var sources = cb.Sources; | |||||
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource() | |||||
{ | |||||
Optional = true, | |||||
Path = "appsettings.localhost.json", | |||||
ReloadOnChange = false | |||||
}); | |||||
}) | |||||
.UseStartup<Startup>() | |||||
.Build(); | |||||
} | |||||
} |
@ -0,0 +1,29 @@ | |||||
{ | |||||
"iisSettings": { | |||||
"windowsAuthentication": false, | |||||
"anonymousAuthentication": true, | |||||
"iisExpress": { | |||||
"applicationUrl": "http://localhost:57425/", | |||||
"sslPort": 0 | |||||
} | |||||
}, | |||||
"profiles": { | |||||
"IIS Express": { | |||||
"commandName": "IISExpress", | |||||
"launchBrowser": true, | |||||
"launchUrl": "api/values", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
}, | |||||
"PurchaseForMvc": { | |||||
"commandName": "Project", | |||||
"launchBrowser": true, | |||||
"launchUrl": "api/values", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
}, | |||||
"applicationUrl": "http://localhost:61632/" | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,53 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Authentication; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | |||||
{ | |||||
public class BasketService : IBasketService | |||||
{ | |||||
private readonly IHttpClient _apiClient; | |||||
private readonly ILogger<BasketService> _logger; | |||||
private readonly UrlsConfig _urls; | |||||
private readonly IHttpContextAccessor _httpContextAccessor; | |||||
public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger<BasketService> logger, IOptionsSnapshot<UrlsConfig> config) | |||||
{ | |||||
_apiClient = httpClient; | |||||
_logger = logger; | |||||
_urls = config.Value; | |||||
_httpContextAccessor = httpContextAccessor; | |||||
} | |||||
public async Task<BasketData> GetById(string id) | |||||
{ | |||||
var token = await GetUserTokenAsync(); | |||||
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id), token); | |||||
var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null; | |||||
return basket; | |||||
} | |||||
public async Task Update(BasketData currentBasket) | |||||
{ | |||||
var token = await GetUserTokenAsync(); | |||||
var data = await _apiClient.PostAsync<BasketData>(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token); | |||||
int i = 0; | |||||
} | |||||
async Task<string> GetUserTokenAsync() | |||||
{ | |||||
var context = _httpContextAccessor.HttpContext; | |||||
return await context.GetTokenAsync("access_token"); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,43 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | |||||
{ | |||||
public class CatalogService : ICatalogService | |||||
{ | |||||
private readonly IHttpClient _apiClient; | |||||
private readonly ILogger<CatalogService> _logger; | |||||
private readonly UrlsConfig _urls; | |||||
public CatalogService(IHttpClient httpClient, ILogger<CatalogService> logger, IOptionsSnapshot<UrlsConfig> config) | |||||
{ | |||||
_apiClient = httpClient; | |||||
_logger = logger; | |||||
_urls = config.Value; | |||||
} | |||||
public async Task<CatalogItem> GetCatalogItem(int id) | |||||
{ | |||||
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); | |||||
var item = JsonConvert.DeserializeObject<CatalogItem>(data); | |||||
return item; | |||||
} | |||||
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids) | |||||
{ | |||||
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); | |||||
var item = JsonConvert.DeserializeObject<CatalogItem[]>(data); | |||||
return item; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,15 @@ | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | |||||
{ | |||||
public interface IBasketService | |||||
{ | |||||
Task<BasketData> GetById(string id); | |||||
Task Update(BasketData currentBasket); | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | |||||
{ | |||||
public interface ICatalogService | |||||
{ | |||||
Task<CatalogItem> GetCatalogItem(int id); | |||||
Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids); | |||||
} | |||||
} |
@ -0,0 +1,13 @@ | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | |||||
{ | |||||
public interface IOrderApiClient | |||||
{ | |||||
Task<OrderData> GetOrderDraftFromBasket(BasketData basket); | |||||
} | |||||
} |
@ -0,0 +1,37 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Newtonsoft.Json; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | |||||
{ | |||||
public class OrderApiClient : IOrderApiClient | |||||
{ | |||||
private readonly IHttpClient _apiClient; | |||||
private readonly ILogger<OrderApiClient> _logger; | |||||
private readonly UrlsConfig _urls; | |||||
public OrderApiClient(IHttpClient httpClient, ILogger<OrderApiClient> logger, IOptionsSnapshot<UrlsConfig> config) | |||||
{ | |||||
_apiClient = httpClient; | |||||
_logger = logger; | |||||
_urls = config.Value; | |||||
} | |||||
public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket) | |||||
{ | |||||
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); | |||||
var response = await _apiClient.PostAsync<BasketData>(url, basket); | |||||
response.EnsureSuccessStatusCode(); | |||||
var jsonResponse = await response.Content.ReadAsStringAsync(); | |||||
return JsonConvert.DeserializeObject<OrderData>(jsonResponse); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,138 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IdentityModel.Tokens.Jwt; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; | |||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; | |||||
using Swashbuckle.AspNetCore.Swagger; | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator | |||||
{ | |||||
public class Startup | |||||
{ | |||||
public Startup(IConfiguration configuration) | |||||
{ | |||||
Configuration = configuration; | |||||
} | |||||
public IConfiguration Configuration { get; } | |||||
// This method gets called by the runtime. Use this method to add services to the container. | |||||
public void ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||||
services.AddSingleton<IHttpClient, StandardHttpClient>(); | |||||
services.AddTransient<ICatalogService, CatalogService>(); | |||||
services.AddTransient<IBasketService, BasketService>(); | |||||
services.AddTransient<IOrderApiClient, OrderApiClient>(); | |||||
services.AddOptions(); | |||||
services.Configure<UrlsConfig>(Configuration.GetSection("urls")); | |||||
services.AddMvc(); | |||||
services.AddSwaggerGen(options => | |||||
{ | |||||
options.DescribeAllEnumsAsStrings(); | |||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info | |||||
{ | |||||
Title = "Shopping Aggregator for Web Clients", | |||||
Version = "v1", | |||||
Description = "Shopping Aggregator for Web Clients", | |||||
TermsOfService = "Terms Of Service" | |||||
}); | |||||
options.AddSecurityDefinition("oauth2", new OAuth2Scheme | |||||
{ | |||||
Type = "oauth2", | |||||
Flow = "implicit", | |||||
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", | |||||
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", | |||||
Scopes = new Dictionary<string, string>() | |||||
{ | |||||
{ "webshoppingagg", "Shopping Aggregator for Web Clients" } | |||||
} | |||||
}); | |||||
options.OperationFilter<AuthorizeCheckOperationFilter>(); | |||||
}); | |||||
services.AddCors(options => | |||||
{ | |||||
options.AddPolicy("CorsPolicy", | |||||
builder => builder.AllowAnyOrigin() | |||||
.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials()); | |||||
}); | |||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); | |||||
var identityUrl = Configuration.GetValue<string>("urls:identity"); | |||||
services.AddAuthentication(options => | |||||
{ | |||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
}).AddJwtBearer(options => | |||||
{ | |||||
options.Authority = identityUrl; | |||||
options.RequireHttpsMetadata = false; | |||||
options.Audience = "webshoppingagg"; | |||||
options.Events = new JwtBearerEvents() | |||||
{ | |||||
OnAuthenticationFailed = async ctx => | |||||
{ | |||||
int i = 0; | |||||
}, | |||||
OnTokenValidated = async ctx => | |||||
{ | |||||
int i = 0; | |||||
} | |||||
}; | |||||
}); | |||||
} | |||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||||
{ | |||||
var pathBase = Configuration["PATH_BASE"]; | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
app.UseCors("CorsPolicy"); | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
app.UseDeveloperExceptionPage(); | |||||
} | |||||
app.UseAuthentication(); | |||||
app.UseMvc(); | |||||
app.UseSwagger().UseSwaggerUI(c => | |||||
{ | |||||
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); | |||||
c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI"); | |||||
}); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||||
<AssemblyName>Web.Shopping.HttpAggregator</AssemblyName> | |||||
<RootNamespace>Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator</RootNamespace> | |||||
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<Folder Include="wwwroot\" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> | |||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.1.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@ -0,0 +1,15 @@ | |||||
{ | |||||
"Logging": { | |||||
"IncludeScopes": false, | |||||
"Debug": { | |||||
"LogLevel": { | |||||
"Default": "Warning" | |||||
} | |||||
}, | |||||
"Console": { | |||||
"LogLevel": { | |||||
"Default": "Warning" | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,8 @@ | |||||
{ | |||||
"urls": { | |||||
"basket": "http://localhost:55105", | |||||
"catalog": "http://localhost:55101", | |||||
"orders": "http://localhost:55102", | |||||
"identity": "http://localhost:55105" | |||||
} | |||||
} |
@ -0,0 +1,142 @@ | |||||
{ | |||||
"ReRoutes": [ | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "catalog.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/c/{everything}", | |||||
"UpstreamHttpMethod": [ "GET" ] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "basket.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/b/{everything}", | |||||
"UpstreamHttpMethod": [], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/api/{version}/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "ordering.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/api/{version}/o/{everything}", | |||||
"UpstreamHttpMethod": [], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "webshoppingagg", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/{everything}", | |||||
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ], | |||||
"AuthenticationOptions": { | |||||
"AuthenticationProviderKey": "IdentityApiKey", | |||||
"AllowedScopes": [] | |||||
} | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "ordering.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/orders-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "basket.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/basket-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "catalog.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/catalog-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "marketing.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/marketing-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "payment.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/payment-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
}, | |||||
{ | |||||
"DownstreamPathTemplate": "/{everything}", | |||||
"DownstreamScheme": "http", | |||||
"DownstreamHostAndPorts": [ | |||||
{ | |||||
"Host": "locations.api", | |||||
"Port": 80 | |||||
} | |||||
], | |||||
"UpstreamPathTemplate": "/location-api/{everything}", | |||||
"UpstreamHttpMethod": [] | |||||
} | |||||
], | |||||
"GlobalConfiguration": { | |||||
"RequestIdKey": "OcRequestId", | |||||
"AdministrationPath": "/administration" | |||||
} | |||||
} | |||||