Skip to content
This repository has been archived by the owner on Apr 17, 2024. It is now read-only.

Commit

Permalink
Add Rollbar (#6)
Browse files Browse the repository at this point in the history
* (BREAKING CHANGE) Give LocalPerformer access to Model;
* Add Rollbar;
* Add logo;
* Prettify;
* Add .editorconfig;
* Fixes some `package.json` and `yarn.lock`;
* Code cleanups;
* Bump to 0.0.2.

NOTE: Uses `url.path` as Rollbar's scope (different from what we are using now).
  • Loading branch information
PedroHLC authored May 12, 2021
1 parent 6870076 commit bab687e
Show file tree
Hide file tree
Showing 25 changed files with 584 additions and 172 deletions.
20 changes: 20 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# http://editorconfig.org
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 80
trim_trailing_whitespace = true

[*.md]
max_line_length = 0
trim_trailing_whitespace = false

[*.elm]
indent_style = space
indent_size = 4

[COMMIT_EDITMSG]
max_line_length = 0
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
**/elm-stuff/
**/elm.json
**/.cache
example/dist
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frontend-elm-kit

![Paack's Frontend Elm KITT](https://repository-images.githubusercontent.com/358355444/10442e00-b1b6-11eb-98c7-90c0f758b844)

A set of tools and integrations used by our Elm applications.

## Installing
Expand Down Expand Up @@ -33,12 +35,27 @@ Add it as a source directory in the `elm.json`:
In order to integrate this package into your project there are some extra changes that need to be made:

- Install all the [required packages](https://github.com/PaackEng/frontend-elm-kit/blob/main/example/elm.json) with exception of `elm/html`
- Make sure that the modules `Main.Model`, `Main.Msg`, `Main.Update`, `Effects.Local` and `Effects.Performer` all exist. Look at the example folder for a minimal setup
- Make sure that the modules `Data.Environment`, `Main.Model`, `Main.Msg`, `Main.Update`, `Effects.Local` and `Effects.Performer` all exist. Look at the example folder for a minimal setup
- Ensure that the ports `checkSession`, `login` and `logout` are all present
- Provide all the four seeds (`randomSeed1`, 2, 3 and 4) in the app's `Flags`
- Install the [Auth0 SPA SDK](https://github.com/auth0/auth0-spa-js)
- If your application isn't using `Effect` yet you can use `Auth.performEffects` to convert them on the fly. Otherwise, it's recommended to use `Effects.MainHelper`

### Rollbar

- Make sure your model includes `appConfig.environment`. `codeVersion`, `rollbarToken` and `url`;
- You'll need a message for receiving feedback, see [how the example performs the effect](https://github.com/PaackEng/frontend-elm-kit/blob/main/example/src/Effects/LocalPerformer.elm);

## Suggestions

### Rollbar

- Don't forget to update the url value in model when it changes;
- Compose errors with `Paack.Rollbar` module;
- Easy transform Http errors with `Paack.Rollbar.Http` module;
- Easy transform Graphql errors with `Paack.Rollbar.Graphql` module;
- Produce the effect with `Paack.Rollbar.Dispatch`.

## Running the example

Navigate to the example folder, then create a `.env` file and fill in the values (you can copy them from any project where Auth0 is configured). After that you can run it with `yarn start`.
40 changes: 21 additions & 19 deletions auth0/connect.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
import { Auth0Client } from "@auth0/auth0-spa-js";
import * as auth from "./auth";
import { AuthPorts } from "./elm";
import { Auth0Client } from '@auth0/auth0-spa-js';
import * as auth from './auth';
import { AuthPorts } from './elm';

export function connectAppToAuth(
app: ElmApp<AuthPorts>,
authClient: Promise<Auth0Client>,
authAutoLogin: boolean
authAutoLogin: boolean,
): void {
/* The following function handles notifying Elm's app about possible failures and or success.
It does not returns a feedback only when the user is redirected to the login page.
*/
const callback = (
lambda: (
client: Auth0Client,
autoLogin: boolean
) => Promise<auth.AuthSuccess | "NO_FEEDBACK">
) => async (): Promise<void> => {
try {
const result = await lambda(await authClient, authAutoLogin);
if (result !== "NO_FEEDBACK") app.ports.authResult.send(result);
} catch (err: unknown) {
if (err instanceof auth.ElmTreatableError)
app.ports.authResult.send(err.toJSON());
else throw err;
}
};
const callback =
(
lambda: (
client: Auth0Client,
autoLogin: boolean,
) => Promise<auth.AuthSuccess | 'NO_FEEDBACK'>,
) =>
async (): Promise<void> => {
try {
const result = await lambda(await authClient, authAutoLogin);
if (result !== 'NO_FEEDBACK') app.ports.authResult.send(result);
} catch (err: unknown) {
if (err instanceof auth.ElmTreatableError)
app.ports.authResult.send(err.toJSON());
else throw err;
}
};

app.ports.logout.subscribe(async () => auth.logout(await authClient));
app.ports.login.subscribe(async () => auth.login(await authClient));
Expand Down
2 changes: 1 addition & 1 deletion auth0/elm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { User } from "@auth0/auth0-spa-js";
import { User } from '@auth0/auth0-spa-js';

type AuthPorts = {
checkSession: PortFromElm<void>;
Expand Down
16 changes: 8 additions & 8 deletions elm/Paack/Effects/MainHelper.elm
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ performedInit flags url key =

( newSeeds, cmds ) =
Effects.apply
(effectsApplier key)
(effectsApplier key appModel)
( firstSeeds, Cmd.none )
effects
in
Expand All @@ -51,7 +51,7 @@ performedUpdate msg performerModel =

( newSeeds, cmds ) =
Effects.apply
(effectsApplier performerModel.key)
(effectsApplier performerModel.key appModel)
( performerModel.seeds, Cmd.none )
effects
in
Expand All @@ -60,22 +60,22 @@ performedUpdate msg performerModel =
)


effectsApplier : Nav.Key -> Effects.Effect Msg -> ( Seeds, Cmd Msg ) -> ( Seeds, Cmd Msg )
effectsApplier key effect ( seeds, accumulator ) =
effectsApplier : Nav.Key -> Model -> Effects.Effect Msg -> ( Seeds, Cmd Msg ) -> ( Seeds, Cmd Msg )
effectsApplier key model effect ( seeds, accumulator ) =
let
( newSeeds, cmd ) =
effectPerform key seeds effect
effectPerform key seeds model effect
in
( newSeeds
, Cmd.batch [ cmd, accumulator ]
)


effectPerform : Nav.Key -> Seeds -> Effects.Effect Msg -> ( Seeds, Cmd Msg )
effectPerform key seeds effect =
effectPerform : Nav.Key -> Seeds -> Model -> Effects.Effect Msg -> ( Seeds, Cmd Msg )
effectPerform key seeds model effect =
case effect of
Effects.LocalEffect localEffect ->
LocalPerformer.effectPerform key seeds localEffect
LocalPerformer.effectPerform key seeds model localEffect

Effects.CommonEffect commonEffect ->
CommonPerformer.effectPerform key seeds commonEffect
100 changes: 100 additions & 0 deletions elm/Paack/Rollbar.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
module Paack.Rollbar exposing
( MaybeToken(..)
, RollbarErrorPayload
, RollbarPayload(..)
, codedErrorPayload
, errorPayload
, initToken
, notToRoll
, prependDescription
, withEntry
, withPagination
)

{-| Composes the error payload
-}

import Dict exposing (Dict)
import Json.Encode as Encode exposing (Value)
import Rollbar


type alias RollbarErrorPayload =
{ description : String
, details : Dict String Value
}


type RollbarPayload
= RollError RollbarErrorPayload
| NotToRoll


type MaybeToken
= DisabledForDevelopment
| JustToken Rollbar.Token


notToRoll : RollbarPayload
notToRoll =
NotToRoll


errorPayload : String -> RollbarPayload
errorPayload description =
RollError
{ description = description
, details = Dict.empty
}


{-| A shortcut for InternalServerError and any GraphqlError error with { code : String }
-}
codedErrorPayload : { description : String, code : String } -> RollbarPayload
codedErrorPayload { description, code } =
RollError
{ description = description
, details =
Dict.insert "code"
(Encode.string code)
Dict.empty
}


withEntry : String -> Value -> RollbarPayload -> RollbarPayload
withEntry key value payload =
case payload of
RollError ({ details } as error) ->
RollError { error | details = Dict.insert key value details }

NotToRoll ->
payload


withPagination : { pageSize : Int, offset : Int } -> RollbarPayload -> RollbarPayload
withPagination { pageSize, offset } =
Encode.object
[ ( "page", Encode.int pageSize )
, ( "offset", Encode.int offset )
]
|> withEntry "pagination"


prependDescription : String -> RollbarPayload -> RollbarPayload
prependDescription parent payload =
-- If ever needed, feel free to expose this function
case payload of
RollError ({ description } as error) ->
RollError { error | description = parent ++ "/" ++ description }

NotToRoll ->
payload


initToken : String -> MaybeToken
initToken tokenFromFlags =
if String.isEmpty tokenFromFlags then
DisabledForDevelopment

else
JustToken <| Rollbar.token tokenFromFlags
48 changes: 48 additions & 0 deletions elm/Paack/Rollbar/Dispatch.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module Paack.Rollbar.Dispatch exposing (sendError)

{-| Composes the error effect
-}

import Dict
import Effects.Local as LocalEffects
import Json.Encode as Encode
import Paack.Effects as Effects exposing (Effects)
import Paack.Rollbar exposing (RollbarPayload(..))
import Paack.Rollbar.Effect as RollbarEffect
import Rollbar


{-|
- parent: The (Elm) message where did it occurred.
In rollbar as "body"."message"."parent"
E.g.: `"Pages.FleetAssignment.DriversFetched"`
- payload.description: The error union identification.
In rollbar as "body"."message"."description"
E.g.: `"Api.Drivers.List.InternalServerError"`
- payload.details: Custom additions to "body"."message"
E.g.: `Dict.fromList [ "bad-status", (Encode.int 404) ]`
-}
sendError :
String
-> RollbarPayload
-> Effects msg
sendError parent payload =
case payload of
NotToRoll ->
Effects.none

RollError { description, details } ->
RollbarEffect.Send
{ body =
details
|> Dict.insert "description" (Encode.string description)
|> Dict.insert "parent" (Encode.string parent)
, title =
parent ++ "/" ++ description
, level =
Rollbar.Error
}
|> LocalEffects.RollbarEffect
|> Effects.fromLocal
31 changes: 31 additions & 0 deletions elm/Paack/Rollbar/Effect.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module Paack.Rollbar.Effect exposing (Effect(..), Payload, RollbarResult)

{-| Perform the error effect
-}

import Dict exposing (Dict)
import Http as ElmHttp
import Json.Encode exposing (Value)
import Rollbar


type Effect
= Send Payload


{-|
- title: Usually `(parent ++ "/" ++ description)`
- body: In rollbar as "body"."message"
- level: As seen in rollbar dashboard
-}
type alias Payload =
{ title : String
, body : Dict String Value
, level : Rollbar.Level
}


type alias RollbarResult =
Result ElmHttp.Error ()
Loading

0 comments on commit bab687e

Please sign in to comment.