Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
fdb9ae6c40 | |||
3c82ffc0ab | |||
5dd3103aba | |||
84fc81f531 | |||
a20cbc62a5 | |||
e6a93e2838 | |||
3cff54561f |
@ -1,3 +1,12 @@
|
|||||||
|
### **2.0.4** (2021-07-03)
|
||||||
|
|
||||||
|
- Fix: #7
|
||||||
|
|
||||||
|
### **2.0.3** (2021-06-30)
|
||||||
|
|
||||||
|
- Escape double quote in ftl to js conversion #15
|
||||||
|
- Update readme
|
||||||
|
|
||||||
### **2.0.2** (2021-06-28)
|
### **2.0.2** (2021-06-28)
|
||||||
|
|
||||||
- Updagte README for implementing non incuded pages
|
- Updagte README for implementing non incuded pages
|
||||||
|
@ -208,7 +208,9 @@ and the result you can expect:
|
|||||||
If you want to go beyond only customizing the CSS you can re-implement some of the
|
If you want to go beyond only customizing the CSS you can re-implement some of the
|
||||||
pages or event add new ones.
|
pages or event add new ones.
|
||||||
|
|
||||||
If you want to go this way checkout the demo setup provided [here](https://github.com/garronej/keycloakify-demo-app/tree/look_and_feel)
|
If you want to go this way checkout the demo setup provided [here](https://github.com/garronej/keycloakify-demo-app/tree/look_and_feel).
|
||||||
|
If you prefer a real life example you can checkout [onyxia-web's source](https://github.com/InseeFrLab/onyxia-web/tree/main/src/app/components/KcApp).
|
||||||
|
The web app is in production [here](https://datalab.sspcloud.fr).
|
||||||
|
|
||||||
Main takeaways are:
|
Main takeaways are:
|
||||||
- You must declare your custom pages in the package.json. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/package.json#L17-L22)
|
- You must declare your custom pages in the package.json. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/package.json#L17-L22)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "2.0.2",
|
"version": "2.0.4",
|
||||||
"description": "Keycloak theme generator for Reacts app",
|
"description": "Keycloak theme generator for Reacts app",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -113,7 +113,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<#attempt>
|
<#attempt>
|
||||||
"${object?no_esc}"
|
"${object?replace('"', '\\"')?no_esc}"
|
||||||
<#recover>
|
<#recover>
|
||||||
/* couldn't convert into string non hash, non method, non boolean, non enumerable object */
|
/* couldn't convert into string non hash, non method, non boolean, non enumerable object */
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -8,9 +8,9 @@ import { deepAssign } from "../tools/deepAssign";
|
|||||||
|
|
||||||
|
|
||||||
export type ExtendsKcContextBase<
|
export type ExtendsKcContextBase<
|
||||||
KcContextExtended extends ({ pageId: string; } | undefined)
|
KcContextExtended extends { pageId: string; }
|
||||||
> =
|
> =
|
||||||
KcContextExtended extends undefined ?
|
[KcContextExtended] extends [never] ?
|
||||||
KcContextBase :
|
KcContextBase :
|
||||||
AndByDiscriminatingKey<
|
AndByDiscriminatingKey<
|
||||||
"pageId",
|
"pageId",
|
||||||
@ -18,7 +18,7 @@ export type ExtendsKcContextBase<
|
|||||||
KcContextBase
|
KcContextBase
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export function getKcContext<KcContextExtended extends ({ pageId: string; } | undefined) = undefined>(
|
export function getKcContext<KcContextExtended extends { pageId: string; } = never>(
|
||||||
params?: {
|
params?: {
|
||||||
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
||||||
mockData?: readonly DeepPartial<ExtendsKcContextBase<KcContextExtended>>[];
|
mockData?: readonly DeepPartial<ExtendsKcContextBase<KcContextExtended>>[];
|
||||||
@ -51,22 +51,15 @@ export function getKcContext<KcContextExtended extends ({ pageId: string; } | un
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const kcContext: any = { "pageId": mockPageId };
|
const kcContext: any = {};
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": kcContext,
|
"target": kcContext,
|
||||||
"source": kcContextCommonMock
|
"source": kcContextDefaultMock !== undefined ?
|
||||||
|
kcContextDefaultMock :
|
||||||
|
{ "pageId": mockPageId, ...kcContextCommonMock, }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (kcContextDefaultMock !== undefined) {
|
|
||||||
|
|
||||||
deepAssign({
|
|
||||||
"target": kcContext,
|
|
||||||
"source": kcContextDefaultMock
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (partialKcContextCustomMock !== undefined) {
|
if (partialKcContextCustomMock !== undefined) {
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { is } from "tsafe/is";
|
import { is } from "tsafe/is";
|
||||||
|
|
||||||
|
//Warning: Be mindful that because of array this is not idempotent.
|
||||||
export function deepAssign(
|
export function deepAssign(
|
||||||
params: {
|
params: {
|
||||||
target: Record<string, unknown>;
|
target: Record<string, unknown>;
|
||||||
|
@ -6,202 +6,245 @@ import { same } from "evt/tools/inDepth";
|
|||||||
import { doExtends } from "tsafe/doExtends";
|
import { doExtends } from "tsafe/doExtends";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { kcContextMocks, kcContextCommonMock } from "../../lib/getKcContext/kcContextMocks";
|
import { kcContextMocks, kcContextCommonMock } from "../../lib/getKcContext/kcContextMocks";
|
||||||
import { deepClone } from "../../lib/tools/deepClone";
|
import { deepClone } from "../../lib/tools/deepClone";
|
||||||
import type { Any } from "ts-toolbelt";
|
import type { Any } from "ts-toolbelt";
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
const authorizedMailDomains = [
|
const authorizedMailDomains = [
|
||||||
"example.com",
|
"example.com",
|
||||||
"another-example.com",
|
"another-example.com",
|
||||||
"*.yet-another-example.com",
|
"*.yet-another-example.com",
|
||||||
"*.example.com",
|
"*.example.com",
|
||||||
"hello-world.com"
|
"hello-world.com"
|
||||||
];
|
];
|
||||||
|
|
||||||
const displayName = "this is an overwritten common value";
|
const displayName = "this is an overwritten common value";
|
||||||
|
|
||||||
const aNonStandardValue = "a non standard value";
|
const aNonStandardValue1 = "a non standard value 1";
|
||||||
|
const aNonStandardValue2 = "a non standard value 2";
|
||||||
|
|
||||||
type KcContextExtended = {
|
type KcContextExtended = {
|
||||||
pageId: "register.ftl";
|
pageId: "register.ftl";
|
||||||
authorizedMailDomains: string[];
|
authorizedMailDomains: string[];
|
||||||
} | {
|
} | {
|
||||||
pageId: "my-extra-page-1.ftl";
|
pageId: "info.ftl";
|
||||||
} | {
|
aNonStandardValue1: string;
|
||||||
pageId: "my-extra-page-2.ftl";
|
} | {
|
||||||
aNonStandardValue: string;
|
pageId: "my-extra-page-1.ftl";
|
||||||
};
|
} | {
|
||||||
|
pageId: "my-extra-page-2.ftl";
|
||||||
|
aNonStandardValue2: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getKcContextProxy = (
|
||||||
|
params: {
|
||||||
|
mockPageId: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
|
||||||
|
const { mockPageId } = params;
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext<KcContextExtended>({
|
||||||
|
mockPageId,
|
||||||
|
"mockData": [
|
||||||
|
{
|
||||||
|
"pageId": "login.ftl",
|
||||||
|
"realm": { displayName }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pageId": "info.ftl",
|
||||||
|
aNonStandardValue1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pageId": "register.ftl",
|
||||||
|
authorizedMailDomains
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pageId": "my-extra-page-2.ftl",
|
||||||
|
aNonStandardValue2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return { kcContext };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const pageId = "login.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Login>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
//NOTE: deepClone for printIfExists or other functions...
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
||||||
|
|
||||||
|
mock.realm.displayName = displayName;
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
function getKcContextProxy(
|
|
||||||
params: {
|
|
||||||
mockPageId: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
|
||||||
}
|
}
|
||||||
) {
|
|
||||||
|
|
||||||
const { mockPageId } = params;
|
{
|
||||||
|
const pageId = "info.ftl";
|
||||||
|
|
||||||
const { kcContext } = getKcContext<KcContextExtended>({
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
mockPageId,
|
|
||||||
"mockData": [
|
assert(kcContext?.pageId === pageId);
|
||||||
{
|
|
||||||
"pageId": "login.ftl",
|
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||||
"realm": { displayName }
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Info & { pageId: typeof pageId; aNonStandardValue1: string; }>, 1>();
|
||||||
},
|
|
||||||
{
|
assert(same(
|
||||||
"pageId": "register.ftl",
|
deepClone(kcContext),
|
||||||
authorizedMailDomains
|
(() => {
|
||||||
},
|
|
||||||
{
|
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
||||||
"pageId": "my-extra-page-2.ftl",
|
|
||||||
aNonStandardValue
|
Object.assign(mock, { aNonStandardValue1 });
|
||||||
}
|
|
||||||
]
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const pageId = "register.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Register & { pageId: typeof pageId; authorizedMailDomains: string[]; }>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
||||||
|
|
||||||
|
Object.assign(mock, { authorizedMailDomains });
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const pageId = "my-extra-page-2.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Common & { pageId: typeof pageId; aNonStandardValue2: string; }>, 1>();
|
||||||
|
|
||||||
|
kcContext.aNonStandardValue2;
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextCommonMock);
|
||||||
|
|
||||||
|
Object.assign(mock, { pageId, aNonStandardValue2 });
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const pageId = "my-extra-page-1.ftl";
|
||||||
|
|
||||||
|
console.log("We expect a warning here =>");
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Common & { pageId: typeof pageId; }>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextCommonMock);
|
||||||
|
|
||||||
|
Object.assign(mock, { pageId });
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const pageId = "login.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext({
|
||||||
|
"mockPageId": pageId
|
||||||
});
|
});
|
||||||
|
|
||||||
return { kcContext };
|
doExtends<Any.Equals<typeof kcContext, KcContextBase | undefined>, 1>();
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
const pageId= "login.ftl";
|
|
||||||
|
|
||||||
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
assert(kcContext?.pageId === pageId);
|
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase.Login>();
|
|
||||||
|
|
||||||
assert(same(
|
|
||||||
//NOTE: deepClone for printIfExists or other functions...
|
|
||||||
deepClone(kcContext),
|
|
||||||
(() => {
|
|
||||||
|
|
||||||
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
|
||||||
|
|
||||||
mock.realm.displayName = displayName;
|
|
||||||
|
|
||||||
return mock;
|
|
||||||
|
|
||||||
})()
|
|
||||||
));
|
|
||||||
|
|
||||||
console.log(`PASS ${pageId}`);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const pageId = "register.ftl";
|
|
||||||
|
|
||||||
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
assert(kcContext?.pageId === pageId);
|
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase.Register>();
|
|
||||||
|
|
||||||
assert(same(
|
assert(same(
|
||||||
deepClone(kcContext),
|
deepClone(kcContext),
|
||||||
(() => {
|
deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!)
|
||||||
|
|
||||||
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
|
||||||
|
|
||||||
Object.assign(mock, { authorizedMailDomains });
|
|
||||||
|
|
||||||
return mock;
|
|
||||||
|
|
||||||
})()
|
|
||||||
));
|
));
|
||||||
|
|
||||||
console.log(`PASS ${pageId}`);
|
console.log("PASS no extension");
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
const pageId = "my-extra-page-2.ftl";
|
|
||||||
|
|
||||||
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
assert(kcContext?.pageId === pageId);
|
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<typeof kcContext, KcContextBase>();
|
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase.Common>();
|
|
||||||
|
|
||||||
assert(same(
|
|
||||||
deepClone(kcContext),
|
|
||||||
(() => {
|
|
||||||
|
|
||||||
const mock = deepClone(kcContextCommonMock);
|
|
||||||
|
|
||||||
Object.assign(mock, { pageId, aNonStandardValue });
|
|
||||||
|
|
||||||
return mock;
|
|
||||||
|
|
||||||
})()
|
|
||||||
));
|
|
||||||
|
|
||||||
console.log(`PASS ${pageId}`);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
const pageId = "my-extra-page-1.ftl";
|
|
||||||
|
|
||||||
console.log("We expect a warning here =>");
|
|
||||||
|
|
||||||
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
assert(kcContext?.pageId === pageId);
|
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<typeof kcContext, KcContextBase>();
|
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase.Common>();
|
|
||||||
|
|
||||||
assert(same(
|
|
||||||
deepClone(kcContext),
|
|
||||||
(() => {
|
|
||||||
|
|
||||||
const mock = deepClone(kcContextCommonMock);
|
|
||||||
|
|
||||||
Object.assign(mock, { pageId });
|
|
||||||
|
|
||||||
return mock;
|
|
||||||
|
|
||||||
})()
|
|
||||||
));
|
|
||||||
|
|
||||||
console.log(`PASS ${pageId}`);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
const { kcContext } = getKcContext();
|
const { kcContext } = getKcContext();
|
||||||
|
|
||||||
//@ts-expect-error
|
doExtends<Any.Equals<typeof kcContext, KcContextBase | undefined>, 1>();
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase | undefined>();
|
assert(kcContext === undefined);
|
||||||
|
|
||||||
|
console.log("PASS no extension, no mock");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user