Browse Source

Code now expects refresh tokens and requests its own access tokens (not tested)

master
Bianca Steffes 3 years ago
parent
commit
524139dac8
  1. 1
      export/.gitignore
  2. 199
      export/src/export/Main.java

1
export/.gitignore

@ -1,2 +1,3 @@
/bin/ /bin/
/target/ /target/
/data.zip

199
export/src/export/Main.java

@ -11,10 +11,12 @@ import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -30,6 +32,7 @@ import org.json.simple.parser.ParseException;
public class Main public class Main
{ {
public static final String baseUrl = "https://www.strava.com/api/v3/"; public static final String baseUrl = "https://www.strava.com/api/v3/";
public static final String tokenUrl = "https://www.strava.com/api/v3/oauth/token";
private static int athleteId = 0; private static int athleteId = 0;
private static JSONParser parser = new JSONParser(); private static JSONParser parser = new JSONParser();
private static String testRequest; private static String testRequest;
@ -54,6 +57,7 @@ public class Main
/** /**
* For testing: ensures the test requests are used * For testing: ensures the test requests are used
*
* @param testrequest Request to be used * @param testrequest Request to be used
*/ */
static void setTest(String testrequest) static void setTest(String testrequest)
@ -63,6 +67,7 @@ public class Main
/** /**
* Saves the data of one athlete into a temp file and returns it. * Saves the data of one athlete into a temp file and returns it.
*
* @param token Identifier / authorization token of the athlete * @param token Identifier / authorization token of the athlete
* @return created temp file * @return created temp file
*/ */
@ -105,6 +110,7 @@ public class Main
/** /**
* Adds the streams to the given activity * Adds the streams to the given activity
*
* @param id Strava id of the activity * @param id Strava id of the activity
* @param data general information of the activity * @param data general information of the activity
* @param token Identifier / authorization token of the athlete * @param token Identifier / authorization token of the athlete
@ -116,7 +122,7 @@ public class Main
// TODO: Array representation is not tested // TODO: Array representation is not tested
String requestUrlExtension = "activities/" + id + "/streams?keys=[time, distance, latlng, altitude, " String requestUrlExtension = "activities/" + id + "/streams?keys=[time, distance, latlng, altitude, "
+ "velocity_smooth, heartrate, cadence, watts, temp, moving, grade_smooth]&key_by_type="; + "velocity_smooth, heartrate, cadence, watts, temp, moving, grade_smooth]&key_by_type=";
String json = makeOneRequest(requestUrlExtension, token);
String json = makeOneGetRequest(requestUrlExtension, token);
String type = "type"; String type = "type";
Object obj; Object obj;
@ -138,20 +144,24 @@ public class Main
} }
/** /**
* Gathers all activities of a user, extracts the general information and the ids.
* Gathers all activities of a user, extracts the general information and the
* ids.
*
* @param token Identifier / authorization token of the athlete * @param token Identifier / authorization token of the athlete
* @return A Map with key = Strava Id of an activity and value = JSONObject with the general information of the activity
* @return A Map with key = Strava Id of an activity and value = JSONObject with
* the general information of the activity
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static Map<String, JSONObject> getActivities(String token) static Map<String, JSONObject> getActivities(String token)
{ {
Map<String, JSONObject> result = new HashMap<>(); Map<String, JSONObject> result = new HashMap<>();
/* /*
* Possible values = AlpineSki, BackcountrySki, Canoeing, Crossfit, EBikeRide, Elliptical, Golf,
* Handcycle, Hike, IceSkate, InlineSkate, Kayaking, Kitesurf, NordicSki, Ride, RockClimbing,
* RollerSki, Rowing, Run, Sail, Skateboard, Snowboard, Snowshoe, Soccer, StairStepper,
* StandUpPaddling, Surfing, Swim, Velomobile, VirtualRide, VirtualRun, Walk, WeightTraining,
* Wheelchair, Windsurf, Workout, Yoga
* Possible values = AlpineSki, BackcountrySki, Canoeing, Crossfit, EBikeRide,
* Elliptical, Golf, Handcycle, Hike, IceSkate, InlineSkate, Kayaking, Kitesurf,
* NordicSki, Ride, RockClimbing, RollerSki, Rowing, Run, Sail, Skateboard,
* Snowboard, Snowshoe, Soccer, StairStepper, StandUpPaddling, Surfing, Swim,
* Velomobile, VirtualRide, VirtualRun, Walk, WeightTraining, Wheelchair,
* Windsurf, Workout, Yoga
*/ */
String type = "type"; String type = "type";
String timezone = "timezone"; String timezone = "timezone";
@ -162,7 +172,7 @@ public class Main
while (true) while (true)
{ {
String requestExtension = "athlete/activities?before=&after=&page=" + pageIndex + "&per_page="; String requestExtension = "athlete/activities?before=&after=&page=" + pageIndex + "&per_page=";
String json = makeOneRequest(requestExtension, token);
String json = makeOneGetRequest(requestExtension, token);
if (json.isEmpty() || json.isBlank() || json.equals("")) // don't know where the last page is... if (json.isEmpty() || json.isBlank() || json.equals("")) // don't know where the last page is...
{ {
break; break;
@ -201,6 +211,7 @@ public class Main
/** /**
* Extracts an athletes general information. * Extracts an athletes general information.
*
* @param token Identifier / authorization token of the athlete * @param token Identifier / authorization token of the athlete
* @return extracted data or null if there was an error * @return extracted data or null if there was an error
*/ */
@ -213,7 +224,7 @@ public class Main
String meas_pref = "measurement_preference"; // Possible values = feet, meters String meas_pref = "measurement_preference"; // Possible values = feet, meters
String weight = "weight"; String weight = "weight";
String json = makeOneRequest("athlete", token);
String json = makeOneGetRequest("athlete", token);
JSONObject toSave = new JSONObject(); JSONObject toSave = new JSONObject();
try try
@ -238,6 +249,7 @@ public class Main
/** /**
* Zip a list of files in one .zip file. * Zip a list of files in one .zip file.
*
* @param files HasMap of <intended Filename, File> which should be zipped * @param files HasMap of <intended Filename, File> which should be zipped
* @throws IOException If there was an error zipping * @throws IOException If there was an error zipping
*/ */
@ -254,7 +266,8 @@ public class Main
byte[] bytes = new byte[1024]; byte[] bytes = new byte[1024];
int length; int length;
while((length = fis.read(bytes)) >= 0) {
while ((length = fis.read(bytes)) >= 0)
{
zipOut.write(bytes, 0, length); zipOut.write(bytes, 0, length);
} }
fis.close(); fis.close();
@ -264,12 +277,14 @@ public class Main
} }
/** /**
* Handles one request to the API
* Handles one GET request to the API
*
* @param requestUrlExtension Extension for the baseUrl (without '/') * @param requestUrlExtension Extension for the baseUrl (without '/')
* @param token Identification / authorization token of the athlete
* @param token Identification / authorization token of the
* athlete
* @return The response as a String, an empty String in case of error. * @return The response as a String, an empty String in case of error.
*/ */
static String makeOneRequest(String requestUrlExtension, String token)
static String makeOneGetRequest(String requestUrlExtension, String token)
{ {
// TODO: http requests are not tested // TODO: http requests are not tested
if (testRequest != null) if (testRequest != null)
@ -278,23 +293,41 @@ public class Main
testRequest = null; testRequest = null;
return varTestRequest; return varTestRequest;
} }
HttpURLConnection connection = null;
StringBuilder result = new StringBuilder();
HttpsURLConnection connection = null;
try try
{ {
// Create connection // Create connection
URL url = new URL(baseUrl + requestUrlExtension); URL url = new URL(baseUrl + requestUrlExtension);
connection = (HttpsURLConnection) url.openConnection(); connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("GET"); connection.setRequestMethod("GET");
connection.setRequestProperty("Authorization", "Bearer [["+token+"]]");
connection.setRequestProperty("Authorization", "Bearer " + token);
int responseCode = connection.getResponseCode();
return getResponse(connection);
}
catch (IOException e)
{
writeError("Error while handling GET request: " + e.toString());
}
return "";
}
/**
* Reads the response from the connection and returns it as a String
*
* @param connection Connection to the site
* @return Response as a String
* @throws IOException in case of error
*/
static String getResponse(HttpsURLConnection connection) throws IOException
{
StringBuilder result = new StringBuilder();
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) if (responseCode != HttpURLConnection.HTTP_OK)
{ {
writeError("Wrong response code: " + responseCode); writeError("Wrong response code: " + responseCode);
return ""; return "";
} }
try (Reader reader = new BufferedReader( try (Reader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), Charset.forName(StandardCharsets.UTF_8.name())))) new InputStreamReader(connection.getInputStream(), Charset.forName(StandardCharsets.UTF_8.name()))))
{ {
@ -305,26 +338,106 @@ public class Main
c = reader.read(); c = reader.read();
} }
} }
// Numbers too long for int or long are turned into Strings
return result.toString().replaceAll("\\:\\s*([0-9]{15,})\\,", ":\"$1\",");
}
/**
* Used for generating the accessToken
*
* @param data Triplet containing: a the client_id, b the client_secret, c the
* refresh_token
* @return The response as a String, an empty String in case of error.
*/
static String makeOnePostRequest(Triplet data)
{
// TODO: http requests are not tested
HttpsURLConnection connection = null;
try
{
// Create connection
URL url = new URL(tokenUrl);
connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
Map<String, String> params = new LinkedHashMap<>();
params.put("client_id", data.getA());
params.put("client_secret", data.getB());
params.put("grant_type", "refresh_token");
params.put("refresh_token", data.getC());
StringBuilder postData = new StringBuilder();
for (String key : params.keySet())
{
if (postData.length() != 0)
{
postData.append('&');
}
postData.append(URLEncoder.encode(key, "UTF-8"));
postData.append('=');
postData.append(URLEncoder.encode(params.get(key), "UTF-8"));
}
byte[] postDataBytes = postData.toString().getBytes("UTF-8");
connection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
connection.setDoOutput(true);
connection.getOutputStream().write(postDataBytes);
return getResponse(connection);
} }
catch (IOException e) catch (IOException e)
{ {
writeError("Error while handling request: "+e.toString());
writeError("Error while handling POST request: " + e.toString());
}
return ""; return "";
} }
//Numbers too long for int or long are turned into Strings
return result.toString().replaceAll("\\:\\s*([0-9]{15,})\\,", ":\"$1\",");
/**
* Sends the old refresh token to strava and retrieves a new one and an access token
* @param refreshInfo Refresh data with a the cliend_id, b the client_secret and c the refresh_token
* @return A tuple with a the access_token and b the new refresh_token or {@code null} if there was an error
*/
static Tuple getAccessToken(Triplet refreshInfo)
{
String json = makeOnePostRequest(refreshInfo);
try
{
Object obj = parser.parse(json);
JSONObject data = (JSONObject) obj;
return new Tuple(data.get("access_token").toString(), data.get("refresh_token").toString());
}
catch (ParseException e)
{
writeError("Athlete " + athleteId + ": Error parsing general information.");
}
return null;
} }
public static void main(String[] args) public static void main(String[] args)
{ {
// TODO: tokens need to be added by Enduco // TODO: tokens need to be added by Enduco
List<String> tokens = new ArrayList<>();
// Triplet a is client_id, b is client_secret, c is refresh_token
List<Triplet> refreshTokens = new ArrayList<>();
// don't know if you need it but here you will find the new refresh tokens after
// the requests
List<Triplet> newRefreshTokens = new ArrayList<>();
// TODO: Should we expect problems with memory?? // TODO: Should we expect problems with memory??
Map<String, File> allFiles = new HashMap<>(); Map<String, File> allFiles = new HashMap<>();
for (String token : tokens)
for (Triplet oneUser : refreshTokens)
{ {
File athlete = oneAthlete(token);
// a is access_token and b is new refresh_token
Tuple withAccessToken = getAccessToken(oneUser);
if (withAccessToken == null)
{
writeError("Coulnd't get new access token for client "+athleteId);
}
newRefreshTokens.add(new Triplet(oneUser.getA(), oneUser.getB(), withAccessToken.getB()));
File athlete = oneAthlete(withAccessToken.getA());
if (athlete != null) if (athlete != null)
{ {
allFiles.put("Athlete_" + athleteId + ".json", athlete); allFiles.put("Athlete_" + athleteId + ".json", athlete);
@ -341,3 +454,41 @@ public class Main
} }
} }
} }
class Tuple
{
private String a;
private String b;
public Tuple(String a, String b)
{
this.a = a;
this.b = b;
}
public String getA()
{
return a;
}
public String getB()
{
return b;
}
}
class Triplet extends Tuple
{
private String c;
public Triplet(String a, String b, String c)
{
super(a, b);
this.c = c;
}
public String getC()
{
return c;
}
}
Loading…
Cancel
Save