Difference between puppet hiera() and lookup() in puppet module code - puppet

I am trying to switch from hiera() to lookup() but it doesn't work.
I used to have the following line of code:
if (hiera("ntp::enabled",0) == 1 ){
and it worked correctly.
After simply replacing hiera with lookup:
if (lookup("ntp::enabled",0) == 1 ){
I am getting a huge error:
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Error while evaluating a Function Call, 'lookup' expects one of:
(NameType = Variant[String, Array[String]] name, ValueType = Type value_type?, MergeType = Variant[String[1], Hash[String, Scalar]] merge?)
rejected: parameter 'value_type' expects a ValueType = Type value, got Integer
(NameType = Variant[String, Array[String]] name, Optional[ValueType] value_type, Optional[MergeType] merge, DefaultValueType = Any default_value)
rejected: expects 4 arguments, got 2
(NameType = Variant[String, Array[String]] name, ValueType = Type value_type?, MergeType = Variant[String[1], Hash[String, Scalar]] merge?)
rejected: parameter 'value_type' expects a ValueType = Type value, got Integer
(OptionsWithName = Struct[{'name' => NameType = Variant[String, Array[String]], 'value_type' => Optional[ValueType = Type], 'default_value' => DefaultValueType = Any, 'override' => Optional[Hash[String, Any]], 'default_values_hash' => Optional[Hash[String, Any]], 'merge' => Optional[MergeType = Variant[String[1], Hash[String, Scalar]]]}] options_hash, BlockType = Callable[NameType = Variant[String, Array[String]]] block?)
rejected: expects 1 argument, got 2
(Variant[String, Array[String]] name, OptionsWithoutName = Struct[{'value_type' => Optional[ValueType = Type], 'default_value' => DefaultValueType = Any, 'override' => Optional[Hash[String, Any]], 'default_values_hash' => Optional[Hash[String, Any]], 'merge' => Optional[MergeType = Variant[String[1], Hash[String, Scalar]]]}] options_hash, BlockType = Callable[NameType = Variant[String, Array[String]]] block?)
rejected: parameter 'options_hash' expects an OptionsWithoutName = Struct[{'value_type' => Optional[ValueType = Type], 'default_value' => DefaultValueType = Any, 'override' => Optional[Hash[String, Any]], 'default_values_hash' => Optional[Hash[String, Any]], 'merge' => Optional[MergeType = Variant[String[1], Hash[String, Scalar]]]}] value, got Integer (file: /etc/puppetlabs/code/environments/sandbox/modules/ntp_mycompany/manifests/init.pp, line: 38, column: 5) on node mybox.mycompany.com
Probably I am missing something simple but I thought that one can simply replace deprecated hiera() call with newer lookup() call.

I am not a puppet expert, but looking at the documentation for lookup it seems to require a different set of parameters than the hiera() call:
lookup( <NAME>, [<VALUE TYPE>], [<MERGE BEHAVIOR>], [<DEFAULT VALUE>] )
If you wanted to provide a default value using positional options, you would also need to fill in values for <VALUE TYPE> and <MERGE BEHAVIOR>. It looks like an alternative is to provide a hash of arguments instead of list. If you continue reading those docs, you see an alternative way of calling the function is:
lookup( [<NAME>], <OPTIONS HASH> )
An example of that mechanism might look like this:
lookup({"name" => "some::thing", "default_value" => "default"})

Related

Typescript, Enums with strings and numbers

I have an interface with
interface mathTest {
mathAction: MathActionEnum;
}
The reason for this is that I want this property to have just one of the specific values from the enum below.
enum MathActionEnum {
'byOne' = 1,
'byTwo' = 2,
'byFour' = 4,
'byEight' = 8,
}
Assume mathAction = 'byOne' -> received from an API response.
First scenario: doing an arithmetic operation, I need the number value: let result: number = amount / MathActionEnum[mathAction] but I get an error:
The right-hand side of an arithmetic operation must be of type 'any',
'number', 'bigint' or an enum type
It is a number but still I need to cast it with Number(MathActionEnum[mathAction]) for the error to go away.
Second scenario: equality check, I need the string value: if (mathAction === MathActionEnum[MathActionEnum.byOne]) but I get an error:
This condition will always return 'false' since the types
'MathActionEnum' and 'string' have no overlap
Which makes sense.
I'm a bit lost, is there a way to syntax it as I expect it to be? Maybe I need to define things differently?
Thanks
TypeScript enums are absolutely NOT suitable for any sort of key-value mapping. The intent is to have a grouping of uniquely identifiable labels, but labels are where it ends. While they may indeed have a number representation under the hood, they are not intended for use as a key-value store. You will have to cast it to "extract the number", and then the type is just number, so you effectively defeat the purpose of enums.
For all intents and purposes, think of them like keys with no useful values:
const MathActionEnum = Object.freeze({
byOne: Symbol(),
byTwo: Symbol(),
byFour: Symbol(),
byEight: Symbol(),
})
Consider the newer alternative, const assertion, instead. They'll provide you with type safety on both keys and values:
const MathActions = {
'byOne': 1,
'byTwo': 2,
'byFour': 4,
'byEight': 8,
} as const
type MathAction = keyof typeof MathActions
type MathActionValue = typeof MathActions[MathAction]
You get full type safety on both keys and values:
const example = (action: MathAction) => {
return 2 * MathActions[action]
}
example('byOne')
// compile error, not a valid key
example('foo')
// -------------
const example2 = (actionValue: MathActionValue) => {
return 2 * actionValue
}
example2(4)
// compile error, not a valid value
example2(19)
You can even add type assertions to check if arbitrary values are a key or value:
const isAction = (action: string): action is MathAction => {
return Object.keys(MathActions).includes(action)
}
isAction
const isActionValue = (actionValue: number): actionValue is MathActionValue => {
return Object.values(MathActions).includes(actionValue as any)
}
You'll even get IDE autocompletion for both keys and values:
Here's a Playground

Zig 0.8.0 error: values of type '(enum literal)' must be comptime known

In Zig 0.8.0, When switching over u8 characters to get an enum type, I encountered a strange compiler error from this code:
.op_type = switch(c1) {
'+' => .add, '-' => .sub,
'*' => .mul, '/' => .div,
'%' => .mod, '^' => .exp,
'|' => .bor, '~' => .bxor,
'&' => .band,
'<' => if (is_long) .lte else .lt,
'>' => if (is_long) .gte else .gt,
'=' => if (is_long) .eq else .nop,
'!' => if (is_long) .neq else return TokenError.NotAToken,
else => unreachable
}
The error was:
.\lib.zig:137:36: error: values of type '(enum literal)' must be comptime known
'<' => if (is_long) .lte else .lt,
^
Normally in zig, "must be comptime known" messages mean that I have left off a type signature on a runtime value, such as const x = 3;. However, there aren't signatures in a switch expression, and the compiler should know what the type is anyway because the field .op_type takes an Op type enum.
I was able to solve the issue by using a switch statement instead of an expression, which I used to assign a placeholder value. The result is atrocious:
var op_type: Op = undefined;
switch(c1) {
'+' => op_type = .add, '-' => op_type = .sub,
'*' => op_type = .mul, '/' => op_type = .div,
'%' => op_type = .mod, '^' => op_type = .exp,
'|' => op_type = .bor, '~' => op_type = .bxor,
'&' => op_type = .band,
'<' => if (is_long) {op_type = .lte;} else {op_type = .lt;},
'>' => if (is_long) {op_type = .gte;} else {op_type = .gt;},
'=' => if (is_long) {op_type = .eq ;} else {op_type = .nop;},
'!' => if (is_long) {op_type = .neq;} else return TokenError.NotAToken,
else => unreachable
}
...
... {
...
.op_type = op_type
}
The reason I'm posting this question is that I don't really understand the problem with the first implementation, and I would like to see if there is a better solution than what I came up with.
What you're experiencing is a quirk of enum literals. When you're writing .sub, at first that's an enum literal that is yet to be coerced to an actual enum type. Normally this process works transparently but in this case the type system doesn't seem to be able to "reason" through your if expressions.
It might be that this will be improved in the self-hosted compiler, but in the meantime the solution is to simply be explicit about the enum type when you encounter this problem.
Here's a simplified version of your code snippet that compiles: https://zig.godbolt.org/z/zeTnf3a67

Automapper through relations in Entity Framework

As a newbie to automapper (v10.0.0) I'm trying to replace one of my queries. I currently use this to generate my response:
var query = from a in _context.ApprovalStatuses.AsNoTracking()
.Include(x => x.ApprovalOrder).ThenInclude(x => x.Worker)
where a.RequestId == request.Id
orderby a.ApprovalOrder.Position
let w = a.ApprovalOrder.Worker
select new RequestApprovalStatusDTO {
AssignedUtc = a.AssignedUtc,
Comments = a.Comments,
DecisionDateUtc = a.ApprovedDateUtc ?? a.RejectedDateUtc,
Email = w.Email,
Name = w.Name,
Uuid = a.Uuid
};
So I started by creating my mapping in my Profile subclass:
CreateMap<ApprovalStatus, RequestApprovalStatusDTO>()
.ForMember(x => x.DecisionDateUtc, x => x.MapFrom(y => y.ApprovedDateUtc ?? y.RejectedDateUtc))
.ForMember(x => x.Email, x => x.MapFrom(y => y.ApprovalOrder.Worker.Email))
.ForMember(x => x.Name, x => x.MapFrom(y => y.ApprovalOrder.Worker.Name));
And then I rewrote the query like so:
var query = _context.ApprovalStatuses
.Include(x => x.ApprovalOrder)
.ThenInclude(x => x.Worker)
.Where(x => x.RequestId == request.Id)
.OrderBy(x => x.ApprovalOrder.Position);
return Ok(_mapper.Map<RequestApprovalStatusDTO>(query));
At runtime, it's telling me
AutoMapperMappingException: Missing type map configuration or unsupported mapping.
Mapping types:
Object -> RequestApprovalStatusDTO
System.Object -> BoltOn.RequestApprovalStatusDTO
lambda_method(Closure , object , RequestApprovalStatusDTO , ResolutionContext )
I understand it's telling me that it doesn't know how to convert from object, but I'm not sure why it's trying to do that since query is an IOrderedQueryable<ApprovalStatus>.
Thanks to Lucian's pointer I was able to solve it like so:
var query = _context.ApprovalStatuses
.Where(x => x.Request.Uuid == uuid)
.OrderBy(x => x.ApprovalOrder.Position);
var approvals = await _mapper.ProjectTo<RequestApprovalStatusDTO>(query).ToArrayAsync();
if (approvals.Length == 0)
return NotFound();
return Ok(approvals);

Accept which array method by caller in typescript

Background:
The only difference between the following functions is:
createGrid uses map (taking a callback returning type T) and returns T[][]
visitGridCoordinates uses forEach (taking a callback returning type void) and returns void.
const createGrid = <T>(width: number, height: number, callback: (x: number, y:number) => T) =>
Array
.from(Array(width).keys())
.map(
(x: number) => Array
.from(Array(height).keys())
.map((y: number) => callback(x, y))
);
const visitGridCoordinates = (width: number, height: number, callback: (x: number, y:number) => void) =>
Array
.from(Array(width).keys())
.forEach(
(x: number) => Array
.from(Array(height).keys())
.forEach((y: number) => callback(x, y);)
);
Question:
Is there a way to create a wrapper function over these that takes which method to use as an argument? I tried several things but kept running into problems.
const createGrid = verbGrid<string>(Array.prototype.map); // or ('map') alternately
const visitGridCoordinates = verbGrid(Array.prototype.forEach); // or ('forEach') alternately
const verbGrid = ???
I tried writing such a function, then used "infer parameter types from usages" and the result almost works:
const verbGrid = <U>(arrayMethod: (callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any) => void) => (
width: number,
height: number,
callback: ((x: number, y:number) => U) | ((x: number, y:number) => boolean)
) => {
const result: void | U[][] = arrayMethod.call(
Array.from(Array(width).keys()),
(x: number) => arrayMethod.call(
Array.from(Array(height).keys()),
(y: number) => callback(x, y)
)
)
return result;
};
But when I try to use createGrid its return type is always only void. And of course it is, because the type of arrayMethod says it returns void. When I try using unioned types for the entire type descriptor or just for the return value, various different errors occur.
I played around with using varof as in varof Array<number>["map"] | varof Array<number>["forEach"] to get more explicit with the types and avoid having to re-describe both method types. However I still had no luck (and I notice the map version doesn't specify U anywhere and I don't know how to do that).
Is this even possible, and if so, do you mind helping me understand where I'm going wrong?
Various results I've had:
The result of verbGrid being type unknown.
No errors in verbGrid but being unable to assign the result to a variable of type U[][] at the call site.
Passing in 'map' and 'forEach' as strings and using them as [method] (instead of doing method.call).
I tried to simplify things a little like so:
type ValueReturning = <T, U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[];
type VoidReturning = <T>(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any) => void;
class Enumerable {
static range(start: number, count: number) {
return Array.from(Array(count).keys()).map(index => start + index);
}
}
But this doesn't work at all ("Type 'ValueReturning' is not generic"):
const verbGrid = <U>(arrayMethod: ValueReturning<number, U> | VoidReturning<number>) => (
Appreciate any guidance.
Note: I am new to TypeScript but not new to Generics (from C#).
Addendum
In case it helps, here are the call sites to these two functions.
this._grid = createGrid(width, height, (x: number, y: number) => {
const cell = new Cell<string>(x, y);
this._cells.add(cell);
return cell;
});
visitGridCoordinates(width, height, (x, y) => {
getNeighborCoordinates(width, height, x, y)
.forEach(([neighborX, neighborY]) => {
this.grid[x][y].neighbors.add(this._grid[neighborX][neighborY]);
});
});
It's fine to suggest alternate methods for achieving the same goal, but I really was more interested in learning TypeScript better than I am in fixing this code to be perfectly optimal. (I spent a couple of hours creating a Boggle/Wordament solver last night—it works great and was fun to build.)
I ran into some annoying difficulties with this, like you did. One type function that I found useful is this:
type ArrayUnlessVoid<T> = T extends void ? void : Array<T>;
It's a conditional type that turns T into T[] unless T is void, in which case it's just void. We might want this because it unifies what map() and forEach() do: they each take a callback that returns a T and returns an ArrayUnlessVoid<T>.
Okay, here is the typing for verbGrid():
const verbGrid =
<R, O>(arrayMethod: (
callbackfn: (value: any, index: number, array: any[]) => R,
thisArg?: any
) => O) =>
(width: number, height: number, callback: ((x: number, y: number) => R)) => {
const outerArrayMethod = arrayMethod as any as (
callbackfn: (value: any, index: number, array: any[]) => O,
thisArg?: any
) => ArrayUnlessVoid<O>;
const result = outerArrayMethod.call(
Array.from(Array(width).keys()),
(x: number) => arrayMethod.call(
Array.from(Array(height).keys()),
(y: number) => callback(x, y)
));
return result;
}
Blecch. It would be nice if verbGrid could take something that turns a callback-returning-T into an ArrayUnlessVoid<T> and returns an ArrayUnlessVoid<ArrayUnlessVoid<T>>. But the compiler can't really see that Array.prototype.map() matches the former definition. So I add a new type parameter O to take the place of ArrayUnlessVoid<T> and return ArrayUnlessVoid<O> instead.
It would also be nice if there were some easier way to describe the two different contexts in which you use arrayMethod in the implementation. Instead I just give up and use a type assertion to give the outer call to arrayMethod a different type signature.
Anyway, you can see that these work as advertised:
const createGrid = verbGrid(Array.prototype.map);
// const createGrid: <U>(
// width: number, height: number, callback: (x: number, y: number) => U
// ) => U[][]
const visitGridCoordinates = verbGrid(Array.prototype.forEach);
// const visitGridCoordinates: (
// width: number, height: number,
// callback: (x: number, y: number) => void
// ) => void
Okay, hope that helps; good luck!
Playground link to code

Typescript: Evaluate type of generic function

Is there any trick to "evaluate" the type of a generic fuction?
Consider the following:
type Arr = <A>() => A[]
type Ev<G, A> = ???
Question: Is it possible to fill in ??? such that Ev<Arr, A> equals () => A[]? (As compared to <A>() => A[])
(Update 2022/04/26)
Something similar/related will be possible with TS 4.7; Even though that's not fully what we'd want here.
https://github.com/microsoft/TypeScript/pull/47607
Some more examples for the desired behavior:
Ev<<A>() => A[], number>
// should evaluate to
// () => number[]
Ev<<A>() => string, number>
// should evaluate to
// () => string
Ev<<A>() => [string, A], { some: "thing" }>
// should evaluate to
// () => [string, { some: "thing" }]
A simplified version of the question would be: Can we define
type EvNum<A> = ???
such that
EvNum<
<X>() => X
> // should be `number`
EvNum<
<X>() => X[]
> // should be `number[]`
EvNum<
<X>() => [X, "hi"]
> // should be `[number, "hi"]`
EvNum<
<X>() => SomeGenericType<X>
> // should be `SomeGenericType<number>`
EvNum<
<X>() => "constant"
> // should be `"constant"`
As to your first question, those are referred to as Higher Kinded Types, and are not supported in Typescript as of this answer.
A Higher Kinded Type is simply "A type that abstracts over some type that, in turn, abstracts over another type." So if you want a type, that you pass in a type to abstractly create a new type, that is an example of a higher kinded type. And it is not possible in TS.
You cannot pass a second type into a generic type and come out with a derived type.
Your last example (simplified) is literally ReturnType so not sure what you are meaning. It is perfectly possible to come up with. But you can't make a type that comes up with it.
type EvNum<T> = () => T;
type Arr<T> = T[];
function func<T>(param: EvNum<T>): T {
return param();
}
let x1 = func(()=> 4); //number
let x2 = func(()=> [4]); //number[]
let x3 = func(()=> [4, "hi"] as const); //[4, "hi"]
let x4 = func(()=> "constant" as const); //"constant"
let cool: Arr<number> = [4, 5, 6];
let x5 = func(() => cool); //Arr<number>
This passes your requested types
If I understand you correctly it should be:
type EV<T> = () => T;
Otherwise the question makes no sense or must be explained in more detail.

Resources