diff options
-rwxr-xr-x[-rw-r--r--] | README.md | 4 | ||||
-rwxr-xr-x | pom.xml | 12 | ||||
-rwxr-xr-x | src/main/java/io/trygvis/esper/testing/Uuid.java | 88 | ||||
-rwxr-xr-x | src/main/java/io/trygvis/esper/testing/web/resource/CoreResource.java | 78 | ||||
-rwxr-xr-x[-rw-r--r--] | src/main/webapp/apps/app.css | 17 | ||||
-rwxr-xr-x[-rw-r--r--] | src/main/webapp/apps/app.js | 2 | ||||
-rwxr-xr-x | src/main/webapp/apps/buildApp/build.html | 8 | ||||
-rwxr-xr-x[-rw-r--r--] | src/main/webapp/apps/buildApp/buildApp.js | 5 | ||||
-rwxr-xr-x[-rw-r--r--] | src/main/webapp/apps/core/navbar.html | 7 | ||||
-rwxr-xr-x | src/main/webapp/apps/frontPageApp/buildList.html | 50 | ||||
-rwxr-xr-x | src/main/webapp/apps/frontPageApp/frontPageApp.js | 7 | ||||
-rwxr-xr-x | src/main/webapp/apps/frontPageApp/person.html | 2 | ||||
-rwxr-xr-x | src/test/java/io/trygvis/esper/testing/UuidTest.java | 66 |
13 files changed, 264 insertions, 82 deletions
diff --git a/README.md b/README.md index 3d9acb1..51b8ad7 100644..100755 --- a/README.md +++ b/README.md @@ -38,3 +38,7 @@ N commits per day/week # BF3 Nomenclature Two types of "awards": "ribbons" and "medals". A medal is given for repeatedly awarded a ribbon. + +# TODO: + +* Convert UUIDs from char(36) to bigint (8 bytes) @@ -8,6 +8,10 @@ <maven.compiler.target>1.7</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <version.httpcache4>3.4</version.httpcache4> + <dbdeploy.url>jdbc:postgresql://localhost/esper</dbdeploy.url> + <dbdeploy.userid>esper</dbdeploy.userid> + <dbdeploy.password>esper</dbdeploy.password> + <dbdeploy.dbms>pgsql</dbdeploy.dbms> </properties> <dependencies> <dependency> @@ -172,10 +176,10 @@ <configuration> <scriptdirectory>src/main/sql/dbdelta</scriptdirectory> <driver>org.postgresql.Driver</driver> - <url>jdbc:postgresql://localhost/esper</url> - <userid>esper</userid> - <password>esper</password> - <dbms>pgsql</dbms> + <url>${dbdeploy.url}</url> + <userid>${dbdeploy.userid}</userid> + <password>${dbdeploy.password}</password> + <dbms>${dbdeploy.dbms}</dbms> <delimiter>;</delimiter> <delimiterType>row</delimiterType> <outputfile>dbdeploy-outputfile.tmp.sql</outputfile> diff --git a/src/main/java/io/trygvis/esper/testing/Uuid.java b/src/main/java/io/trygvis/esper/testing/Uuid.java index 64fd6e5..a884959 100755 --- a/src/main/java/io/trygvis/esper/testing/Uuid.java +++ b/src/main/java/io/trygvis/esper/testing/Uuid.java @@ -17,6 +17,10 @@ public class Uuid { return uuid.toString(); } + public UUID toUUID() { + return uuid; + } + public String toString() { return toStringBase64(); } @@ -89,67 +93,71 @@ public class Uuid { } if (s.length() == 22) { - long most = 0; - int i = 0; - int shift = 64; - for(; i < 10; i++) { - char c = s.charAt(i); - long b = alphabetR[c]; - - if(b == 0) { - throw new IllegalArgumentException(s); - } - - b--; + return parseBase64(s); + } - shift -= 6; + throw new IllegalArgumentException("Illegal: " + s); + } - long l = b << shift; + public static Uuid parseBase64(String s) { + long most = 0; + int i = 0; + int shift = 64; + for(; i < 10; i++) { + char c = s.charAt(i); + long b = alphabetR[c]; - most |= l; + if(b == 0) { + throw new IllegalArgumentException(s); } - long least; - - { - char c = s.charAt(i++); - long b = alphabetR[c]; + b--; - if (b == 0) { - throw new IllegalArgumentException(s); - } + shift -= 6; - b--; + long l = b << shift; - long l = b >> 2; + most |= l; + } - most |= l; + long least; - shift = 64 - 2; + { + char c = s.charAt(i++); + long b = alphabetR[c]; - least = (b & 0x03) << shift; + if (b == 0) { + throw new IllegalArgumentException(s); } - for(; i < 22; i++) { - char c = s.charAt(i); - long b = alphabetR[c]; + b--; + + long l = b >> 2; + + most |= l; - if(b == 0) { - throw new IllegalArgumentException(s); - } + shift = 64 - 2; - b--; + least = (b & 0x03) << shift; + } - shift -= 6; + for(; i < 22; i++) { + char c = s.charAt(i); + long b = alphabetR[c]; - long l = b << shift; - least |= l; + if(b == 0) { + throw new IllegalArgumentException(s); } - return new Uuid(new UUID(most, least)); + b--; + + shift -= 6; + + long l = b << Math.max(shift, 0); + least |= l; } - throw new IllegalArgumentException("Illegal: " + s); + return new Uuid(new UUID(most, least)); } // http://en.wikipedia.org/wiki/Base64 diff --git a/src/main/java/io/trygvis/esper/testing/web/resource/CoreResource.java b/src/main/java/io/trygvis/esper/testing/web/resource/CoreResource.java index 22290d9..bd61855 100755 --- a/src/main/java/io/trygvis/esper/testing/web/resource/CoreResource.java +++ b/src/main/java/io/trygvis/esper/testing/web/resource/CoreResource.java @@ -8,7 +8,6 @@ import io.trygvis.esper.testing.util.sql.*; import io.trygvis.esper.testing.web.*; import org.joda.time.*; -import javax.servlet.http.*; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.sql.*; @@ -16,7 +15,6 @@ import java.util.*; import java.util.List; import static fj.data.Option.fromNull; -import static io.trygvis.esper.testing.util.sql.PageRequest.*; @Path("/resource/core") @Produces(MediaType.APPLICATION_JSON) @@ -49,7 +47,7 @@ public class CoreResource extends AbstractResource { @GET @Path("/person/{uuid}") - public PersonDetailJson getPerson(@PathParam("uuid") final Uuid uuid) throws Exception { + public PersonDetailJson getPerson(@MagicParam final Uuid uuid) throws Exception { return sql(new CoreDaosCallback<SqlOption<PersonDetailJson>>() { protected SqlOption<PersonDetailJson> run() throws SQLException { return daos.personDao.selectPerson(uuid).map(super.getPersonDetailJson); @@ -63,20 +61,27 @@ public class CoreResource extends AbstractResource { @GET @Path("/build") - public List<BuildJson> getBuilds(@MagicParam final PageRequest page, @MagicParam(query = "person") final Uuid person) throws Exception { - return da.inTransaction(new DatabaseAccess.DaosCallback<List<BuildJson>>() { - public List<BuildJson> run(Daos daos) throws SQLException { + public List<Object> getBuilds(@MagicParam final PageRequest page, + @MagicParam(query = "person") final Uuid person, + @QueryParam("fields") final List<String> fields) throws Exception { + return da.inTransaction(new CoreDaosCallback<List<Object>>() { + public List<Object> run() throws SQLException { List<BuildDto> buildDtos; + boolean detailed = fields.contains("detailed"); + if (person != null) { buildDtos = daos.buildDao.selectBuildsByPerson(person, page); } else { buildDtos = daos.buildDao.selectBuilds(page); } - List<BuildJson> list = new ArrayList<>(); + List<Object> list = new ArrayList<>(); + + SqlF<BuildDto, ?> buildDtoSqlF = detailed ? getBuildDetailJson : getBuildJson; + for (BuildDto build : buildDtos) { - list.add(getBuildJson(build)); + list.add(buildDtoSqlF.apply(build)); } return list; } @@ -99,23 +104,14 @@ public class CoreResource extends AbstractResource { @GET @Path("/build/{uuid}") - public BuildJson getBuild(@MagicParam final UUID uuid) throws Exception { - return get(new DatabaseAccess.DaosCallback<Option<BuildJson>>() { - public Option<BuildJson> run(Daos daos) throws SQLException { - SqlOption<BuildDto> o = daos.buildDao.selectBuild(uuid); - if (o.isNone()) { - return Option.none(); - } - - return Option.some(getBuildJson(o.get())); + public BuildDetailJson getBuild(@MagicParam final UUID uuid) throws Exception { + return sql(new CoreDaosCallback<SqlOption<BuildDetailJson>>() { + public SqlOption<BuildDetailJson> run() throws SQLException { + return daos.buildDao.selectBuild(uuid).map(getBuildDetailJson); } }); } - private BuildJson getBuildJson(BuildDto build) { - return new BuildJson(build.uuid, build.timestamp, build.success); - } - // ----------------------------------------------------------------------- // Badge // ----------------------------------------------------------------------- @@ -146,6 +142,10 @@ public class CoreResource extends AbstractResource { return run(); } + // ----------------------------------------------------------------------- + // Person + // ----------------------------------------------------------------------- + protected final SqlF<PersonDto, PersonJson> getPersonJson = new SqlF<PersonDto, PersonJson>() { public PersonJson apply(PersonDto person) throws SQLException { return new PersonJson(person.uuid, person.name, person.mail); @@ -173,6 +173,32 @@ public class CoreResource extends AbstractResource { } }; + // ----------------------------------------------------------------------- + // Build + // ----------------------------------------------------------------------- + + protected final SqlF<BuildDto, BuildJson> getBuildJson = new SqlF<BuildDto, BuildJson>() { + public BuildJson apply(BuildDto dto) throws SQLException { + return new BuildJson(dto.uuid, dto.timestamp, dto.success); + } + }; + + protected final SqlF<BuildDto, BuildDetailJson> getBuildDetailJson = new SqlF<BuildDto, BuildDetailJson>() { + public BuildDetailJson apply(BuildDto build) throws SQLException { + List<PersonJson> list = new ArrayList<>(); + for (PersonDto person : daos.buildDao.selectPersonsFromBuildParticipant(build.uuid)) { + list.add(getPersonJson.apply(person)); + } + + return new BuildDetailJson(getBuildJson.apply(build), + list); + } + }; + + // ----------------------------------------------------------------------- + // Badge + // ----------------------------------------------------------------------- + protected SqlF<PersonalBadgeDto, BadgeJson> getBadgeJson = new SqlF<PersonalBadgeDto, BadgeJson>() { public BadgeJson apply(PersonalBadgeDto badge) throws SQLException { return new BadgeJson(badge.createdDate, badge.type.name(), badge.level); @@ -203,3 +229,13 @@ class BuildJson { this.success = success; } } + +class BuildDetailJson { + public final BuildJson build; + public final List<PersonJson> participants; + + BuildDetailJson(BuildJson build, List<PersonJson> participants) { + this.build = build; + this.participants = participants; + } +} diff --git a/src/main/webapp/apps/app.css b/src/main/webapp/apps/app.css index 5ffd104..3088282 100644..100755 --- a/src/main/webapp/apps/app.css +++ b/src/main/webapp/apps/app.css @@ -1,4 +1,7 @@ /* + * Badges + */ +/* bronze = #8c7853 bronze ii = #a67d3d */ @@ -7,6 +10,20 @@ .badge-level-2 { background-color: silver; color: #000000 } .badge-level-3 { background-color: #ffd700; color: #000000 } +/* + * Avatar + */ + +/* This has to match the Gravatar image */ +.avatar80 { + width: 80px; + height: 80px; +} + +/* + * + */ + #content { background-color: #ffffff; padding-bottom: 60px; diff --git a/src/main/webapp/apps/app.js b/src/main/webapp/apps/app.js index 36736cd..8c683b6 100644..100755 --- a/src/main/webapp/apps/app.js +++ b/src/main/webapp/apps/app.js @@ -91,6 +91,6 @@ directives.directive('personAvatar', function () { scope: { person: '=person' }, - template: '<img ng-src="{{person.gravatar}}?default=identicon" class="avatar-image" title="{{person.name}}"/>' + template: '<img ng-src="{{person.gravatar}}?default=identicon" class="avatar-image avatar80" title="{{person.name}}"/>' } }); diff --git a/src/main/webapp/apps/buildApp/build.html b/src/main/webapp/apps/buildApp/build.html index 2feee28..fec38d2 100755 --- a/src/main/webapp/apps/buildApp/build.html +++ b/src/main/webapp/apps/buildApp/build.html @@ -9,19 +9,19 @@ <table> <tr> <th>Date</th> - <td>{{build.timestamp | date:'medium'}}</td> + <td>{{build.build.timestamp | date:'medium'}}</td> </tr> <tr> <th>Status</th> <td> - <span ng-show="build.success">SUCCESS</span> - <span ng-hide="build.success">FAILURE</span> + <span ng-show="build.build.success">SUCCESS</span> + <span ng-hide="build.build.success">FAILURE</span> </td> </tr> </table> <h3>Participants</h3> - <p ng-repeat="participant in participants"> + <p ng-repeat="participant in build.participants"> <span>{{participant.name}}</span> </p> diff --git a/src/main/webapp/apps/buildApp/buildApp.js b/src/main/webapp/apps/buildApp/buildApp.js index 187b240..170af16 100644..100755 --- a/src/main/webapp/apps/buildApp/buildApp.js +++ b/src/main/webapp/apps/buildApp/buildApp.js @@ -7,9 +7,6 @@ var buildApp = angular.module('buildApp', ['build', 'buildParticipant']).config( function BuildCtrl($scope, Build, BuildParticipant) { Build.get({uuid: uuid}, function(build) { - window.build = $scope.build = build; - }); - BuildParticipant.query({uuid: uuid}, function(persons) { - $scope.participants = persons; + $scope.build = build; }); } diff --git a/src/main/webapp/apps/core/navbar.html b/src/main/webapp/apps/core/navbar.html index 871bfde..914258e 100644..100755 --- a/src/main/webapp/apps/core/navbar.html +++ b/src/main/webapp/apps/core/navbar.html @@ -4,9 +4,10 @@ <span class="brand"><a href="/">Wat</a></span> <div class="nav-collapse collapse"> <ul class="nav"> - <li class=""><a href="/#/">Home</a></li> - <li class=""><a href="/#/badge/">Badges</a></li> - <li class=""><a href="/#/person/">People</a></li> + <li class=""><a href="#/">Home</a></li> + <li class=""><a href="#/badge/">Badges</a></li> + <li class=""><a href="#/person/">People</a></li> + <li class=""><a href="#/build/">Builds</a></li> <li class="divider-vertical"></li> <li class=""><a href="/jenkins">Jenkins</a></li> </ul> diff --git a/src/main/webapp/apps/frontPageApp/buildList.html b/src/main/webapp/apps/frontPageApp/buildList.html new file mode 100755 index 0000000..13a1dc3 --- /dev/null +++ b/src/main/webapp/apps/frontPageApp/buildList.html @@ -0,0 +1,50 @@ +<div class="container"> + + <navbar/> + + <div class="page-header"> + <h1>Builds</h1> + </div> + + <style> + .avatar-image { + /* This has to match bootstrap's row margin. */ + margin-left: 30px; + padding-right: 1em; + float: left; + margin-top: 0; + } + </style> + + <div class="row"> + <div class="span12"> + <table class="table"> + <tbody ng-repeat="build in builds.rows"> + <tr class="{{{true: 'success', false: 'error'}[build.build.success]}}"> + <td>{{build.build.timestamp | date:'medium'}}</td> + <td> + <span ng-show="build.build.success">SUCCESS</span> + <span ng-hide="build.build.success">FAILURE</span> + </td> + <td><a class="btn" href="/build/{{build.build.uuid}}"><i class="icon-chevron-right"></i></a></td> + </tr> + <tr> + <td colspan="3"> + <span ng-repeat="p in build.participants"> + <person-avatar person="p"/> + </span> + </td> + </tr> + </tbody> + </table> + <ul class="pager"> + <li class="previous" ng-show="builds.startIndex > 0"> + <a ng-click="builds.prev()">← Prev</a> + </li> + <li class="next"> + <a ng-click="builds.next()">Next →</a> + </li> + </ul> + </div> + </div> +</div> diff --git a/src/main/webapp/apps/frontPageApp/frontPageApp.js b/src/main/webapp/apps/frontPageApp/frontPageApp.js index 5f1cf2c..a67e2dc 100755 --- a/src/main/webapp/apps/frontPageApp/frontPageApp.js +++ b/src/main/webapp/apps/frontPageApp/frontPageApp.js @@ -5,7 +5,8 @@ var frontPageApp = angular.module('frontPageApp', ['ngGrid', 'person', 'badge', when('/', {controller: FrontPageCtrl, templateUrl: '/apps/frontPageApp/frontPage.html?noCache=' + noCache}). when('/badge/', {controller: BadgeListCtrl, templateUrl: '/apps/frontPageApp/badgeList.html?noCache=' + noCache}). when('/person/', {controller: PersonListCtrl, templateUrl: '/apps/frontPageApp/personList.html?noCache=' + noCache}). - when('/person/:personUuid', {controller: PersonCtrl, templateUrl: '/apps/frontPageApp/person.html?noCache=' + noCache}); + when('/person/:personUuid', {controller: PersonCtrl, templateUrl: '/apps/frontPageApp/person.html?noCache=' + noCache}). + when('/build/', {controller: BuildListCtrl, templateUrl: '/apps/frontPageApp/buildList.html?noCache=' + noCache}); }); function FrontPageCtrl($scope, Person, Badge) { @@ -95,3 +96,7 @@ function PersonCtrl($scope, $routeParams, Person, Build, PagingTableService) { $scope.recentBuilds = builds; }); } + +function BuildListCtrl($scope, Build, PagingTableService) { + $scope.builds = PagingTableService.create($scope, PagingTableService.defaultCallback(Build, {fields: "detailed"})); +} diff --git a/src/main/webapp/apps/frontPageApp/person.html b/src/main/webapp/apps/frontPageApp/person.html index 0ca5679..ba5c18d 100755 --- a/src/main/webapp/apps/frontPageApp/person.html +++ b/src/main/webapp/apps/frontPageApp/person.html @@ -76,7 +76,7 @@ </tr> </thead> <tbody> - <tr ng-repeat="build in builds.rows" class="{{{true: 'success', false: 'error'}[build.success]}}"> + <tr ng-repeat="build in builds.rows" class="{{{true: 'build-success', false: 'build-error'}[build.success]}}"> <td>{{build.timestamp | date:'medium'}}</td> <td>{{build.success}}</td> </tr> diff --git a/src/test/java/io/trygvis/esper/testing/UuidTest.java b/src/test/java/io/trygvis/esper/testing/UuidTest.java index 1efec73..8d36177 100755 --- a/src/test/java/io/trygvis/esper/testing/UuidTest.java +++ b/src/test/java/io/trygvis/esper/testing/UuidTest.java @@ -10,6 +10,8 @@ import static org.junit.Assert.*; public class UuidTest { + boolean silent = System.getProperty("idea.launcher") != null; + @Test public void testToString() { String s = "fedcba98-7654-3210-fedc-ba9876543210"; @@ -34,15 +36,55 @@ public class UuidTest { } @Test + public void test2_() { + UUID uuid = UUID.fromString("01234567-89ab-cdef-0123-456789abcdef"); + + String base64 = new Uuid(uuid).toStringBase64(); + + assertEquals(toBase64(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()), base64); + + assertEquals(uuid, Uuid.parseBase64(base64).toUUID()); + } + + @Test + public void test3() { + Uuid uuid = Uuid.fromString("3f4fc3bc-967a-40a7-9fc2-46398d35fa25"); + + UUID x = UUID.fromString(uuid.toUuidString()); + String s = toBase64(x.getMostSignificantBits(), x.getLeastSignificantBits()); + System.out.println(s); + + System.out.println("uuid.toUuidString() = " + uuid.toUuidString()); + System.out.println("uuid.toStringBase64() = " + uuid.toStringBase64()); + + assertEquals(uuid, Uuid.fromString(uuid.toUuidString())); + + assertEquals(uuid.toUuidString(), Uuid.fromString(uuid.toStringBase64()).toUuidString()); + } + + @Test + public void test4() { + Uuid uuid = Uuid.fromString("c541ff9e-e0a6-4eda-8eea-3f19e5ef893a"); + + System.out.println(System.getProperties()); + + assertEquals(uuid, Uuid.fromString(uuid.toUuidString())); + + assertEquals(uuid.toUuidString(), Uuid.fromString(uuid.toStringBase64()).toUuidString()); + } + + @Test public void random() { Random random = new Random(0); for(int i = 0; i < 100; i++) { long most = random.nextLong(); long least = random.nextLong(); - System.out.println(format("i=%2d, most=%08x, least=%08x", i, most, least)); + if(!silent) + System.out.println(format("i=%2d, most=%08x, least=%08x", i, most, least)); UUID s = new UUID(most, least); + System.out.println("s = " + s); Uuid uuid = Uuid.fromString(s.toString()); assertEquals(s.toString(), uuid.toUuidString()); @@ -52,16 +94,34 @@ public class UuidTest { private String toBase64(long most, long least) { String x = StringUtils.leftPad(Long.toBinaryString(most), 64, '0') + StringUtils.leftPad(Long.toBinaryString(least), 64, '0'); - System.out.println(x); + if(!silent) + System.out.println(x); String r = ""; for (int i = 0; i < 22; i++) { int end = Math.min((i + 1) * 6, x.length()); String y = x.substring(i * 6, end); int number = Integer.parseInt(y, 2); - System.out.println(format("% 4d % 4d binary=%8s, dec=%2d %c", i, end, y, number, Uuid.alphabet[number])); + if(!silent) + System.out.println(format("% 4d % 4d binary=%8s, dec=%2d %c", i, end, y, number, Uuid.alphabet[number])); r += Uuid.alphabet[number]; } return r; } + + /* + private UUID fromBase64(String s) { + assertEquals(22, s.length()); + + int shift = 60; + long most = 0, least = 0; + + for (int i = 0; i < 22; i += 6) { + String x = s.substring(i, Math.min(s.length(), i + 6)); + System.out.println("x = " + x); + Integer.parseInt(x); + + } + } + */ } |