Git Product home page Git Product logo

atlassian-connect-http4s's Introduction

Atlassian Connect Scala for Http4s

Build Status Maven Central

This library serves as a starter to develop Atlassian Connect Jira and Confluence add on. It is dependent on http4s which favors pure functional programming.

Getting Started

Add this to your build.sbt:

scalacOptions += "-Ypartial-unification"

To add library dependencies:

libraryDependencies += "com.github.allantl" %% "atlassian-connect-http4s" % "0.1.0"

Quick Start

This example is written using http4s version 0.20.0.

1- Define lifecycle repository

You'll need to define an implementation to save and find Atlassian Host. Note that, when add on is uninstalled, it is never deleted from storage but simply change the installed status. In production, please use real database.

import cats.effect.IO
import cats.effect.concurrent.Ref
import com.allantl.atlassian.connect.http4s.domain.AtlassianHost
import com.allantl.atlassian.connect.http4s.repository.algebra.AtlassianHostRepositoryAlgebra

class AtlassianHostRepository(storage: Ref[IO, List[AtlassianHost]]) extends AtlassianHostRepositoryAlgebra[IO] {

  override def findByClientKey(clientKey: String, onlyInstalled: Boolean): IO[Option[AtlassianHost]] =
    if (onlyInstalled) {
      storage.get.map(_.find(h => h.clientKey == clientKey && h.installed))
    } else {
      storage.get.map(_.find(_.clientKey == clientKey))
    }

  override def findByBaseUrl(baseUrl: String, onlyInstalled: Boolean): IO[Option[AtlassianHost]] =
    if (onlyInstalled) {
      storage.get.map(_.find(h => h.baseUrl == baseUrl && h.installed))
    } else {
      storage.get.map(_.find(_.baseUrl == baseUrl))
    }

  override def save(atlassianHost: AtlassianHost): IO[AtlassianHost] =
    storage
      .update(atlassianHost :: _.filter(_.clientKey == atlassianHost.clientKey))
      .map(_ => atlassianHost)
}

2- Define your service endpoints

Use AcHttpRoutes to define your endpoints and asAcAuth to get the authenticated user.

import com.allantl.atlassian.connect.http4s._ must be in scope.

import cats.effect.IO
import com.allantl.atlassian.connect.http4s._
import org.http4s.dsl.Http4sDsl

class AcServiceEndpoints extends Http4sDsl[IO] {

  val endpoints = AcHttpRoutes.of[IO] {
    case GET -> Root / "ping" asAcAuth user =>
      Ok(s"Received response from ${user.host.baseUrl}")
  }
}

3- Initiliaze components

import cats.effect._
import cats.effect.concurrent.Ref
import cats.implicits._
import com.allantl.atlassian.connect.http4s.auth.atlassian.jwt.JwtValidator
import com.allantl.atlassian.connect.http4s.auth.middleware.AcHttpService
import com.allantl.atlassian.connect.http4s.domain.AtlassianHost
import com.allantl.atlassian.connect.http4s.endpoints.LifecycleEndpoints
import com.allantl.atlassian.connect.http4s.services.lifecycle.LifecycleService
import org.http4s.implicits._
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder

object Main extends IOApp {

  def run(args: List[String]): IO[ExitCode] = {
    Ref.of[IO, List[AtlassianHost]](List.empty).flatMap { ref =>

      implicit val atlassianHostRepo = new AtlassianHostRepository(ref)
      implicit val jwtValidator = new JwtValidator[IO]()

      // AcHttpService is needed to transform `AcHttpRoutes` into http4s `HttpRoutes`
      val acHttpService: AcHttpService[IO] = AcHttpService(jwtValidator)
      val myService = acHttpService.liftRoutes(new AcServiceEndpoints().endpoints)

      val lifecycleService = new LifecycleService[IO](atlassianHostRepo, infoLogger = log => IO.delay(println(log)))
      val lifecycleEndpoints = LifecycleEndpoints(jwtValidator, atlassianHostRepo, lifecycleService).endpoints

      // It is recommended to split lifecycle to different routes
      val httpApp = Router(
        "/api/lifecycle" -> lifecycleEndpoints,
        "/api" -> myService
      ).orNotFound

      BlazeServerBuilder[IO]
        .bindHttp(8080, "localhost")
        .withHttpApp(httpApp)
        .serve
        .compile
        .drain
        .as(ExitCode.Success)
    }
  }
}

Providing your own LifecycleService

You can take a look at the implementation here and roll your own service instead.

Perform operation after lifecycle events

You can provide your own implementation of LifecycleEventHandler when initializing LifecycleEndpoints. Method will run asynchronously in the background and will not block lifecycle event.

Composing Routes

AcHttpRoutes can be composed with other AcHttpRoutes.

Make sure you do not compose this with Http4sRoutes, since AcHttpRoutes needs authentication.

import cats.implicits._

val e1 = new AcServiceEndpoints()
val e2 = new AcServiceEndpoints()

val e3 = e1.endpoints <+> e2.endpoints

License Check

There is a middleware that handles license check.

import cats.effect._
import org.http4s.dsl.io._

// For development, you can set this to false
implicit val atlassianConnectConfig = AtlassianConnectConfig(licenseCheckEnabled = true)

val notLicensedEndpoints: Request[IO] => IO[Response[IO]] = _ => Ok("License not active")
val licenseCheck: LicenseCheck[IO] = new LicenseCheck[IO](notLicensedEndpoints)

val endpoints: HttpRoutes[IO] = ???
val licensedEndpoints = licenseCheck(endpoints)

Serving frontend assets

To render frontend html page, you can use http4s with twirl, take a look at the documentation.

Frontend JWT Authentication

This is explained in atlassian connect documentation, under Retrieving context using AP.context.getToken().

Jira Client

If you need jira client, its available here.

Interop with this library:

object JiraClient {

  type JiraClient[R[_]] = JiraMultiTenantClient[R]

  def apply[R[_], S](acJwtConfig: AcJwtConfig)(
    implicit sttpBackend: SttpBackend[R, S]
  ): JiraMultiTenantClient[R] =
    JiraMultiTenantClient(acJwtConfig)
}

ScalaJs Atlaskit components

For those who is developing with scalajs-react and is looking for atlaskit components, please take a look here

atlassian-connect-http4s's People

Contributors

allantl avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.