parkchu / atdd-with-kotlin Goto Github PK
View Code? Open in Web Editor NEW아들과 함께 ATDD를 경험하기 위한 프로젝트
아들과 함께 ATDD를 경험하기 위한 프로젝트
POST /login/token
요청 시 JWT 토큰 응답 받기Feature: Basic Auth 로그인
Scenario: Basic Auth를 통한 로그인 시도
Given 회원 등록되어 있음
When 로그인 요청
Then 로그인 됨
POST /login/token HTTP/1.1
authorization: Basic ZW1haWxAZW1haWwuY29tOnBhc3N3b3Jk
accept: application/json
content-length: 0
host: localhost:57634
connection: Keep-Alive
user-agent: Apache-HttpClient/4.5.12 (Java/1.8.0_252)
accept-encoding: gzip,deflate
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 245
Date: Tue, 14 Jul 2020 09:28:12 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJpZFwiOjEsXCJlbWFpbFwiOlwiZW1haWxAZW1haWwuY29tXCIsXCJwYXNzd29yZFwiOlwicGFzc3dvcmRcIixcImFnZVwiOjIwfSIsImlhdCI6MTU5NDcxODg5MywiZXhwIjoxNTk0NzIyNDkzfQ.SMyb9RNrs5Uy5eqVZ0jZw3SEgWFsZaifnlslI-cEQ-c"
}
- HttpServletRequest에서 로그인 정보를 추출 하여 검증 객체(AuthenticationToken)를 생성
- AuthenticationToken을 통해 인증을 시도하여 성공하면 인증 객체(Authentication)를 생성
- Authentication가 정상적으로 생성되면 세션에 저장
MockHttpServletRequest request = new MockHttpServletRequest();
byte[] targetBytes = (EMAIL + REGEX + PASSWORD).getBytes();
byte[] encodedBytes = Base64.getEncoder().encode(targetBytes);
String credentials = new String(encodedBytes);
request.addHeader("Authorization", "Basic " + credentials);
MockHttpServletResponse response = new MockHttpServletResponse();
interceptor.preHandle(request, response, new Object());
JwtTokenProvider jwtTokenProvider = mock(JwtTokenProvider.class);
when(jwtTokenProvider.createToken(anyString())).thenReturn("jwtToken");
Authorization: <type> <credentials>
// basic auth
String credentials = AuthorizationExtractor.extract(request, AuthorizationType.BASIC);
// bearer auth
String credentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER);
String payload = new ObjectMapper().writeValueAsString(authentication.getPrincipal());
String token = jwtTokenProvider.createToken(payload);
TokenResponse tokenResponse = new TokenResponse(token);
jwtTokenProvider.validateToken(credentials)
String payload = jwtTokenProvider.getPayload(credentials);
인수 조건
이 제공됩니다.기능 설명
과 페이지
를 참고해서 진행해주세요.Feature: 지하철 노선에서 역 제외 기능
Background:
Given 지하철역이 등록되어 있음
And 지하철 노선이 등록되어 있음
And 지하철 노선에 지하철역 등록되어 있음
Scenario: 지하철 노선에 등록된 마지막 지하철역을 제외한다.
When 지하철 노선의 마지막에 지하철역 제외 요청
Then 지하철 노선에 지하철역 제외됨
When 지하철 노선 상세정보 조회 요청
Then 지하철 노선에 지하철역 제외 확인됨
And 지하철 노선에 지하철역 순서 정렬됨
Scenario: 지하철 노선에 등록된 중간 지하철역을 제외한다.
When 지하철 노선의 중간 지하철역 제외 요청
Then 지하철 노선에 지하철역 제외됨
When 지하철 노선 상세정보 조회 요청
Then 지하철 노선에 지하철역 제외 확인됨
And 지하철 노선에 지하철역 순서 정렬됨
Scenario: 지하철 노선에서 등록되지 않는 역을 제외한다.
When 지하철 노선에 등록되지 않은 역 제외 요청
Then 지하철 노선에 지하철역 제외 실패됨
Feature: 내 정보 조회 기능
Scenario: 로그인을 통해 내 정보 조회
Given 회원 등록되어 있음
And 로그인되어있음
When 내 회원 정보 조회 요청
Then 회원 정보 조회됨
- HttpServletRequest에서 JWT 토큰을 추출 후 유효성 검사
- JWT 토큰에서 payload를 추출하여 SecurityContext 추출
- SecurityContext이 정상적으로 존재하면 SecurityContextHolder에 저장
jwtTokenProvider.validateToken(credentials)
String payload = jwtTokenProvider.getPayload(credentials);
Feature: 즐겨찾기를 관리한다.
Background
Given 지하철역 등록되어 있음
And 지하철 노선 등록되어 있음
And 지하철 노선에 지하철역 등록되어 있음
And 회원 등록되어 있음
And 로그인 되어있음
Scenario: 즐겨찾기를 관리
When 즐겨찾기 생성을 요청
Then 즐겨찾기 생성됨
When 즐겨찾기 목록 조회 요청
Then 즐겨찾기 목록 조회됨
When 즐겨찾기 삭제 요청
Then 즐겨찾기 삭제됨
프론트엔드 코드는 모두 구현이 되어 있습니다. API만 구현해주세요 :)
인수 테스트가 제공
됩니다.기능 제약조건
을 두었습니다.기능 설명
과 페이지
를 참고해서 진행해주세요.Feature: 지하철 노선에 역 등록 기능
Background:
Given 지하철역이 등록되어 있음
And 지하철 노선이 등록되어 있음
Scenario: 지하철 노선에 역을 등록한다.
When 지하철 노선의 마지막에 지하철역 등록 요청
Then 지하철 노선에 지하철역 등록됨
Scenario: 지하철 노선 상세정보 조회 시 역 정보가 포함된다.
Given 지하철 노선에 지하철역 등록되어 있음
When 지하철 노선 상세정보 조회 요청
Then 지하철 노선 상세정보 응답됨
Scenario: 지하철 노선에 역을 마지막에 등록한다.
Given 지하철 노선에 지하철역 등록되어 있음
When 지하철 노선의 마지막에 지하철역 등록 요청
Then 지하철 노선에 지하철역 등록됨
When 지하철 노선 상세정보 조회 요청
Then 지하철 노선 상세정보 응답됨
And 등록된 지하철역이 마지막에 위치됨
Scenario: 지하철 노선에 역을 중간에 등록한다.
Given 지하철 노선에 지하철역 등록되어 있음
When 지하철 노선의 중간에 지하철역 등록 요청
Then 지하철 노선에 지하철역 등록됨
When 지하철 노선 상세정보 조회 요청
Then 지하철 노선 상세정보 응답됨
And 등록된 지하철역이 중간에 위치됨
출발역은 하나
만 존재하고 단방향
으로 관리함
@ManyToMany
로 관계를 맺을 수 있음@ManyToOne
관계로 설정public interface AuthenticationConverter {
AuthenticationToken convert(HttpServletRequest request);
}
public abstract void afterAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException;
public abstract class SecurityContextInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
SecurityContextHolder.clearContext();
}
}
개발을 하면서 pr이 merge될 때마다 수동으로 배포하는 것은 여간 번거로운 작업이 아니다.
즉, 매번 터미널로 서버에 접속해 deploy.sh 파일을 배포해야 한다.
이런 단점을 보완하기 위해 CI 도구를 활용해 배포를 자동화한다.
"스프링 부트와 AWS로 혼자 구현하는 웹 서비스" 책의 9장을 보면 "코드가 푸시되면 자동으로 배포해 보자 - Travis CI 배포 자동화"가 있다.
이 내용을 참고해 배포를 자동화한다.
Feature: 지하철 경로 검색
Scenario: 두 역의 최단 거리 경로를 조회
Given 지하철역이 등록되어있음
And 지하철 노선이 등록되어있음
And 지하철 노선에 지하철역이 등록되어있음
When 출발역에서 도착역까지의 최단 거리 경로 조회를 요청
Then 최단 거리 경로를 응답
And 총 거리와 소요 시간을 함께 응답함
And 지하철 이용 요금도 함께 응답함
지하철 운임은 거리비례제로 책정됩니다. (실제 경로가 아닌 최단거리 기준)
private int calculateOverFare(int distance) {
return (int) ((Math.ceil((distance - 1) / 5) + 1) * 100);
}
Feature: 지하철 노선 관련 기능
Scenario: 지하철 노선을 생성한다.
When 지하철 노선을 생성 요청한다.
Then 지하철 노선이 생성된다.
Scenario: 기존에 존재하는 지하철 노선 이름으로 지하철 노선을 생성한다.
Given 지하철 노선이 등록되어 있다
When 지하철 노선을 생성 요청한다.
Then 지하철 노선 생성이 실패된다.
Scenario: 지하철 노선 목록을 조회한다.
Given 지하철 노선이 등록되어 있다
When 지하철 노선 목록을 조회 요청한다.
Then 지하철 노선 목록이 응답된다.
And 응답에 등록된 목록이 포함된다.
Scenario: 지하철 노선을 조회한다.
Given 지하철 노선이 등록되어 있다
When 지하철 노선을 조회 요청한다.
Then 지하철 노선이 응답된다.
Scenario: 지하철 노선을 수정한다.
Given 지하철 노선이 등록되어 있다
When 지하철 노선을 수정 요청한다.
Then 지하철 노선이 수정된다.
Scenario: 지하철 노선을 제거한다.
Given 지하철 노선이 등록되어 있다
When 지하철 노선을 삭제 요청한다.
Then 지하철 노선이 삭제된다.
자세한 사용법은 Usage Guide를 참고
Feature: 회원 정보를 관리한다.
Scenario: 회원 정보를 관리
When 회원 생성을 요청
Then 회원 생성됨
When 회원 정보 조회 요청
Then 회원 정보 조회됨
When 회원 정보 수정 요청
Then 회원 정보 수정됨
When 회원 삭제 요청
Then 회원 삭제됨
GET /members/me
요청을 처리하는 컨트롤러 메서드를 완성하기public AuthenticationToken convert(HttpServletRequest request) {
Map<String, String[]> paramMap = request.getParameterMap();
String principal = paramMap.get(USERNAME_FIELD)[0];
String credentials = paramMap.get(PASSWORD_FIELD)[0];
return new AuthenticationToken(principal, credentials);
}
public Authentication authenticate(AuthenticationToken token) {
String principal = token.getPrincipal();
LoginMember userDetails = userDetailsService.loadUserByUsername(principal);
checkAuthentication(userDetails, token);
return new Authentication(userDetails);
}
HttpSession httpSession = request.getSession();
httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, new SecurityContext(authentication));
response.setStatus(HttpServletResponse.SC_OK);
public class SecurityContextHolder {
public static final String SPRING_SECURITY_CONTEXT_KEY = "SECURITY_CONTEXT";
private static final ThreadLocal<SecurityContext> contextHolder;
static {
contextHolder = new ThreadLocal<>();
}
...
}
public static void clearContext() {
contextHolder.remove();
}
public static SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
public static void setContext(SecurityContext context) {
if (context != null) {
contextHolder.set(context);
}
}
public ResponseEntity<MemberResponse> findMemberOfMine(HttpServletRequest request) {
SecurityContext context = (SecurityContext) request.getSession().getAttribute(SPRING_SECURITY_CONTEXT_KEY);
LoginMember loginMember = (LoginMember) context.getAuthentication().getPrincipal();
...
}
public class SecurityContext {
private Authentication authentication;
...
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
SecurityContext securityContext = (SecurityContext) request.getSession().getAttribute(SPRING_SECURITY_CONTEXT_KEY);
if (securityContext != null) {
SecurityContextHolder.setContext(securityContext);
}
return true;
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
SecurityContextHolder.clearContext();
}
- 청소년: 13세 이상~19세 미만
- 어린이: 6세 이상~ 13세 미만
최소 시간 경로 기능
을 추가하세요.Feature: 지하철 경로 검색
Scenario: 두 역의 최소 시간 경로를 조회
Given 지하철역이 등록되어있음
And 지하철 노선이 등록되어있음
And 지하철 노선에 지하철역이 등록되어있음
When 출발역에서 도착역까지의 최소 시간 경로 조회를 요청
Then 최소 시간 경로를 응답
And 총 거리와 소요 시간을 함께 응답함
프론트엔드 코드는 모두 구현이 되어 있습니다. API만 구현해주세요 :)
Feature: 지하철 노선에 역 등록 기능
Background:
Given 지하철역이 등록되어 있음
And 지하철 노선이 등록되어 있음
Scenario: 이미 등록되어 있던 역을 등록한다.
Given 지하철 노선에 지하철역이 등록되어 있음
When 지하철 노선에 이미 등록되어있는 지하철역 등록 요청
Then 지하철 노선에 지하철역 등록 실패됨
Scenario: 존재하지 않는 역을 등록한다.
Given 지하철 노선에 지하철역이 등록되어 있음
When 지하철 노선에 존재하지 않는 지하철역 등록 요청
Then 지하철 노선에 지하철역 등록 실패됨
@BeforeEach
활용하여 중복 제거하기Feature: 지하철 노선도 조회 기능
Scenario: 지하철 노선도를 조회한다.
Given 지하철역이 등록되어 있음
And 지하철 노선이 등록되어 있음
And 지하철 노선에 지하철역 등록되어 있음
When 지하철 노선도 조회 요청
Then 지하철 노선도 응답됨
And 지하철 노선도에 노선별 지하철역 순서 정렬됨
GET /maps HTTP/1.1
accept: application/json
host: localhost:49254
connection: Keep-Alive
user-agent: Apache-HttpClient/4.5.12 (Java/1.8.0_252)
accept-encoding: gzip,deflate
HTTP/1.1 200
ETag: "0e14d3fbd586864ca048f7287c2e348e4"
Content-Type: application/json
Content-Length: 1240
Date: Mon, 13 Jul 2020 21:18:58 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"lineResponses": [
{
"id": 1,
"name": "2호선",
"color": "GREEN",
"startTime": "05:30:00",
"endTime": "23:30:00",
"intervalTime": 5,
"stations": [
{
"station": {
"id": 1,
"name": "강남역",
"createdDate": "2020-07-14T06:18:58.754",
"modifiedDate": "2020-07-14T06:18:58.754"
},
"preStationId": null,
"distance": 0,
"duration": 0
},
{
"station": {
"id": 2,
"name": "역삼역",
"createdDate": "2020-07-14T06:18:58.775",
"modifiedDate": "2020-07-14T06:18:58.775"
},
"preStationId": 1,
"distance": 5,
"duration": 2
},
{
"station": {
"id": 3,
"name": "선릉역",
"createdDate": "2020-07-14T06:18:58.793",
"modifiedDate": "2020-07-14T06:18:58.793"
},
"preStationId": 2,
"distance": 5,
"duration": 2
}
],
"createdDate": "2020-07-14T06:18:58.617",
"modifiedDate": "2020-07-14T06:18:58.617"
},
{
"id": 2,
"name": "신분당성",
"color": "RED",
"startTime": "05:30:00",
"endTime": "23:30:00",
"intervalTime": 5,
"stations": [
{
"station": {
"id": 1,
"name": "강남역",
"createdDate": "2020-07-14T06:18:58.754",
"modifiedDate": "2020-07-14T06:18:58.754"
},
"preStationId": null,
"distance": 5,
"duration": 2
},
{
"station": {
"id": 4,
"name": "양재역",
"createdDate": "2020-07-14T06:18:58.82",
"modifiedDate": "2020-07-14T06:18:58.82"
},
"preStationId": 1,
"distance": 5,
"duration": 2
}
],
"createdDate": "2020-07-14T06:18:58.723",
"modifiedDate": "2020-07-14T06:18:58.723"
}
]
}
LineService의 findAllLines 메서드는 노선의 목록을 조회하는 메서드 입니다. 지하철 노선도를 조회하는 새로운 비즈니스 로직을 구현해주세요 :)
RestAssured.given().log().all().
...
when().
get(uri).
then().
header("ETag", notNullValue()).
...
RestAssured.given().log().all().
header("If-None-Match", eTag).
accept(MediaType.APPLICATION_JSON_VALUE).
...
추가 자료 문서 https://www.baeldung.com/etags-for-rest-with-spring
조금 더 가독성 좋게 리팩터링 해보세요 :)
@ExtendWith(MockitoExtension.class)
public class MapServiceTest {
@Mock
private LineService lineService;
private List<LineResponse> lines;
private MapService mapService;
@BeforeEach
void setUp() {
StationResponse stationResponse1 = new StationResponse(1L, "교대역", LocalDateTime.now(), LocalDateTime.now());
StationResponse stationResponse2 = new StationResponse(2L, "강남역", LocalDateTime.now(), LocalDateTime.now());
StationResponse stationResponse3 = new StationResponse(3L, "양재역", LocalDateTime.now(), LocalDateTime.now());
StationResponse stationResponse4 = new StationResponse(4L, "남부터미널역", LocalDateTime.now(), LocalDateTime.now());
LineStationResponse lineStationResponse1 = new LineStationResponse(stationResponse1, null, 2, 2);
LineStationResponse lineStationResponse2 = new LineStationResponse(stationResponse2, 1L, 2, 2);
LineStationResponse lineStationResponse3 = new LineStationResponse(stationResponse2, null, 2, 2);
LineStationResponse lineStationResponse4 = new LineStationResponse(stationResponse3, 2L, 2, 1);
LineStationResponse lineStationResponse5 = new LineStationResponse(stationResponse1, null, 2, 2);
LineStationResponse lineStationResponse6 = new LineStationResponse(stationResponse4, 1L, 1, 2);
LineStationResponse lineStationResponse7 = new LineStationResponse(stationResponse3, 4L, 2, 2);
LineResponse lineResponse1 = new LineResponse(1L, "2호선", "GREEN", LocalTime.now(), LocalTime.now(), 5, Lists.newArrayList(lineStationResponse1, lineStationResponse2), LocalDateTime.now(), LocalDateTime.now());
LineResponse lineResponse2 = new LineResponse(2L, "신분당선", "RED", LocalTime.now(), LocalTime.now(), 5, Lists.newArrayList(lineStationResponse3, lineStationResponse4), LocalDateTime.now(), LocalDateTime.now());
LineResponse lineResponse3 = new LineResponse(3L, "3호선", "ORANGE", LocalTime.now(), LocalTime.now(), 5, Lists.newArrayList(lineStationResponse5, lineStationResponse6, lineStationResponse7), LocalDateTime.now(), LocalDateTime.now());
lines = Lists.newArrayList(lineResponse1, lineResponse2, lineResponse3);
mapService = new MapService(lineService);
}
...
}
현재 DB에 접근하기 위해 사용하는 기술은 spring data jpa라는 기술이다.
다음 문서 빠르게 읽어보면 spring data jpa 사용하는데 도움 될거다.
그리 길지 않은 문서니 읽어보고 다 읽었으면 알아서 이슈 close해라.
뼈대 코드는 원활한 미션 진행을 위해 제공되는 코드입니다. 얼마든지 변경해서 사용하세요 :)
이번 미션의 목적은 완벽한 알고리즘을 구현하기 보다는 복잡한 로직을 TDD로 구현하는 경험을 하는 것 입니다. 모든 상황을 고려한 로직을 구현하려다보면 미션의 목적을 잃고 헤맬 수 있습니다. 고려해야 할 경우가 많다고 느껴 질 경우 제약사항을 통해 기능을 단순화 시켜보세요. (제약사항을 둘 경우 MD파일로 남겨주세요.)
GET /paths?source=1&target=3&type=ARRIVAL_TIME&time=202007221800 HTTP/1.1
accept: application/json
host: localhost:50460
connection: Keep-Alive
user-agent: Apache-HttpClient/4.5.12 (Java/1.8.0_252)
accept-encoding: gzip,deflate
HTTP/1.1 200
Set-Cookie: JSESSIONID=DD307C8D09D4DD94A70311B8E41B3703; Path=/; HttpOnly
Content-Type: application/json
Transfer-Encoding: chunked
Date: Wed, 22 Jul 2020 09:40:29 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"stations": [
{
"id": 1,
"name": "교대역"
},
{
"id": 2,
"name": "강남역"
},
{
"id": 3,
"name": "양재역"
}
],
"duration": 3,
"distance": 4,
"fare": 1250
}
JgraphTest의 getKShortestPaths 테스트 메서드를 참고하여 모든 경로를 조회하세요.
@Test
public void getKShortestPaths() {
String source = "v3";
String target = "v1";
Multigraph<String, DefaultWeightedEdge> graph = new Multigraph(DefaultWeightedEdge.class);
graph.addVertex("v1");
graph.addVertex("v2");
graph.addVertex("v3");
graph.addEdge("v1", "v2");
graph.addEdge("v2", "v3");
graph.addEdge("v1", "v3");
List<GraphPath> paths = new KShortestPaths(graph, 1000).getPaths(source, target);
assertThat(paths).hasSize(2);
paths.forEach(it -> {
assertThat(it.getVertexList()).startsWith(source);
assertThat(it.getVertexList()).endsWith(target);
});
}
Feature: 지하철 경로 검색
Scenario: 두 역의 최단 거리 경로를 조회
Given 지하철역이 등록되어있음
And 지하철 노선이 등록되어있음
And 지하철 노선에 지하철역이 등록되어있음
When 출발역에서 도착역까지의 최단 거리 경로 조회를 요청
Then 최단 거리 경로를 응답
And 총 거리와 소요 시간을 함께 응답함
HTTP/1.1 200
Request method: GET
Request URI: http://localhost:55494/paths?source=1&target=6
Headers: Accept=application/json
Content-Type=application/json; charset=UTF-8
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 09 May 2020 14:54:11 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"stations": [
{
"id": 5,
"name": "양재시민의숲역",
"createdAt": "2020-05-09T23:54:12.007"
},
{
"id": 4,
"name": "양재역",
"createdAt": "2020-05-09T23:54:11.995"
},
{
"id": 1,
"name": "강남역",
"createdAt": "2020-05-09T23:54:11.855"
},
{
"id": 2,
"name": "역삼역",
"createdAt": "2020-05-09T23:54:11.876"
},
{
"id": 3,
"name": "선릉역",
"createdAt": "2020-05-09T23:54:11.893"
}
],
"distance": 40,
"duration": 40
}
프론트엔드 코드는 모두 구현이 되어 있습니다. API만 구현해주세요 :)
TDD의 방향보다 테스트를 통해 구현할 기능을 명세하는것과 리팩터링이 더 중요합니다!
ex) 경로 조회를 수행하는 도메인 구현 예시
- 1. PathFinder 라는 클래스 작성 후 경로 조회를 위한 테스트를 작성
- 2. 경로 조회 메서드에서 Line을 인자로 받고 그 결과로 원하는 응답을 리턴하도록 테스트 완성
- 3. 테스트를 성공시키기 위해 JGraph의 실제 객체를 활용(테스트에서는 알 필요가 없음)
두 방향성을 모두 사용해보시고 테스트가 협력 객체의 세부 구현에 의존하는 경우(가짜 협력 객체 사용)와 테스트 대상이 협력 객체와 독립적이지 못하고 변경에 영향을 받는 경우(실제 협력 객체 사용)를 모두 경험해보세요 :)
거리
로 설정@Test
public void getDijkstraShortestPath() {
WeightedMultigraph<String, DefaultWeightedEdge> graph
= new WeightedMultigraph(DefaultWeightedEdge.class);
graph.addVertex("v1");
graph.addVertex("v2");
graph.addVertex("v3");
graph.setEdgeWeight(graph.addEdge("v1", "v2"), 2);
graph.setEdgeWeight(graph.addEdge("v2", "v3"), 2);
graph.setEdgeWeight(graph.addEdge("v1", "v3"), 100);
DijkstraShortestPath dijkstraShortestPath
= new DijkstraShortestPath(graph);
List<String> shortestPath
= dijkstraShortestPath.getPath("v3", "v1").getVertexList();
assertThat(shortestPath.size()).isEqualTo(3);
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.