@ -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" | |||
} | |||
} | |||