Slide 1

Slide 1 text

LIFFの説明 & Backendの話 LINE Developer Meetup #56 Kyoto Adam Millerchip サーバー開発チーム

Slide 2

Slide 2 text

⾃自⼰己紹介 イギリス出身 
 ノーサンプトン市⽣生まれ エディンバラ⼤大学
 Software Engineering 2014年年ワーホリとして来⽇日
 LINE Fukuoka⼊入社
 主にCreators Market
 少しLINE STORE 2018年年2⽉月、LINE Kyoto転勤勤
 Clova CEK SDK (Elixir版)
 LIFF https://www.instagram.com/handluggageonly/

Slide 3

Slide 3 text

以前のMeetup 2017年年07 #18 福岡
 アプリで簡単にスタンプを販売で きるためのAPI開発 
 2018年年08 京都 ElixirでClovaのスキルを開発して みる 採⽤用の⽇日など

Slide 4

Slide 4 text

LIFFって何?

Slide 5

Slide 5 text

LIFF LINE Front-end Framework のBack-end

Slide 6

Slide 6 text

LIFF 1. Use only static resources (HTML, JS, CSS…) Backend不不要 2. Open your website inside LINE chat 3. Make REST API calls on behalf of the user • Including send messages as user

Slide 7

Slide 7 text

Demo https://github.com/adamu/line_dm_liff

Slide 8

Slide 8 text

Let’s try • https://developers.line.biz/ • Adam’s Demo: https://github.com/adamu/line_dm_liff • Official Starter: https://github.com/line/line-liff-starter

Slide 9

Slide 9 text

How does it work? LIFF Thrift API liff id line access token Thrift API line access token user id Talk Server REST API user id client id scope rest access token Login Server uri liff id client_id login scopes view metadata

Slide 10

Slide 10 text

How does it work? LIFF Thrift API liff id line access token Thrift API line access token user id Talk Server REST API user id client id scope rest access token Login Server uri liff id client_id login scopes view metadata OAuth2 Browser App Server

Slide 11

Slide 11 text

How does it work? LIFF url view metatada rest access token Thrift API liff id line access token Thrift API line access token user id Talk Server REST API user id client id scope rest access token Login Server uri liff id client_id login scopes view metadata

Slide 12

Slide 12 text

How does it work? LIFF Talk Server uri liff id client_id login scopes view metadata url view metatada rest access token Thrift API liff id line access token REST API user id client id scope rest access token Thrift API line access token user id Login Server https://example.com#access_token=foobarhoge

Slide 13

Slide 13 text

No consent? LIFF consent required REST API user id client id scope consent required Thrift API liff id line access token Login Server

Slide 14

Slide 14 text

Revoke LIFF Thrift API rest access token REST API rest access token Login Server line access token

Slide 15

Slide 15 text

Kotlin • Originally Java • Now 60% Kotlin, 35% Java • All tests are Kotlin • All new files are Kotlin • Convert other files when possible

Slide 16

Slide 16 text

data class User( val userId: String ) ? @Data class User { private String userId; } data class User( val userId: String? = null )

Slide 17

Slide 17 text

? Foo foo = Optional.ofNullable(app.getFoo()).orElse(Foo.DEFAULT); val foo = Optional.ofNullable(app.foo).orElse(Foo.DEFAULT) val foo = app.foo ?: Foo.DEFAULT

Slide 18

Slide 18 text

Extensions? data class User( val userId: Optional? ) val userId : String? = user.userId?.orElse(null) fun Optional.unwrap(): T? = orElse(null) val userId : String? = user.userId?.unwrap()

Slide 19

Slide 19 text

Extensions val foo = restTemplate.getForObject(url) restTemplate.getForObject( url, Foo::class.java ) fun getFoo(uri: URI) : Foo? = restTemplate.getForObject(uri)

Slide 20

Slide 20 text

public static String getTokenHash(@Nullable String token) { if (token == null) { return null; } try { val messageDigest = MessageDigest.getInstance(SHA256); val fullHash = messageDigest.digest(token.getBytes(StandardCharsets.UTF_8)); return Base64URL.encode(Arrays.copyOf(fullHash, fullHash.length / 2)).toString(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("MessageDigest instantiation error", e); } } Extensions (chaining)

Slide 21

Slide 21 text

public static String getTokenHash(@Nullable String token) { if (token == null) { return null; } try { val messageDigest = MessageDigest.getInstance(SHA256); val fullHash = messageDigest.digest(token.getBytes(StandardCharsets.UTF_8)); return Base64URL.encode(Arrays.copyOf(fullHash, fullHash.length / 2)).toString(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("MessageDigest instantiation error", e); } } Extensions (chaining) fun getTokenHash(token: String?): String? = token ?.toByteArray(StandardCharsets.UTF_8) ?.let { MessageDigest.getInstance("SHA-256").digest(it) } ?.let { Base64URL.encode(it.copyOf(it.size / 2)).toString() }

Slide 22

Slide 22 text

Extensions (no more streams) int sum = 0; for(String string : strings) { sum += Integer.valueOf(string); } List strings = Arrays.asList("1", "2", "3"); int sum = strings.stream().mapToInt(Integer::valueOf).sum(); val sum = strings.map(Integer::valueOf).sum()

Slide 23

Slide 23 text

Coroutines? • Not using yet • Server makes blocking calls • Improvement for the future?

Slide 24

Slide 24 text

Monitoring / Stats • Aspect Oriented Programming (AOP)
 https://www.eclipse.org/aspectj/

Slide 25

Slide 25 text

Call Logging @Pointcut("execution (public * com.linecorp.liff.*(..))") public void liffMethods() { } @Slf4j @Aspect public static class LiffLoggingAspect { @Around("liffMethods()") public Object logApiCall(ProceedingJoinPoint pjp) { val info = pjp.getInfo…() Object result = pjp.proceed(); log.info("Call {}.{} took:{}us args:[{}] returns:{} ", ...); return result; } }

Slide 26

Slide 26 text

Log Viewing

Slide 27

Slide 27 text

Monitoring • IMON • Alerts: Email / Slack / など • Statistics

Slide 28

Slide 28 text

Deployment • Github Enterprise • Maven • Jenkins • PMC • Ansible? Circle CI? Drone?

Slide 29

Slide 29 text

TODO • Convert more to Kotlin + use more Kotlin features • Implement New Features • Concurrency / Non-Blocking • Improve Logging / Monitoring • Improved Performance Monitoring • ??

Slide 30

Slide 30 text

ご静聴ありがとうございました