/**
 * Copyright 2018 Twitter, Inc.
 * Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 */ import * as micro from "micro" ; import * as microQuery from "micro-query" ; import * as dispatch from "micro-route/dispatch" ; import { IncomingMessage , ServerResponse , Server } from "http" ; import { Context , FeatureMap , Executable , Execution , FiredRule } from "sqrl" ; import { CliManipulator } from "sqrl-cli-functions" ; function userInvariant ( cond , message ) { if ( ! cond ) { throw micro . createError ( 400 , message ) ; } } interface ApiRulesResponse { [ ruleName ] : { reason : string ; } ; } interface ApiResponse { /** Overall block/allow verdict for the action */ allow : boolean ; /** Details about why the verdict was reached */ verdict : { blockRules : string [ ] ; whitelistRules : string [ ] ; } ; rules : ApiRulesResponse ; /** Returns features requested via the API */ features ? : FeatureMap ; } async function run ( ctx , executable , req , res ) { const query = microQuery ( req ) ; const featureTimeoutMs = parseInt ( query . timeoutMs || "1000" , 10 ) ; userInvariant ( ! isNaN ( featureTimeoutMs ) , "timeoutMs was not a valid integer" ) ; const inputs = await micro . json ( req , { limit : "128mb" } ) ; const manipulator = new CliManipulator ( ) ; const execution = await executable . execute ( ctx , { manipulator , inputs , featureTimeoutMs , } ) ; await execution . fetchFeature ( "SqrlExecutionComplete" ) ; const rules = { } ; function ruleToName ( rule ) { rules [ rule . name ] = { reason : rule . reason , } ; return rule . name ; } const rv = { allow : ! manipulator . wasBlocked ( ) , verdict : { blockRules : manipulator . blockedRules . map ( ruleToName ) || [ ] , whitelistRules : manipulator . whitelistedRules . map ( ruleToName ) || [ ] , } , rules , } ; if ( query . features ) { const featureNames = query . features . split ( "," ) ; try { rv . features = { } ; await Promise . all ( featureNames . map ( async ( featureName ) => { rv . features [ featureName ] = await execution . fetchValue ( featureName ) ; } ) ) ; } catch ( e ) { throw micro . createError ( 500 , e . message , e ) ; } } await manipulator . mutate ( ctx ) ; res . setHeader ( "Content-Type" , "application/json" ) ; if ( query . hasOwnProperty ( "pretty" ) ) { return JSON . stringify ( rv , undefined , 2 ) + "\n" ; } else { return JSON . stringify ( rv ) ; } } async function deleteRoute ( ctx , req , res ) { throw micro . createError ( 500 , "Not implemented\n" ) ; } export function createSqrlServer ( ctx , executable ) { const router = dispatch ( ) . dispatch ( "/run" , [ "POST" ] , ( req , res ) => run ( ctx , executable , req , res ) ) . dispatch ( "/delete" , [ "POST" ] , ( req , res ) => deleteRoute ( ctx , req , res ) ) . otherwise ( async ( req , res ) => { throw micro . createError ( 404 , "Route not found\n" ) ; } ) ; return micro ( router ) ; } export type ServerWaitCallback = ( props : { server : Server ; } ) => Promise < void > ;