Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repl API #10

Open
wants to merge 35 commits into
base: lamdera-next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d8ae362
llvm@13 fixes local M1 build, try on buildserver
supermario Sep 11, 2023
15a7069
Patch in repl API functionality for elm-notebook exploration
supermario Sep 14, 2023
c097d3e
change alllowed origins to http://localhost:8007 only
jxxcarlson Sep 17, 2023
b2a2e56
Exclude Jim's experimental files
jxxcarlson Sep 17, 2023
e0d55f0
Change Test.hs so as to talk to elm-notebook
jxxcarlson Oct 2, 2023
7c8f310
Add debug statements
jxxcarlson Oct 2, 2023
8290dee
Added: '_ -> error $ "unreachable:" ++ show e' to function 'watch'. …
jxxcarlson Oct 2, 2023
fa3cfe3
I am committing this, but I can't see what has changed.
jxxcarlson Oct 2, 2023
441f1ce
Add elm-community/list-extra to outlines/repl/elm.json
jxxcarlson Oct 2, 2023
a27d4bb
Remove duplicate elm.json entries
jxxcarlson Oct 4, 2023
d591f19
Add module Endpoint.Package from extra/, (2) Chane Develop (in termin…
jxxcarlson Oct 5, 2023
d1d0cd9
Fix path for writing the elm.json file
jxxcarlson Oct 5, 2023
92c1c80
Fix stray space in word "dependencies"
jxxcarlson Oct 5, 2023
1e775be
Add type ElmPackage and decoder for it.
jxxcarlson Oct 5, 2023
9e57721
Return outlines/repl/elm.json to its original state
jxxcarlson Oct 5, 2023
c9af64d
Introduced a deterministic delay for executing Notebook.Package.nowSe…
jxxcarlson Oct 5, 2023
87727e6
When packages are added to elm.json, report to the client how many we…
jxxcarlson Oct 12, 2023
e544b92
Draft 1
jxxcarlson Oct 13, 2023
48def3e
Draft 2
jxxcarlson Oct 13, 2023
3391d5f
Fix JSON output
jxxcarlson Oct 13, 2023
b727101
Implement dynamic loading of packages submitted to the Elm compiler.
jxxcarlson Oct 14, 2023
8046918
Return outlines/repl/elm.json to its original state
jxxcarlson Oct 14, 2023
3935f9d
fix but in properly computng evalstate before compilation.
jxxcarlson Oct 15, 2023
effbe40
Add debug code to Endpoint/Repl.hs
jxxcarlson Oct 30, 2023
8a8a994
Add 'compiler.iml' to .gitignore
jxxcarlson Oct 30, 2023
bdf9ffc
Return to previous working state
jxxcarlson Oct 30, 2023
7d4f078
add back extra/Artifacts.hs
jxxcarlson Oct 30, 2023
be46c86
Restore elm.json to original state
jxxcarlson Oct 30, 2023
ac87fe8
Renamed: extra/Artifacts.hs -> extra/ReplArtifacts.hs
jxxcarlson Oct 30, 2023
dc1fbe5
Add comment explaining the purpose of module Package
jxxcarlson Oct 30, 2023
b422951
Add comments to explain the purpose and operation of extra/Endpoint/P…
jxxcarlson Oct 30, 2023
14b46e6
More comments
jxxcarlson Oct 30, 2023
20bb71e
Comments for ReplArtifacts
jxxcarlson Oct 30, 2023
9173633
Add clause 'Types.OnConnected sessionId clientId' to Backend.update:
jxxcarlson Jan 17, 2024
1505d77
Merge branch 'lamdera-next' into elm-notebook-repl
supermario Jan 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ghci
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
:set -fbyte-code
:set -fobject-code
:set -fwarn-name-shadowing
:def rr const $ return $ unlines ["Ext.Common.killTrackedThreads",":r","Test.target"]
:def rr const $ return $ unlines ["Ext.Common.killTrackedThreads",":r","Test.target"]
2 changes: 1 addition & 1 deletion .github/workflows/build-macos-arm64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ jobs:
- name: Run distribution script
run: |
test -x "$(which ghcup)" && curl https://downloads.haskell.org/~ghcup/aarch64-apple-darwin-ghcup -o ~/.local/bin/ghcup && chmod a+x ~/.local/bin/ghcup
brew install llvm@12
brew install llvm@13
cd distribution
./build-macos-arm64.sh
1 change: 0 additions & 1 deletion .github/workflows/build-macos-x86_64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,5 @@ jobs:
- name: Run distribution script
run: |
test -x "$(which ghcup)" && curl https://downloads.haskell.org/~ghcup/aarch64-apple-darwin-ghcup -o ~/.local/bin/ghcup && chmod a+x ~/.local/bin/ghcup
brew install llvm@12
cd distribution
./build-macos-x86_64.sh
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ extra/.cache
# @TESTS
elm-home

# Jim
experimental/
.vscode/
compiler.iml
2 changes: 1 addition & 1 deletion distribution/build-macos-arm64.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ git submodule init && git submodule update
ffiLibs="$(xcrun --show-sdk-path)/usr/include/ffi" # Workaround for GHC9.0.2 bug until we can use GHC9.2.3+
export C_INCLUDE_PATH=$ffiLibs # https://gitlab.haskell.org/ghc/ghc/-/issues/20592#note_436353

export PATH="/opt/homebrew/opt/llvm@12/bin:$PATH" # The arm64 build currently requires llvm until we get to GHC 9.4+
export PATH="/opt/homebrew/opt/llvm@13/bin:$PATH" # The arm64 build currently requires llvm until we get to GHC 9.4+

$stack install --local-bin-path $dist

Expand Down
4 changes: 4 additions & 0 deletions elm.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ Executable lamdera
Test.Wire
Lamdera.Evergreen.TestMigrationHarness
Lamdera.Evergreen.TestMigrationGenerator
Endpoint.Repl
Artifacts
Cors


-- Debug helpers --
Expand Down Expand Up @@ -397,6 +400,7 @@ Executable lamdera
-- Debug
unicode-show,
network-info,
network-uri,

-- Future
conduit-extra,
Expand Down
1 change: 1 addition & 0 deletions ext-sentry/Ext/Filewatch.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ watch root action =
Modified f _ _ -> f
Removed f _ _ -> f
Unknown f _ _ _ -> f
_ -> error $ "unreachable:" ++ show e

-- @TODO it would be better to not listen to these folders in the `watchTree` when available
-- https://github.com/haskell-fswatch/hfsnotify/issues/101
Expand Down
48 changes: 48 additions & 0 deletions extra/Cors.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{-# OPTIONS_GHC -Wall #-}
module Cors
( allow
)
where


import qualified Data.HashSet as HashSet
import Network.URI (parseURI)
import Snap.Core (Snap, Method, method)
import Snap.Util.CORS (CORSOptions(..), HashableMethod(..), OriginList(Origins), applyCORS, mkOriginSet)



-- ALLOW


allow :: Method -> [String] -> Snap () -> Snap ()
allow method_ origins snap =
applyCORS (toOptions method_ origins) $ method method_ $
snap



-- TO OPTIONS


toOptions :: (Monad m) => Method -> [String] -> CORSOptions m
toOptions method_ origins =
let
allowedOrigins = toOriginList origins
allowedMethods = HashSet.singleton (HashableMethod method_)
in
CORSOptions
{ corsAllowOrigin = return allowedOrigins
, corsAllowCredentials = return True
, corsExposeHeaders = return HashSet.empty
, corsAllowedMethods = return allowedMethods
, corsAllowedHeaders = return
}


toOriginList :: [String] -> OriginList
toOriginList origins =
Origins $ mkOriginSet $
case traverse parseURI origins of
Just uris -> uris
Nothing -> error "invalid entry given to toOriginList list"
126 changes: 126 additions & 0 deletions extra/Endpoint/Package.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}

module Endpoint.Package (handlePost, reportOnInstalledPackages) where

{-
1.

This endpoint will respond to POST requests to "https://repl.lamdera.com/packageList"
with a JSON body of the form:
[
{ "name": "elm/core", "version": "1.0.5" },
{ "name": "elm/html", "version": "1.0.0" }
]
It will write an elm.json file to the repl directory, and then reload the repl.
This response is mediate by function `handlePost` below.

2.

In additon, this endpoint will respond to GET requests to "https://repl.lamdera.com/reportOnInstalledPackages"
with a JSON body of the form:
[
{ "name": "elm/core", "version": "1.0.5" },
{ "name": "elm/html", "version": "1.0.0" }
]
The json body reports on the packages that are currently installed in the repl.
This response is mediated by function `reportOnInstalledPackages` below.

NOTE. handlePost and reportOnInstalledPackages
are referenced in the Snap webserver at Develop.runWithRoot
via the code fragments

SnapCore.path "packageList" $ Package.handlePost artifactRef)
SnapCore.path "reportOnInstalledPackages" $ Package.reportOnInstalledPackages)
-}


import GHC.Generics (Generic)
import Snap.Core
import Snap.Http.Server
import Data.Aeson (FromJSON, eitherDecode, encode, ToJSON, toJSON, object, (.=))
import Data.ByteString (ByteString)
import Data.ByteString.Lazy (toStrict)
import qualified Data.ByteString.Lazy.Char8 as BL
import qualified Data.ByteString.Char8 as ByteString
import GHC.Generics
import System.IO (writeFile)
import Control.Monad.IO.Class (liftIO)
import qualified Data.Map as Map
---
import Snap.Util.FileServe
import qualified Data.ByteString.Lazy as LBS
import qualified Data.HashMap.Strict as HM
import Data.Text.Encoding (decodeUtf8)
import Snap.Http.Server.Config (setPort, defaultConfig)
import qualified ReplArtifacts
import Data.IORef


data Package = Package { name :: String, version :: String } deriving (Show, Generic)

instance FromJSON Package
instance ToJSON Package


type PackageList = [Package]

writeElmJson :: PackageList -> IO ()
writeElmJson pkgs = do
let directDeps = Map.fromList $ ("elm/core", "1.0.5"):[(name p, version p) | p <- pkgs]
elmJson = object [
"type" .= ("application" :: String),
"source-directories" .= (["../../repl-src"] :: [String]),
"elm-version" .= ("0.19.1" :: String),
"dependencies" .= object [
"direct" .= directDeps,
"indirect" .= object [
"elm/json" .= ("1.1.3" :: String)
]
],
"test-dependencies" .= object [
"direct" .= (Map.empty :: Map.Map String String),
"indirect" .= (Map.empty :: Map.Map String String)
]
]
writeFile "./outlines/repl/elm.json" ( BL.unpack $ encode elmJson)


handlePost :: IORef ReplArtifacts.Artifacts -> Snap ()
handlePost artifactRef = do
body <- readRequestBody 10000
let maybePackageList = eitherDecode body :: Either String PackageList
case maybePackageList of
Left err -> writeBS $ "Error: Could not decode JSON: " <> (ByteString.pack err)
Right packages -> do
liftIO $ writeElmJson packages
let message = ByteString.pack $ "Packages added: " ++ (show $ length packages)
writeBS message
newArtifacts <- liftIO ReplArtifacts.loadRepl
liftIO $ writeIORef artifactRef newArtifacts



data Dependencies = Dependencies {
direct :: HM.HashMap String String
} deriving (Generic, Show)

data TopLevel = TopLevel {
dependencies :: Dependencies
} deriving (Generic, Show)

instance FromJSON TopLevel

instance FromJSON Dependencies

--- curl -X POST -H "Content-Length: 0" http://localhost:8000/reportOnInstalledPackages

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For testing the endpoint that returns a list of installed packages, for example:

curl -X POST -H "Content-Length: 0" http://localhost:8000/reportOnInstalledPackages
[{"name":"elm/parser","version":"1.1.0"},{"name":"elm/core","version":"1.0.5"}]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I not add comments explaining the purpose of this file?

reportOnInstalledPackages :: Snap ()
reportOnInstalledPackages = do
jsonData <- liftIO $ LBS.readFile "./outlines/repl/elm.json"
case eitherDecode jsonData :: Either String TopLevel of
Left err -> writeBS $ "Failed to parse JSON: " <> (LBS.toStrict jsonData)
Right topLevel -> do
let directDeps = HM.toList $ direct $ dependencies topLevel
let outputList = map (\(name, version) -> object ["name" .= name, "version" .= version]) directDeps
writeBS . LBS.toStrict . encode $ outputList
Loading