Skip to content
This repository has been archived by the owner on Mar 7, 2020. It is now read-only.

Commit

Permalink
authentication via Environment Variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Waldemar Lammert committed Nov 20, 2019
1 parent 10ab410 commit 5a47008
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 35 deletions.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.gradle
.idea

build
*.log
build/**
out/**
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM gcr.io/distroless/java:11-debug

COPY build/libs/bk_amazon_sync.jar /sync.jar

ENTRYPOINT ["java","-jar","/sync.jar"]
42 changes: 18 additions & 24 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,37 +1,31 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java Library project to get you started.
* For more details take a look at the Java Libraries chapter in the Gradle
* User Manual available at https://docs.gradle.org/5.5/userguide/java_library_plugin.html
*/

plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
id 'java'
}

repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
}

dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'

// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:27.1-jre'

// Use JUnit Jupiter API for testing.
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2'
implementation 'com.squareup.okhttp3:okhttp:4.2.1'
implementation 'org.json:json:20190722'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'org.apache.logging.log4j:log4j-api:2.12.1'
implementation 'org.apache.logging.log4j:log4j-core:2.12.0'

// Use JUnit Jupiter Engine for testing.
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.2'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.5.1'
testImplementation 'org.hamcrest:hamcrest-library:2.1'
}

test {
// Use junit platform for unit tests
useJUnitPlatform()
}

jar {
manifest {
attributes 'Main-Class': 'eu.dev089.BkUpdaterApplication'
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
9 changes: 0 additions & 9 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
*
* Detailed information about configuring a multi-project build in Gradle can be found
* in the user manual at https://docs.gradle.org/5.5/userguide/multi_project_builds.html
*/

rootProject.name = 'bk_amazon_sync'
14 changes: 14 additions & 0 deletions src/main/java/eu/dev089/BkUpdaterApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package eu.dev089;

import eu.dev089.components.Updater;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class BkUpdaterApplication {

public static void main(String[] args) {
var updater = new Updater();
var scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleWithFixedDelay(updater, 1, 5, TimeUnit.MINUTES);
}
}
80 changes: 80 additions & 0 deletions src/main/java/eu/dev089/components/RequestCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package eu.dev089.components;

import eu.dev089.model.Product;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;

public class RequestCreator {

private static final String URL = "https://www.bienenkorb-shop.de/index.php/rest/V1/";

private Request.Builder requestBuilder(String contextPath) {
var bk_token = System.getenv().get("BK_TOKEN");
if (bk_token == null || bk_token.isEmpty()) {
throw new IllegalArgumentException("please provide a bearer token via ENV");
}
return new Request.Builder()
.url(URL + contextPath)
.addHeader("Authorization", String.format("Bearer %s", bk_token))
.addHeader("Cache-Control", "no-cache")
.addHeader("Connection", "keep-alive");
}

public Request getActiveProductSkus() {
var query = "searchCriteria[filter_groups][0][filters][0][field]=status&searchCriteria[filter_groups][0][filters][0][value]=1&searchCriteria[filter_groups][0][filters][0][condition_type]=finset&fields=items[sku]";
return requestBuilder("products" + "?" + query).get().build();
}

public Request get100SkuForPage(int page) {
var query =
"searchCriteria[filter_groups][0][filters][0][field]=status&searchCriteria[filter_groups][0][filters][0][value]=1&searchCriteria[filter_groups][0][filters][0][condition_type]=finset&fields=items[sku]&searchCriteria[current_page]="
+ String.valueOf(page) + "&searchCriteria[page_size]=500";
return requestBuilder("products" + "?" + query).get().build();
}

public Request getAmazonCategoryProducts() {
var query = "searchCriteria[filterGroups][0][filters][0][field]=category_id& searchCriteria[filterGroups][0][filters][0][value]=2927&searchCriteria[filterGroups][0][filters][0][conditionType]=eq&fields=items[sku,name]";
return requestBuilder("products" + "?" + query).get().build();
}

public Request getAmazonCategoryProducts(int page) {
var query =
"searchCriteria[filterGroups][0][filters][0][field]=category_id& searchCriteria[filterGroups][0][filters][0][value]=2927&searchCriteria[filterGroups][0][filters][0][conditionType]=eq&fields=items[sku,name]&searchCriteria[current_page]="
+ page + "&searchCriteria[page_size]=200";
return requestBuilder("products" + "?" + query).get().build();
}

public Request getFirst100ForTesting() {
var request =
"products?searchCriteria[filterGroups][0][filters][0][field]=category_id& searchCriteria[filterGroups][0][filters][0][value]=2927&searchCriteria[filterGroups][0][filters][0][conditionType]=eq&fields=items[sku,name]&searchCriteria[current_page]=1&searchCriteria[page_size]=100";
return requestBuilder(request).get().build();
}

public Request updateProductRequest(Product product) {
var anotherJson = String.format("{\n"
+ " \"product\": {\n"
+ " \t\"sku\": \"%s\",\n"
+ " \"custom_attributes\": [\n"
+ " {\n"
+ " \"attribute_code\": \"amazon_qty\",\n"
+ " \"value\": \"%d\"\n"
+ " },{\n"
+ " \"attribute_code\": \"delivery\",\n"
+ " \"value\": \"%d\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"saveOptions\": true\n"
+ "}", product.getSku(), product.getAmazonQuantity(), product.getAmazonDelivery());

var body = RequestBody.create(anotherJson, MediaType.parse("application/json"));
return requestBuilder(String.format("products/%s/", product.getSku())).put(body).build();
}

public Request getProduktDataForSku(String sku) {
var query = "?fields=sku,name,extension_attributes[stock_item[qty]],custom_attributes";
return requestBuilder(String.format("products/%s/%s", sku, query)).get().build();
}
}

132 changes: 132 additions & 0 deletions src/main/java/eu/dev089/components/Updater.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package eu.dev089.components;

import static eu.dev089.model.Product.STANDARD_AMAZON_QTY;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import eu.dev089.model.Product;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
import okhttp3.Request;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Updater extends TimerTask {

private static final Logger LOGGER = LogManager.getLogger(Updater.class);
private final RequestCreator creator;
private ConnectionPool pool = new ConnectionPool(10, 30, TimeUnit.SECONDS);
private OkHttpClient client;

public Updater() {
this.creator = new RequestCreator();

}

@Override
public void run() {
this.client = new Builder()
.connectionPool(pool)
.readTimeout(300, TimeUnit.SECONDS)
.build();
var previousLastsku = "";
var process = true;
var page = 1;
var productsToUpdate = new ArrayList<Product>();
while (process) {
var jsonObject = readResponse(creator.getAmazonCategoryProducts(page));
if (jsonObject.isPresent()) {
var items = jsonObject.get().get("items").getAsJsonArray();
var skus = new ArrayList<String>();
for (JsonElement item : items) {
String sku = item.getAsJsonObject().get("sku").getAsString();
if (!sku.contains("/")) {
skus.add(sku);
}
}
var lastSku = skus.get(skus.size() - 1);
if (!previousLastsku.equals(lastSku)) {
previousLastsku = lastSku;
for (String sku : skus) {
Optional<Product> product = processSku(sku);
product.ifPresent(productsToUpdate::add);
}
page = page + 1;
} else {
process = false;
}
} else {
process = false;
}
}
updateProducts(productsToUpdate);
client.dispatcher().cancelAll();
client.connectionPool().evictAll();
}

private void updateProducts(List<Product> updatableProducts) {
if (!updatableProducts.isEmpty()) {
for (var product : updatableProducts) {
var request = creator.updateProductRequest(product);
var response = readResponse(request);
if (response.isPresent()) {
if (product.getAmazonQuantity() == STANDARD_AMAZON_QTY) {
LOGGER.info("Successfully updated sold out product {}, amazonQuantity:{}, amazonDelivery:{}",
product.getSku(), product.getAmazonQuantity(), product.getAmazonDelivery());
} else {
LOGGER.info("Successfully updated restocked product {}, amazonQuantity:{}, amazonDelivery:{}",
product.getSku(), product.getAmazonQuantity(), product.getAmazonDelivery());
}
} else {
LOGGER.error("could not process request - pleaze check: {}", request);
}
}
LOGGER.debug("Upadted {} products successfully", updatableProducts.size());
}
}

private Optional<Product> processSku(String sku) {
var jsonObject = readResponse(creator.getProduktDataForSku(sku));
if (jsonObject.isPresent()) {
var product = Product.builder()
.setSku(sku)
.setJson(jsonObject.get())
.build();
if (product.isUpdatable()) {
return Optional.of(product);
}
}
return Optional.empty();
}

private Optional<JsonObject> readResponse(Request request) {
Optional<JsonObject> jsonObject = Optional.empty();
String body = null;
try {
var response = client.newCall(request).execute();
if (response.isSuccessful()) {
body = response.body().string();
jsonObject = Optional.of(JsonParser.parseString(body).getAsJsonObject());
} else {
LOGGER.error("Bienenkorb responded with responseCode {} for request {}", response.code(),
request.toString());
}
} catch (IOException e) {
LOGGER.error("Unparsable answer from Bienenkorb: {}", body, e);
} catch (NullPointerException e) {
LOGGER.error("Empty answer from Bienenkorb: {}", e.getMessage(), e);
} catch (Exception e) {
LOGGER.error("Error returned during call to Bienenkorb: {}", e.getMessage(), e);
}

return Optional.of(jsonObject).orElse(Optional.empty());
}
}
Loading

0 comments on commit 5a47008

Please sign in to comment.