<commit_before>package ufront.web;

import haxe.PosInfos;
import tink.core.Error;
import ufront.auth.AuthError;
import ufront.remoting.RemotingError;
using tink.CoreApi;

/**
Helper functions to create `tink.core.Error` objects that are common in Http requests.
**/
class HttpError {

	/**
	Wrap an existing error into a Error

	- If it was a `tink.core.Error` already, return it as is.
	- If it was a normal exception, use code 500, with "Internal Server Error" as the message, the exception as the data, and the call site as the pos.

	@param e The original exception.
	@param msg (optional) The message to use with the error.  The default message is "Internal Server Error".
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 500.
	**/
	static public function wrap( e:Dynamic, ?msg="Internal Server Error", ?pos:PosInfos ):Error {
		if ( Std.is(e, Error) ) return cast e;
		else return Error.withData( ErrorCode.InternalError, msg, e, pos );
	}

	/**
	A Http 400 "Bad Request" error.

	@param reason (optional) If supplied, the error message will be `Bad Request: $reason`.
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 400.
	**/
	static public inline function badRequest( ?reason:String, ?pos:PosInfos ):Error {
		var message = "Bad Request";
		if ( reason!=null )
			message += ': $reason';
		return new Error(400, message, pos);
	}

	/**
	A Http 500 "Internal Server Error", optionally containing the inner error.

	@param msg (optional) The error message. The default message is "Internal Server Error".
	@param inner (optional) The inner data of the error, if any.
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 500.
	**/
	static public function internalServerError( ?msg="Internal Server Error", ?inner:Dynamic, ?pos:PosInfos ):Error {
		return Error.withData( 500, msg, inner, pos );
	}

	/**
	A Http 405 "Method Not Allowed" error.

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 405.
	**/
	static public function methodNotAllowed( ?pos:PosInfos ):Error {
		return new Error( 405, "Method Not Allowed", pos );
	}

	/**
	A Http 404 "Page Not Found" error.

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 404.
	**/
	static public function pageNotFound( ?pos:PosInfos ):Error {
		return new Error( 404, "Page Not Found", pos );
	}

	/**
	A Http 401 "Unauthorized Access" error.

	@param message (optional) A description of the error. Default is "Unauthorized Access".
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 401.
	**/
	static public function unauthorized( ?message:String="Unauthorized Access", ?pos:PosInfos ):Error {
		return new Error( 401, message, pos );
	}

	/**
	A Http 422 "Unprocessable Entity" error.

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 422.
	**/
	static public function unprocessableEntity( ?pos:PosInfos ):Error {
		return new Error( 422, "Unprocessable Entity", pos );
	}

	/**
	A Http 401 "Unauthorized Access" error based on an `AuthError`.

	@param error The AuthError that has been raised.
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 401.
	**/
	static public function authError( error:AuthError, ?pos:PosInfos ):TypedError<AuthError> {
		var msg = switch error {
			case ANotLoggedIn: 'Not Logged In';
			case ALoginFailed( msg ): 'Login Failed: $msg';
			case ANotLoggedInAs( u ): 'Not Logged In As $u';
			case ANoPermission( p ): 'Permission $p denied';
		}
		return Error.typed( 401, msg, error, pos );
	}

	/**
	A Http 500 "Internal Server Error" error based on a `RemotingError`.

	@param error The AuthError that has been raised.
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 401.
	**/
	static public function remotingError<T>( error:RemotingError<T>, ?pos:PosInfos ):TypedError<RemotingError<T>> {
		return switch error {
			case RHttpError( remotingCallString, responseCode, responseData ): Error.typed( responseCode, 'HTTP $responseCode Error during $remotingCallString', error, pos );
			case RApiNotFound( remotingCallString, errorMessage ): Error.typed( 404, 'Remoting API $remotingCallString not found: $errorMessage', error, pos );
			case RServerSideException( remotingCallString, e, stack ):
				var errorObj = Std.instance( e, Error );
				if ( errorObj!=null )
					Error.typed( errorObj.code, errorObj.message, error, pos )
				else
					Error.typed( 500, 'Internal Server Error while executing $remotingCallString', error, pos );
			case RClientCallbackException( remotingCallString, e ): Error.typed( 500, 'Error during callback after $remotingCallString: $e', error, pos );
			case RUnserializeFailed( remotingCallString, troubleLine, err ): Error.typed( 422, 'Remoting serialization failed for call $remotingCallString: could not process $troubleLine', error, pos );
			case RNoRemotingResult( remotingCallString, responseData ): Error.typed( 500, 'Error with response for $remotingCallString: no remoting response found', error, pos );
			case RApiFailure( remotingCallString, data ): Error.typed( 500, 'Call to $remotingCallString failed: $data', error, pos );
			case RUnknownException( e ): Error.typed( 500, 'Unknown exception during remoting call', error, pos );
		}
	}

	/**
	A shortcut for an error when a method is not implemented (Http 500).

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 500.
	**/
	static public function notImplemented( ?pos:PosInfos ):Error {
		var methodName = pos.className + "." + pos.methodName;
		return new Error( 500, 'Internal Server Error: $methodName is not implemented on this platform', pos );
	}

	/**
	A shortcut for an error when a method is abstract and should be overridden (Http 500).

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 500.
	**/
	static public function abstractMethod( ?pos:PosInfos ):Error {
		var methodName = pos.className + "." + pos.methodName;
		return new Error( 500, 'Internal Server Error: $methodName is an abstract method and should be overridden by a subclass', pos );
	}

	/**
	A shortcut to throw an `Error` if a particular value is `null` (Http 500).

	@param val The value to check.
	@param name (optional) The name of the argument that is being checked, to provide a more helpful error message. Default is "argument".
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@throws A wrapped `Error` object, with the error code 500, if `val` was null.
	**/
	static public function throwIfNull( val:Dynamic, ?name:String="argument", ?pos:PosInfos ):Void {
		if ( val==null )
			throw new Error( 500, '$name should not be null', pos );
	}

	/**
	Generate a fake `haxe.PosInfos` position for a given class, method and args.

	This is used by some ufront internals to give an accurate position for messages and errors, when we have details about the context but not the actual position.

	For example, `MVCHandler` knows the Controller, Action and Arguments that were called, but if an exception is thrown it does not have a valid position.
	This allows us to fake a "close enough" position, to aid in debugging.

	@param obj - The object (controller / module / handler) from which we derive the class name. Must be a class instance.
	@param method - The method name to use in our position
	@param args - Any arguments parsed to that method.  If not given, an empty array will be used.
	**/
	static inline public function fakePosition( obj:Dynamic, method:String, ?args:Array<Dynamic> ) {
		return {
			methodName: method,
			lineNumber: -1,
			fileName: "",
			customParams: args,
			className: Type.getClassName(Type.getClass(obj))
		};
	}
}
<commit_msg>Improve error message when Async API returns a Failure.<commit_after>package ufront.web;

import haxe.PosInfos;
import tink.core.Error;
import ufront.auth.AuthError;
import ufront.remoting.RemotingError;
using tink.CoreApi;

/**
Helper functions to create `tink.core.Error` objects that are common in Http requests.
**/
class HttpError {

	/**
	Wrap an existing error into a Error

	- If it was a `tink.core.Error` already, return it as is.
	- If it was a normal exception, use code 500, with "Internal Server Error" as the message, the exception as the data, and the call site as the pos.

	@param e The original exception.
	@param msg (optional) The message to use with the error.  The default message is "Internal Server Error".
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 500.
	**/
	static public function wrap( e:Dynamic, ?msg="Internal Server Error", ?pos:PosInfos ):Error {
		if ( Std.is(e, Error) ) return cast e;
		else return Error.withData( ErrorCode.InternalError, msg, e, pos );
	}

	/**
	A Http 400 "Bad Request" error.

	@param reason (optional) If supplied, the error message will be `Bad Request: $reason`.
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 400.
	**/
	static public inline function badRequest( ?reason:String, ?pos:PosInfos ):Error {
		var message = "Bad Request";
		if ( reason!=null )
			message += ': $reason';
		return new Error(400, message, pos);
	}

	/**
	A Http 500 "Internal Server Error", optionally containing the inner error.

	@param msg (optional) The error message. The default message is "Internal Server Error".
	@param inner (optional) The inner data of the error, if any.
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 500.
	**/
	static public function internalServerError( ?msg="Internal Server Error", ?inner:Dynamic, ?pos:PosInfos ):Error {
		return Error.withData( 500, msg, inner, pos );
	}

	/**
	A Http 405 "Method Not Allowed" error.

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 405.
	**/
	static public function methodNotAllowed( ?pos:PosInfos ):Error {
		return new Error( 405, "Method Not Allowed", pos );
	}

	/**
	A Http 404 "Page Not Found" error.

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 404.
	**/
	static public function pageNotFound( ?pos:PosInfos ):Error {
		return new Error( 404, "Page Not Found", pos );
	}

	/**
	A Http 401 "Unauthorized Access" error.

	@param message (optional) A description of the error. Default is "Unauthorized Access".
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 401.
	**/
	static public function unauthorized( ?message:String="Unauthorized Access", ?pos:PosInfos ):Error {
		return new Error( 401, message, pos );
	}

	/**
	A Http 422 "Unprocessable Entity" error.

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 422.
	**/
	static public function unprocessableEntity( ?pos:PosInfos ):Error {
		return new Error( 422, "Unprocessable Entity", pos );
	}

	/**
	A Http 401 "Unauthorized Access" error based on an `AuthError`.

	@param error The AuthError that has been raised.
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 401.
	**/
	static public function authError( error:AuthError, ?pos:PosInfos ):TypedError<AuthError> {
		var msg = switch error {
			case ANotLoggedIn: 'Not Logged In';
			case ALoginFailed( msg ): 'Login Failed: $msg';
			case ANotLoggedInAs( u ): 'Not Logged In As $u';
			case ANoPermission( p ): 'Permission $p denied';
		}
		return Error.typed( 401, msg, error, pos );
	}

	/**
	A Http 500 "Internal Server Error" error based on a `RemotingError`.

	@param error The AuthError that has been raised.
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 401.
	**/
	static public function remotingError<T>( error:RemotingError<T>, ?pos:PosInfos ):TypedError<RemotingError<T>> {
		return switch error {
			case RHttpError( remotingCallString, responseCode, responseData ): Error.typed( responseCode, 'HTTP $responseCode Error during $remotingCallString', error, pos );
			case RApiNotFound( remotingCallString, errorMessage ): Error.typed( 404, 'Remoting API $remotingCallString not found: $errorMessage', error, pos );
			case RServerSideException( remotingCallString, e, stack ):
				var errorObj = Std.instance( e, Error );
				if ( errorObj!=null )
					Error.typed( errorObj.code, errorObj.message, error, pos )
				else
					Error.typed( 500, 'Internal Server Error while executing $remotingCallString', error, pos );
			case RClientCallbackException( remotingCallString, e ): Error.typed( 500, 'Error during callback after $remotingCallString: $e', error, pos );
			case RUnserializeFailed( remotingCallString, troubleLine, err ): Error.typed( 422, 'Remoting serialization failed for call $remotingCallString: could not process $troubleLine', error, pos );
			case RNoRemotingResult( remotingCallString, responseData ): Error.typed( 500, 'Error with response for $remotingCallString: no remoting response found', error, pos );
			case RApiFailure( remotingCallString, data ):
				if ( Std.is(data,Error) ) {
					var e:Error = cast data;
					Error.typed( e.code, e.message, error, e.pos );
				}
				else Error.typed( 500, 'Call to $remotingCallString failed: $data', error, pos );
			case RUnknownException( e ): Error.typed( 500, 'Unknown exception during remoting call', error, pos );
		}
	}

	/**
	A shortcut for an error when a method is not implemented (Http 500).

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 500.
	**/
	static public function notImplemented( ?pos:PosInfos ):Error {
		var methodName = pos.className + "." + pos.methodName;
		return new Error( 500, 'Internal Server Error: $methodName is not implemented on this platform', pos );
	}

	/**
	A shortcut for an error when a method is abstract and should be overridden (Http 500).

	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@return A wrapped `Error` object, with the error code 500.
	**/
	static public function abstractMethod( ?pos:PosInfos ):Error {
		var methodName = pos.className + "." + pos.methodName;
		return new Error( 500, 'Internal Server Error: $methodName is an abstract method and should be overridden by a subclass', pos );
	}

	/**
	A shortcut to throw an `Error` if a particular value is `null` (Http 500).

	@param val The value to check.
	@param name (optional) The name of the argument that is being checked, to provide a more helpful error message. Default is "argument".
	@param pos (optional) The position of the error. Can be supplied, otherwise the call site of this function is used.
	@throws A wrapped `Error` object, with the error code 500, if `val` was null.
	**/
	static public function throwIfNull( val:Dynamic, ?name:String="argument", ?pos:PosInfos ):Void {
		if ( val==null )
			throw new Error( 500, '$name should not be null', pos );
	}

	/**
	Generate a fake `haxe.PosInfos` position for a given class, method and args.

	This is used by some ufront internals to give an accurate position for messages and errors, when we have details about the context but not the actual position.

	For example, `MVCHandler` knows the Controller, Action and Arguments that were called, but if an exception is thrown it does not have a valid position.
	This allows us to fake a "close enough" position, to aid in debugging.

	@param obj - The object (controller / module / handler) from which we derive the class name. Must be a class instance.
	@param method - The method name to use in our position
	@param args - Any arguments parsed to that method.  If not given, an empty array will be used.
	**/
	static inline public function fakePosition( obj:Dynamic, method:String, ?args:Array<Dynamic> ) {
		return {
			methodName: method,
			lineNumber: -1,
			fileName: "",
			customParams: args,
			className: Type.getClassName(Type.getClass(obj))
		};
	}
}

{'fraction_non_alphanumeric': 0.09558493948901838, 'fraction_numerical': 0.013895114298520844, 'mean_word_length': 4.151226551226551, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6786547', 'n_tokens_mistral': 5525, 'n_tokens_neox': 5059, 'n_words': 2364}