mirror of https://github.com/grafana/grafana
Frontend: Foundations for multi tenant frontend (#78815)
* Frontend: Foundations for multi tenant frontend * improve manifest parsing for multi-tenant frontend (#78876) * add test * add test * ?? * Updates * Added cache * test cleanup * lint * fix test * fix error templates * cleanup * remove copy * revert changes to list testdata * comment cleanup * prepare integration tests * Remove integrety --------- Co-authored-by: Ryan McKinley <ryantxu@gmail.com>pull/79056/head
parent
7b78061235
commit
ed128ea964
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,91 @@ |
|||||||
|
package webassets |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/api/dtos" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
) |
||||||
|
|
||||||
|
type ManifestInfo struct { |
||||||
|
FilePath string `json:"src,omitempty"` |
||||||
|
Integrity string `json:"integrity,omitempty"` |
||||||
|
|
||||||
|
// The known entrypoints
|
||||||
|
App *EntryPointInfo `json:"app,omitempty"` |
||||||
|
Dark *EntryPointInfo `json:"dark,omitempty"` |
||||||
|
Light *EntryPointInfo `json:"light,omitempty"` |
||||||
|
} |
||||||
|
|
||||||
|
type EntryPointInfo struct { |
||||||
|
Assets struct { |
||||||
|
JS []string `json:"js,omitempty"` |
||||||
|
CSS []string `json:"css,omitempty"` |
||||||
|
} `json:"assets,omitempty"` |
||||||
|
} |
||||||
|
|
||||||
|
var entryPointAssetsCache *dtos.EntryPointAssets = nil |
||||||
|
|
||||||
|
func GetWebAssets(cfg *setting.Cfg) (*dtos.EntryPointAssets, error) { |
||||||
|
if cfg.Env != setting.Dev && entryPointAssetsCache != nil { |
||||||
|
return entryPointAssetsCache, nil |
||||||
|
} |
||||||
|
|
||||||
|
result, err := readWebAssets(filepath.Join(cfg.StaticRootPath, "build", "assets-manifest.json")) |
||||||
|
entryPointAssetsCache = result |
||||||
|
|
||||||
|
return entryPointAssetsCache, err |
||||||
|
} |
||||||
|
|
||||||
|
func readWebAssets(manifestpath string) (*dtos.EntryPointAssets, error) { |
||||||
|
//nolint:gosec
|
||||||
|
bytes, err := os.ReadFile(manifestpath) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("failed to load assets-manifest.json %w", err) |
||||||
|
} |
||||||
|
|
||||||
|
manifest := map[string]ManifestInfo{} |
||||||
|
err = json.Unmarshal(bytes, &manifest) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("failed to read assets-manifest.json %w", err) |
||||||
|
} |
||||||
|
|
||||||
|
integrity := make(map[string]string, 100) |
||||||
|
for _, v := range manifest { |
||||||
|
if v.Integrity != "" && v.FilePath != "" { |
||||||
|
integrity[v.FilePath] = v.Integrity |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
entryPoints, ok := manifest["entrypoints"] |
||||||
|
if !ok { |
||||||
|
return nil, fmt.Errorf("could not find entrypoints in asssets-manifest") |
||||||
|
} |
||||||
|
|
||||||
|
if entryPoints.App == nil || len(entryPoints.App.Assets.JS) == 0 { |
||||||
|
return nil, fmt.Errorf("missing app entry") |
||||||
|
} |
||||||
|
if entryPoints.Dark == nil || len(entryPoints.Dark.Assets.CSS) == 0 { |
||||||
|
return nil, fmt.Errorf("missing dark entry") |
||||||
|
} |
||||||
|
if entryPoints.Light == nil || len(entryPoints.Light.Assets.CSS) == 0 { |
||||||
|
return nil, fmt.Errorf("missing light entry") |
||||||
|
} |
||||||
|
|
||||||
|
entryPointJSAssets := make([]dtos.EntryPointAsset, 0) |
||||||
|
for _, entry := range entryPoints.App.Assets.JS { |
||||||
|
entryPointJSAssets = append(entryPointJSAssets, dtos.EntryPointAsset{ |
||||||
|
FilePath: entry, |
||||||
|
Integrity: integrity[entry], |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return &dtos.EntryPointAssets{ |
||||||
|
JSFiles: entryPointJSAssets, |
||||||
|
CSSDark: entryPoints.Dark.Assets.CSS[0], |
||||||
|
CSSLight: entryPoints.Light.Assets.CSS[0], |
||||||
|
}, nil |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package webassets |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
) |
||||||
|
|
||||||
|
func TestReadWebassets(t *testing.T) { |
||||||
|
assets, err := readWebAssets("testdata/sample-assets-manifest.json") |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
dto, err := json.MarshalIndent(assets, "", " ") |
||||||
|
require.NoError(t, err) |
||||||
|
//fmt.Printf("%s\n", string(dto))
|
||||||
|
|
||||||
|
require.JSONEq(t, `{ |
||||||
|
"JSFiles": [ |
||||||
|
{ |
||||||
|
"FilePath": "public/build/runtime.20ed8c01880b812ed29f.js", |
||||||
|
"Integrity": "sha256-rcdxIHk6cWgu4jiFa1a+pWlileYD/R72GaS8ZACBUdw= sha384-I/VJZQkt+TuJTvu61ihdWPds7EHfLrW5CxeQ0x9gtSqoPg9Z17Uawz1yoYaTdxqQ sha512-4CPAbh4KdTmGxHoQw4pgpYmgAquupVfwfo6UBV2cGU3vGFnEwkhq320037ETwWs+n9xB/bAMOvrdabp1SA1+8g==" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"FilePath": "public/build/3951.4e474348841d792ab1ba.js", |
||||||
|
"Integrity": "sha256-dHqXXTRA3osYhHr9rol8hOV0nC4VP0pr5tbMp5VD95Q= sha384-4QJaSTibnxdYeYsLnmXtd1+If6IkAmXlLR0uYHN5+N+fS0FegHRH7MIFaRGjiO1B sha512-vRLEeEGbxBCx0z+l/m14fSK49reqWGA9zQzsCrD+TQQBmP07YIoRPwopMMyxtKljbbRFV0bW2bUZ7ZvzOZYoIQ==" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"FilePath": "public/build/3651.4e8f7603e9778e1e9b59.js", |
||||||
|
"Integrity": "sha256-+N7caL91pVANd7C/aquAneRTjBQenCwaEKqj+3qkjxc= sha384-GQR7GyHPEwwEVph9gGYWEWvMYxkITwcOjieehbPidXZrybuQyw9cpDkjnWo1tj/w sha512-zyPM+8AxyLuECEXjb9w6Z2Sy8zmJdkfTWQphcvAb8AU4ZdkCqLmyjmOs/QQlpfKDe0wdOLyR3V9QgTDDlxtVlQ==" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"FilePath": "public/build/1272.8c79fc44bf7cd993c953.js", |
||||||
|
"Integrity": "sha256-d7MRVimV83v4YQ5rdURfTaaFtiedXP3EMLT06gvvBuQ= sha384-8tRpYHQ+sEkZ8ptiIbKAbKPpHTJVnmaWDN56vJoWWUCzV1Q2w034wcJNKDJDJdAs sha512-cIZWoJHusF8qODBOj2j4b18ewcLLMo/92YQSwYQjln2G5e3o1bSO476ox2I2iecJ/tnhQK5j01h9BzTt3dNTrA==" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"FilePath": "public/build/6902.070074e8f5a989b8f4c3.js", |
||||||
|
"Integrity": "sha256-TMo/uTZueyEHtkBzlLZzhwYKWF0epE4qbouo5xcwZkU= sha384-xylZJMtJ7+EsUBBdQZvPh+BeHJ3BnfclqI2vx/8QC9jvfYe/lhRsWW9OMJsxE/Aq sha512-EOmf+KZQMFPoTWAROL8bBLFfHhgvDH8ONycq37JaV7lz+sQOTaWBN2ZD0F/mMdOD5zueTg/Y1RAUP6apoEcHNQ==" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"FilePath": "public/build/app.0439db6f56ee4aa501b2.js", |
||||||
|
"Integrity": "sha256-q6muaKY7BuN2Ff+00aw69628MXatcFnLNzWRnAD98DI= sha384-gv6lAbkngOHR05bvyOR8dm/J3wIjQQWSjyxK7W8vt2rG9uxcjvvDQV7aI6YbUhfX sha512-o/0mSlJ/OoqrpGdOIWCE3ZCe8n+qqLbgNCERtx9G8FIzsv++CvIWSGbbILjOTGfnEfEQWcKMH0macVpVBSe1Og==" |
||||||
|
} |
||||||
|
], |
||||||
|
"CSSDark": "public/build/grafana.dark.a28b24b45b2bbcc628cc.css", |
||||||
|
"CSSLight": "public/build/grafana.light.3572f6d5f8b7daa8d8d0.css" |
||||||
|
}`, string(dto)) |
||||||
|
} |
@ -1,42 +0,0 @@ |
|||||||
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
|
||||||
|
|
||||||
/* |
|
||||||
* This plugin returns the css associated with entrypoints. Those chunks can be found |
|
||||||
* in `htmlWebpackPlugin.files.cssChunks`. |
|
||||||
* The HTML Webpack plugin removed the chunks object in v5 in favour of an array however if we want |
|
||||||
* to do anything smart with hashing (e.g. [contenthash]) we need a map of { themeName: chunkNameWithHash }. |
|
||||||
*/ |
|
||||||
class HTMLWebpackCSSChunks { |
|
||||||
/** |
|
||||||
* @param {import('webpack').Compiler} compiler |
|
||||||
*/ |
|
||||||
apply(compiler) { |
|
||||||
compiler.hooks.compilation.tap( |
|
||||||
'HTMLWebpackCSSChunks', |
|
||||||
/** |
|
||||||
* @param {import('webpack').Compilation} compilation |
|
||||||
*/ |
|
||||||
(compilation) => { |
|
||||||
HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync( |
|
||||||
'HTMLWebpackCSSChunks', |
|
||||||
(data, cb) => { |
|
||||||
data.assets.cssChunks = {}; |
|
||||||
|
|
||||||
for (const entryPoint of compilation.entrypoints.values()) { |
|
||||||
for (const chunk of entryPoint.chunks) { |
|
||||||
const cssFile = [...chunk.files].find((file) => file.endsWith('.css')); |
|
||||||
if (cssFile !== undefined) { |
|
||||||
data.assets.cssChunks[chunk.name] = cssFile; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
cb(null, data); |
|
||||||
} |
|
||||||
); |
|
||||||
} |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = HTMLWebpackCSSChunks; |
|
Loading…
Reference in new issue