mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-13 18:21:57 +08:00
Compare commits
78 Commits
fix/editor
...
fix/sql
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75945c7c65 | ||
|
|
8b61bbfcbb | ||
|
|
2c39611b47 | ||
|
|
475cb85600 | ||
|
|
37de8700d5 | ||
|
|
9db05bddbd | ||
|
|
4bda81bf52 | ||
|
|
00c5254ea9 | ||
|
|
bc97fc4c03 | ||
|
|
f953b96d52 | ||
|
|
40793efe4e | ||
|
|
22a29605d8 | ||
|
|
cbfd4f7fcb | ||
|
|
992cf82e5c | ||
|
|
ea89ac864c | ||
|
|
35a4a93ca7 | ||
|
|
2544d4b12d | ||
|
|
d6ee7ef76a | ||
|
|
daa2c03643 | ||
|
|
92a61e7c45 | ||
|
|
e6ad9b29d6 | ||
|
|
ea040b7912 | ||
|
|
28e874bdcd | ||
|
|
4125c618d7 | ||
|
|
088615d13f | ||
|
|
b02d07917d | ||
|
|
9f1d44e542 | ||
|
|
587e8a197f | ||
|
|
5023e962b3 | ||
|
|
28c41b02c0 | ||
|
|
7a80313f1d | ||
|
|
6bc1233253 | ||
|
|
f82ca986c5 | ||
|
|
d45b08c5d3 | ||
|
|
d0c6ae1835 | ||
|
|
be2d3a0d0e | ||
|
|
c81c799e44 | ||
|
|
0bcf1b09bd | ||
|
|
f7f0270f75 | ||
|
|
8033ccbe6d | ||
|
|
694c208ee3 | ||
|
|
41c7388f63 | ||
|
|
2f2a9b2d32 | ||
|
|
083ec6c0d7 | ||
|
|
32690e98da | ||
|
|
ed8c2f3168 | ||
|
|
1a9f53150b | ||
|
|
1779eef91b | ||
|
|
d7b3f82d18 | ||
|
|
fb34a7176c | ||
|
|
c5316929ba | ||
|
|
1ef1818327 | ||
|
|
472c7f415b | ||
|
|
c43d294ab4 | ||
|
|
06747df054 | ||
|
|
2aeb2ad65e | ||
|
|
3fd6e859df | ||
|
|
6d85bbe488 | ||
|
|
5bb1252795 | ||
|
|
dfaf120314 | ||
|
|
be5ccbbc0b | ||
|
|
12950051d6 | ||
|
|
03cd25b6c0 | ||
|
|
829615ffec | ||
|
|
6a24436418 | ||
|
|
c726a1a342 | ||
|
|
7d7835ee9c | ||
|
|
17d30c0e8d | ||
|
|
62bd5c339f | ||
|
|
111f702b9b | ||
|
|
3dd115dce1 | ||
|
|
75925cb53a | ||
|
|
de5bed02f8 | ||
|
|
72c670570b | ||
|
|
a450b4ed5a | ||
|
|
66be61efa3 | ||
|
|
caddd0f93d | ||
|
|
cc32cbe79c |
@@ -34,6 +34,7 @@
|
||||
"@astrojs/sitemap": "^3.2.0",
|
||||
"@astrojs/tailwind": "^5.1.2",
|
||||
"@fingerprintjs/fingerprintjs": "^4.5.0",
|
||||
"@microsoft/clarity": "^1.0.0",
|
||||
"@nanostores/react": "^0.8.0",
|
||||
"@napi-rs/image": "^1.9.2",
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
@@ -72,14 +73,15 @@
|
||||
"satori-html": "^0.3.2",
|
||||
"sharp": "^0.33.5",
|
||||
"slugify": "^1.6.6",
|
||||
"tiptap-markdown": "^0.8.10",
|
||||
"tailwind-merge": "^2.5.3",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"tiptap-markdown": "^0.8.10",
|
||||
"turndown": "^7.2.0",
|
||||
"unified": "^11.0.5",
|
||||
"zustand": "^4.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ai-sdk/google": "^1.1.19",
|
||||
"@playwright/test": "^1.48.0",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
@@ -90,6 +92,7 @@
|
||||
"@types/react-slick": "^0.23.13",
|
||||
"@types/sanitize-html": "^2.13.0",
|
||||
"@types/turndown": "^5.0.5",
|
||||
"ai": "^4.1.51",
|
||||
"csv-parser": "^3.0.0",
|
||||
"gh-pages": "^6.2.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
|
||||
278
pnpm-lock.yaml
generated
278
pnpm-lock.yaml
generated
@@ -10,19 +10,22 @@ importers:
|
||||
dependencies:
|
||||
'@astrojs/node':
|
||||
specifier: ^8.3.4
|
||||
version: 8.3.4(astro@4.16.18)
|
||||
version: 8.3.4(astro@4.16.18(@types/node@18.19.74)(rollup@4.32.0)(typescript@5.7.3))
|
||||
'@astrojs/react':
|
||||
specifier: ^3.6.2
|
||||
version: 3.6.3(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
version: 3.6.3(@types/node@18.19.74)(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@astrojs/sitemap':
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.1
|
||||
'@astrojs/tailwind':
|
||||
specifier: ^5.1.2
|
||||
version: 5.1.5(astro@4.16.18)(tailwindcss@3.4.17)
|
||||
version: 5.1.5(astro@4.16.18(@types/node@18.19.74)(rollup@4.32.0)(typescript@5.7.3))(tailwindcss@3.4.17)
|
||||
'@fingerprintjs/fingerprintjs':
|
||||
specifier: ^4.5.0
|
||||
version: 4.5.1
|
||||
'@microsoft/clarity':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
'@nanostores/react':
|
||||
specifier: ^0.8.0
|
||||
version: 0.8.4(nanostores@0.11.3)(react@18.3.1)
|
||||
@@ -43,7 +46,7 @@ importers:
|
||||
version: 18.3.5(@types/react@18.3.18)
|
||||
astro:
|
||||
specifier: ^4.16.1
|
||||
version: 4.16.18(typescript@5.7.3)
|
||||
version: 4.16.18(@types/node@18.19.74)(rollup@4.32.0)(typescript@5.7.3)
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
@@ -109,10 +112,10 @@ importers:
|
||||
version: 18.3.1(react@18.3.1)
|
||||
react-tooltip:
|
||||
specifier: ^5.28.0
|
||||
version: 5.28.0(react-dom@18.3.1)(react@18.3.1)
|
||||
version: 5.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
reactflow:
|
||||
specifier: ^11.11.4
|
||||
version: 11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
version: 11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rehype-external-links:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
@@ -145,7 +148,7 @@ importers:
|
||||
version: 3.4.17
|
||||
tiptap-markdown:
|
||||
specifier: ^0.8.10
|
||||
version: 0.8.10(@tiptap/core@2.11.3)
|
||||
version: 0.8.10(@tiptap/core@2.11.3(@tiptap/pm@2.11.3))
|
||||
turndown:
|
||||
specifier: ^7.2.0
|
||||
version: 7.2.0
|
||||
@@ -156,6 +159,9 @@ importers:
|
||||
specifier: ^4.5.5
|
||||
version: 4.5.6(@types/react@18.3.18)(react@18.3.1)
|
||||
devDependencies:
|
||||
'@ai-sdk/google':
|
||||
specifier: ^1.1.19
|
||||
version: 1.1.19(zod@3.24.1)
|
||||
'@playwright/test':
|
||||
specifier: ^1.48.0
|
||||
version: 1.50.0
|
||||
@@ -186,6 +192,9 @@ importers:
|
||||
'@types/turndown':
|
||||
specifier: ^5.0.5
|
||||
version: 5.0.5
|
||||
ai:
|
||||
specifier: ^4.1.51
|
||||
version: 4.1.51(react@18.3.1)(zod@3.24.1)
|
||||
csv-parser:
|
||||
specifier: ^3.0.0
|
||||
version: 3.1.0
|
||||
@@ -216,6 +225,46 @@ importers:
|
||||
|
||||
packages:
|
||||
|
||||
'@ai-sdk/google@1.1.19':
|
||||
resolution: {integrity: sha512-Q4l2iWAADUf1pGbXX60A2nnUqEtPLtLpXsbjr3hVcgI9M3q9BqUmSoGsoJ/AAwvZU3uarEb0IJuv+7zlitvCBw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.0.0
|
||||
|
||||
'@ai-sdk/provider-utils@2.1.10':
|
||||
resolution: {integrity: sha512-4GZ8GHjOFxePFzkl3q42AU0DQOtTQ5w09vmaWUf/pKFXJPizlnzKSUkF0f+VkapIUfDugyMqPMT1ge8XQzVI7Q==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@ai-sdk/provider@1.0.9':
|
||||
resolution: {integrity: sha512-jie6ZJT2ZR0uVOVCDc9R2xCX5I/Dum/wEK28lx21PJx6ZnFAN9EzD2WsPhcDWfCgGx3OAZZ0GyM3CEobXpa9LA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@ai-sdk/react@1.1.20':
|
||||
resolution: {integrity: sha512-4QOM9fR9SryaRraybckDjrhl1O6XejqELdKmrM5g9y9eLnWAfjwF+W1aN0knkSHzbbjMqN77sy9B9yL8EuJbDw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
zod: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@ai-sdk/ui-utils@1.1.16':
|
||||
resolution: {integrity: sha512-jfblR2yZVISmNK2zyNzJZFtkgX57WDAUQXcmn3XUBJyo8LFsADu+/vYMn5AOyBi9qJT0RBk11PEtIxIqvByw3Q==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@alloc/quick-lru@5.2.0':
|
||||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -789,6 +838,9 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||
|
||||
'@microsoft/clarity@1.0.0':
|
||||
resolution: {integrity: sha512-2QY6SmXnqRj6dWhNY8NYCN3e53j4zCFebH4wGnNhdGV1mqAsQwql2fT0w8TISxCvwwfVp8idsWLIdrRHOms1PQ==}
|
||||
|
||||
'@mixmark-io/domino@2.2.0':
|
||||
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
|
||||
|
||||
@@ -889,6 +941,10 @@ packages:
|
||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@opentelemetry/api@1.9.0':
|
||||
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
'@oslojs/encoding@1.1.0':
|
||||
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
|
||||
|
||||
@@ -1281,6 +1337,9 @@ packages:
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
'@types/diff-match-patch@1.0.36':
|
||||
resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==}
|
||||
|
||||
'@types/dom-to-image@2.6.7':
|
||||
resolution: {integrity: sha512-me5VbCv+fcXozblWwG13krNBvuEOm6kA5xoa4RrjDJCNFOZSWR3/QLtOXimBHk1Fisq69Gx3JtOoXtg1N1tijg==}
|
||||
|
||||
@@ -1389,6 +1448,18 @@ packages:
|
||||
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
|
||||
ai@4.1.51:
|
||||
resolution: {integrity: sha512-CuJgbi2Ktfv/7jjxvUhFOGZ8OFxWQ8a7ZF19lwJuVLauL4uWHLetm6R3iaafahJ8ZkueSbhR/Bnroy5apd1nCw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
zod: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
ansi-align@3.0.1:
|
||||
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
|
||||
|
||||
@@ -1737,6 +1808,9 @@ packages:
|
||||
didyoumean@1.2.2:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
|
||||
diff-match-patch@1.0.5:
|
||||
resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
|
||||
|
||||
diff@5.2.0:
|
||||
resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
@@ -1857,6 +1931,10 @@ packages:
|
||||
eventemitter3@5.0.1:
|
||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||
|
||||
eventsource-parser@3.0.0:
|
||||
resolution: {integrity: sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
extend-shallow@2.0.1:
|
||||
resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2171,11 +2249,19 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
json-schema@0.4.0:
|
||||
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
|
||||
|
||||
json5@2.2.3:
|
||||
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
jsondiffpatch@0.6.0:
|
||||
resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
|
||||
jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
|
||||
@@ -2987,6 +3073,9 @@ packages:
|
||||
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
secure-json-parse@2.7.0:
|
||||
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
|
||||
|
||||
semver@6.3.1:
|
||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
@@ -3115,6 +3204,11 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
swr@2.3.2:
|
||||
resolution: {integrity: sha512-RosxFpiabojs75IwQ316DGoDRmOqtiAj0tg8wCcbEu4CiLZBs/a9QNtHV7TUfDXmmlgqij/NqzKq/eLelyv9xA==}
|
||||
peerDependencies:
|
||||
react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
tailwind-merge@2.6.0:
|
||||
resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
|
||||
|
||||
@@ -3130,6 +3224,10 @@ packages:
|
||||
thenify@3.3.1:
|
||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||
|
||||
throttleit@2.1.0:
|
||||
resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tiny-inflate@1.0.3:
|
||||
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
|
||||
|
||||
@@ -3407,6 +3505,43 @@ packages:
|
||||
|
||||
snapshots:
|
||||
|
||||
'@ai-sdk/google@1.1.19(zod@3.24.1)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.9
|
||||
'@ai-sdk/provider-utils': 2.1.10(zod@3.24.1)
|
||||
zod: 3.24.1
|
||||
|
||||
'@ai-sdk/provider-utils@2.1.10(zod@3.24.1)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.9
|
||||
eventsource-parser: 3.0.0
|
||||
nanoid: 3.3.8
|
||||
secure-json-parse: 2.7.0
|
||||
optionalDependencies:
|
||||
zod: 3.24.1
|
||||
|
||||
'@ai-sdk/provider@1.0.9':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
'@ai-sdk/react@1.1.20(react@18.3.1)(zod@3.24.1)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider-utils': 2.1.10(zod@3.24.1)
|
||||
'@ai-sdk/ui-utils': 1.1.16(zod@3.24.1)
|
||||
swr: 2.3.2(react@18.3.1)
|
||||
throttleit: 2.1.0
|
||||
optionalDependencies:
|
||||
react: 18.3.1
|
||||
zod: 3.24.1
|
||||
|
||||
'@ai-sdk/ui-utils@1.1.16(zod@3.24.1)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.9
|
||||
'@ai-sdk/provider-utils': 2.1.10(zod@3.24.1)
|
||||
zod-to-json-schema: 3.24.1(zod@3.24.1)
|
||||
optionalDependencies:
|
||||
zod: 3.24.1
|
||||
|
||||
'@alloc/quick-lru@5.2.0': {}
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
@@ -3441,9 +3576,9 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@astrojs/node@8.3.4(astro@4.16.18)':
|
||||
'@astrojs/node@8.3.4(astro@4.16.18(@types/node@18.19.74)(rollup@4.32.0)(typescript@5.7.3))':
|
||||
dependencies:
|
||||
astro: 4.16.18(typescript@5.7.3)
|
||||
astro: 4.16.18(@types/node@18.19.74)(rollup@4.32.0)(typescript@5.7.3)
|
||||
send: 0.19.1
|
||||
server-destroy: 1.0.1
|
||||
transitivePeerDependencies:
|
||||
@@ -3453,15 +3588,15 @@ snapshots:
|
||||
dependencies:
|
||||
prismjs: 1.29.0
|
||||
|
||||
'@astrojs/react@3.6.3(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)':
|
||||
'@astrojs/react@3.6.3(@types/node@18.19.74)(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@types/react': 18.3.18
|
||||
'@types/react-dom': 18.3.5(@types/react@18.3.18)
|
||||
'@vitejs/plugin-react': 4.3.4(vite@5.4.14)
|
||||
'@vitejs/plugin-react': 4.3.4(vite@5.4.14(@types/node@18.19.74))
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
ultrahtml: 1.5.3
|
||||
vite: 5.4.14
|
||||
vite: 5.4.14(@types/node@18.19.74)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
@@ -3479,9 +3614,9 @@ snapshots:
|
||||
stream-replace-string: 2.0.0
|
||||
zod: 3.24.1
|
||||
|
||||
'@astrojs/tailwind@5.1.5(astro@4.16.18)(tailwindcss@3.4.17)':
|
||||
'@astrojs/tailwind@5.1.5(astro@4.16.18(@types/node@18.19.74)(rollup@4.32.0)(typescript@5.7.3))(tailwindcss@3.4.17)':
|
||||
dependencies:
|
||||
astro: 4.16.18(typescript@5.7.3)
|
||||
astro: 4.16.18(@types/node@18.19.74)(rollup@4.32.0)(typescript@5.7.3)
|
||||
autoprefixer: 10.4.20(postcss@8.5.1)
|
||||
postcss: 8.5.1
|
||||
postcss-load-config: 4.0.2(postcss@8.5.1)
|
||||
@@ -3904,6 +4039,8 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
'@microsoft/clarity@1.0.0': {}
|
||||
|
||||
'@mixmark-io/domino@2.2.0': {}
|
||||
|
||||
'@nanostores/react@0.8.4(nanostores@0.11.3)(react@18.3.1)':
|
||||
@@ -3983,6 +4120,8 @@ snapshots:
|
||||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.18.0
|
||||
|
||||
'@opentelemetry/api@1.9.0': {}
|
||||
|
||||
'@oslojs/encoding@1.1.0': {}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
@@ -3992,9 +4131,9 @@ snapshots:
|
||||
dependencies:
|
||||
playwright: 1.50.0
|
||||
|
||||
'@reactflow/background@11.3.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)':
|
||||
'@reactflow/background@11.3.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
classcat: 5.0.5
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -4003,9 +4142,9 @@ snapshots:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/controls@11.2.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)':
|
||||
'@reactflow/controls@11.2.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
classcat: 5.0.5
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -4014,7 +4153,7 @@ snapshots:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/core@11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)':
|
||||
'@reactflow/core@11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@types/d3': 7.4.3
|
||||
'@types/d3-drag': 3.0.7
|
||||
@@ -4031,9 +4170,9 @@ snapshots:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/minimap@11.7.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)':
|
||||
'@reactflow/minimap@11.7.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@types/d3-selection': 3.0.11
|
||||
'@types/d3-zoom': 3.0.8
|
||||
classcat: 5.0.5
|
||||
@@ -4046,9 +4185,9 @@ snapshots:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/node-resizer@2.2.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)':
|
||||
'@reactflow/node-resizer@2.2.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
classcat: 5.0.5
|
||||
d3-drag: 3.0.0
|
||||
d3-selection: 3.0.0
|
||||
@@ -4059,9 +4198,9 @@ snapshots:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/node-toolbar@1.3.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)':
|
||||
'@reactflow/node-toolbar@1.3.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
classcat: 5.0.5
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -4123,11 +4262,13 @@ snapshots:
|
||||
'@resvg/resvg-js-win32-ia32-msvc': 2.6.2
|
||||
'@resvg/resvg-js-win32-x64-msvc': 2.6.2
|
||||
|
||||
'@rollup/pluginutils@5.1.4':
|
||||
'@rollup/pluginutils@5.1.4(rollup@4.32.0)':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 4.0.2
|
||||
optionalDependencies:
|
||||
rollup: 4.32.0
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.32.0':
|
||||
optional: true
|
||||
@@ -4415,6 +4556,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/ms': 2.1.0
|
||||
|
||||
'@types/diff-match-patch@1.0.36': {}
|
||||
|
||||
'@types/dom-to-image@2.6.7': {}
|
||||
|
||||
'@types/estree@1.0.6': {}
|
||||
@@ -4503,14 +4646,14 @@ snapshots:
|
||||
|
||||
'@ungap/structured-clone@1.3.0': {}
|
||||
|
||||
'@vitejs/plugin-react@4.3.4(vite@5.4.14)':
|
||||
'@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@18.19.74))':
|
||||
dependencies:
|
||||
'@babel/core': 7.26.7
|
||||
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.7)
|
||||
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.7)
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.14.2
|
||||
vite: 5.4.14
|
||||
vite: 5.4.14(@types/node@18.19.74)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -4524,6 +4667,18 @@ snapshots:
|
||||
dependencies:
|
||||
humanize-ms: 1.2.1
|
||||
|
||||
ai@4.1.51(react@18.3.1)(zod@3.24.1):
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.9
|
||||
'@ai-sdk/provider-utils': 2.1.10(zod@3.24.1)
|
||||
'@ai-sdk/react': 1.1.20(react@18.3.1)(zod@3.24.1)
|
||||
'@ai-sdk/ui-utils': 1.1.16(zod@3.24.1)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
jsondiffpatch: 0.6.0
|
||||
optionalDependencies:
|
||||
react: 18.3.1
|
||||
zod: 3.24.1
|
||||
|
||||
ansi-align@3.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
@@ -4559,7 +4714,7 @@ snapshots:
|
||||
|
||||
array-union@2.1.0: {}
|
||||
|
||||
astro@4.16.18(typescript@5.7.3):
|
||||
astro@4.16.18(@types/node@18.19.74)(rollup@4.32.0)(typescript@5.7.3):
|
||||
dependencies:
|
||||
'@astrojs/compiler': 2.10.3
|
||||
'@astrojs/internal-helpers': 0.4.1
|
||||
@@ -4569,7 +4724,7 @@ snapshots:
|
||||
'@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.7)
|
||||
'@babel/types': 7.26.7
|
||||
'@oslojs/encoding': 1.1.0
|
||||
'@rollup/pluginutils': 5.1.4
|
||||
'@rollup/pluginutils': 5.1.4(rollup@4.32.0)
|
||||
'@types/babel__core': 7.20.5
|
||||
'@types/cookie': 0.6.0
|
||||
acorn: 8.14.0
|
||||
@@ -4615,8 +4770,8 @@ snapshots:
|
||||
tsconfck: 3.1.4(typescript@5.7.3)
|
||||
unist-util-visit: 5.0.0
|
||||
vfile: 6.0.3
|
||||
vite: 5.4.14
|
||||
vitefu: 1.0.5(vite@5.4.14)
|
||||
vite: 5.4.14(@types/node@18.19.74)
|
||||
vitefu: 1.0.5(vite@5.4.14(@types/node@18.19.74))
|
||||
which-pm: 3.0.0
|
||||
xxhash-wasm: 1.1.0
|
||||
yargs-parser: 21.1.1
|
||||
@@ -4884,6 +5039,8 @@ snapshots:
|
||||
|
||||
didyoumean@1.2.2: {}
|
||||
|
||||
diff-match-patch@1.0.5: {}
|
||||
|
||||
diff@5.2.0: {}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
@@ -5015,6 +5172,8 @@ snapshots:
|
||||
|
||||
eventemitter3@5.0.1: {}
|
||||
|
||||
eventsource-parser@3.0.0: {}
|
||||
|
||||
extend-shallow@2.0.1:
|
||||
dependencies:
|
||||
is-extendable: 0.1.1
|
||||
@@ -5363,8 +5522,16 @@ snapshots:
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
json-schema@0.4.0: {}
|
||||
|
||||
json5@2.2.3: {}
|
||||
|
||||
jsondiffpatch@0.6.0:
|
||||
dependencies:
|
||||
'@types/diff-match-patch': 1.0.36
|
||||
chalk: 5.4.1
|
||||
diff-match-patch: 1.0.5
|
||||
|
||||
jsonfile@6.1.0:
|
||||
dependencies:
|
||||
universalify: 2.0.1
|
||||
@@ -5869,6 +6036,7 @@ snapshots:
|
||||
form-data-encoder: 1.7.2
|
||||
formdata-node: 4.4.1
|
||||
node-fetch: 2.7.0
|
||||
optionalDependencies:
|
||||
zod: 3.24.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
@@ -5984,8 +6152,9 @@ snapshots:
|
||||
postcss-load-config@4.0.2(postcss@8.5.1):
|
||||
dependencies:
|
||||
lilconfig: 3.1.3
|
||||
postcss: 8.5.1
|
||||
yaml: 2.7.0
|
||||
optionalDependencies:
|
||||
postcss: 8.5.1
|
||||
|
||||
postcss-nested@6.2.0(postcss@8.5.1):
|
||||
dependencies:
|
||||
@@ -6025,6 +6194,7 @@ snapshots:
|
||||
prettier-plugin-tailwindcss@0.6.11(prettier-plugin-astro@0.14.1)(prettier@3.4.2):
|
||||
dependencies:
|
||||
prettier: 3.4.2
|
||||
optionalDependencies:
|
||||
prettier-plugin-astro: 0.14.1
|
||||
|
||||
prettier@3.4.2: {}
|
||||
@@ -6178,7 +6348,7 @@ snapshots:
|
||||
|
||||
react-refresh@0.14.2: {}
|
||||
|
||||
react-tooltip@5.28.0(react-dom@18.3.1)(react@18.3.1):
|
||||
react-tooltip@5.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.6.13
|
||||
classnames: 2.5.1
|
||||
@@ -6189,14 +6359,14 @@ snapshots:
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
|
||||
reactflow@11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1):
|
||||
reactflow@11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@reactflow/background': 11.3.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/controls': 11.2.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/minimap': 11.7.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/node-resizer': 2.2.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/node-toolbar': 1.3.14(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@reactflow/background': 11.3.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/controls': 11.2.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/minimap': 11.7.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/node-resizer': 2.2.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/node-toolbar': 1.3.14(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
@@ -6414,6 +6584,8 @@ snapshots:
|
||||
extend-shallow: 2.0.1
|
||||
kind-of: 6.0.3
|
||||
|
||||
secure-json-parse@2.7.0: {}
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.6.3: {}
|
||||
@@ -6571,6 +6743,12 @@ snapshots:
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
swr@2.3.2(react@18.3.1):
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
react: 18.3.1
|
||||
use-sync-external-store: 1.4.0(react@18.3.1)
|
||||
|
||||
tailwind-merge@2.6.0: {}
|
||||
|
||||
tailwindcss@3.4.17:
|
||||
@@ -6608,11 +6786,13 @@ snapshots:
|
||||
dependencies:
|
||||
any-promise: 1.3.0
|
||||
|
||||
throttleit@2.1.0: {}
|
||||
|
||||
tiny-inflate@1.0.3: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
|
||||
tiptap-markdown@0.8.10(@tiptap/core@2.11.3):
|
||||
tiptap-markdown@0.8.10(@tiptap/core@2.11.3(@tiptap/pm@2.11.3)):
|
||||
dependencies:
|
||||
'@tiptap/core': 2.11.3(@tiptap/pm@2.11.3)
|
||||
'@types/markdown-it': 13.0.9
|
||||
@@ -6639,7 +6819,7 @@ snapshots:
|
||||
ts-interface-checker@0.1.13: {}
|
||||
|
||||
tsconfck@3.1.4(typescript@5.7.3):
|
||||
dependencies:
|
||||
optionalDependencies:
|
||||
typescript: 5.7.3
|
||||
|
||||
tslib@2.8.1: {}
|
||||
@@ -6753,17 +6933,18 @@ snapshots:
|
||||
'@types/unist': 3.0.3
|
||||
vfile-message: 4.0.2
|
||||
|
||||
vite@5.4.14:
|
||||
vite@5.4.14(@types/node@18.19.74):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.5.1
|
||||
rollup: 4.32.0
|
||||
optionalDependencies:
|
||||
'@types/node': 18.19.74
|
||||
fsevents: 2.3.3
|
||||
|
||||
vitefu@1.0.5(vite@5.4.14):
|
||||
dependencies:
|
||||
vite: 5.4.14
|
||||
vitefu@1.0.5(vite@5.4.14(@types/node@18.19.74)):
|
||||
optionalDependencies:
|
||||
vite: 5.4.14(@types/node@18.19.74)
|
||||
|
||||
w3c-keyname@2.2.8: {}
|
||||
|
||||
@@ -6835,8 +7016,9 @@ snapshots:
|
||||
|
||||
zustand@4.5.6(@types/react@18.3.18)(react@18.3.1):
|
||||
dependencies:
|
||||
use-sync-external-store: 1.4.0(react@18.3.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.3.18
|
||||
react: 18.3.1
|
||||
use-sync-external-store: 1.4.0(react@18.3.1)
|
||||
|
||||
zwitch@2.0.4: {}
|
||||
|
||||
Binary file not shown.
BIN
public/pdfs/roadmaps/cloudflare.pdf
Normal file
BIN
public/pdfs/roadmaps/cloudflare.pdf
Normal file
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -458,8 +458,29 @@
|
||||
},
|
||||
"O7wjldZ3yTA2s_F-UnJw_": {
|
||||
"title": "Rate Limiting",
|
||||
"description": "Rate Limiting is a critical aspect of API Design that dictates the number of API calls a client can make within a specified timeframe. This helps in managing resource allocation, preventing abuse of the API, and maintaining the overall health of the API system. Proper rate limiting measures should be in place to ensure the API's stability, thereby delivering a consistent and reliable service to all consumers. It works primarily by setting a limit on the frequency of client requests, thereby preventing individual users from overloading the system. It is crucial to design and implement rate limiting carefully for maintaining API availability and performance.",
|
||||
"links": []
|
||||
"description": "Rate Limiting is a critical aspect of API Design that dictates the number of API calls a client can make within a specified timeframe. This helps in managing resource allocation, preventing abuse of the API, and maintaining the overall health of the API system. Proper rate limiting measures should be in place to ensure the API's stability, thereby delivering a consistent and reliable service to all consumers. It works primarily by setting a limit on the frequency of client requests, thereby preventing individual users from overloading the system. It is crucial to design and implement rate limiting carefully for maintaining API availability and performance.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Rate limit",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Glossary/Rate_limit",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Throttle",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Glossary/Throttle",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Debounce",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Glossary/Debounce",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is rate limiting? | Rate limiting and bots",
|
||||
"url": "https://www.cloudflare.com/en-gb/learning/bots/what-is-rate-limiting/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"20KEgZH6cu_UokqWpV-9I": {
|
||||
"title": "Idempotency",
|
||||
@@ -569,6 +590,11 @@
|
||||
"title": "Caching REST API Response",
|
||||
"url": "https://restfulapi.net/caching/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "HTTP caching",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
2875
public/roadmap-content/aspnet-core.json
Normal file
2875
public/roadmap-content/aspnet-core.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1690,8 +1690,8 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Architectural Patterns in a nutshell",
|
||||
"url": "https://towardsdatascience.com/10-common-software-architectural-patterns-in-a-nutshell-a0b47a1e9013",
|
||||
"title": "10 Common Software Architectural Patterns in a nutshell",
|
||||
"url": "https://theiotacademy.medium.com/10-common-software-architectural-patterns-in-a-nutshell-1b1f6cf5036b",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
|
||||
1643
public/roadmap-content/cloudflare.json
Normal file
1643
public/roadmap-content/cloudflare.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -55,12 +55,12 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Python Website",
|
||||
"title": "Python",
|
||||
"url": "https://www.python.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Python Getting Started",
|
||||
"title": "Getting Started with Python",
|
||||
"url": "https://www.python.org/about/gettingstarted/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -182,6 +182,11 @@
|
||||
"title": "Rust",
|
||||
"description": "Rust is a modern systems programming language focusing on safety, speed, and concurrency. It accomplishes these goals by being memory safe without using garbage collection.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Rust Roadmap",
|
||||
"url": "https://roadmap.sh/rust",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "The Rust Programming Language - online book",
|
||||
"url": "https://doc.rust-lang.org/book/",
|
||||
@@ -213,6 +218,11 @@
|
||||
"title": "C++",
|
||||
"description": "C++ is a powerful general-purpose programming language. It can be used to develop operating systems, browsers, games, and so on. C++ supports different ways of programming like procedural, object-oriented, functional, and so on. This makes C++ powerful as well as flexible.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "C++ Roadmap",
|
||||
"url": "https://roadmap.sh/cpp",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Learn C++",
|
||||
"url": "https://learncpp.com/",
|
||||
@@ -238,11 +248,6 @@
|
||||
"url": "https://www.w3schools.com/cpp/default.asp",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "C++ Roadmap",
|
||||
"url": "https://roadmap.sh/cpp",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about C++ Programming",
|
||||
"url": "https://app.daily.dev/tags/c++?ref=roadmapsh",
|
||||
@@ -326,6 +331,11 @@
|
||||
"url": "https://techdevguide.withgoogle.com/paths/data-structures-and-algorithms/",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Visit Dedicated DSA Roadmap",
|
||||
"url": "https://roadmap.sh/datastructures-and-algorithms",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Data Structures and Algorithms",
|
||||
"url": "https://www.javatpoint.com/data-structure-tutorial",
|
||||
@@ -645,6 +655,11 @@
|
||||
"title": "Complete Binary Tree - Programiz",
|
||||
"url": "https://www.programiz.com/dsa/complete-binary-tree",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Complete Binary Trees",
|
||||
"url": "https://www.wscubetech.com/resources/dsa/complete-binary-tree",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -771,8 +786,14 @@
|
||||
},
|
||||
"HZ1kk0TQ13FLC9t13BZl5": {
|
||||
"title": "Adjacency Matrix",
|
||||
"description": "",
|
||||
"links": []
|
||||
"description": "An adjacency matrix is a square matrix used to represent a finite graph. It is used to represent the connections between vertices in a graph. The matrix is filled with 0s and 1s, where a 1 represents a connection between two vertices and a 0 represents no connection.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Adjacency Matrix",
|
||||
"url": "https://en.wikipedia.org/wiki/Adjacency_matrix",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rTnKJcPniUtqvfOyC88N0": {
|
||||
"title": "Adjacency List",
|
||||
@@ -1017,8 +1038,14 @@
|
||||
},
|
||||
"7a6-AnBI-3tAU1dkOvPkx": {
|
||||
"title": "Common Algorithms",
|
||||
"description": "Here are some common algorithms that you should know. You can find more information about them in the [Algorithms](https://www.khanacademy.org/computing/computer-science/algorithms) section of the Computer Science course.\n\n* Sorting\n* Recursion\n* Searching\n* Cache Algorithms\n* Tree Algorithms\n* Graph Algorithms\n* Greedy Algorithms\n* Backtracking\n* Substring Search\n* Suffix Arrays\n* Dynamic Programming",
|
||||
"links": []
|
||||
"description": "Here are some common algorithms that you should know. You can find more information about them in the Algorithms section of the Computer Science course.\n\n* Sorting\n* Recursion\n* Searching\n* Cache Algorithms\n* Tree Algorithms\n* Graph Algorithms\n* Greedy Algorithms\n* Backtracking\n* Substring Search\n* Suffix Arrays\n* Dynamic Programming\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Top Algorithms and Data Structures",
|
||||
"url": "https://towardsdatascience.com/top-algorithms-and-data-structures-you-really-need-to-know-ab9a2a91c7b5",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"0_qNhprnXU3i8koW3XTdD": {
|
||||
"title": "Tail Recursion",
|
||||
@@ -1143,20 +1170,26 @@
|
||||
"XwyqBK9rgP1MMcJrdIzm5": {
|
||||
"title": "Linear Search",
|
||||
"description": "Linear search is a very simple algorithm that is used to search for a value in an array. It sequentially checks each element of the array until a match is found or until all the elements have been searched.\n\nVisit the following resources to learn more:",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "Linear Search",
|
||||
"url": "https://www.programiz.com/dsa/linear-search",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"4wGBYFZpcdTt97WTbSazx": {
|
||||
"title": "Bubble Sort",
|
||||
"description": "Bubble sort is a simple sorting algorithm that repeatedly steps through the list, compares adjacent elements and swaps them if they are in the wrong order. The pass through the list is repeated until the list is sorted.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Bubble Sort",
|
||||
"url": "https://www.youtube.com/watch?v=P00xJgWzz2c&index=1&list=PL89B61F78B552C1AB",
|
||||
"type": "video"
|
||||
"title": "Bubble Sort Algorithm",
|
||||
"url": "https://www.programiz.com/dsa/bubble-sort",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Analyzing Bubble Sort",
|
||||
"url": "https://www.youtube.com/watch?v=ni_zk257Nqo&index=7&list=PL89B61F78B552C1AB",
|
||||
"title": "Bubble Sort",
|
||||
"url": "https://www.youtube.com/watch?v=P00xJgWzz2c&index=1&list=PL89B61F78B552C1AB",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
@@ -1375,7 +1408,13 @@
|
||||
"Yf5gOIe7oiL19MjEVcpdw": {
|
||||
"title": "Depth First Search",
|
||||
"description": "Depth first search is a graph traversal algorithm that starts at a root node and explores as far as possible along each branch before backtracking.\n\nVisit the following resources to learn more:",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "Depth-first Search",
|
||||
"url": "https://en.wikipedia.org/wiki/Depth-first_search",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"eY4nK2lPYsrR-a_8y2sao": {
|
||||
"title": "Bellman Ford's Algorithm",
|
||||
@@ -1442,8 +1481,13 @@
|
||||
},
|
||||
"aBjBHpq_OajgQjxdCobXD": {
|
||||
"title": "Finding Hamiltonian Paths",
|
||||
"description": "Hamiltonian paths are paths that visit every node in a graph exactly once. They are named after the famous mathematician [Hamilton](https://en.wikipedia.org/wiki/William_Rowan_Hamilton). Hamiltonian paths are a special case of [Hamiltonian cycles](https://en.wikipedia.org/wiki/Hamiltonian_cycle), which are cycles that visit every node in a graph exactly once.\n\nVisit the following resources to learn more:",
|
||||
"description": "Hamiltonian paths are paths that visit every node in a graph exactly once. They are named after the famous mathematician Hamilton. Hamiltonian paths are a special case of Hamiltonian cycles, which are cycles that visit every node in a graph exactly once.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Hamiltonian Cycles",
|
||||
"url": "https://en.wikipedia.org/wiki/Hamiltonian_cycle",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Hamiltonian Path",
|
||||
"url": "https://www.hackerearth.com/practice/algorithms/graphs/hamiltonian-path/tutorial/",
|
||||
@@ -1967,13 +2011,13 @@
|
||||
"description": "Class Diagrams are used to model the static structure of a system. They are used to show the classes, their attributes, operations (or methods), and the relationships between objects.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "UML Class Diagram Tutorial",
|
||||
"url": "https://www.youtube.com/watch?v=UI6lqHOVHic",
|
||||
"type": "video"
|
||||
"title": "Class Diagrams",
|
||||
"url": "https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-class-diagram-tutorial/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "UML Class Diagram Tutorial",
|
||||
"url": "https://www.youtube.com/watch?v=3cmzqZzwNDM&list=PLfoY2ARMh0hC2FcJKP5voAKCpk6PZXSd5&index=2",
|
||||
"url": "https://www.youtube.com/watch?v=UI6lqHOVHic",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
@@ -2354,26 +2398,6 @@
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 23 - NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=ItHp5laE1VE&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=23",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 24 - Satisfiability",
|
||||
"url": "https://www.youtube.com/watch?v=inaFJeCzGxU&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=24",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 25 - More NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=B-bhKxjZLlc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=25",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 26 - NP-Completeness Challenge",
|
||||
"url": "https://www.youtube.com/watch?v=_EzetTkG_Cc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=26",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2425,26 +2449,6 @@
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 23 - NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=ItHp5laE1VE&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=23",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 24 - Satisfiability",
|
||||
"url": "https://www.youtube.com/watch?v=inaFJeCzGxU&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=24",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 25 - More NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=B-bhKxjZLlc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=25",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 26 - NP-Completeness Challenge",
|
||||
"url": "https://www.youtube.com/watch?v=_EzetTkG_Cc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=26",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2496,26 +2500,6 @@
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 23 - NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=ItHp5laE1VE&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=23",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 24 - Satisfiability",
|
||||
"url": "https://www.youtube.com/watch?v=inaFJeCzGxU&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=24",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 25 - More NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=B-bhKxjZLlc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=25",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 26 - NP-Completeness Challenge",
|
||||
"url": "https://www.youtube.com/watch?v=_EzetTkG_Cc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=26",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2567,26 +2551,6 @@
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 23 - NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=ItHp5laE1VE&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=23",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 24 - Satisfiability",
|
||||
"url": "https://www.youtube.com/watch?v=inaFJeCzGxU&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=24",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 25 - More NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=B-bhKxjZLlc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=25",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 26 - NP-Completeness Challenge",
|
||||
"url": "https://www.youtube.com/watch?v=_EzetTkG_Cc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=26",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2643,26 +2607,6 @@
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 23 - NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=ItHp5laE1VE&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=23",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 24 - Satisfiability",
|
||||
"url": "https://www.youtube.com/watch?v=inaFJeCzGxU&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=24",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 25 - More NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=B-bhKxjZLlc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=25",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 26 - NP-Completeness Challenge",
|
||||
"url": "https://www.youtube.com/watch?v=_EzetTkG_Cc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=26",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2941,11 +2885,6 @@
|
||||
"url": "https://www.youtube.com/watch?v=svfnVhJOfMc&index=8&list=PLA5Lqm4uh9Bbq-E0ZnqTIa8LRaL77ica6",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "MIT 6.851 - Memory Hierarchy Models",
|
||||
"url": "https://www.youtube.com/watch?v=V3omVLzI0WE&index=7&list=PLUl4u3cNGP61hsJNdULdudlRL493b-XZf",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "B-Trees (playlist) in 26 minutes",
|
||||
"url": "https://www.youtube.com/playlist?list=PL9xmBV_5YoZNFPPv98DjTdD9X6UI9KMHz",
|
||||
@@ -3057,6 +2996,11 @@
|
||||
"title": "CDN",
|
||||
"description": "A CDN is a network of servers that are distributed geographically. The servers are connected to each other and to the internet. The servers are used to deliver content to users. The content is delivered to the user from the server that is closest to the user. This is done to reduce latency and improve the performance of the content delivery.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a CDN?",
|
||||
"url": "https://www.cloudflare.com/learning/cdn/what-is-a-cdn/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Content Delivery Network (CDN) - System Design",
|
||||
"url": "https://dev.to/karanpratapsingh/system-design-the-complete-course-10fo#content-delivery-network-cdn",
|
||||
@@ -3163,6 +3107,11 @@
|
||||
"title": "GraphQL",
|
||||
"description": "GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated GraphQL Roadmap",
|
||||
"url": "https://roadmap.sh/graphql",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Apollo GraphQL Tutorials",
|
||||
"url": "https://www.apollographql.com/tutorials/",
|
||||
@@ -3227,7 +3176,7 @@
|
||||
"description": "Long polling is a technique used to implement server push functionality over HTTP. It is a method of opening a request on the server and keeping it open until an event occurs, at which point the server responds. This is in contrast to a regular HTTP request, where the server responds immediately with whatever data is available at the time.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Long polling",
|
||||
"title": "Long Polling",
|
||||
"url": "https://javascript.info/long-polling",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3252,7 +3201,18 @@
|
||||
"bVjI14VismTHNCyA0mEBP": {
|
||||
"title": "Web Sockets",
|
||||
"description": "Web sockets are a bidirectional communication protocol between a client and a server. They are used for real-time applications like chat, multiplayer games, and live data updates. Web sockets are also used to establish a connection between a server and a client. This connection is then used to send data in both directions.\n\nVisit the following resources to learn more:",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "WebSockets",
|
||||
"url": "https://en.wikipedia.org/wiki/WebSocket",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Web Sockets API",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"JckRqZA8C6IqQLPpTCgf4": {
|
||||
"title": "SSE",
|
||||
@@ -3280,12 +3240,12 @@
|
||||
"description": "A database is a collection of useful data of one or more related organizations structured in a way to make data an asset to the organization. A database management system is a software designed to assist in maintaining and extracting large collections of data in a timely fashion.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Oracle: What is a Database?",
|
||||
"title": "What is a Database?",
|
||||
"url": "https://www.oracle.com/database/what-is-database/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Prisma.io: What are Databases?",
|
||||
"title": "What are Databases?",
|
||||
"url": "https://www.prisma.io/dataguide/intro/what-are-databases",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3362,7 +3322,13 @@
|
||||
"ii1vF74u3yrFNlw_21b3B": {
|
||||
"title": "DDL",
|
||||
"description": "DDL or Data Definition Language actually consists of the SQL commands that can be used to define the database schema. It simply deals with descriptions of the database schema and is used to create and modify the structure of database objects in the database. DDL is a set of SQL commands used to create, modify, and delete database structures but not data. These commands are normally not used by a general user, who should be accessing the database via an application.\n\nVisit the following resources to learn more:",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "DDL",
|
||||
"url": "https://en.wikipedia.org/wiki/Data_definition_language",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tcQSH-eAvJUZuePTDjAIb": {
|
||||
"title": "DML",
|
||||
@@ -3383,12 +3349,29 @@
|
||||
"05lkb3B86Won7Rkf-8DeD": {
|
||||
"title": "DQL",
|
||||
"description": "DQL statements are used for performing queries on the data within schema objects. The purpose of the DQL Command is to get some schema relation based on the query passed to it. We can define DQL as follows it is a component of SQL statement that allows getting data from the database and imposing order upon it. It includes the SELECT statement. This command allows getting the data out of the database to perform operations with it. When a SELECT is fired against a table or tables the result is compiled into a further temporary table, which is displayed or perhaps received by the program i.e. a front-end.\n\nVisit the following resources to learn more:",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "Data Query Language",
|
||||
"url": "https://en.wikipedia.org/wiki/Data_query_language",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"4bUmfuP2qgcli8I2Vm9zh": {
|
||||
"title": "DCL",
|
||||
"description": "DCL includes commands such as GRANT and REVOKE which mainly deal with the rights, permissions, and other controls of the database system.\n\nVisit the following resources to learn more:",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "DCL",
|
||||
"url": "https://en.wikipedia.org/wiki/Data_Control_Language",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "DCL Commands",
|
||||
"url": "https://www.geeksforgeeks.org/sql-ddl-dql-dml-dcl-tcl-commands/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"_sm63rZNKoibVndeNgOpW": {
|
||||
"title": "Locking",
|
||||
@@ -3435,7 +3418,13 @@
|
||||
"q3nRhTYS5wg9tYnQe2sCF": {
|
||||
"title": "BASE",
|
||||
"description": "The rise in popularity of NoSQL databases provided a flexible and fluidity with ease to manipulate data and as a result, a new database model was designed, reflecting these properties. The acronym BASE is slightly more confusing than ACID but however, the words behind it suggest ways in which the BASE model is different and acronym BASE stands for:-\n\n* **B**asically **A**vailable\n* **S**oft state\n* **E**ventual consistency\n\nVisit the following resources to learn more:",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "BASE Model vs. ACID Model",
|
||||
"url": "https://www.geeksforgeeks.org/acid-model-vs-base-model-for-database/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uqfeiQ9K--QkGNwks4kjk": {
|
||||
"title": "CAP Theorem",
|
||||
@@ -3804,7 +3793,7 @@
|
||||
"description": "Public-key cryptography, or asymmetric cryptography, is the field of cryptographic systems that use pairs of related keys. Each key pair consists of a public key and a corresponding private key. Key pairs are generated with cryptographic algorithms based on mathematical problems termed one-way functions.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Public-key cryptography - Wikipedia",
|
||||
"title": "Public-key Cryptography",
|
||||
"url": "https://en.wikipedia.org/wiki/Public-key_cryptography",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3877,7 +3866,12 @@
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "Wikipedia - OWASP",
|
||||
"title": "OWASP",
|
||||
"url": "https://owasp.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "OWASP - Wiki",
|
||||
"url": "https://en.wikipedia.org/wiki/OWASP",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3926,7 +3920,7 @@
|
||||
},
|
||||
"1eglba39q426Nh0E0qcdj": {
|
||||
"title": "How CPU Executes Programs",
|
||||
"description": "Visit the following resources to learn more:",
|
||||
"description": "The CPU executes programs by repeatedly fetching instructions from memory, decoding them to understand the operation, and then executing those operations. This cycle, called the fetch-decode-execute cycle, continues for each instruction in the program, with the CPU using registers for temporary storage and a program counter to keep track of the next instruction. Modern CPUs use techniques like pipelining and caches to speed up this process, enabling them to execute complex programs efficiently.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Explore top posts about Computing",
|
||||
@@ -3934,7 +3928,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How CPU executes a program",
|
||||
"title": "How CPU Executes a Program",
|
||||
"url": "https://www.youtube.com/watch?v=XM4lGflQFvA",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -3964,8 +3958,13 @@
|
||||
},
|
||||
"AxiGqbteK7ZSXEUt_zckH": {
|
||||
"title": "Instructions and Programs",
|
||||
"description": "Visit the following resources to learn more:",
|
||||
"description": "Instructions are the most basic commands a CPU can understand, directing it to perform specific actions like adding numbers or moving data. A program, on the other hand, is a collection of these instructions, organized in a sequence to accomplish a particular task. Think of instructions as individual words and a program as a complete sentence or story; the CPU executes these instructions one by one, following the program's logic, to achieve the desired outcome.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Instruction and Programs",
|
||||
"url": "https://nerdfighteria.info/v/zltgXvg6r3k/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Instructions and Programs",
|
||||
"url": "https://youtu.be/zltgXvg6r3k",
|
||||
@@ -3975,8 +3974,13 @@
|
||||
},
|
||||
"DjTQjMbika4_yTzrBpcmB": {
|
||||
"title": "CPU Cache",
|
||||
"description": "Visit the following resources to learn more:",
|
||||
"description": "A CPU cache is a hardware cache used by the central processing unit of a computer to reduce the average cost to access data from the main memory. A cache is a smaller, faster memory, located closer to a processor core, which stores copies of the data from frequently used main memory locations.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is CPU Cache",
|
||||
"url": "https://www.howtogeek.com/854138/what-is-cpu-cache/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Computing",
|
||||
"url": "https://app.daily.dev/tags/computing?ref=roadmapsh",
|
||||
@@ -4070,10 +4074,10 @@
|
||||
},
|
||||
"xUo5Ox_HTgGyeQMDIkVyK": {
|
||||
"title": "Concurrency in Multiple Cores",
|
||||
"description": "Visit the following resources to learn more:",
|
||||
"description": "Concurrency or Parallelism is simultaneous execution of processes on a multiple cores per CPU or multiple CPUs (on a single motherboard). Concurrency is when Parallelism is achieved on a single core/CPU by using scheduling algorithms that divides the CPU's time (time-slice).\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is the difference between multicore and concurrent programming?",
|
||||
"title": "Difference between Multi-core and concurrent Programming?",
|
||||
"url": "https://stackoverflow.com/questions/5372861/what-is-the-difference-between-multicore-and-concurrent-programming",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -4081,12 +4085,17 @@
|
||||
"title": "Concurrency in Multicore systems",
|
||||
"url": "https://cs.stackexchange.com/questions/140793/concurrency-in-multiple-core",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Mastering Concurrency",
|
||||
"url": "https://www.harrisonclarke.com/blog/mastering-concurrency-a-guide-for-software-engineers",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Ge2nagN86ofa2y-yYR1lv": {
|
||||
"title": "Scheduling Algorithms",
|
||||
"description": "CPU Scheduling is the process of selecting a process from the ready queue and allocating the CPU to it. The selection of a process is based on a particular scheduling algorithm. The scheduling algorithm is chosen depending on the type of system and the requirements of the processes.\n\nHere is the list of some of the most commonly used scheduling algorithms:\n\n* **First Come First Serve (FCFS):** The process that arrives first is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Job First (SJF):** The process with the smallest execution time is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Remaining Time First (SRTF):** The process with the smallest remaining execution time is allocated the CPU first. It is a preemptive algorithm.\n* **Round Robin (RR):** The process is allocated the CPU for a fixed time slice. The time slice is usually 10 milliseconds. It is a preemptive algorithm.\n* **Priority Scheduling:** The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Feedback Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. If a process is preempted, it is moved to the next queue. It is a preemptive algorithm.\n* **Highest Response Ratio Next(HRRN):** CPU is allotted to the next process which has the highest response ratio and not to the process having less burst time. It is a Non-Preemptive algorithm.\n* **Lottery Scheduling:** The process is allocated the CPU based on a lottery system. It is a preemptive algorithm.\n\nVisit the following resources to learn more :",
|
||||
"description": "CPU Scheduling is the process of selecting a process from the ready queue and allocating the CPU to it. The selection of a process is based on a particular scheduling algorithm. The scheduling algorithm is chosen depending on the type of system and the requirements of the processes.\n\nHere is the list of some of the most commonly used scheduling algorithms:\n\n* **First Come First Serve (FCFS):** The process that arrives first is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Job First (SJF):** The process with the smallest execution time is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Remaining Time First (SRTF):** The process with the smallest remaining execution time is allocated the CPU first. It is a preemptive algorithm.\n* **Round Robin (RR):** The process is allocated the CPU for a fixed time slice. The time slice is usually 10 milliseconds. It is a preemptive algorithm.\n* **Priority Scheduling:** The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Feedback Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. If a process is preempted, it is moved to the next queue. It is a preemptive algorithm.\n* **Highest Response Ratio Next(HRRN):** CPU is allotted to the next process which has the highest response ratio and not to the process having less burst time. It is a Non-Preemptive algorithm.\n* **Lottery Scheduling:** The process is allocated the CPU based on a lottery system. It is a preemptive algorithm.\n\nVisit the following resources to learn more",
|
||||
"links": [
|
||||
{
|
||||
"title": "CPU Scheduling in Operating Systems - geeksforgeeks",
|
||||
@@ -4120,7 +4129,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Video on Interrupts",
|
||||
"title": "Interrupts",
|
||||
"url": "https://youtu.be/iKlAWIKEyuw",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -4222,26 +4231,6 @@
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 23 - NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=ItHp5laE1VE&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=23",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 24 - Satisfiability",
|
||||
"url": "https://www.youtube.com/watch?v=inaFJeCzGxU&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=24",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 25 - More NP-Completeness",
|
||||
"url": "https://www.youtube.com/watch?v=B-bhKxjZLlc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=25",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CSE373 2020 - Lecture 26 - NP-Completeness Challenge",
|
||||
"url": "https://www.youtube.com/watch?v=_EzetTkG_Cc&list=PLOtl7M3yp-DX6ic0HGT0PUX_wiNmkWkXx&index=26",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Replace Function",
|
||||
"url": "https://support.microsoft.com/en-us/office/replace-function-6acf209b-01b7-4078-b4b8-e0a4ef67d181",
|
||||
"url": "https://support.microsoft.com/en-us/office/replace-function-8d799074-2425-4a8a-84bc-82472868878a",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -162,8 +162,14 @@
|
||||
},
|
||||
"7PBmYoSmIgZT21a2Ip3_S": {
|
||||
"title": "Trust / Influence Building",
|
||||
"description": "Building trust and influence is crucial for any Engineering Manager. This involves establishing a solid reputation, delivering on promises and being an active listener to your team's ideas and issues. It's a manager's job to ensure there's an open, honest environment that promotes trust. Balancing delegation and taking charge, especially in difficult situations, is key to building influence.\n\nOne challenge in this area is building trust between team members of varying experiences and skills. Managers must not only show the team they're competent, but also that they value everyone's inputs. They can achieve this by promoting inclusivity and praising team contributions regularly.\n\nBeing patient, communicate clearly, and showing empathy are critical skills that can help an Engineering Manager in trust and influence building. By embodying these traits, managers can build a stronger, united, and more effective engineering team.",
|
||||
"links": []
|
||||
"description": "Building trust and influence is crucial for any Engineering Manager. This involves establishing a solid reputation, delivering on promises and being an active listener to your team's ideas and issues. It's a manager's job to ensure there's an open, honest environment that promotes trust. Balancing delegation and taking charge, especially in difficult situations, is key to building influence.\n\nOne challenge in this area is building trust between team members of varying experiences and skills. Managers must not only show the team they're competent, but also that they value everyone's inputs. They can achieve this by promoting inclusivity and praising team contributions regularly.\n\nBeing patient, communicate clearly, and showing empathy are critical skills that can help an Engineering Manager in trust and influence building. By embodying these traits, managers can build a stronger, united, and more effective engineering team.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Understanding The Trust Equation",
|
||||
"url": "https://trustedadvisor.com/why-trust-matters/understanding-trust/understanding-the-trust-equation",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"b3qoH_LuW-Gz4N8WdGnZs": {
|
||||
"title": "One-on-One Meetings",
|
||||
|
||||
@@ -364,8 +364,13 @@
|
||||
"description": "Joints in game development primarily refer to the connections between two objects, often used in the context of physics simulations and character animations. These might simulate the physics of real-world joints like hinges or springs. Developers can control various characteristics of joints such as their constraints, forces, and reactions. The different types come with various properties suitable for specific needs. For example, Fixed joints keep objects together, Hinge joints allow rotation around an axis, and Spring joints apply a force to keep objects apart.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Game Character Rigging Fundamentals",
|
||||
"url": "https://learn.unity.com/project/game-character-rigging-fundamentals",
|
||||
"title": "Introduction to joints",
|
||||
"url": "https://docs.unity3d.com/Manual/Joints.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Character Rigging for Video Games",
|
||||
"url": "https://game-ace.com/blog/character-rigging-for-video-games/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -744,7 +749,7 @@
|
||||
},
|
||||
"7OffO2mBmfBKqPBTZ9ngI": {
|
||||
"title": "Godot",
|
||||
"description": "Godot is an open-source, multi-platform game engine that is known for being feature-rich and user-friendly. It is developed by hundreds of contributors from around the world and supports the creation of both 2D and 3D games. Godot uses its own scripting language, GDScript, which is similar to Python, but it also supports C# and visual scripting. It is equipped with a unique scene system and comes with a multitude of tools that can expedite the development process. Godot's design philosophy centers around flexibility, extensibility, and ease of use, providing a handy tool for both beginners and pros in game development.\n\nVisit the following resources to learn more:",
|
||||
"description": "Godot is an open-source, multi-platform game engine that is known for being feature-rich and user-friendly. It is developed by hundreds of contributors from around the world and supports the creation of both 2D and 3D games. Godot uses its own scripting language, GDScript, which is similar to Python, but it also supports C#. It is equipped with a unique scene system and comes with a multitude of tools that can expedite the development process. Godot's design philosophy centers around flexibility, extensibility, and ease of use, providing a handy tool for both beginners and pros in game development.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "godotengine/godot",
|
||||
@@ -763,7 +768,17 @@
|
||||
},
|
||||
{
|
||||
"title": "Godot in 100 Seconds",
|
||||
"url": "https://m.youtube.com/watch?v=QKgTZWbwD1U",
|
||||
"url": "https://www.youtube.com/watch?v=QKgTZWbwD1U",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Tutorial - How to make a Video Game in Godot",
|
||||
"url": "https://www.youtube.com/watch?v=LOhfqjmasi0",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Tutorial - How to make 3D Games in Godot",
|
||||
"url": "https://www.youtube.com/watch?v=ke5KpqcoiIU",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
@@ -882,8 +897,8 @@
|
||||
"description": "**C** and **C++ (commonly known as CPP)** are two of the most foundational high-level programming languages in computer science. **C** was developed in the 1970s and it is a procedural language, meaning it follows a step-by-step approach. Its fundamental principles include structured programming and lexical variable scope.\n\nOn the other hand, **C++** follows the paradigm of both procedural and object-oriented programming. It was developed as an extension to C to add the concept of \"classes\" - a core feature of object-oriented programming. C++ enhances C by introducing new features like function overloading, exception handling, and templates.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "The C Programming Language",
|
||||
"url": "https://www.iso.org/standard/74528.html",
|
||||
"title": "C Programming Language",
|
||||
"url": "https://en.wikipedia.org/wiki/C_%28programming_language%29",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1440,7 +1440,7 @@
|
||||
},
|
||||
"uxqJzQFRcALqatNRIWR0w": {
|
||||
"title": "Unstaged Changes",
|
||||
"description": "For changes that are not yet staged with `git add`, such as untracked new files or modified existing ones, use `git diff --unified`. This command compares your working directory against the latest committed version of each file. It's a useful tool for reviewing any local modifications before deciding whether to stage them for future commits.",
|
||||
"description": "For changes that are not yet staged with `git add`, such as untracked new files or modified existing ones , use `git diff`. This command compares your working directory (your current changes) against the staging area (changes already staged with `git add`). It’s a useful tool for reviewing local modifications before deciding whether to stage them for future commits.\n\nThe `--unified` option (or -U) controls the number of context lines shown in the diff output. By default, Git shows 3 lines of context around each change. For example, `git diff --unified=5` will display 5 lines of context around each change, making it easier to understand the surrounding code or content.",
|
||||
"links": [
|
||||
{
|
||||
"title": "What are unstaged changes in GitHub?",
|
||||
|
||||
1591
public/roadmap-content/java.json
Normal file
1591
public/roadmap-content/java.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -652,7 +652,7 @@
|
||||
},
|
||||
"P_Di-XPSDITmU3xKQew8G": {
|
||||
"title": "Object Oriented Programming",
|
||||
"description": "In Python, object-oriented Programming (OOPs) is a programming paradigm that uses objects and classes in programming. It aims to implement real-world entities like inheritance, polymorphisms, encapsulation, etc. in the programming. The main concept of OOPs is to bind the data and the functions that work on that together as a single unit so that no other part of the code can access this data.\n\nVisit the following resources to learn more:",
|
||||
"description": "In Python, object-oriented Programming (OOPs) is a programming paradigm that uses objects and classes in programming. It aims to implement real-world entities like inheritance, polymorphism, encapsulation, etc., in programming. The main concept of OOPs is to bind the data and the functions that work on that together as a single unit so that no other part of the code can access this data.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Object Oriented Programming in Python",
|
||||
@@ -715,7 +715,7 @@
|
||||
},
|
||||
"zAS4YiEJ6VPsyABrkIG8i": {
|
||||
"title": "Methods, Dunder",
|
||||
"description": "A method in python is somewhat similar to a function, except it is associated with object/classes. Methods in python are very similar to functions except for two major differences.\n\n* The method is implicitly used for an object for which it is called.\n* The method is accessible to data that is contained within the class.\n\nDunder or magic methods in Python are the methods having two prefix and suffix underscores in the method name. Dunder here means “Double Under (Underscores)”. These are commonly used for operator overloading. Few examples for magic methods are: **`__init__`**, **`__add__`**, **`__len__`**, **`__repr__`** etc.\n\nVisit the following resources to learn more:",
|
||||
"description": "A method in python is somewhat similar to a function, except it is associated with object/classes. Methods in python are very similar to functions except for two major differences.\n\n* The method is implicitly used for an object for which it is called.\n* The method is accessible to data that is contained within the class.\n\nDunder or magic methods in Python are the methods that have two prefix and suffix underscores in the method name. Dunder here means “Double Under (Underscores)”. These are commonly used for operator overloading. Few examples for magic methods are: **`__init__`**, **`__add__`**, **`__len__`**, **`__repr__`** etc.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Method vs Function in Python",
|
||||
@@ -1314,7 +1314,7 @@
|
||||
"description": "An extremely fast Python linter and code formatter, written in Rust.",
|
||||
"links": [
|
||||
{
|
||||
"title": "ruff documentation",
|
||||
"title": "Ruff documentation",
|
||||
"url": "https://docs.astral.sh/ruff/",
|
||||
"type": "article"
|
||||
}
|
||||
|
||||
@@ -257,7 +257,18 @@
|
||||
"E4H3hniIW6hKpH3Qr--N5": {
|
||||
"title": "C/C++",
|
||||
"description": "\"C\" and \"C++\", often written as \"C/CPP\", are two significantly prominent and similar programming languages widely used in server-side game development. \"C\" is a procedural language, which means that it follows a step-by-step procedure to solve a problem, while \"C++\" is both a procedural and object-oriented programming (OOP) language. This dual nature of \"C++\" allows it to handle more complex interrelated data and functions efficiently, which is a beneficial feature in game development. Moreover, \"C++\" is an extension of \"C\", meaning that any legal \"C\" program is also a valid \"C++\" program. Both languages offer a high degree of control over system resources and memory, making them an excellent choice for building fast and efficient server-side applications, such as multiplayer game servers.",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "C Programming Language",
|
||||
"url": "https://en.wikipedia.org/wiki/C_%28programming_language%29",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "C++ Programming Language",
|
||||
"url": "https://en.wikipedia.org/wiki/C%2B%2B",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"DuyUc9a-47Uz03yr4aeyg": {
|
||||
"title": "C#",
|
||||
|
||||
@@ -800,11 +800,6 @@
|
||||
"title": "TypeScript Utility Types Guide",
|
||||
"url": "https://camchenry.com/blog/typescript-utility-types",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "TypeScript Utility Types: Key Concepts And Best Practices",
|
||||
"url": "https://marketsplash.com/tutorials/typescript/typescript-utility-types/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
BIN
public/roadmaps/cloudflare.png
Normal file
BIN
public/roadmaps/cloudflare.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 506 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 512 KiB |
@@ -43,6 +43,7 @@ Here is the list of available roadmaps with more being actively worked upon.
|
||||
- [AI and Data Scientist Roadmap](https://roadmap.sh/ai-data-scientist)
|
||||
- [AI Engineer Roadmap](https://roadmap.sh/ai-engineer)
|
||||
- [AWS Roadmap](https://roadmap.sh/aws)
|
||||
- [Cloudflare Roadmap](https://roadmap.sh/cloudflare)
|
||||
- [Linux Roadmap](https://roadmap.sh/linux)
|
||||
- [Terraform Roadmap](https://roadmap.sh/terraform)
|
||||
- [Data Analyst Roadmap](https://roadmap.sh/data-analyst)
|
||||
|
||||
181
scripts/gemini-roadmap-content.ts
Normal file
181
scripts/gemini-roadmap-content.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import type { Edge, Node } from 'reactflow';
|
||||
import matter from 'gray-matter';
|
||||
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
|
||||
import { slugify } from '../src/lib/slugger';
|
||||
import { runPromisesInBatchSequentially } from '../src/lib/promise';
|
||||
|
||||
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
||||
import { generateText } from 'ai';
|
||||
|
||||
// ERROR: `__dirname` is not defined in ES module scope
|
||||
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Usage: tsx ./scripts/editor-roadmap-content.ts <roadmapId>
|
||||
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
|
||||
console.log('GEMINI_API_KEY:', GEMINI_API_KEY);
|
||||
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
|
||||
const roadmapId = process.argv[2];
|
||||
|
||||
const google = createGoogleGenerativeAI({
|
||||
apiKey: process.env.GEMINI_API_KEY,
|
||||
});
|
||||
|
||||
const allowedRoadmapIds = await fs.readdir(ROADMAP_CONTENT_DIR);
|
||||
if (!roadmapId) {
|
||||
console.error('Roadmap Id is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!allowedRoadmapIds.includes(roadmapId)) {
|
||||
console.error(`Invalid roadmap key ${roadmapId}`);
|
||||
console.error(`Allowed keys are ${allowedRoadmapIds.join(', ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const roadmapFrontmatterDir = path.join(
|
||||
ROADMAP_CONTENT_DIR,
|
||||
roadmapId,
|
||||
`${roadmapId}.md`,
|
||||
);
|
||||
const roadmapFrontmatterRaw = await fs.readFile(roadmapFrontmatterDir, 'utf-8');
|
||||
const { data } = matter(roadmapFrontmatterRaw);
|
||||
|
||||
const roadmapFrontmatter = data as RoadmapFrontmatter;
|
||||
if (!roadmapFrontmatter) {
|
||||
console.error('Invalid roadmap frontmatter');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (roadmapFrontmatter.renderer !== 'editor') {
|
||||
console.error('Only Editor Rendered Roadmaps are allowed');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const roadmapDir = path.join(
|
||||
ROADMAP_CONTENT_DIR,
|
||||
roadmapId,
|
||||
`${roadmapId}.json`,
|
||||
);
|
||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
||||
let { nodes, edges } = JSON.parse(roadmapContent) as {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
};
|
||||
const enrichedNodes = nodes
|
||||
.filter(
|
||||
(node) =>
|
||||
node?.type &&
|
||||
['topic', 'subtopic'].includes(node.type) &&
|
||||
node.data?.label,
|
||||
)
|
||||
.map((node) => {
|
||||
// Because we only need the parent id and title for subtopics
|
||||
if (node.type !== 'subtopic') {
|
||||
return node;
|
||||
}
|
||||
|
||||
const parentNodeId =
|
||||
edges.find((edge) => edge.target === node.id)?.source || '';
|
||||
const parentNode = nodes.find((n) => n.id === parentNodeId);
|
||||
|
||||
return {
|
||||
...node,
|
||||
parentId: parentNodeId,
|
||||
parentTitle: parentNode?.data?.label || '',
|
||||
};
|
||||
}) as (Node & { parentId?: string; parentTitle?: string })[];
|
||||
|
||||
const roadmapContentDir = path.join(ROADMAP_CONTENT_DIR, roadmapId, 'content');
|
||||
const stats = await fs.stat(roadmapContentDir).catch(() => null);
|
||||
if (!stats || !stats.isDirectory()) {
|
||||
await fs.mkdir(roadmapContentDir, { recursive: true });
|
||||
}
|
||||
|
||||
function writeTopicContent(
|
||||
roadmapTitle: string,
|
||||
childTopic: string,
|
||||
parentTopic?: string,
|
||||
) {
|
||||
const updatedTitle = roadmapTitle.replace('Roadmap', '').trim().replace('Developer', '');
|
||||
let prompt = `I will give you a topic and you need to write a brief introduction for that in "${roadmapTitle}". Your format should be as follows and be in strictly markdown format:
|
||||
|
||||
# (Put a heading for the topic without adding parent "Subtopic in Topic" or "Topic in Roadmap" or "Subtopic under XYZ" etc.)
|
||||
|
||||
(Briefly explain the topic in one paragraph using simple english. Don't start with explaining how important the topic is with regard to "${roadmapTitle}". Don't say something along the lines of "XYZ plays a crucial role in ${roadmapTitle}". Don't include anything saying "In the context of ${roadmapTitle}". Instead, start with a simple explanation of the topic itself. For example, if the topic is "React", you can start with "React is a JavaScript library for building user interfaces."".)
|
||||
`;
|
||||
|
||||
if (!parentTopic) {
|
||||
prompt += `First topic is: ${childTopic}`;
|
||||
} else {
|
||||
prompt += `First topic is: "${parentTopic} > ${childTopic}"`;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
generateText({
|
||||
model: google('gemini-2.0-flash'),
|
||||
prompt: prompt,
|
||||
providerOptions: {
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
const article = response.text;
|
||||
|
||||
resolve(article);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function writeNodeContent(node: Node & { parentTitle?: string }) {
|
||||
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
|
||||
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
||||
console.log(`Missing file for: ${nodeDirPattern}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeDir = path.join(roadmapContentDir, nodeDirPattern);
|
||||
const nodeContent = await fs.readFile(nodeDir, 'utf-8');
|
||||
const isFileEmpty = !nodeContent.replace(`# ${node.data.label}`, '').trim();
|
||||
if (!isFileEmpty) {
|
||||
console.log(`❌ Ignoring ${nodeDirPattern}. Not empty.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const topic = node.data.label;
|
||||
const parentTopic = node.parentTitle;
|
||||
|
||||
console.log(`⏳ Generating content for ${topic}...`);
|
||||
let newContentFile = '';
|
||||
if (GEMINI_API_KEY) {
|
||||
newContentFile = (await writeTopicContent(
|
||||
roadmapFrontmatter.title,
|
||||
topic,
|
||||
parentTopic,
|
||||
)) as string;
|
||||
} else {
|
||||
newContentFile = `# ${topic}`;
|
||||
}
|
||||
|
||||
await fs.writeFile(nodeDir, newContentFile, 'utf-8');
|
||||
console.log(`✅ Content generated for ${topic}`);
|
||||
}
|
||||
|
||||
let roadmapContentFiles = await fs.readdir(roadmapContentDir, {
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
if (!GEMINI_API_KEY) {
|
||||
console.log('----------------------------------------');
|
||||
console.log('GEMINI_API_KEY not found. Skipping gemini api calls...');
|
||||
console.log('----------------------------------------');
|
||||
}
|
||||
const promises = enrichedNodes.map((node) => () => writeNodeContent(node));
|
||||
await runPromisesInBatchSequentially(promises, 20);
|
||||
console.log('✅ All content generated');
|
||||
14
src/components/Analytics/Clarity.astro
Normal file
14
src/components/Analytics/Clarity.astro
Normal file
@@ -0,0 +1,14 @@
|
||||
<script type='text/javascript'>
|
||||
(function (c, l, a, r, i, t, y) {
|
||||
c[a] =
|
||||
c[a] ||
|
||||
function () {
|
||||
(c[a].q = c[a].q || []).push(arguments);
|
||||
};
|
||||
t = l.createElement(r);
|
||||
t.async = 1;
|
||||
t.src = 'https://www.clarity.ms/tag/' + i;
|
||||
y = l.getElementsByTagName(r)[0];
|
||||
y.parentNode.insertBefore(t, y);
|
||||
})(window, document, 'clarity', 'script', 'qcw723i36o');
|
||||
</script>
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
COURSE_PURCHASE_PARAM,
|
||||
setAuthToken,
|
||||
} from '../../lib/jwt';
|
||||
import { cn } from '../../../editor/utils/classname.ts';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { Spinner } from '../ReactIcons/Spinner.tsx';
|
||||
import { CHECKOUT_AFTER_LOGIN_KEY } from './CourseLoginPopup.tsx';
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import {
|
||||
ChartColumn,
|
||||
CheckCircle,
|
||||
CheckSquare,
|
||||
FolderGit2,
|
||||
Pencil,
|
||||
SquarePen,
|
||||
Zap,
|
||||
type LucideIcon,
|
||||
type LucideIcon
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { AllowedProfileVisibility } from '../../api/user.ts';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import type { GuideFileType } from '../../lib/guide';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import type { QuestionGroupType } from '../../lib/question-group';
|
||||
import type { AllowedRoadmapRenderer } from '../../lib/roadmap.ts';
|
||||
import type { VideoFileType } from '../../lib/video';
|
||||
import { $accountStreak, type StreakResponse } from '../../stores/streak';
|
||||
import type { PageType } from '../CommandMenu/CommandMenu';
|
||||
import { FeaturedGuideList } from '../FeaturedGuides/FeaturedGuideList';
|
||||
import { FeaturedVideoList } from '../FeaturedVideos/FeaturedVideoList';
|
||||
import {
|
||||
FavoriteRoadmaps,
|
||||
type AIRoadmapType,
|
||||
@@ -24,12 +27,21 @@ import {
|
||||
import { HeroRoadmap } from '../HeroSection/HeroRoadmap.tsx';
|
||||
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
|
||||
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||||
import { projectGroups } from '../../pages/index.astro';
|
||||
import type { QuestionGroupType } from '../../lib/question-group';
|
||||
import { FeaturedGuideList } from '../FeaturedGuides/FeaturedGuideList';
|
||||
import { FeaturedVideoList } from '../FeaturedVideos/FeaturedVideoList';
|
||||
import type { GuideFileType } from '../../lib/guide';
|
||||
import type { VideoFileType } from '../../lib/video';
|
||||
|
||||
const projectGroups = [
|
||||
{
|
||||
title: 'Frontend',
|
||||
id: 'frontend',
|
||||
},
|
||||
{
|
||||
title: 'Backend',
|
||||
id: 'backend',
|
||||
},
|
||||
{
|
||||
title: 'DevOps',
|
||||
id: 'devops',
|
||||
},
|
||||
];
|
||||
|
||||
type UserDashboardResponse = {
|
||||
name: string;
|
||||
|
||||
@@ -68,7 +68,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
|
||||
isEmpty={!isLoading && progress.length === 0}
|
||||
emptyTitle={
|
||||
<>
|
||||
No bookmars found
|
||||
No bookmarks found
|
||||
<a
|
||||
href="#role-based-roadmaps"
|
||||
className="ml-1.5 inline-flex items-center gap-1 font-medium text-blue-500 underline-offset-2 hover:underline"
|
||||
|
||||
@@ -21,20 +21,10 @@ import { CourseAnnouncement } from '../SQLCourse/CourseAnnouncement';
|
||||
</a>
|
||||
|
||||
<a
|
||||
href='/teams'
|
||||
class='group relative inline text-blue-300 hover:text-white sm:hidden'
|
||||
href='/roadmaps'
|
||||
class='group relative inline text-gray-400 hover:text-white sm:hidden'
|
||||
>
|
||||
Teams
|
||||
|
||||
<span class='absolute -right-[11px] top-0'>
|
||||
<span class='relative flex h-2 w-2'>
|
||||
<span
|
||||
class='absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-400 opacity-75'
|
||||
></span>
|
||||
<span class='relative inline-flex h-2 w-2 rounded-full bg-sky-500'
|
||||
></span>
|
||||
</span>
|
||||
</span>
|
||||
Roadmaps
|
||||
</a>
|
||||
|
||||
<!-- Desktop navigation items -->
|
||||
@@ -49,19 +39,9 @@ import { CourseAnnouncement } from '../SQLCourse/CourseAnnouncement';
|
||||
</a>
|
||||
<a
|
||||
href='/changelog'
|
||||
class='group relative ml-0.5 hidden text-blue-300 hover:text-white md:block'
|
||||
class='group relative ml-0.5 hidden text-gray-400 hover:text-white md:block'
|
||||
>
|
||||
Changelog
|
||||
|
||||
<span class='absolute -right-[11px] top-0'>
|
||||
<span class='relative flex h-2 w-2'>
|
||||
<span
|
||||
class='absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-400 opacity-75'
|
||||
></span>
|
||||
<span class='relative inline-flex h-2 w-2 rounded-full bg-sky-500'
|
||||
></span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'relative hidden rounded-b-[5px] border-t bg-white text-sm font-medium hover:bg-gray-50 sm:block',
|
||||
'relative block rounded-b-[5px] border-t bg-white text-sm font-medium hover:bg-gray-50 sm:block',
|
||||
{
|
||||
'rounded-0 -mx-4 sm:mx-0': isAnswerVisible,
|
||||
// @FIXME:
|
||||
@@ -41,7 +41,6 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
||||
// the frontend roadmap. This is because we did not use to have the question
|
||||
// on mobile devices before and we don't want to cause any SEO issues. It will
|
||||
// be enabled on other roadmaps in the future.
|
||||
block: roadmapId === 'frontend',
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -257,6 +257,12 @@ const groups: GroupType[] = [
|
||||
type: 'skill',
|
||||
otherGroups: ['Web Development'],
|
||||
},
|
||||
{
|
||||
title: 'Cloudflare',
|
||||
link: '/cloudflare',
|
||||
type: 'skill',
|
||||
otherGroups: ['Web Development'],
|
||||
},
|
||||
{
|
||||
title: 'Linux',
|
||||
link: '/linux',
|
||||
|
||||
41
src/components/SQLCourse/AuthorCredentials.tsx
Normal file
41
src/components/SQLCourse/AuthorCredentials.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Award } from 'lucide-react';
|
||||
|
||||
export function AuthorCredentials() {
|
||||
return (
|
||||
<div className="mx-auto mt-8 flex flex-col items-start gap-4 text-sm text-zinc-400 sm:flex-row sm:flex-wrap sm:items-center md:mt-12 md:justify-center md:gap-x-3 md:gap-y-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<img
|
||||
src="https://assets.roadmap.sh/guest/kamran-lqjta.jpeg"
|
||||
className="size-8 rounded-full object-cover mr-1.5"
|
||||
alt="Kamran Ahmed"
|
||||
/>
|
||||
<span>Course by</span>
|
||||
<a
|
||||
href="https://twitter.com/kamrify"
|
||||
target="_blank"
|
||||
className="font-medium text-yellow-500 hover:text-yellow-400"
|
||||
>
|
||||
Kamran Ahmed
|
||||
</a>
|
||||
</div>
|
||||
<div className="hidden flex-wrap items-center gap-x-3 gap-y-2 sm:flex sm:justify-center">
|
||||
<a
|
||||
href="https://github.com/kamranahmedse"
|
||||
target="_blank"
|
||||
className="hidden items-center gap-1 sm:inline-flex text-yellow-500 hover:text-yellow-400"
|
||||
>
|
||||
<svg className="size-4 fill-zinc-400" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.603-3.369-1.342-3.369-1.342-.454-1.155-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.022A9.607 9.607 0 0 1 12 6.82c.85.004 1.705.114 2.504.336 1.909-1.291 2.747-1.022 2.747-1.022.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z" />
|
||||
</svg>
|
||||
#2 most-starred on GitHub
|
||||
</a>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<svg className="size-3 fill-current" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" />
|
||||
</svg>
|
||||
founder roadmap.sh
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +1,59 @@
|
||||
import { QuoteIcon } from 'lucide-react';
|
||||
import { Award, QuoteIcon, Trophy } from 'lucide-react';
|
||||
|
||||
export function AuthorQuoteMessage() {
|
||||
return (
|
||||
<div className="mx-auto mt-14 max-w-2xl sm:mt-20">
|
||||
<div className="relative overflow-hidden rounded-2xl bg-gradient-to-br from-yellow-500/10 via-yellow-400/5 to-yellow-300/10 p-6 sm:p-10">
|
||||
<div className="relative">
|
||||
<p className="mb-6 text-base sm:text-xl leading-relaxed text-zinc-200">
|
||||
"As someone who has worked extensively with databases throughout my
|
||||
career, I know firsthand how crucial SQL skills are. I've created
|
||||
this course to share the practical knowledge that has helped me
|
||||
build and scale data systems at various companies."
|
||||
</p>
|
||||
<div className="relative overflow-hidden rounded-2xl bg-gradient-to-br from-yellow-500/10 via-yellow-400/5 to-yellow-300/10 p-8 sm:p-12">
|
||||
<div className="absolute right-0 top-0 -translate-y-1/2 translate-x-1/2">
|
||||
<div className="size-[500px] rounded-full bg-yellow-500/5 blur-3xl" />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 border-t border-yellow-500/20 pt-6">
|
||||
<div className="relative flex flex-col items-center text-center">
|
||||
<h2 className="mb-4 hidden text-2xl font-semibold text-yellow-500 md:block">
|
||||
From your Instructor
|
||||
</h2>
|
||||
|
||||
<div className="mt-4 hidden flex-wrap items-center justify-center gap-x-4 gap-y-2 text-sm text-zinc-400 md:flex">
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full bg-yellow-500/10 px-3 py-1">
|
||||
<Trophy className="size-4 text-yellow-500/80" />
|
||||
Multiple GitHub Star Awards
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full bg-yellow-500/10 px-3 py-1">
|
||||
<svg className="size-4 fill-yellow-500/80" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.603-3.369-1.342-3.369-1.342-.454-1.155-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.022A9.607 9.607 0 0 1 12 6.82c.85.004 1.705.114 2.504.336 1.909-1.291 2.747-1.022 2.747-1.022.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z" />
|
||||
</svg>
|
||||
#2 Most Starred Developer
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full bg-yellow-500/10 px-3 py-1">
|
||||
<Award className="size-4 text-yellow-500/80" />
|
||||
Founder roadmap.sh
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full bg-yellow-500/10 px-3 py-1">
|
||||
<Award className="size-4 text-yellow-500/80" />
|
||||
Google Developer Expert
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative mt-0 md:mt-8">
|
||||
<p className="text-base leading-relaxed text-zinc-200 sm:text-xl">
|
||||
"As someone who has worked extensively with databases throughout
|
||||
my career, I know firsthand how crucial SQL skills are. I've
|
||||
created this course to share the practical knowledge that has
|
||||
helped me build and scale data systems at various companies."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex items-center gap-4">
|
||||
<img
|
||||
src="https://assets.roadmap.sh/guest/kamran-lqjta.jpeg"
|
||||
alt="Kamran Ahmed"
|
||||
className="size-14 rounded-full ring-2 ring-yellow-500/20"
|
||||
/>
|
||||
<div>
|
||||
<div className="text-left">
|
||||
<h3 className="font-medium text-yellow-500">Kamran Ahmed</h3>
|
||||
<p className="text-sm text-zinc-400">
|
||||
Founder roadmap.sh <span className="mx-1 sm:inline hidden">·</span>
|
||||
Founder roadmap.sh{' '}
|
||||
<span className="mx-1 hidden sm:inline">·</span>
|
||||
<a
|
||||
href="https://twitter.com/kamrify"
|
||||
target="_blank"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { ArrowRightIcon } from 'lucide-react';
|
||||
import { ArrowRightIcon, Play } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { cn } from '../../lib/classname';
|
||||
import {
|
||||
@@ -14,6 +14,7 @@ import { CourseLoginPopup } from '../AuthenticationFlow/CourseLoginPopup';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { httpPost } from '../../lib/query-http';
|
||||
import { deleteUrlParam, getUrlParams } from '../../lib/browser';
|
||||
import { VideoModal } from '../VideoModal';
|
||||
|
||||
export const SQL_COURSE_SLUG = 'sql';
|
||||
|
||||
@@ -35,6 +36,7 @@ export function BuyButton(props: BuyButtonProps) {
|
||||
const { variant = 'main' } = props;
|
||||
|
||||
const [isLoginPopupOpen, setIsLoginPopupOpen] = useState(false);
|
||||
const [isVideoModalOpen, setIsVideoModalOpen] = useState(false);
|
||||
const toast = useToast();
|
||||
|
||||
const { data: coursePricing, isLoading: isLoadingCourse } = useQuery(
|
||||
@@ -164,6 +166,12 @@ export function BuyButton(props: BuyButtonProps) {
|
||||
return (
|
||||
<div className="relative flex w-full flex-col items-center gap-2 md:w-auto">
|
||||
{courseLoginPopup}
|
||||
{isVideoModalOpen && (
|
||||
<VideoModal
|
||||
videoId="6S1CcF-ngeQ"
|
||||
onClose={() => setIsVideoModalOpen(false)}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
onClick={onBuyClick}
|
||||
disabled={isLoadingPricing}
|
||||
@@ -200,8 +208,14 @@ export function BuyButton(props: BuyButtonProps) {
|
||||
</button>
|
||||
|
||||
{!isLoadingPricing && (
|
||||
<span className="absolute top-full translate-y-2.5 text-sm text-yellow-400">
|
||||
Lifetime access <span className="mx-1">·</span> Free updates
|
||||
<span className="absolute top-full z-50 flex w-[300px] translate-y-3 flex-row items-center justify-center text-sm text-yellow-400">
|
||||
Lifetime access <span className="mx-2">·</span>{' '}
|
||||
<button
|
||||
onClick={() => setIsVideoModalOpen(true)}
|
||||
className="flex cursor-pointer flex-row items-center gap-1.5 underline underline-offset-4 hover:text-yellow-500"
|
||||
>
|
||||
<Play className="size-3 fill-current" /> Watch Video (3 min)
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,40 +1,34 @@
|
||||
import { SectionHeader } from './SectionHeader';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Play } from 'lucide-react';
|
||||
import { VideoModal } from '../VideoModal';
|
||||
|
||||
export function PlatformDemo() {
|
||||
const [isZoomed, setIsZoomed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
function onScroll() {
|
||||
if (isZoomed) {
|
||||
setIsZoomed(false);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', onScroll);
|
||||
return () => window.removeEventListener('scroll', onScroll);
|
||||
}, [isZoomed]);
|
||||
const [isVideoModalOpen, setIsVideoModalOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isZoomed && (
|
||||
<div
|
||||
onClick={() => setIsZoomed(false)}
|
||||
className="fixed inset-0 z-[999] flex cursor-zoom-out items-center justify-center bg-black bg-opacity-75"
|
||||
>
|
||||
<img
|
||||
src="https://assets.roadmap.sh/guest/course-environment-87jg8.png"
|
||||
alt="Course Environment"
|
||||
className="max-h-[90vh] max-w-[90vw] rounded-xl object-contain"
|
||||
/>
|
||||
</div>
|
||||
{isVideoModalOpen && (
|
||||
<VideoModal
|
||||
videoId="6S1CcF-ngeQ"
|
||||
onClose={() => setIsVideoModalOpen(false)}
|
||||
/>
|
||||
)}
|
||||
<img
|
||||
src="https://assets.roadmap.sh/guest/course-environment-87jg8.png"
|
||||
alt="Course Environment"
|
||||
onClick={() => setIsZoomed(true)}
|
||||
className="mt-12 sm:mt-20 w-full max-w-5xl rounded-xl cursor-zoom-in"
|
||||
/>
|
||||
<div className="relative mt-12 w-full max-w-5xl sm:mt-24">
|
||||
<img
|
||||
src="https://assets.roadmap.sh/guest/course-environment-87jg8.png"
|
||||
alt="Course Environment"
|
||||
className="w-full rounded-xl"
|
||||
/>
|
||||
<div
|
||||
onClick={() => setIsVideoModalOpen(true)}
|
||||
className="group absolute inset-0 flex cursor-pointer items-center justify-center rounded-xl bg-black/40 transition-all hover:bg-black/50"
|
||||
>
|
||||
<div className="flex size-12 items-center justify-center rounded-full bg-white/90 transition-transform group-hover:scale-105 lg:size-16">
|
||||
<Play className="ml-1 fill-current text-black lg:size-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
254
src/components/SQLCourse/ReviewsSection.tsx
Normal file
254
src/components/SQLCourse/ReviewsSection.tsx
Normal file
@@ -0,0 +1,254 @@
|
||||
import { ChevronDownIcon, StarIcon, User2Icon } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { cn } from '../../../editor/utils/classname';
|
||||
import { markdownToHtml } from '../../lib/markdown';
|
||||
|
||||
type Review = {
|
||||
name: string;
|
||||
role: string;
|
||||
rating: number;
|
||||
text: string | string[];
|
||||
avatarUrl?: string;
|
||||
isProminent?: boolean;
|
||||
isSecondaryProminent?: boolean;
|
||||
};
|
||||
|
||||
export function ReviewsSection() {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const reviews: Review[] = [
|
||||
{
|
||||
name: 'Robin Wieruch',
|
||||
role: 'Author - Multiple best-selling books',
|
||||
rating: 5,
|
||||
text: [
|
||||
'Kamran has been in the **educative space for a long time**, and it shows in the way he teaches SQL: clear, structured, and straight to the point.',
|
||||
"He breaks down SQL fundamentals in a way that's both **intuitive and practical**, helping you not just write queries, but truly understand how databases work.",
|
||||
"Even if you've used SQL before, this **course will fill in gaps you didn't even realize you had**. Get ready to level up your database skills!",
|
||||
],
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/robin.jpeg',
|
||||
isProminent: true,
|
||||
},
|
||||
{
|
||||
name: 'William Imoh',
|
||||
role: 'Founder and Data Enthusiast',
|
||||
rating: 5,
|
||||
text: [
|
||||
'I have been working with SQL and databases for a long time, I bought this course for the advanced chapters but ended up completing the entire course. I learned a lot of new things and it was **well worth the investment**.',
|
||||
'No matter your SQL experience, this course is **a must-have** if you want to level up your SQL and data analysis skills. Highly recommended!',
|
||||
],
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/william-imoh-sd2dk.jpg',
|
||||
isProminent: true,
|
||||
},
|
||||
{
|
||||
name: 'Tomáš Janků',
|
||||
role: 'Software Engineer',
|
||||
rating: 5,
|
||||
text: "The course and it's interactivity is excellent and I'd honestly say it's **one of the best** on the SQL theme I've seen out there.",
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/tomas-janku-6bg89.jpeg',
|
||||
},
|
||||
{
|
||||
name: 'Gourav Khunger',
|
||||
role: 'Software Engineer',
|
||||
rating: 5,
|
||||
text: 'This course was **absolutely brilliant!** The integrated database environment to practice what I learned was the best part.',
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/gourav-h2f3a.png',
|
||||
},
|
||||
{
|
||||
name: 'Meabed',
|
||||
role: 'CTO',
|
||||
rating: 5,
|
||||
text: 'Kamran has **clearly put a lot of thought** into this course. The content, structure and exercises were all great.',
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/meabed-fu83q.jpeg',
|
||||
},
|
||||
{
|
||||
name: 'Mohsin Aheer',
|
||||
role: 'Sr. Software Engineer',
|
||||
rating: 5,
|
||||
text: 'I already knew SQL but this course **taught me a bunch of new things.** Practical examples and challenges were great. Highly recommended!',
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/mohsinaheer-szchu.jpeg',
|
||||
},
|
||||
{
|
||||
name: 'Reeve Tee',
|
||||
role: 'Software Engineer',
|
||||
rating: 5,
|
||||
text: 'I found the course **highly comprehensive and incredibly valuable**. I would love to see more courses like this!',
|
||||
avatarUrl: '',
|
||||
},
|
||||
{
|
||||
name: 'Zeeshan',
|
||||
role: 'Sr. Software Engineer',
|
||||
rating: 5,
|
||||
text: 'Loved the teaching style and the way the course was structured. The **AI tutor was a great help** when I got stuck.',
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/ziishaned-qjepj.png',
|
||||
},
|
||||
{
|
||||
name: 'Adnan Ahmed',
|
||||
role: 'Engineering Manager',
|
||||
rating: 5,
|
||||
text: 'Having the integrated IDE made a huge difference. Being able to **immediately practice** what I learned was **invaluable**.',
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/idnan-fzps5.jpeg',
|
||||
},
|
||||
{
|
||||
name: 'Kalvin Chakma',
|
||||
role: 'Jr. Software Engineer',
|
||||
rating: 5,
|
||||
text: "Best SQL course I've taken. The progression from basic to advanced concepts is **well thought out**, and the challenges are **excellent**.",
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/kalvin-d65ol.jpeg',
|
||||
},
|
||||
{
|
||||
name: 'Faisal Ahsan',
|
||||
role: 'Software Engineer',
|
||||
rating: 5,
|
||||
text: 'The course and the learning experience was great. What I really liked was the **no-fluff explanations** and **practical examples**.',
|
||||
avatarUrl: 'https://assets.roadmap.sh/guest/faisal-q78p2.jpeg',
|
||||
},
|
||||
];
|
||||
|
||||
const prominentReviews = reviews.filter((r) => r.isProminent);
|
||||
const regularReviews = reviews.filter((r) => !r.isProminent);
|
||||
|
||||
return (
|
||||
<div className="relative max-w-5xl">
|
||||
<div
|
||||
className={cn('rounded-2xl pb-0 pt-24', {
|
||||
'pb-8': isExpanded,
|
||||
})}
|
||||
>
|
||||
{/* Prominent Reviews */}
|
||||
<div className="mb-4 md:mb-6">
|
||||
<div className="grid grid-cols-1 gap-4 md:gap-6 md:grid-cols-2">
|
||||
{prominentReviews.map((review, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="review-testimonial relative overflow-hidden rounded-2xl bg-gradient-to-br from-yellow-500/10 via-yellow-500/5 to-transparent p-8 backdrop-blur [&_strong]:font-normal [&_strong]:text-yellow-300/70"
|
||||
>
|
||||
<div className="absolute -right-8 -top-8 h-32 w-32 rounded-full bg-yellow-500/5" />
|
||||
<div className="flex items-center gap-4">
|
||||
{review.avatarUrl && (
|
||||
<img
|
||||
src={review.avatarUrl}
|
||||
alt={review.name}
|
||||
className="h-16 w-16 rounded-full border-2 border-yellow-500/20 object-cover"
|
||||
/>
|
||||
)}
|
||||
{!review.avatarUrl && (
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-zinc-800">
|
||||
<User2Icon className="h-8 w-8 text-zinc-400" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-zinc-100">
|
||||
{review.name}
|
||||
</h3>
|
||||
<p className="text-sm text-yellow-500/70">{review.role}</p>
|
||||
<div className="mt-1 flex">
|
||||
{Array.from({ length: review.rating }).map((_, i) => (
|
||||
<StarIcon
|
||||
key={i}
|
||||
className="h-4 w-4 fill-yellow-500 text-yellow-500"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex flex-col gap-3">
|
||||
{(typeof review.text === 'string'
|
||||
? [review.text]
|
||||
: review.text
|
||||
).map((text, index) => (
|
||||
<p
|
||||
key={index}
|
||||
className="text-zinc-300"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: markdownToHtml(text),
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'relative grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3',
|
||||
isExpanded ? '' : 'max-h-[400px] overflow-hidden',
|
||||
)}
|
||||
>
|
||||
{regularReviews.map((review, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
'review-testimonial flex-shrink-0 break-inside-avoid-column rounded-xl p-6 backdrop-blur [&_strong]:font-normal [&_strong]:text-yellow-300/70',
|
||||
{
|
||||
'bg-gradient-to-br from-yellow-500/10 via-yellow-500/5 to-transparent':
|
||||
review.isSecondaryProminent,
|
||||
'bg-zinc-800/30': !review.isSecondaryProminent,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
{review.avatarUrl && (
|
||||
<img
|
||||
src={review.avatarUrl}
|
||||
alt={review.name}
|
||||
className="h-12 w-12 rounded-full object-cover"
|
||||
/>
|
||||
)}
|
||||
{!review.avatarUrl && (
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-zinc-800">
|
||||
<User2Icon className="h-6 w-6 text-zinc-400" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h3 className="font-semibold text-zinc-100">{review.name}</h3>
|
||||
<p className="text-sm text-zinc-400">{review.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 flex">
|
||||
{Array.from({ length: review.rating }).map((_, i) => (
|
||||
<StarIcon
|
||||
key={i}
|
||||
className="h-4 w-4 fill-yellow-500 text-yellow-500"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p
|
||||
className="mt-4 text-zinc-300"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: markdownToHtml(review.text),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'absolute bottom-0 left-0 right-0 h-40 bg-gradient-to-t from-[#121212] via-[#121212]/80 to-transparent',
|
||||
isExpanded ? 'opacity-0' : 'opacity-100',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn('absolute left-1/2 top-full -translate-x-1/2', {
|
||||
'-translate-y-1/2': !isExpanded,
|
||||
})}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="flex items-center gap-2 rounded-full bg-zinc-800 px-6 py-2 text-sm font-medium text-zinc-300 transition-all hover:bg-zinc-700 hover:text-zinc-100"
|
||||
>
|
||||
{isExpanded ? 'Show Less' : 'Show More Reviews'}
|
||||
<ChevronDownIcon
|
||||
className={`h-4 w-4 transition-transform ${
|
||||
isExpanded ? 'rotate-180' : ''
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
ArrowRightIcon,
|
||||
ArrowUpDownIcon,
|
||||
BarChartIcon,
|
||||
BrainIcon,
|
||||
@@ -15,18 +14,21 @@ import {
|
||||
TableIcon,
|
||||
WrenchIcon,
|
||||
} from 'lucide-react';
|
||||
import { RoadmapLogoIcon } from '../ReactIcons/RoadmapLogo';
|
||||
import { AccountButton } from './AccountButton';
|
||||
import { AuthorCredentials } from './AuthorCredentials';
|
||||
import { AuthorQuoteMessage } from './AuthorQuoteMessage';
|
||||
import { BuyButton } from './BuyButton';
|
||||
import { ChapterRow } from './ChapterRow';
|
||||
import { CourseAuthor } from './CourseAuthor';
|
||||
import { CourseFeature } from './CourseFeature';
|
||||
import { FAQSection } from './FAQSection';
|
||||
import { FloatingPurchase } from './FloatingPurchase';
|
||||
import { PlatformDemo } from './PlatformDemo';
|
||||
import { ReviewsSection } from './ReviewsSection';
|
||||
import { SectionHeader } from './SectionHeader';
|
||||
import { Spotlight } from './Spotlight';
|
||||
import { FloatingPurchase } from './FloatingPurchase';
|
||||
import { CourseAuthor } from './CourseAuthor';
|
||||
import { FAQSection } from './FAQSection';
|
||||
import { BuyButton } from './BuyButton';
|
||||
import { AccountButton } from './AccountButton';
|
||||
import { RoadmapLogoIcon } from '../ReactIcons/RoadmapLogo';
|
||||
import { PlatformDemo } from './PlatformDemo';
|
||||
import { AuthorQuoteMessage } from './AuthorQuoteMessage';
|
||||
|
||||
type ChapterData = {
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
@@ -254,8 +256,9 @@ export function SQLCoursePage() {
|
||||
</a>
|
||||
<AccountButton />
|
||||
</div>
|
||||
<div className="relative mt-7 max-w-3xl text-left md:mt-20 md:text-center">
|
||||
<div className="relative mt-7 max-w-4xl text-left md:mt-20 md:text-center">
|
||||
<Spotlight className="left-[-170px] top-[-200px]" fill="#EAB308" />
|
||||
|
||||
<div className="inline-block rounded-full bg-yellow-500/10 px-4 py-1.5 text-base text-yellow-500 md:px-6 md:py-2 md:text-lg">
|
||||
<span className="hidden sm:block">
|
||||
Complete Course to Master Practical SQL
|
||||
@@ -270,6 +273,7 @@ export function SQLCoursePage() {
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<AuthorCredentials />
|
||||
<p className="mx-auto my-5 max-w-2xl text-xl text-zinc-300 md:my-12 lg:text-2xl">
|
||||
A structured course to master database querying - perfect for
|
||||
developers, data analysts, and anyone working with data.
|
||||
@@ -299,14 +303,16 @@ export function SQLCoursePage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AuthorQuoteMessage />
|
||||
<ReviewsSection />
|
||||
|
||||
<PlatformDemo />
|
||||
|
||||
<AuthorQuoteMessage />
|
||||
|
||||
<SectionHeader
|
||||
title="Not your average SQL course"
|
||||
description="Built around a text-based interactive approach and packed with practical challenges, this course stands out with features that make it truly unique."
|
||||
className="mt-16 md:mt-32"
|
||||
description="This SQL programming class is designed to help you go from beginner to expert through hands-on practice with real-world scenarios, mastering everything from basic to complex queries."
|
||||
className="mt-16 md:mt-20"
|
||||
/>
|
||||
|
||||
<div className="mx-auto mt-6 w-full max-w-5xl md:mt-10">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import '../FrameRenderer/FrameRenderer.css';
|
||||
import '../EditorRoadmap/EditorRoadmapRenderer.css';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { wireframeJSONToSVG } from 'roadmap-renderer';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import '../FrameRenderer/FrameRenderer.css';
|
||||
import { useOutsideClick } from '../../hooks/use-outside-click';
|
||||
import { useKeydown } from '../../hooks/use-keydown';
|
||||
import type { TeamMember } from './TeamProgressPage';
|
||||
@@ -59,6 +60,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
useState<MemberProgressResponse>();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const toast = useToast();
|
||||
const [renderer, setRenderer] = useState<PageType['renderer']>('balsamiq');
|
||||
|
||||
let resourceJsonUrl = import.meta.env.DEV
|
||||
? 'http://localhost:3000'
|
||||
@@ -98,6 +100,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
}
|
||||
|
||||
const renderer = page.renderer || 'balsamiq';
|
||||
setRenderer(renderer);
|
||||
|
||||
const res = await fetch(jsonUrl, {});
|
||||
const json = await res.json();
|
||||
@@ -275,7 +278,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
return (
|
||||
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
|
||||
<div
|
||||
id={'customized-roadmap'}
|
||||
id={renderer === 'editor' ? undefined : 'customized-roadmap'}
|
||||
className="relative mx-auto h-full w-full max-w-4xl p-4 md:h-auto"
|
||||
>
|
||||
<div
|
||||
|
||||
28
src/components/VideoModal.tsx
Normal file
28
src/components/VideoModal.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Modal } from './Modal';
|
||||
|
||||
type VideoModalProps = {
|
||||
videoId: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export function VideoModal(props: VideoModalProps) {
|
||||
const { videoId, onClose } = props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={onClose}
|
||||
wrapperClassName="w-[90vw] max-w-4xl h-auto"
|
||||
bodyClassName="p-0 bg-black"
|
||||
overlayClassName="items-start md:items-center"
|
||||
>
|
||||
<div className="relative w-full pt-[56.25%]">
|
||||
<iframe
|
||||
className="absolute inset-0 h-full w-full"
|
||||
src={`https://www.youtube.com/embed/${videoId}?autoplay=1`}
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
21
src/data/changelogs/cloudflare-roadmap-new-dashboard.md
Normal file
21
src/data/changelogs/cloudflare-roadmap-new-dashboard.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: 'Cloudflare and ASP.NET Roadmaps, New Dashboard'
|
||||
description: 'We just launched our first paid SQL course'
|
||||
images:
|
||||
'New Dashboard': 'https://assets.roadmap.sh/guest/new-dashboard.png'
|
||||
'Cloudflare Roadmap': 'https://assets.roadmap.sh/guest/cloudflare-roadmap.png'
|
||||
'ASP.NET Roadmap Revised': 'https://assets.roadmap.sh/guest/aspnet-core-revision.png'
|
||||
seo:
|
||||
title: 'Cloudflare and ASP.NET Roadmaps, New Dashboard'
|
||||
description: ''
|
||||
date: 2025-02-21
|
||||
---
|
||||
|
||||
We have launched a new Cloudflare roadmap, revised ASP.NET Core roadmap and introduced a new dashboard design.
|
||||
|
||||
- Brand new [Cloudflare roadmap](https://roadmap.sh/cloudflare) to help you learn Cloudflare
|
||||
- [ASP.NET Core roadmap](https://roadmap.sh/aspnet-core) has been revised with new content
|
||||
- Fresh new dashboard design with improved navigation and performance
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
|
||||
@@ -104,16 +104,16 @@ Go and Java are powerful languages with distinct approaches to handling errors.
|
||||
|
||||
```go
|
||||
func divide(a, b int) (int, error) {
|
||||
if b == 0 {
|
||||
return 0, errors.New("division by zero")
|
||||
}
|
||||
return a / b, nil
|
||||
if b == 0 {
|
||||
return 0, errors.New("division by zero")
|
||||
}
|
||||
return a / b, nil
|
||||
}
|
||||
|
||||
// Usage
|
||||
result, err := divide(10, 0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -105,26 +105,26 @@ package api
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type Book struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author"`
|
||||
Year int `json:"year"`
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author"`
|
||||
Year int `json:"year"`
|
||||
}
|
||||
|
||||
type JsonResponse struct {
|
||||
Status int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Data any `json:"data"`
|
||||
Status int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func ResponseJSON(c *gin.Context, status int, message string, data any) {
|
||||
response := JsonResponse{
|
||||
Status: status,
|
||||
Message: message,
|
||||
Data: data,
|
||||
}
|
||||
response := JsonResponse{
|
||||
Status: status,
|
||||
Message: message,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
c.JSON(status, response)
|
||||
c.JSON(status, response)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -140,45 +140,45 @@ To create a new book handler function, create an `api/handlers.go` file that wil
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/joho/godotenv"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/joho/godotenv"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
func InitDB() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
|
||||
dsn := os.Getenv("DB_URL")
|
||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
dsn := os.Getenv("DB_URL")
|
||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
|
||||
// migrate the schema
|
||||
if err := DB.AutoMigrate(&Book{}); err != nil {
|
||||
log.Fatal("Failed to migrate schema:", err)
|
||||
}
|
||||
// migrate the schema
|
||||
if err := DB.AutoMigrate(&Book{}); err != nil {
|
||||
log.Fatal("Failed to migrate schema:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func CreateBook(c *gin.Context) {
|
||||
var book Book
|
||||
var book Book
|
||||
|
||||
//bind the request body
|
||||
if err := c.ShouldBindJSON(&book); err != nil {
|
||||
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
|
||||
return
|
||||
}
|
||||
DB.Create(&book)
|
||||
ResponseJSON(c, http.StatusCreated, "Book created successfully", book)
|
||||
//bind the request body
|
||||
if err := c.ShouldBindJSON(&book); err != nil {
|
||||
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
|
||||
return
|
||||
}
|
||||
DB.Create(&book)
|
||||
ResponseJSON(c, http.StatusCreated, "Book created successfully", book)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -192,9 +192,9 @@ To retrieve the list of books, create a `GetBooks` handler that uses the `DB` va
|
||||
|
||||
```go
|
||||
func GetBooks(c *gin.Context) {
|
||||
var books []Book
|
||||
DB.Find(&books)
|
||||
ResponseJSON(c, http.StatusOK, "Books retrieved successfully", books)
|
||||
var books []Book
|
||||
DB.Find(&books)
|
||||
ResponseJSON(c, http.StatusOK, "Books retrieved successfully", books)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -204,12 +204,12 @@ To retrieve a book, create a `GetBook` handler that uses the `DB` variable to ge
|
||||
|
||||
```go
|
||||
func GetBook(c *gin.Context) {
|
||||
var book Book
|
||||
if err := DB.First(&book, c.Param("id")).Error; err != nil {
|
||||
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
|
||||
return
|
||||
}
|
||||
ResponseJSON(c, http.StatusOK, "Book retrieved successfully", book)
|
||||
var book Book
|
||||
if err := DB.First(&book, c.Param("id")).Error; err != nil {
|
||||
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
|
||||
return
|
||||
}
|
||||
ResponseJSON(c, http.StatusOK, "Book retrieved successfully", book)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -219,20 +219,20 @@ To update a book, create an `UpdateBook` handler that uses the `DB` variable to
|
||||
|
||||
```go
|
||||
func UpdateBook(c *gin.Context) {
|
||||
var book Book
|
||||
if err := DB.First(&book, c.Param("id")).Error; err != nil {
|
||||
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
|
||||
return
|
||||
}
|
||||
var book Book
|
||||
if err := DB.First(&book, c.Param("id")).Error; err != nil {
|
||||
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// bind the request body
|
||||
if err := c.ShouldBindJSON(&book); err != nil {
|
||||
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
|
||||
return
|
||||
}
|
||||
// bind the request body
|
||||
if err := c.ShouldBindJSON(&book); err != nil {
|
||||
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
|
||||
return
|
||||
}
|
||||
|
||||
DB.Save(&book)
|
||||
ResponseJSON(c, http.StatusOK, "Book updated successfully", book)
|
||||
DB.Save(&book)
|
||||
ResponseJSON(c, http.StatusOK, "Book updated successfully", book)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -242,13 +242,13 @@ To delete a book, create a `DeleteBook` handler that uses the `DB` variable to g
|
||||
|
||||
```go
|
||||
func DeleteBook(c *gin.Context) {
|
||||
var book Book
|
||||
if err := DB.Delete(&book, c.Param("id")).Error; err != nil {
|
||||
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
|
||||
return
|
||||
}
|
||||
ResponseJSON(c, http.StatusOK, "Book deleted successfully", nil)
|
||||
}
|
||||
var book Book
|
||||
if err := DB.Delete(&book, c.Param("id")).Error; err != nil {
|
||||
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
|
||||
return
|
||||
}
|
||||
ResponseJSON(c, http.StatusOK, "Book deleted successfully", nil)
|
||||
}
|
||||
```
|
||||
|
||||
### Putting it all together
|
||||
@@ -259,22 +259,22 @@ With the handlers set up, you need to create the application entry point and spe
|
||||
package main
|
||||
|
||||
import (
|
||||
"go_book_api/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go_book_api/api"
|
||||
)
|
||||
|
||||
func main() {
|
||||
api.InitDB()
|
||||
r := gin.Default()
|
||||
api.InitDB()
|
||||
r := gin.Default()
|
||||
|
||||
//routes
|
||||
r.POST("/book", api.CreateBook)
|
||||
r.GET("/books", api.GetBooks)
|
||||
r.GET("/book/:id", api.GetBook)
|
||||
r.PUT("/book/:id", api.UpdateBook)
|
||||
r.DELETE("/book/:id", api.DeleteBook)
|
||||
//routes
|
||||
r.POST("/book", api.CreateBook)
|
||||
r.GET("/books", api.GetBooks)
|
||||
r.GET("/book/:id", api.GetBook)
|
||||
r.PUT("/book/:id", api.UpdateBook)
|
||||
r.DELETE("/book/:id", api.DeleteBook)
|
||||
|
||||
r.Run(":8080")
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -366,31 +366,31 @@ Go comes with a testing package that makes it easy to write unit tests, integrat
|
||||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"go_book_api/api"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go_book_api/api"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupTestDB() {
|
||||
var err error
|
||||
api.DB, err = gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic("failed to connect test database")
|
||||
}
|
||||
api.DB.AutoMigrate(&api.Book{})
|
||||
var err error
|
||||
api.DB, err = gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic("failed to connect test database")
|
||||
}
|
||||
api.DB.AutoMigrate(&api.Book{})
|
||||
}
|
||||
|
||||
func addBook() api.Book {
|
||||
book := api.Book{Title: "Go Programming", Author: "John Doe", Year: 2023}
|
||||
api.DB.Create(&book)
|
||||
return book
|
||||
book := api.Book{Title: "Go Programming", Author: "John Doe", Year: 2023}
|
||||
api.DB.Create(&book)
|
||||
return book
|
||||
}
|
||||
```
|
||||
|
||||
@@ -404,29 +404,29 @@ Create a `TestCreateBook` test function that sets up a mock HTTP server using Gi
|
||||
|
||||
```go
|
||||
func TestCreateBook(t *testing.T) {
|
||||
setupTestDB()
|
||||
router := gin.Default()
|
||||
router.POST("/book", api.CreateBook)
|
||||
setupTestDB()
|
||||
router := gin.Default()
|
||||
router.POST("/book", api.CreateBook)
|
||||
|
||||
book := api.Book{
|
||||
Title: "Demo Book name", Author: "Demo Author name", Year: 2021,
|
||||
}
|
||||
book := api.Book{
|
||||
Title: "Demo Book name", Author: "Demo Author name", Year: 2021,
|
||||
}
|
||||
|
||||
jsonValue, _ := json.Marshal(book)
|
||||
req, _ := http.NewRequest("POST", "/book", bytes.NewBuffer(jsonValue))
|
||||
jsonValue, _ := json.Marshal(book)
|
||||
req, _ := http.NewRequest("POST", "/book", bytes.NewBuffer(jsonValue))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if status := w.Code; status != http.StatusCreated {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusCreated, status)
|
||||
}
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
if status := w.Code; status != http.StatusCreated {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusCreated, status)
|
||||
}
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
|
||||
if response.Data == nil {
|
||||
t.Errorf("Expected book data, got nil")
|
||||
}
|
||||
if response.Data == nil {
|
||||
t.Errorf("Expected book data, got nil")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -436,25 +436,25 @@ Similar to the `TestCreateBook` test function, create a `TestGetBooks` that test
|
||||
|
||||
```go
|
||||
func TestGetBooks(t *testing.T) {
|
||||
setupTestDB()
|
||||
addBook()
|
||||
router := gin.Default()
|
||||
router.GET("/books", api.GetBooks)
|
||||
setupTestDB()
|
||||
addBook()
|
||||
router := gin.Default()
|
||||
router.GET("/books", api.GetBooks)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/books", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
req, _ := http.NewRequest("GET", "/books", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
|
||||
if len(response.Data.([]interface{})) == 0 {
|
||||
t.Errorf("Expected non-empty books list")
|
||||
}
|
||||
if len(response.Data.([]interface{})) == 0 {
|
||||
t.Errorf("Expected non-empty books list")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -464,25 +464,25 @@ Create a `TestGetBook` test function that tests the `GetBook` handler functional
|
||||
|
||||
```go
|
||||
func TestGetBook(t *testing.T) {
|
||||
setupTestDB()
|
||||
book := addBook()
|
||||
router := gin.Default()
|
||||
router.GET("/book/:id", api.GetBook)
|
||||
setupTestDB()
|
||||
book := addBook()
|
||||
router := gin.Default()
|
||||
router.GET("/book/:id", api.GetBook)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/book/"+strconv.Itoa(int(book.ID)), nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
req, _ := http.NewRequest("GET", "/book/"+strconv.Itoa(int(book.ID)), nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
|
||||
if response.Data == nil || response.Data.(map[string]interface{})["id"] != float64(book.ID) {
|
||||
t.Errorf("Expected book ID %d, got nil or wrong ID", book.ID)
|
||||
}
|
||||
if response.Data == nil || response.Data.(map[string]interface{})["id"] != float64(book.ID) {
|
||||
t.Errorf("Expected book ID %d, got nil or wrong ID", book.ID)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -492,30 +492,30 @@ Create a `TestUpdateBook` test function that tests the `UpdateBook` handler func
|
||||
|
||||
```go
|
||||
func TestUpdateBook(t *testing.T) {
|
||||
setupTestDB()
|
||||
book := addBook()
|
||||
router := gin.Default()
|
||||
router.PUT("/book/:id", api.UpdateBook)
|
||||
setupTestDB()
|
||||
book := addBook()
|
||||
router := gin.Default()
|
||||
router.PUT("/book/:id", api.UpdateBook)
|
||||
|
||||
updateBook := api.Book{
|
||||
Title: "Advanced Go Programming", Author: "Demo Author name", Year: 2021,
|
||||
}
|
||||
jsonValue, _ := json.Marshal(updateBook)
|
||||
updateBook := api.Book{
|
||||
Title: "Advanced Go Programming", Author: "Demo Author name", Year: 2021,
|
||||
}
|
||||
jsonValue, _ := json.Marshal(updateBook)
|
||||
|
||||
req, _ := http.NewRequest("PUT", "/book/"+strconv.Itoa(int(book.ID)), bytes.NewBuffer(jsonValue))
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
req, _ := http.NewRequest("PUT", "/book/"+strconv.Itoa(int(book.ID)), bytes.NewBuffer(jsonValue))
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
|
||||
if response.Data == nil || response.Data.(map[string]interface{})["title"] != "Advanced Go Programming" {
|
||||
t.Errorf("Expected updated book title 'Advanced Go Programming', got %v", response.Data)
|
||||
}
|
||||
if response.Data == nil || response.Data.(map[string]interface{})["title"] != "Advanced Go Programming" {
|
||||
t.Errorf("Expected updated book title 'Advanced Go Programming', got %v", response.Data)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -525,32 +525,32 @@ Create a `TestDeleteBook` test function that tests the `DeleteBook` handler func
|
||||
|
||||
```go
|
||||
func TestDeleteBook(t *testing.T) {
|
||||
setupTestDB()
|
||||
book := addBook()
|
||||
router := gin.Default()
|
||||
router.DELETE("/book/:id", api.DeleteBook)
|
||||
setupTestDB()
|
||||
book := addBook()
|
||||
router := gin.Default()
|
||||
router.DELETE("/book/:id", api.DeleteBook)
|
||||
|
||||
req, _ := http.NewRequest("DELETE", "/book/"+strconv.Itoa(int(book.ID)), nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
req, _ := http.NewRequest("DELETE", "/book/"+strconv.Itoa(int(book.ID)), nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
|
||||
if response.Message != "Book deleted successfully" {
|
||||
t.Errorf("Expected delete message 'Book deleted successfully', got %v", response.Message)
|
||||
}
|
||||
if response.Message != "Book deleted successfully" {
|
||||
t.Errorf("Expected delete message 'Book deleted successfully', got %v", response.Message)
|
||||
}
|
||||
|
||||
//verify that the book was deleted
|
||||
var deletedBook api.Book
|
||||
result := api.DB.First(&deletedBook, book.ID)
|
||||
if result.Error == nil {
|
||||
t.Errorf("Expected book to be deleted, but it still exists")
|
||||
}
|
||||
//verify that the book was deleted
|
||||
var deletedBook api.Book
|
||||
result := api.DB.First(&deletedBook, book.ID)
|
||||
if result.Error == nil {
|
||||
t.Errorf("Expected book to be deleted, but it still exists")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -604,40 +604,40 @@ Next, create a `middleware.go` file inside the `api` folder and add the snippet
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Secret key for signing JWT
|
||||
var jwtSecret = []byte(os.Getenv("SECRET_TOKEN"))
|
||||
|
||||
func JWTAuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
tokenString := c.GetHeader("Authorization")
|
||||
if tokenString == "" {
|
||||
ResponseJSON(c, http.StatusUnauthorized, "Authorization token required", nil)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// parse and validate the token
|
||||
_, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// Validate the signing method
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return jwtSecret, nil
|
||||
})
|
||||
if err != nil {
|
||||
ResponseJSON(c, http.StatusUnauthorized, "Invalid token", nil)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// Token is valid, proceed to the next handler
|
||||
c.Next()
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
tokenString := c.GetHeader("Authorization")
|
||||
if tokenString == "" {
|
||||
ResponseJSON(c, http.StatusUnauthorized, "Authorization token required", nil)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// parse and validate the token
|
||||
_, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// Validate the signing method
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return jwtSecret, nil
|
||||
})
|
||||
if err != nil {
|
||||
ResponseJSON(c, http.StatusUnauthorized, "Invalid token", nil)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// Token is valid, proceed to the next handler
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -648,33 +648,33 @@ Next, update the `handlers.go` file with a `GenerateJWT` function that generates
|
||||
```go
|
||||
package api
|
||||
|
||||
import (
|
||||
//other imports
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
import (
|
||||
//other imports
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func GenerateJWT(c *gin.Context) {
|
||||
var loginRequest LoginRequest
|
||||
if err := c.ShouldBindJSON(&loginRequest); err != nil {
|
||||
ResponseJSON(c, http.StatusBadRequest, "Invalid request payload", nil)
|
||||
return
|
||||
}
|
||||
if loginRequest.Username != "admin" || loginRequest.Password != "password" {
|
||||
ResponseJSON(c, http.StatusUnauthorized, "Invalid credentials", nil)
|
||||
return
|
||||
}
|
||||
expirationTime := time.Now().Add(15 * time.Minute)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"exp": expirationTime.Unix(),
|
||||
})
|
||||
// Sign the token
|
||||
tokenString, err := token.SignedString(jwtSecret)
|
||||
if err != nil {
|
||||
ResponseJSON(c, http.StatusInternalServerError, "Could not generate token", nil)
|
||||
return
|
||||
}
|
||||
ResponseJSON(c, http.StatusOK, "Token generated successfully", gin.H{"token": tokenString})
|
||||
}
|
||||
func GenerateJWT(c *gin.Context) {
|
||||
var loginRequest LoginRequest
|
||||
if err := c.ShouldBindJSON(&loginRequest); err != nil {
|
||||
ResponseJSON(c, http.StatusBadRequest, "Invalid request payload", nil)
|
||||
return
|
||||
}
|
||||
if loginRequest.Username != "admin" || loginRequest.Password != "password" {
|
||||
ResponseJSON(c, http.StatusUnauthorized, "Invalid credentials", nil)
|
||||
return
|
||||
}
|
||||
expirationTime := time.Now().Add(15 * time.Minute)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"exp": expirationTime.Unix(),
|
||||
})
|
||||
// Sign the token
|
||||
tokenString, err := token.SignedString(jwtSecret)
|
||||
if err != nil {
|
||||
ResponseJSON(c, http.StatusInternalServerError, "Could not generate token", nil)
|
||||
return
|
||||
}
|
||||
ResponseJSON(c, http.StatusOK, "Token generated successfully", gin.H{"token": tokenString})
|
||||
}
|
||||
```
|
||||
|
||||
> In a real-world application, the credentials used to generate the token will be specific to users and not the default ones.
|
||||
@@ -685,28 +685,28 @@ Lastly, update the `main.go` file inside the `cmd` folder to add a public route
|
||||
package main
|
||||
|
||||
import (
|
||||
"go_book_api/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go_book_api/api"
|
||||
)
|
||||
|
||||
func main() {
|
||||
api.InitDB()
|
||||
r := gin.Default()
|
||||
api.InitDB()
|
||||
r := gin.Default()
|
||||
|
||||
// Public routes
|
||||
r.POST("/token", api.GenerateJWT)
|
||||
// Public routes
|
||||
r.POST("/token", api.GenerateJWT)
|
||||
|
||||
// protected routes
|
||||
protected := r.Group("/", api.JWTAuthMiddleware())
|
||||
{
|
||||
protected.POST("/book", api.CreateBook)
|
||||
protected.GET("/books", api.GetBooks)
|
||||
protected.GET("/book/:id", api.GetBook)
|
||||
protected.PUT("/book/:id", api.UpdateBook)
|
||||
protected.DELETE("/book/:id", api.DeleteBook)
|
||||
}
|
||||
// protected routes
|
||||
protected := r.Group("/", api.JWTAuthMiddleware())
|
||||
{
|
||||
protected.POST("/book", api.CreateBook)
|
||||
protected.GET("/books", api.GetBooks)
|
||||
protected.GET("/book/:id", api.GetBook)
|
||||
protected.PUT("/book/:id", api.UpdateBook)
|
||||
protected.DELETE("/book/:id", api.DeleteBook)
|
||||
}
|
||||
|
||||
r.Run(":8080")
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -750,54 +750,54 @@ Since the API route is now protected, you also need to update the unit test to c
|
||||
package tests
|
||||
|
||||
import (
|
||||
// other imports
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
// other imports
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
var jwtSecret = []byte(os.Getenv("SECRET_TOKEN"))
|
||||
|
||||
func setupTestDB() {
|
||||
// setupTestDB goes here
|
||||
// setupTestDB goes here
|
||||
}
|
||||
|
||||
func addBook() api.Book {
|
||||
// add book code goes here
|
||||
// add book code goes here
|
||||
}
|
||||
|
||||
func generateValidToken() string {
|
||||
expirationTime := time.Now().Add(15 * time.Minute)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"exp": expirationTime.Unix(),
|
||||
})
|
||||
tokenString, _ := token.SignedString(jwtSecret)
|
||||
return tokenString
|
||||
expirationTime := time.Now().Add(15 * time.Minute)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"exp": expirationTime.Unix(),
|
||||
})
|
||||
tokenString, _ := token.SignedString(jwtSecret)
|
||||
return tokenString
|
||||
}
|
||||
|
||||
func TestGenerateJWT(t *testing.T) {
|
||||
router := gin.Default()
|
||||
router.POST("/token", api.GenerateJWT)
|
||||
router := gin.Default()
|
||||
router.POST("/token", api.GenerateJWT)
|
||||
|
||||
loginRequest := map[string]string{
|
||||
"username": "admin",
|
||||
"password": "password",
|
||||
}
|
||||
loginRequest := map[string]string{
|
||||
"username": "admin",
|
||||
"password": "password",
|
||||
}
|
||||
|
||||
jsonValue, _ := json.Marshal(loginRequest)
|
||||
req, _ := http.NewRequest("POST", "/token", bytes.NewBuffer(jsonValue))
|
||||
jsonValue, _ := json.Marshal(loginRequest)
|
||||
req, _ := http.NewRequest("POST", "/token", bytes.NewBuffer(jsonValue))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
|
||||
}
|
||||
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
|
||||
if response.Data == nil || response.Data.(map[string]interface{})["token"] == "" {
|
||||
t.Errorf("Expected token in response, got nil or empty")
|
||||
}
|
||||
if response.Data == nil || response.Data.(map[string]interface{})["token"] == "" {
|
||||
t.Errorf("Expected token in response, got nil or empty")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -807,34 +807,34 @@ Next, use the `generateValidToken` helper function to modify the request object
|
||||
|
||||
```go
|
||||
func TestCreateBook(t *testing.T) {
|
||||
setupTestDB()
|
||||
router := gin.Default()
|
||||
protected := router.Group("/", api.JWTAuthMiddleware()) // add
|
||||
protected.POST("/book", api.CreateBook) // add
|
||||
setupTestDB()
|
||||
router := gin.Default()
|
||||
protected := router.Group("/", api.JWTAuthMiddleware()) // add
|
||||
protected.POST("/book", api.CreateBook) // add
|
||||
|
||||
token := generateValidToken() // add
|
||||
token := generateValidToken() // add
|
||||
|
||||
book := api.Book{
|
||||
Title: "Demo Book name", Author: "Demo Author name", Year: 2021,
|
||||
}
|
||||
jsonValue, _ := json.Marshal(book)
|
||||
book := api.Book{
|
||||
Title: "Demo Book name", Author: "Demo Author name", Year: 2021,
|
||||
}
|
||||
jsonValue, _ := json.Marshal(book)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/book", bytes.NewBuffer(jsonValue))
|
||||
req.Header.Set("Authorization", token) // add
|
||||
req, _ := http.NewRequest("POST", "/book", bytes.NewBuffer(jsonValue))
|
||||
req.Header.Set("Authorization", token) // add
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if status := w.Code; status != http.StatusCreated {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusCreated, status)
|
||||
}
|
||||
if status := w.Code; status != http.StatusCreated {
|
||||
t.Errorf("Expected status %d, got %d", http.StatusCreated, status)
|
||||
}
|
||||
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
var response api.JsonResponse
|
||||
json.NewDecoder(w.Body).Decode(&response)
|
||||
|
||||
if response.Data == nil {
|
||||
t.Errorf("Expected book data, got nil")
|
||||
}
|
||||
if response.Data == nil {
|
||||
t.Errorf("Expected book data, got nil")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -872,54 +872,55 @@ It includes functionality for creating, reading, updating, and deleting books, a
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/joho/godotenv"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/joho/godotenv"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
// InitDB initializes the database connection using environment variables.
|
||||
// It loads the database configuration from a .env file and migrates the Book schema.
|
||||
func InitDB() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
dsn := os.Getenv("DB_URL")
|
||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
// Migrate the schema
|
||||
if err := DB.AutoMigrate(&Book{}); err != nil {
|
||||
log.Fatal("Failed to migrate schema:", err)
|
||||
}
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
dsn := os.Getenv("DB_URL")
|
||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
// Migrate the schema
|
||||
if err := DB.AutoMigrate(&Book{}); err != nil {
|
||||
log.Fatal("Failed to migrate schema:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateBook handles the creation of a new book in the database.
|
||||
// It expects a JSON payload with book details and responds with the created book.
|
||||
func CreateBook(c *gin.Context) {
|
||||
var book Book
|
||||
if err := c.ShouldBindJSON(&book); err != nil {
|
||||
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
|
||||
return
|
||||
}
|
||||
DB.Create(&book)
|
||||
ResponseJSON(c, http.StatusCreated, "Book created successfully", book)
|
||||
var book Book
|
||||
if err := c.ShouldBindJSON(&book); err != nil {
|
||||
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
|
||||
return
|
||||
}
|
||||
DB.Create(&book)
|
||||
ResponseJSON(c, http.StatusCreated, "Book created successfully", book)
|
||||
}
|
||||
|
||||
// GetBooks retrieves all books from the database.
|
||||
// It responds with a list of books.
|
||||
func GetBooks(c *gin.Context) {
|
||||
var books []Book
|
||||
DB.Find(&books)
|
||||
ResponseJSON(c, http.StatusOK, "Books retrieved successfully", books)
|
||||
var books []Book
|
||||
DB.Find(&books)
|
||||
ResponseJSON(c, http.StatusOK, "Books retrieved successfully", books)
|
||||
}
|
||||
|
||||
// other handlers goes below
|
||||
|
||||
@@ -80,29 +80,29 @@ It would be really fun to write a bencode parser, but parsing isn't our focus to
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/jackpal/bencode-go"
|
||||
"github.com/jackpal/bencode-go"
|
||||
)
|
||||
|
||||
type bencodeInfo struct {
|
||||
Pieces string `bencode:"pieces"`
|
||||
PieceLength int `bencode:"piece length"`
|
||||
Length int `bencode:"length"`
|
||||
Name string `bencode:"name"`
|
||||
Pieces string `bencode:"pieces"`
|
||||
PieceLength int `bencode:"piece length"`
|
||||
Length int `bencode:"length"`
|
||||
Name string `bencode:"name"`
|
||||
}
|
||||
|
||||
type bencodeTorrent struct {
|
||||
Announce string `bencode:"announce"`
|
||||
Info bencodeInfo `bencode:"info"`
|
||||
Announce string `bencode:"announce"`
|
||||
Info bencodeInfo `bencode:"info"`
|
||||
}
|
||||
|
||||
// Open parses a torrent file
|
||||
func Open(r io.Reader) (*bencodeTorrent, error) {
|
||||
bto := bencodeTorrent{}
|
||||
err := bencode.Unmarshal(r, &bto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bto, nil
|
||||
bto := bencodeTorrent{}
|
||||
err := bencode.Unmarshal(r, &bto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bto, nil
|
||||
}
|
||||
```
|
||||
|
||||
@@ -114,16 +114,16 @@ Notably, I split `pieces` (previously a string) into a slice of hashes (each `[2
|
||||
|
||||
```go
|
||||
type TorrentFile struct {
|
||||
Announce string
|
||||
InfoHash [20]byte
|
||||
PieceHashes [][20]byte
|
||||
PieceLength int
|
||||
Length int
|
||||
Name string
|
||||
Announce string
|
||||
InfoHash [20]byte
|
||||
PieceHashes [][20]byte
|
||||
PieceLength int
|
||||
Length int
|
||||
Name string
|
||||
}
|
||||
|
||||
func (bto *bencodeTorrent) toTorrentFile() (*TorrentFile, error) {
|
||||
// ...
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
@@ -133,21 +133,21 @@ Now that we have information about the file and its tracker, let's talk to the t
|
||||
|
||||
```go
|
||||
func (t *TorrentFile) buildTrackerURL(peerID [20]byte, port uint16) (string, error) {
|
||||
base, err := url.Parse(t.Announce)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := url.Values{
|
||||
"info_hash": []string{string(t.InfoHash[:])},
|
||||
"peer_id": []string{string(peerID[:])},
|
||||
"port": []string{strconv.Itoa(int(Port))},
|
||||
"uploaded": []string{"0"},
|
||||
"downloaded": []string{"0"},
|
||||
"compact": []string{"1"},
|
||||
"left": []string{strconv.Itoa(t.Length)},
|
||||
}
|
||||
base.RawQuery = params.Encode()
|
||||
return base.String(), nil
|
||||
base, err := url.Parse(t.Announce)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := url.Values{
|
||||
"info_hash": []string{string(t.InfoHash[:])},
|
||||
"peer_id": []string{string(peerID[:])},
|
||||
"port": []string{strconv.Itoa(int(Port))},
|
||||
"uploaded": []string{"0"},
|
||||
"downloaded": []string{"0"},
|
||||
"compact": []string{"1"},
|
||||
"left": []string{strconv.Itoa(t.Length)},
|
||||
}
|
||||
base.RawQuery = params.Encode()
|
||||
return base.String(), nil
|
||||
}
|
||||
```
|
||||
|
||||
@@ -180,25 +180,25 @@ e
|
||||
```go
|
||||
// Peer encodes connection information for a peer
|
||||
type Peer struct {
|
||||
IP net.IP
|
||||
Port uint16
|
||||
IP net.IP
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// Unmarshal parses peer IP addresses and ports from a buffer
|
||||
func Unmarshal(peersBin []byte) ([]Peer, error) {
|
||||
const peerSize = 6 // 4 for IP, 2 for port
|
||||
numPeers := len(peersBin) / peerSize
|
||||
if len(peersBin)%peerSize != 0 {
|
||||
err := fmt.Errorf("Received malformed peers")
|
||||
return nil, err
|
||||
}
|
||||
peers := make([]Peer, numPeers)
|
||||
for i := 0; i < numPeers; i++ {
|
||||
offset := i * peerSize
|
||||
peers[i].IP = net.IP(peersBin[offset : offset+4])
|
||||
peers[i].Port = binary.BigEndian.Uint16(peersBin[offset+4 : offset+6])
|
||||
}
|
||||
return peers, nil
|
||||
const peerSize = 6 // 4 for IP, 2 for port
|
||||
numPeers := len(peersBin) / peerSize
|
||||
if len(peersBin)%peerSize != 0 {
|
||||
err := fmt.Errorf("Received malformed peers")
|
||||
return nil, err
|
||||
}
|
||||
peers := make([]Peer, numPeers)
|
||||
for i := 0; i < numPeers; i++ {
|
||||
offset := i * peerSize
|
||||
peers[i].IP = net.IP(peersBin[offset : offset+4])
|
||||
peers[i].Port = binary.BigEndian.Uint16(peersBin[offset+4 : offset+6])
|
||||
}
|
||||
return peers, nil
|
||||
}
|
||||
```
|
||||
|
||||
@@ -215,7 +215,7 @@ Now that we have a list of peers, it's time to connect with them and start downl
|
||||
```go
|
||||
conn, err := net.DialTimeout("tcp", peer.String(), 3*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, err
|
||||
}
|
||||
```
|
||||
|
||||
@@ -252,27 +252,27 @@ In our code, let's make a struct to represent a handshake, and write a few metho
|
||||
```go
|
||||
// A Handshake is a special message that a peer uses to identify itself
|
||||
type Handshake struct {
|
||||
Pstr string
|
||||
InfoHash [20]byte
|
||||
PeerID [20]byte
|
||||
Pstr string
|
||||
InfoHash [20]byte
|
||||
PeerID [20]byte
|
||||
}
|
||||
|
||||
// Serialize serializes the handshake to a buffer
|
||||
func (h *Handshake) Serialize() []byte {
|
||||
buf := make([]byte, len(h.Pstr)+49)
|
||||
buf[0] = byte(len(h.Pstr))
|
||||
curr := 1
|
||||
curr += copy(buf[curr:], h.Pstr)
|
||||
curr += copy(buf[curr:], make([]byte, 8)) // 8 reserved bytes
|
||||
curr += copy(buf[curr:], h.InfoHash[:])
|
||||
curr += copy(buf[curr:], h.PeerID[:])
|
||||
return buf
|
||||
buf := make([]byte, len(h.Pstr)+49)
|
||||
buf[0] = byte(len(h.Pstr))
|
||||
curr := 1
|
||||
curr += copy(buf[curr:], h.Pstr)
|
||||
curr += copy(buf[curr:], make([]byte, 8)) // 8 reserved bytes
|
||||
curr += copy(buf[curr:], h.InfoHash[:])
|
||||
curr += copy(buf[curr:], h.PeerID[:])
|
||||
return buf
|
||||
}
|
||||
|
||||
// Read parses a handshake from a stream
|
||||
func Read(r io.Reader) (*Handshake, error) {
|
||||
// Do Serialize(), but backwards
|
||||
// ...
|
||||
// Do Serialize(), but backwards
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
@@ -296,36 +296,36 @@ A message starts with a length indicator which tells us how many bytes long the
|
||||
type messageID uint8
|
||||
|
||||
const (
|
||||
MsgChoke messageID = 0
|
||||
MsgUnchoke messageID = 1
|
||||
MsgInterested messageID = 2
|
||||
MsgNotInterested messageID = 3
|
||||
MsgHave messageID = 4
|
||||
MsgBitfield messageID = 5
|
||||
MsgRequest messageID = 6
|
||||
MsgPiece messageID = 7
|
||||
MsgCancel messageID = 8
|
||||
MsgChoke messageID = 0
|
||||
MsgUnchoke messageID = 1
|
||||
MsgInterested messageID = 2
|
||||
MsgNotInterested messageID = 3
|
||||
MsgHave messageID = 4
|
||||
MsgBitfield messageID = 5
|
||||
MsgRequest messageID = 6
|
||||
MsgPiece messageID = 7
|
||||
MsgCancel messageID = 8
|
||||
)
|
||||
|
||||
// Message stores ID and payload of a message
|
||||
type Message struct {
|
||||
ID messageID
|
||||
Payload []byte
|
||||
ID messageID
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// Serialize serializes a message into a buffer of the form
|
||||
// <length prefix><message ID><payload>
|
||||
// Interprets `nil` as a keep-alive message
|
||||
func (m *Message) Serialize() []byte {
|
||||
if m == nil {
|
||||
return make([]byte, 4)
|
||||
}
|
||||
length := uint32(len(m.Payload) + 1) // +1 for id
|
||||
buf := make([]byte, 4+length)
|
||||
binary.BigEndian.PutUint32(buf[0:4], length)
|
||||
buf[4] = byte(m.ID)
|
||||
copy(buf[5:], m.Payload)
|
||||
return buf
|
||||
if m == nil {
|
||||
return make([]byte, 4)
|
||||
}
|
||||
length := uint32(len(m.Payload) + 1) // +1 for id
|
||||
buf := make([]byte, 4+length)
|
||||
binary.BigEndian.PutUint32(buf[0:4], length)
|
||||
buf[4] = byte(m.ID)
|
||||
copy(buf[5:], m.Payload)
|
||||
return buf
|
||||
}
|
||||
```
|
||||
|
||||
@@ -334,30 +334,30 @@ To read a message from a stream, we just follow the format of a message. We read
|
||||
```go
|
||||
// Read parses a message from a stream. Returns `nil` on keep-alive message
|
||||
func Read(r io.Reader) (*Message, error) {
|
||||
lengthBuf := make([]byte, 4)
|
||||
_, err := io.ReadFull(r, lengthBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := binary.BigEndian.Uint32(lengthBuf)
|
||||
lengthBuf := make([]byte, 4)
|
||||
_, err := io.ReadFull(r, lengthBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := binary.BigEndian.Uint32(lengthBuf)
|
||||
|
||||
// keep-alive message
|
||||
if length == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
// keep-alive message
|
||||
if length == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
messageBuf := make([]byte, length)
|
||||
_, err = io.ReadFull(r, messageBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
messageBuf := make([]byte, length)
|
||||
_, err = io.ReadFull(r, messageBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := Message{
|
||||
ID: messageID(messageBuf[0]),
|
||||
Payload: messageBuf[1:],
|
||||
}
|
||||
m := Message{
|
||||
ID: messageID(messageBuf[0]),
|
||||
Payload: messageBuf[1:],
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
return &m, nil
|
||||
}
|
||||
```
|
||||
|
||||
@@ -375,16 +375,16 @@ type Bitfield []byte
|
||||
|
||||
// HasPiece tells if a bitfield has a particular index set
|
||||
func (bf Bitfield) HasPiece(index int) bool {
|
||||
byteIndex := index / 8
|
||||
offset := index % 8
|
||||
return bf[byteIndex]>>(7-offset)&1 != 0
|
||||
byteIndex := index / 8
|
||||
offset := index % 8
|
||||
return bf[byteIndex]>>(7-offset)&1 != 0
|
||||
}
|
||||
|
||||
// SetPiece sets a bit in the bitfield
|
||||
func (bf Bitfield) SetPiece(index int) {
|
||||
byteIndex := index / 8
|
||||
offset := index % 8
|
||||
bf[byteIndex] |= 1 << (7 - offset)
|
||||
byteIndex := index / 8
|
||||
offset := index % 8
|
||||
bf[byteIndex] |= 1 << (7 - offset)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -403,23 +403,23 @@ We'll set up two channels to synchronize our concurrent workers: one for dishing
|
||||
workQueue := make(chan *pieceWork, len(t.PieceHashes))
|
||||
results := make(chan *pieceResult)
|
||||
for index, hash := range t.PieceHashes {
|
||||
length := t.calculatePieceSize(index)
|
||||
workQueue <- &pieceWork{index, hash, length}
|
||||
length := t.calculatePieceSize(index)
|
||||
workQueue <- &pieceWork{index, hash, length}
|
||||
}
|
||||
|
||||
// Start workers
|
||||
for _, peer := range t.Peers {
|
||||
go t.startDownloadWorker(peer, workQueue, results)
|
||||
go t.startDownloadWorker(peer, workQueue, results)
|
||||
}
|
||||
|
||||
// Collect results into a buffer until full
|
||||
buf := make([]byte, t.Length)
|
||||
donePieces := 0
|
||||
for donePieces < len(t.PieceHashes) {
|
||||
res := <-results
|
||||
begin, end := t.calculateBoundsForPiece(res.index)
|
||||
copy(buf[begin:end], res.buf)
|
||||
donePieces++
|
||||
res := <-results
|
||||
begin, end := t.calculateBoundsForPiece(res.index)
|
||||
copy(buf[begin:end], res.buf)
|
||||
donePieces++
|
||||
}
|
||||
close(workQueue)
|
||||
```
|
||||
@@ -430,41 +430,41 @@ We'll spawn a worker goroutine for each peer we've received from the tracker. It
|
||||
|
||||
```go
|
||||
func (t *Torrent) startDownloadWorker(peer peers.Peer, workQueue chan *pieceWork, results chan *pieceResult) {
|
||||
c, err := client.New(peer, t.PeerID, t.InfoHash)
|
||||
if err != nil {
|
||||
log.Printf("Could not handshake with %s. Disconnecting\n", peer.IP)
|
||||
return
|
||||
}
|
||||
defer c.Conn.Close()
|
||||
log.Printf("Completed handshake with %s\n", peer.IP)
|
||||
c, err := client.New(peer, t.PeerID, t.InfoHash)
|
||||
if err != nil {
|
||||
log.Printf("Could not handshake with %s. Disconnecting\n", peer.IP)
|
||||
return
|
||||
}
|
||||
defer c.Conn.Close()
|
||||
log.Printf("Completed handshake with %s\n", peer.IP)
|
||||
|
||||
c.SendUnchoke()
|
||||
c.SendInterested()
|
||||
c.SendUnchoke()
|
||||
c.SendInterested()
|
||||
|
||||
for pw := range workQueue {
|
||||
if !c.Bitfield.HasPiece(pw.index) {
|
||||
workQueue <- pw // Put piece back on the queue
|
||||
continue
|
||||
}
|
||||
for pw := range workQueue {
|
||||
if !c.Bitfield.HasPiece(pw.index) {
|
||||
workQueue <- pw // Put piece back on the queue
|
||||
continue
|
||||
}
|
||||
|
||||
// Download the piece
|
||||
buf, err := attemptDownloadPiece(c, pw)
|
||||
if err != nil {
|
||||
log.Println("Exiting", err)
|
||||
workQueue <- pw // Put piece back on the queue
|
||||
return
|
||||
}
|
||||
// Download the piece
|
||||
buf, err := attemptDownloadPiece(c, pw)
|
||||
if err != nil {
|
||||
log.Println("Exiting", err)
|
||||
workQueue <- pw // Put piece back on the queue
|
||||
return
|
||||
}
|
||||
|
||||
err = checkIntegrity(pw, buf)
|
||||
if err != nil {
|
||||
log.Printf("Piece #%d failed integrity check\n", pw.index)
|
||||
workQueue <- pw // Put piece back on the queue
|
||||
continue
|
||||
}
|
||||
err = checkIntegrity(pw, buf)
|
||||
if err != nil {
|
||||
log.Printf("Piece #%d failed integrity check\n", pw.index)
|
||||
workQueue <- pw // Put piece back on the queue
|
||||
continue
|
||||
}
|
||||
|
||||
c.SendHave(pw.index)
|
||||
results <- &pieceResult{pw.index, buf}
|
||||
}
|
||||
c.SendHave(pw.index)
|
||||
results <- &pieceResult{pw.index, buf}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -474,30 +474,30 @@ We'll keep track of each peer in a struct, and modify that struct as we read mes
|
||||
|
||||
```go
|
||||
type pieceProgress struct {
|
||||
index int
|
||||
client *client.Client
|
||||
buf []byte
|
||||
downloaded int
|
||||
requested int
|
||||
backlog int
|
||||
index int
|
||||
client *client.Client
|
||||
buf []byte
|
||||
downloaded int
|
||||
requested int
|
||||
backlog int
|
||||
}
|
||||
|
||||
func (state *pieceProgress) readMessage() error {
|
||||
msg, err := state.client.Read() // this call blocks
|
||||
switch msg.ID {
|
||||
case message.MsgUnchoke:
|
||||
state.client.Choked = false
|
||||
case message.MsgChoke:
|
||||
state.client.Choked = true
|
||||
case message.MsgHave:
|
||||
index, err := message.ParseHave(msg)
|
||||
state.client.Bitfield.SetPiece(index)
|
||||
case message.MsgPiece:
|
||||
n, err := message.ParsePiece(state.index, state.buf, msg)
|
||||
state.downloaded += n
|
||||
state.backlog--
|
||||
}
|
||||
return nil
|
||||
msg, err := state.client.Read() // this call blocks
|
||||
switch msg.ID {
|
||||
case message.MsgUnchoke:
|
||||
state.client.Choked = false
|
||||
case message.MsgChoke:
|
||||
state.client.Choked = true
|
||||
case message.MsgHave:
|
||||
index, err := message.ParseHave(msg)
|
||||
state.client.Bitfield.SetPiece(index)
|
||||
case message.MsgPiece:
|
||||
n, err := message.ParsePiece(state.index, state.buf, msg)
|
||||
state.downloaded += n
|
||||
state.backlog--
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
@@ -523,43 +523,43 @@ const MaxBlockSize = 16384
|
||||
const MaxBacklog = 5
|
||||
|
||||
func attemptDownloadPiece(c *client.Client, pw *pieceWork) ([]byte, error) {
|
||||
state := pieceProgress{
|
||||
index: pw.index,
|
||||
client: c,
|
||||
buf: make([]byte, pw.length),
|
||||
}
|
||||
state := pieceProgress{
|
||||
index: pw.index,
|
||||
client: c,
|
||||
buf: make([]byte, pw.length),
|
||||
}
|
||||
|
||||
// Setting a deadline helps get unresponsive peers unstuck.
|
||||
// 30 seconds is more than enough time to download a 262 KB piece
|
||||
c.Conn.SetDeadline(time.Now().Add(30 * time.Second))
|
||||
defer c.Conn.SetDeadline(time.Time{}) // Disable the deadline
|
||||
// Setting a deadline helps get unresponsive peers unstuck.
|
||||
// 30 seconds is more than enough time to download a 262 KB piece
|
||||
c.Conn.SetDeadline(time.Now().Add(30 * time.Second))
|
||||
defer c.Conn.SetDeadline(time.Time{}) // Disable the deadline
|
||||
|
||||
for state.downloaded < pw.length {
|
||||
// If unchoked, send requests until we have enough unfulfilled requests
|
||||
if !state.client.Choked {
|
||||
for state.backlog < MaxBacklog && state.requested < pw.length {
|
||||
blockSize := MaxBlockSize
|
||||
// Last block might be shorter than the typical block
|
||||
if pw.length-state.requested < blockSize {
|
||||
blockSize = pw.length - state.requested
|
||||
}
|
||||
for state.downloaded < pw.length {
|
||||
// If unchoked, send requests until we have enough unfulfilled requests
|
||||
if !state.client.Choked {
|
||||
for state.backlog < MaxBacklog && state.requested < pw.length {
|
||||
blockSize := MaxBlockSize
|
||||
// Last block might be shorter than the typical block
|
||||
if pw.length-state.requested < blockSize {
|
||||
blockSize = pw.length - state.requested
|
||||
}
|
||||
|
||||
err := c.SendRequest(pw.index, state.requested, blockSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.backlog++
|
||||
state.requested += blockSize
|
||||
}
|
||||
}
|
||||
err := c.SendRequest(pw.index, state.requested, blockSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.backlog++
|
||||
state.requested += blockSize
|
||||
}
|
||||
}
|
||||
|
||||
err := state.readMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err := state.readMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return state.buf, nil
|
||||
return state.buf, nil
|
||||
}
|
||||
```
|
||||
|
||||
@@ -571,25 +571,25 @@ This is a short one. We're almost there.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/veggiedefender/torrent-client/torrentfile"
|
||||
"github.com/veggiedefender/torrent-client/torrentfile"
|
||||
)
|
||||
|
||||
func main() {
|
||||
inPath := os.Args[1]
|
||||
outPath := os.Args[2]
|
||||
inPath := os.Args[1]
|
||||
outPath := os.Args[2]
|
||||
|
||||
tf, err := torrentfile.Open(inPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tf, err := torrentfile.Open(inPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tf.DownloadToFile(outPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = tf.DownloadToFile(outPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ roadmapIds:
|
||||
- 'cpp'
|
||||
---
|
||||
|
||||
In this project, you will build a simple command line interface (CLI) to fetch data from The Movie Database (TMSB) and display it in the terminal. This project will help you practice your programming skills, including working with APIs, handling JSON data, and building a simple CLI application.
|
||||
In this project, you will build a simple command line interface (CLI) to fetch data from The Movie Database (TMDB) and display it in the terminal. This project will help you practice your programming skills, including working with APIs, handling JSON data, and building a simple CLI application.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -52,4 +52,4 @@ There are some considerations to keep in mind:
|
||||
|
||||
- Handle errors gracefully, such as API failures or network issues.
|
||||
- Use a programming language of your choice to build this project.
|
||||
- Make sure to include a README file with instructions on how to run the application and any other relevant information.
|
||||
- Make sure to include a README file with instructions on how to run the application and any other relevant information.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,6 @@ Machine learning is a field of artificial intelligence that uses statistical tec
|
||||
|
||||
Learn more from the following resources:
|
||||
|
||||
- [@article@Advantages and Disadvantages of AI](https://towardsdatascience.com/advantages-and-disadvantages-of-artificial-intelligence-182a5ef6588c)
|
||||
- [@article@Reinforcement Learning 101](https://towardsdatascience.com/reinforcement-learning-101-e24b50e1d292)
|
||||
- [@article@Understanding AUC-ROC Curve](https://towardsdatascience.com/understanding-auc-roc-curve-68b2303cc9c5)
|
||||
- [@article@Advantages and Disadvantages of AI](https://medium.com/@laners.org/advantages-and-disadvantages-of-artificial-intelligence-cd6e42819b20)
|
||||
- [@article@Reinforcement Learning 101](https://medium.com/towards-data-science/reinforcement-learning-101-e24b50e1d292)
|
||||
- [@article@Understanding AUC-ROC Curve](https://medium.com/towards-data-science/understanding-auc-roc-curve-68b2303cc9c5)
|
||||
|
||||
@@ -5,4 +5,5 @@ HTTP caching is a key aspect of API design which involves storing copies of resp
|
||||
Learn more from the following resources:
|
||||
|
||||
- [@article@Why HTTP Caching matters for APIs](https://thenewstack.io/why-http-caching-matters-for-apis/)
|
||||
- [@article@Caching REST API Response](https://restfulapi.net/caching/)
|
||||
- [@article@Caching REST API Response](https://restfulapi.net/caching/)
|
||||
- [@article@HTTP caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching)
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# Rate Limiting in API Design
|
||||
|
||||
Rate Limiting is a critical aspect of API Design that dictates the number of API calls a client can make within a specified timeframe. This helps in managing resource allocation, preventing abuse of the API, and maintaining the overall health of the API system. Proper rate limiting measures should be in place to ensure the API's stability, thereby delivering a consistent and reliable service to all consumers. It works primarily by setting a limit on the frequency of client requests, thereby preventing individual users from overloading the system. It is crucial to design and implement rate limiting carefully for maintaining API availability and performance.
|
||||
Rate Limiting is a critical aspect of API Design that dictates the number of API calls a client can make within a specified timeframe. This helps in managing resource allocation, preventing abuse of the API, and maintaining the overall health of the API system. Proper rate limiting measures should be in place to ensure the API's stability, thereby delivering a consistent and reliable service to all consumers. It works primarily by setting a limit on the frequency of client requests, thereby preventing individual users from overloading the system. It is crucial to design and implement rate limiting carefully for maintaining API availability and performance.
|
||||
|
||||
Learn more from the following resources:
|
||||
|
||||
- [@article@Rate limit](https://developer.mozilla.org/en-US/docs/Glossary/Rate_limit)
|
||||
- [@article@Throttle](https://developer.mozilla.org/en-US/docs/Glossary/Throttle)
|
||||
- [@article@Debounce](https://developer.mozilla.org/en-US/docs/Glossary/Debounce)
|
||||
- [@article@What is rate limiting? | Rate limiting and bots](https://www.cloudflare.com/en-gb/learning/bots/what-is-rate-limiting/)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,6 +2,7 @@
|
||||
jsonUrl: '/jsons/roadmaps/aspnet-core.json'
|
||||
pdfUrl: '/pdfs/roadmaps/aspnet-core.pdf'
|
||||
order: 9
|
||||
renderer: 'editor'
|
||||
briefTitle: 'ASP.NET Core'
|
||||
briefDescription: 'Step by step guide to becoming an ASP.NET Core Developer in 2025'
|
||||
title: 'ASP.NET Core Developer'
|
||||
@@ -10,7 +11,7 @@ isNew: false
|
||||
hasTopics: true
|
||||
dimensions:
|
||||
width: 968
|
||||
height: 2773.45
|
||||
height: 2920
|
||||
schema:
|
||||
headline: 'ASP.NET Core Developer Roadmap'
|
||||
description: 'Learn how to become a ASP.NET Core Developer with this interactive step by step guide in 2025. We also have resources and short descriptions attached to the roadmap items so you can get everything you want to learn in one place.'
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Azure Devops Services
|
||||
|
||||
Azure DevOps Services is a collection of services provided by Microsoft that can be used to plan, build, test, and deploy .NET applications. These services can be used together or independently to support various aspects of the software development process. Some of the main services include:
|
||||
|
||||
- Azure Boards: Provides features for agile planning and tracking, such as backlogs, boards, and sprint planning.
|
||||
- Azure Repos: Provides source control management for Git and Team Foundation Version Control (TFVC) repositories.
|
||||
- Azure Artifacts: Provides package management for NuGet, npm, and Maven packages.
|
||||
- Azure Test Plans: Provides support for manual and exploratory testing, as well as continuous testing and testing in production.
|
||||
- Azure Pipeline: Provides a way to build, test, and deploy code automatically, with support for multiple languages and platforms, including .NET.
|
||||
|
||||
These services can be used to create a full-featured development environment that can be used to manage all aspects of a software development project, from planning and design to testing and deployment.
|
||||
|
||||
For more information, visit the following links:
|
||||
|
||||
- [@article@Build, test, and deploy .NET Core apps](https://learn.microsoft.com/en-us/azure/devops/pipelines/ecosystems/dotnet-core?view=azure-devops\&tabs=dotnetfive)
|
||||
- [@article@Microsoft Azure DevOps for ASP .NET Core Web apps](https://techmindfactory.com/Microsoft-Azure-DevOps-for-ASP-.NET-Core-Web-apps/)
|
||||
- [@feed@Explore top posts about Azure](https://app.daily.dev/tags/azure?ref=roadmapsh)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Ninject
|
||||
|
||||
Ninject is an open-source dependency injection framework for .NET. It is designed to make it easier to manage the dependencies of an application by automatically resolving and managing the lifetime of objects and their dependencies.
|
||||
|
||||
Ninject uses a technique called "binding" to define the objects and dependencies of an application. This is done by creating instances of the `StandardKernel` class and using its methods to register types, instances and factories. Then, the `Get()` method is called to resolve the dependencies throughout the application.
|
||||
|
||||
For more information, visit the following resources:
|
||||
|
||||
- [@official@Intro to Ninject](http://www.ninject.org/)
|
||||
- [@article@What is Ninject and when do you use it?](https://stackoverflow.com/questions/17375234/what-is-ninject-and-when-do-you-use-it)
|
||||
- [@article@How to use NInject?](https://www.infoworld.com/article/3191648/how-to-implement-di-in-webapi-using-ninject.html)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Castle Windsor
|
||||
|
||||
Castle Windsor is an open-source dependency injection framework for .NET. It is designed to make it easier to manage the dependencies of an application by automatically resolving and managing the lifetime of objects and their dependencies.
|
||||
|
||||
Castle Windsor uses a technique called "registration" to define the objects and dependencies of an application. This is done by creating instances of the `WindsorContainer` class and using its methods to register types, instances and factories. Then, the `Resolve()` method is called to resolve the dependencies throughout the application.
|
||||
|
||||
Visit the following links for more resources:
|
||||
|
||||
- [@article@Getting Started with Dependency Injection Using Castle Windsor](https://www.codementor.io/@copperstarconsulting/getting-started-with-dependency-injection-using-castle-windsor-4meqzcsvh)
|
||||
- [@article@What is Castle Windsor?](https://stackoverflow.com/questions/124871/what-is-castle-windsor-and-why-should-i-care)
|
||||
- [@article@Intro to Castle Windsor](http://www.castleproject.org/projects/windsor/)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Simple Injector
|
||||
|
||||
Simple Injector is an open-source dependency injection framework for .NET. It is designed to make it easy to manage the dependencies of an application by automatically resolving and managing the lifetime of objects and their dependencies.
|
||||
|
||||
Simple Injector uses a technique called "registration" to define the objects and dependencies of an application. This is done by creating an instance of the `Container` class and using its methods to register types, instances and factories. Then, the `GetInstance()` method is called to resolve the dependencies throughout the application.
|
||||
|
||||
For more information, visit the following resources:
|
||||
|
||||
- [@article@How to use Simple Injector?](https://www.infoworld.com/article/3636242/how-to-use-simple-injector-in-aspnet-core-mvc.html)
|
||||
- [@article@Simple Injector's Documentation](https://docs.simpleinjector.org/en/latest/)
|
||||
- [@article@Example of Dependency Injection Using Simple Injector](https://www.c-sharpcorner.com/UploadFile/4d9083/dependency-injection-using-simple-injector/)
|
||||
@@ -1,7 +0,0 @@
|
||||
# Easy Caching
|
||||
|
||||
EasyCaching is an open-source caching library that contains basic usages and some advanced usages of caching which can help us to handle caching more easily.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@article@Easy Caching using Redis](https://easycaching.readthedocs.io/en/latest/Redis/)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Stackexchange Redis
|
||||
|
||||
StackExchange.Redis is a high performance general purpose redis client for .NET languages (C#, etc.). It is the logical successor to BookSleeve, and is the client developed-by (and used-by) Stack Exchange for busy sites like Stack Overflow.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@article@Using StackExchange.Redis with .NET](https://docs.redis.com/latest/rs/references/client_references/client_csharp/)
|
||||
- [@video@Introduction to StackExchange.Redis](https://www.youtube.com/watch?v=rsXvpCHdldg)
|
||||
- [@article@Getting Started with Stackexchange Redis](https://stackexchange.github.io/StackExchange.Redis/)
|
||||
- [@feed@Explore top posts about Redis](https://app.daily.dev/tags/redis?ref=roadmapsh)
|
||||
@@ -1,12 +0,0 @@
|
||||
# Redis
|
||||
|
||||
Redis (Remote Dictionary Server) is an open-source, in-memory data structure store that can be used as a database, cache, and message broker. It supports various data structures such as strings, hashes, lists, sets, and sorted sets.
|
||||
|
||||
In an ASP.NET application, Redis can be used as a caching mechanism to store frequently accessed data in memory, rather than reading it from a slower storage system like a traditional relational database. This can greatly improve the performance of an application by reducing the number of database queries and the amount of data that needs to be read from disk.
|
||||
|
||||
To learn more, visit the following resources:
|
||||
|
||||
- [@official@Redis OM .NET](https://redis.io/docs/stack/get-started/tutorials/stack-dotnet/)
|
||||
- [@article@Caching in ASP.NET using Redis](https://www.codemag.com/Article/2205091/Distributed-Caching-in-ASP.NET-Core-6-Using-Redis-in-Azure)
|
||||
- [@article@Getting started in Redis with .NET](https://docs.redis.com/latest/rs/references/client_references/client_csharp/)
|
||||
- [@feed@Explore top posts about Redis](https://app.daily.dev/tags/redis?ref=roadmapsh)
|
||||
@@ -1,12 +0,0 @@
|
||||
# RavenDB
|
||||
|
||||
RavenDB is an open-source, NoSQL document database designed for the .NET platform. It is a document database, which means that it stores data in semi-structured JSON format, and it is designed to be simple and easy to use. It is also a multi-model database, which allows you to store and query data using a variety of data models, including document, key-value, graph, and column-family.
|
||||
|
||||
In an ASP.NET application, RavenDB can be used as a data store to persist and retrieve application data. There are several libraries available for integrating RavenDB with an ASP.NET application, such as RavenDB.Client, which provides a .NET client for RavenDB that can be used to interact with the RavenDB server from within an ASP.NET application.
|
||||
|
||||
For more information, visit the following links:
|
||||
|
||||
- [@article@Using RavenDB in ASP.NET Applications](https://www.codeguru.com/dotnet/using-ravendb-in-asp-net-applications/)
|
||||
- [@official@Getting started with RavenDB](https://ravendb.net/docs/article-page/5.4/csharp/start/getting-started)
|
||||
- [@article@Using RavenDB Unit of Work and .NET Core MVC](https://ayende.com/blog/187906-B/using-ravendb-unit-of-work-and-net-core-mvc)
|
||||
- [@feed@Explore top posts about RavenDB](https://app.daily.dev/tags/ravendb?ref=roadmapsh)
|
||||
@@ -1,14 +0,0 @@
|
||||
# ELK Stack
|
||||
|
||||
The ELK Stack is a collection of three open-source products — Elasticsearch, Logstash, and Kibana. ELK stack provides centralized logging in order to identify problems with servers or applications. It allows you to search all the logs in a single place. It also helps to find issues in multiple servers by connecting logs during a specific time frame.
|
||||
|
||||
- **E** stands for ElasticSearch: used for storing logs
|
||||
- **L** stands for LogStash : used for both shipping as well as processing and storing logs
|
||||
- **K** stands for Kibana: is a visualization tool (a web interface) which is hosted through Nginx or Apache
|
||||
|
||||
For more information, visit the following links:
|
||||
|
||||
- [@article@What Is Elk?](https://aws.amazon.com/what-is/elk-stack/)
|
||||
- [@article@Complete Guide to Elk](https://logz.io/learn/complete-guide-elk-stack/)
|
||||
- [@article@ELK Stack Tutorial: What is Kibana, Logstash & Elasticsearch?](https://www.guru99.com/elk-stack-tutorial.html)
|
||||
- [@feed@Explore top posts about ELK](https://app.daily.dev/tags/elk?ref=roadmapsh)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Sentry
|
||||
|
||||
Sentry is an open-source error tracking platform that helps developers identify and fix errors in their applications. It provides detailed error reports, including stack traces, context data, and user feedback, making it easy to understand and reproduce errors. Sentry can be integrated with a variety of platforms, including .NET and ASP.NET.
|
||||
|
||||
In an ASP.NET application, Sentry can be used to capture and track errors that occur in the application. Sentry provides an SDK, called Sentry.NET, which can be integrated with an ASP.NET application to automatically capture and track errors. The SDK can be configured to capture and track different types of errors, including unhandled exceptions, handled exceptions, and custom events.
|
||||
|
||||
For more information, visit the following links:
|
||||
|
||||
- [@article@Sentry Documentation in .NET](https://docs.sentry.io/platforms/dotnet/)
|
||||
- [@article@ASP.NET and Sentry](https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/)
|
||||
- [@article@Usage of Sentry in ASP.NET](https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/usage/)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Datadog
|
||||
|
||||
Datadog is a cloud-based monitoring and analytics platform that helps organizations to monitor, troubleshoot, and optimize their applications and infrastructure. It provides a wide range of features including real-time monitoring, log management, tracing, and alerting, and it can be integrated with a variety of platforms, including .NET and ASP.NET.
|
||||
|
||||
In an ASP.NET application, Datadog can be used to monitor and analyze the performance and behavior of the application. Datadog provides an SDK, called Datadog.Trace, which can be integrated with an ASP.NET application to automatically collect and report performance data. The SDK can be configured to report data such as request and response times, error rates, and custom metrics.
|
||||
|
||||
Visit the following links:
|
||||
|
||||
- [@article@Documentation of Datadog](https://www.datadoghq.com/)
|
||||
- [@article@Configuring the .NET Core with Datadog](https://docs.datadoghq.com/tracing/trace_collection/library_config/dotnet-core/?tab=environmentvariables)
|
||||
- [@feed@Explore top posts about DevOps](https://app.daily.dev/tags/devops?ref=roadmapsh)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Loggly
|
||||
|
||||
Loggly is a cloud-based log management and analytics platform that helps organizations to collect, store, and analyze log data from a variety of sources, including .NET and ASP.NET applications. It provides a wide range of features such as real-time log search and analysis, alerting, and reporting, and allows for easy integration with other tools such as Datadog, Splunk, and New Relic.
|
||||
|
||||
In an ASP.NET application, Loggly can be used to collect and analyze log data. Loggly provides an SDK, called Loggly.Serilog, which can be integrated with an ASP.NET application to automatically collect log data and send it to Loggly. The SDK can be configured to collect different types of log data, such as log messages, error messages, and custom events.
|
||||
|
||||
For more resources, visit the following links:
|
||||
|
||||
- [@article@Loggly in ASP.NET Core using Serilog](https://itnext.io/loggly-in-asp-net-core-using-serilog-dc0e2c7d52eb)
|
||||
- [@official@Using Loggly For Troubleshooting Bugs](https://www.loggly.com/blog/use-loggly-troubleshooting-bugs-code/)
|
||||
@@ -1,11 +0,0 @@
|
||||
# ELMAH
|
||||
|
||||
ELMAH (Error Logging Modules and Handlers) is an open-source error logging and management library for ASP.NET applications. It is designed to be easy to use and easy to integrate into existing ASP.NET applications.
|
||||
|
||||
ELMAH is widely used for error logging, management and tracking in ASP.NET applications, it's easy to use, easy to integrate with existing applications, and provides a wealth of features that help developers to identify, diagnose and fix errors in their applications.
|
||||
|
||||
For more resources, visit the following links:
|
||||
|
||||
- [@article@Introduction To ELMAH](https://www.c-sharpcorner.com/article/introduction-to-elmah-in-mvc/)
|
||||
- [@video@How to Configure Elmah in ASP.net MVC](https://www.youtube.com/watch?v=oqheVBZR0PM)
|
||||
- [@article@ELMAH Integration in ASP.NET MVC Application](https://www.ecanarys.com/Blogs/ArticleID/184/ELMAH-Integration-in-ASP-NET-MVC-Application)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Log Management System
|
||||
|
||||
A log management system is a software or service that is designed to collect, store, and analyze log data from a variety of sources. Logs are records of events that occur in a system, such as application logs, system logs, and network logs. Log management systems are used to gather, store and analyze these logs in order to provide insight into the performance, stability and security of a system.
|
||||
|
||||
The use of log management system can help IT professionals to identify and troubleshoot issues, monitor performance, detect security threats and meet regulatory compliance requirements, also it's a fundamental part of IT infrastructure management, and provides a way to measure the health and security of the system.
|
||||
|
||||
Visit the following resources for more information:
|
||||
|
||||
- [@article@Structured Logging and Logs Management in ASP.NET](https://medium.com/@stavsofer/structured-logging-and-logs-management-asp-net-core-serilog-seq-61109f740696)
|
||||
- [@article@ASP.NET Logging Basics](https://www.loggly.com/ultimate-guide/net-logging-basics/)
|
||||
- [@feed@Explore top posts about Logging](https://app.daily.dev/tags/logging?ref=roadmapsh)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Sieve
|
||||
|
||||
Sieve is a language for filtering email messages, it's defined in the Internet standard, also it's called as Sieve: An Email Filtering Language. It provides a simple, human-readable syntax for specifying rules that determine what actions to take on incoming email messages. These actions can include filing messages into different folders, forwarding messages to other addresses, discarding messages, and more.
|
||||
|
||||
Sieve is typically used in email servers and clients, and it can be used to implement server-side and client-side email filtering. It allows users to create their own rules for filtering email, without requiring access to the server's configuration or the need for complex server-side scripts.
|
||||
|
||||
To learn more, visit the following resources:
|
||||
|
||||
- [@article@Analysis of Sieve](https://en.wikipedia.org/wiki/Sieve_analysis)
|
||||
- [@video@Sorting in a .NET Core API using Sieve](https://www.youtube.com/watch?v=x0utCah3cFk)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Express Mapper
|
||||
|
||||
ExpressMapper is an open-source object-to-object mapping library for .NET, similar to AutoMapper. It allows you to easily map between objects of different types, and it is particularly useful when working with domain models and data transfer objects (DTOs) in a layered architecture.
|
||||
|
||||
ExpressMapper uses a convention-based approach to mapping, which means that it automatically maps properties with the same name and type from one object to another. It also provides a fluent API for configuring more complex mappings, such as ignoring certain properties, using custom logic to map properties, or mapping properties based on a value in another property.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@official@Intro to Express Mapper](https://expressmapper.org/)
|
||||
- [@article@Express Mapper - The New .NET Mapper](https://www.codeproject.com/Tips/1009198/Expressmapper-The-New-NET-Mapper)
|
||||
- [@feed@Explore top posts about Express.js](https://app.daily.dev/tags/express?ref=roadmapsh)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Agile Mapper
|
||||
|
||||
AgileMapper is an open-source, convention-based object-to-object mapping library for .NET, similar to AutoMapper and ExpressMapper. It allows you to easily map between objects of different types, and it is particularly useful when working with domain models and data transfer objects (DTOs) in a layered architecture.
|
||||
|
||||
AgileMapper uses a convention-based approach to mapping, which means that it automatically maps properties with the same name and type from one object to another. It also provides a fluent API for configuring more complex mappings, such as ignoring certain properties, using custom logic to map properties, or mapping properties based on a value in another property.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@Comparison of Object Mapper Libraries](https://www.simplilearn.com/tutorials/asp-dot-net-tutorial/automapper-in-c-sharp)
|
||||
- [@article@Overview of Agile Mapper](https://readthedocs.org/projects/agilemapper/)
|
||||
- [@feed@Explore top posts about Agile](https://app.daily.dev/tags/agile?ref=roadmapsh)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Mapster
|
||||
|
||||
Mapster is an open-source, high-performance object-to-object mapping library for .NET, similar to AutoMapper, ExpressMapper, AgileMapper, and AgileMapster. It allows you to easily map between objects of different types, and it is particularly useful when working with domain models and data transfer objects (DTOs) in a layered architecture.
|
||||
|
||||
Mapster uses a convention-based approach to mapping, which means that it automatically maps properties with the same name and type from one object to another. It also provides a fluent API for configuring more complex mappings, such as ignoring certain properties, using custom logic to map properties, or mapping properties based on a value in another property.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@Getting Started With Mapster in ASP.NET](https://code-maze.com/mapster-aspnetcore-introduction/)
|
||||
- [@article@Overview of Mapster in .Net ](https://medium.com/@M-S-2/enjoy-using-mapster-in-net-6-2d3f287a0989)
|
||||
- [@video@Introduction to Mapster](https://youtube.com/watch?v=bClE7Uon9e8)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Selenium
|
||||
|
||||
Selenium is an open-source library that allows developers to automate web browsers and simulate user interactions, it's commonly used for testing web applications. It can be integrated with .NET and provides a .NET binding for interacting with the Selenium API. The Selenium WebDriver API for .NET allows developers to write tests for web applications in C# or other .NET languages and supports various programming paradigms like Page Object Model.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@Automated UI Tests with Selenium and ASP.NET](https://code-maze.com/selenium-aspnet-core-ui-tests/)
|
||||
- [@article@Selenium Web Driver in .NET](https://stephan-bester.medium.com/automated-testing-with-selenium-web-driver-in-net-bde6854d3207)
|
||||
- [@feed@Explore top posts about Selenium](https://app.daily.dev/tags/selenium?ref=roadmapsh)
|
||||
@@ -1,10 +0,0 @@
|
||||
# E2E Testing
|
||||
|
||||
E2E testing is a method of testing the complete functionality of an application, from start to finish, it's used to test the entire workflow of an application and ensure that all the different components and features work together seamlessly. In the context of ASP.NET, E2E testing can be used to test web applications built using the ASP.NET framework. E2E testing can be done using frameworks such as Selenium, Playwright, and Cypress which allow developers to automate browser interactions and simulate user interactions with the application. E2E testing is an important aspect of software development and it helps to identify and fix issues early in the development process and ensure that the application behaves as expected when it is released to the users.
|
||||
|
||||
Visit the following links to learn more:
|
||||
|
||||
- [@video@End-to-End Testing ASP.NET Core APIs](https://www.youtube.com/watch?v=WWN-9ahbdIU)
|
||||
- [@article@ASP.NET Core and its testing](https://blog.devgenius.io/asp-net-core-end-to-end-testing-52325e28e387)
|
||||
- [@article@Unit Test and E2E Test in ASP.NET](https://learn.microsoft.com/en-us/odata/webapi/unittest-e2etest)
|
||||
- [@feed@Explore top posts about Testing](https://app.daily.dev/tags/testing?ref=roadmapsh)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Fluent Assertions
|
||||
|
||||
Fluent Assertions is a fluent, readable, and extensible set of .NET extension methods that allow developers to write more natural and expressive assertions in their unit tests, it provides a fluent, fluent-like API that makes it easy to write assertions in a natural language-like syntax. In the context of ASP.NET, Fluent Assertions can be used in conjunction with test frameworks such as MSTest, xUnit, and NUnit to write more expressive and readable unit tests for the application. It provides advanced features such as support for collection-specific assertions, support for asynchronous code, and support for custom types. It's a widely used and open source library, it's very useful to improve code readability and make the test code more expressive.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@official@Introduction of Fluent Assertions](https://fluentassertions.com/introduction)
|
||||
- [@article@Improving Unit Tests with Fluent Assertions](https://code-maze.com/unit-tests-with-fluent-assertions/)
|
||||
- [@article@Exploring Fluent Assertions in Unit Testing](https://www.meziantou.net/exploring-fluent-assertions.htm)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Unit Testing
|
||||
|
||||
Unit testing is a software testing method in which individual units or components of a software application are tested in isolation from the rest of the application. The goal of unit testing is to validate that each unit of the software application is working as intended and that the interactions between units are also working as intended. In the context of ASP.NET, unit testing can be used to test individual components of a web application built using the ASP.NET framework. Unit testing is a good practice in software development as it helps to catch bugs early in the development process, improves the quality of the code and makes it more maintainable. Additionally, unit tests provide developers with a suite of automated tests that can be run against the application at any time. There are different Unit Testing frameworks available for .NET and many libraries for Mocking, Assertions, and fake objects.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@Overview of .NET Unit Testing](https://www.toptal.com/dot-net/dotnet-unit-testing-tutorial)
|
||||
- [@article@Unit Testing in ASP.NET - Complete Tutorial](https://www.guru99.com/asp-net-unit-testing-project.html)
|
||||
- [@video@How to UnitTest ASP.Net MVC Controllers with XUnit ?](https://www.youtube.com/watch?v=VtPosbYAhD8)
|
||||
- [@feed@Explore top posts about Testing](https://app.daily.dev/tags/testing?ref=roadmapsh)
|
||||
@@ -1,9 +0,0 @@
|
||||
# TestServer
|
||||
|
||||
TestServer in ASP.NET is a programmatic way to create a web server that can be used to run integration or end-to-end tests for a web application. The TestServer simulates the behavior of a real web server, and allows developers to test the web application in a realistic environment, without the need for a physical web server or a browser. The TestServer can be created and configured using the built-in Web Application Factory (WAF) feature in ASP.NET Core. The WAF allows developers to configure the TestServer to use different services, middleware, and settings, depending on the needs of the application. The TestServer can also be used to test the web application against different configurations, such as different databases, different authentication providers, and different hosting environments.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@Integration Tests with TestServer](https://visualstudiomagazine.com/articles/2017/07/01/testserver.aspx)
|
||||
- [@article@ASP.NET Core Integration Tests With TestServer](https://scotthannen.org/blog/2021/11/18/testserver-how-did-i-not-know.html)
|
||||
- [@article@How do you create a TestServer in .NET](https://stackoverflow.com/questions/69897652/how-do-you-create-a-test-server-in-net-6)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Integration Testing
|
||||
|
||||
Integration testing in ASP.NET refers to the process of testing how different components or modules of a web application interact with each other. This type of testing is done to ensure that the different parts of the application are working together correctly and that the application as a whole is functioning properly. Integration testing typically involves testing the interactions between different layers of the application, such as the data access layer, the business logic layer, and the presentation layer. It can also involve testing the interactions between the web application and external systems, such as databases, web services, and other external APIs. Integration testing can be done using a variety of testing frameworks and tools, such as xUnit, NUnit, MSTest, and others. These frameworks provide the ability to create test cases that simulate different scenarios and test the application's behavior under different conditions.
|
||||
|
||||
Visit the following links to learn more:;
|
||||
|
||||
- [@article@Integration tests in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-7.0)
|
||||
- [@video@Integration Testing ASP.NET Core WebAPI Applications](https://www.youtube.com/watch?v=xs8gNQjCXw0)
|
||||
- [@video@A Sample Project Integration Test In ASP.NET](https://www.youtube.com/watch?v=ziSKkR2nvis)
|
||||
- [@feed@Explore top posts about Testing](https://app.daily.dev/tags/testing?ref=roadmapsh)
|
||||
@@ -1,9 +0,0 @@
|
||||
# BDDfy
|
||||
|
||||
BDDfy is an open-source, fluent, and extensible BDD (Behavior-Driven Development) framework for .NET, which allows developers to create automated acceptance tests in a readable and expressive format. BDDfy is commonly used in the context of ASP.NET and other .NET technologies, to write acceptance tests for web applications. With BDDfy, developers can write test scenarios using a fluent API, which allows them to describe the behavior of their application in a natural language format. The framework also provides a set of extension methods, which can be used to add additional functionality, such as validating the output of a test, logging test results, or integrating with other testing tools. BDDfy also comes with a built-in test runner, which makes it easy to execute tests and view the results. The framework supports a variety of test runners, such as NUnit, xUnit, and MSTest, and can be integrated with other BDD frameworks, such as SpecFlow and Cucumber.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@Using BDDfy for Unit Tests](https://www.michael-whelan.net/using-bddfy-for-unit-tests/)
|
||||
- [@article@Introducing BDDfy in ASP.NET](https://codeproject.com/Articles/205381/Introducing-BDDfy-the-simplest-BDD-framework-for-N)
|
||||
- [@video@Implement BDD with .NET](https://www.youtube.com/watch?v=GYN_srjAvyk)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Behavior Testing
|
||||
|
||||
Behavior testing, also known as Behavioral-Driven Development (BDD), is a software development methodology that focuses on the behavior of the system being developed, rather than its implementation. BDD is a variation of Test-Driven Development (TDD), which emphasizes the use of automated tests to drive the development of software. BDD frameworks such as SpecFlow, Cucumber, and LightBDD provide a set of tools for writing and executing BDD tests in the context of ASP.NET. These frameworks allow developers to write tests using a fluent API, which allows them to describe the behavior of their application in a natural language format. They also provide a set of extension methods, which can be used to add additional functionality, such as validating the output of a test, logging test results, or integrating with other testing tools.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@A Complete Tutorial on ASP.NET Testing](https://www.lambdatest.com/blog/aspnet-testing/)
|
||||
- [@article@Learn Behavior Testing in ASP.NET](https://public.vectorly.team/articles/learn_Behavior_Testing_for_Tech_Lead_role_ASP.NET_Developer)
|
||||
- [@feed@Explore top posts about Testing](https://app.daily.dev/tags/testing?ref=roadmapsh)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Message Brokers
|
||||
|
||||
Message Brokers are systems that provide a messaging infrastructure for applications and services to communicate with each other in a reliable, secure and scalable way. They act as a central hub for sending and receiving messages between different applications, services and devices.
|
||||
|
||||
In the context of ASP.NET, message brokers can be used to send and receive messages between different parts of an application or between different applications or services, enabling them to communicate with each other in a decoupled and asynchronous manner.
|
||||
|
||||
Visit the following links to learn more:
|
||||
|
||||
- [@article@What are message brokers?](https://www.ibm.com/topics/message-brokers)
|
||||
- [@article@Building a messaging solution with .Net Core?](https://medium.com/c-sharp-progarmming/quick-start-how-could-you-build-a-messaging-solution-with-net-core-b5f8253f31ea)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Message Bus
|
||||
|
||||
A message bus is a software architecture pattern that enables different parts of an application, or different systems, to communicate with each other asynchronously using a message-based protocol. The message bus acts as a central intermediary, routing messages from senders to receivers and providing features such as message persistence, message routing, and message acknowledgements.
|
||||
|
||||
There are different types of message buses, such as event-driven message buses, command-driven message buses, and publish-subscribe message buses. Each type of message bus is designed to handle specific messaging patterns and use cases.
|
||||
|
||||
Visit the following links to learn more:
|
||||
|
||||
- [@article@Implementation of Message Bus C# microservices](https://stackoverflow.com/questions/51330229/implementation-of-message-bus-c-sharp-microservices)
|
||||
- [@article@Building a Message Bus with .NET Core](https://fatihdumanli.medium.com/build-a-message-bus-implementation-with-net-core-and-rabbitmq-9ba350b777f4)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Docker Swarm
|
||||
|
||||
Docker Swarm is a container orchestration platform for managing and scheduling Docker containers across a cluster of servers. It is a native clustering solution for Docker and provides the ability to create and manage a swarm of Docker nodes as a single virtual host.
|
||||
|
||||
With Docker Swarm, you can easily deploy and scale your applications across multiple servers, ensuring high availability and fault tolerance. It provides features such as load balancing, service discovery, and rolling updates, making it easy to manage and maintain your containerized applications.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@Introduction to Docker Swarm](https://www.section.io/engineering-education/introduction-to-docker-swarm-in-container-orchestration/)
|
||||
- [@article@How - Deploy to Swarm?](https://docs.docker.com/get-started/swarm-deploy/)
|
||||
- [@feed@Explore top posts about Docker](https://app.daily.dev/tags/docker?ref=roadmapsh)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Tye
|
||||
|
||||
Tye (short for "Try Everything") is a developer tool for .NET that makes it easy to develop, test, and deploy microservices and distributed applications. It is built on top of the .NET Core CLI and Docker and provides a simple and consistent way to run multiple services and components locally, making it easy to test and debug distributed applications.
|
||||
|
||||
Tye allows you to define the components of your application in a single YAML file, and then automatically starts, stops and configures all the required services and dependencies. It also provides a set of features to make it easy to manage the application, such as automatic service discovery and configuration, and automatic binding of services to the right ports.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@opensource@Getting Started with Tye](https://github.com/dotnet/tye)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Travis CI
|
||||
|
||||
Travis CI is a cloud-based continuous integration and continuous delivery (CI/CD) platform that allows developers to automate the process of building, testing, and deploying code. It is a popular platform that supports a wide range of languages and frameworks, including ASP.NET.
|
||||
|
||||
In ASP.NET, Travis CI can be used to automate various tasks related to the development, testing, and deployment of ASP.NET applications. For example, you can use Travis CI to automatically build, test, and deploy an ASP.NET application to a hosting provider, such as Azure or AWS, every time you push code to your source control repository.
|
||||
|
||||
Visit the following links to learn more:
|
||||
|
||||
- [@article@How to use Travis CI to Deploy to Azure in ASP.NET](https://devblogs.microsoft.com/cse/2015/09/30/using-travis-ci-to-deploy-to-azure/)
|
||||
- [@article@Concepts for Beginners of Travis CI in ASP.NET](https://docs.travis-ci.com/user/for-beginners/)
|
||||
- [@feed@Explore top posts about CI/CD](https://app.daily.dev/tags/cicd?ref=roadmapsh)
|
||||
@@ -1,12 +0,0 @@
|
||||
# Jenkins
|
||||
|
||||
Jenkins is an open-source automation server that can be used to automate various tasks related to software development, including building, testing, and deploying code. It is a popular platform that supports a wide range of languages and frameworks, including ASP.NET.
|
||||
|
||||
In ASP.NET, Jenkins can be used to automate various tasks related to the development, testing, and deployment of ASP.NET applications. For example, you can use Jenkins to automatically build, test, and deploy an ASP.NET application to a hosting provider, such as Azure or AWS, every time you push code to your source control repository.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@article@Continuous Deployment With Jenkins And .NET](https://www.c-sharpcorner.com/article/continuous-deployment-with-jenkins-and-net/)
|
||||
- [@article@How to build ASP.NET Core code with Jenkins](https://referbruv.com/blog/cicd-getting-started-automating-aspnet-core-build-using-jenkins/)
|
||||
- [@article@How to publish ASP.NET Apps using Jenkins](https://www.ahmetkucukoglu.com/en/how-to-publish-asp-net-core-application-by-using-jenkins)
|
||||
- [@feed@Explore top posts about Jenkins](https://app.daily.dev/tags/jenkins?ref=roadmapsh)
|
||||
@@ -1,11 +0,0 @@
|
||||
# TeamCity
|
||||
|
||||
TeamCity is a Java-based continuous integration and continuous delivery (CI/CD) platform that allows developers to automate the process of building, testing, and deploying code. It is a popular platform that supports a wide range of languages and frameworks, including ASP.NET.
|
||||
|
||||
In ASP.NET, TeamCity can be used to automate various tasks related to the development, testing, and deployment of ASP.NET applications. For example, you can use TeamCity to automatically build, test, and deploy an ASP.NET application to a hosting provider, such as Azure or AWS, every time you push code to your source control repository.
|
||||
|
||||
Visit the following links to learn more:
|
||||
|
||||
- [@article@Tutorial on TeamCity with ASP.NET](https://www.jetbrains.com/help/teamcity/net.html)
|
||||
- [@video@How to Build ASP.NET application with TeamCity?](https://www.youtube.com/watch?v=KNzxyhSWV-4)
|
||||
- [@article@How to deploy ASP.NET Core sites using Teamcity?](https://medium.com/monkii/how-to-deploy-asp-net-core-sites-using-teamcity-or-just-command-line-cf05fdee58f5)
|
||||
@@ -1,7 +0,0 @@
|
||||
# Client Side Libraries
|
||||
|
||||
Client-side libraries in ASP.NET are libraries that are used to add functionality and enhance the user experience of web applications on the client-side, or the browser. Some popular client-side libraries in ASP.NET include jQuery, Bootstrap, React.js, Angular.js, and Vue.js, which can be easily integrated with ASP.NET applications by including them as a dependency in the project, and can be used to enhance the user interface, add interactive elements, and improve the user experience of your application.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@video@How to use Client Side Library in ASP.NET Core](https://www.youtube.com/watch?v=VwqozSbQuec)
|
||||
@@ -1,8 +0,0 @@
|
||||
# DotLiquid
|
||||
|
||||
DotLiquid is an open-source templating engine for .NET that is based on the Liquid template language. It allows you to embed dynamic data in HTML templates, and is commonly used for generating email templates, reports, and other types of documents. It is a .NET implementation of the Liquid template language, it provides a simple and easy-to-use API for parsing and rendering Liquid templates, and can be easily integrated with ASP.NET and other .NET frameworks. It is often used in web applications to separate the logic of the application from the presentation of the data, making it easy to change the appearance of the application without having to change the underlying code.
|
||||
|
||||
To learn more, visit the following resources:
|
||||
|
||||
- [@article@Guide to DotLiquid](https://discoverdot.net/projects/dotliquid)
|
||||
- [@article@What is DotLiquid in ASP.NET Core?](https://grandnode.medium.com/dotliquid-in-asp-net-core-e-commerce-platform-how-to-handle-message-templates-a6865be3a612)
|
||||
@@ -1,8 +0,0 @@
|
||||
# Noda Time
|
||||
|
||||
Noda Time is an open-source library for .NET that provides a more complete and accurate way of working with dates, times, and time zones. It addresses the limitations and inconsistencies of the built-in .NET DateTime and TimeZone classes, and provides a number of features that are not present in the .NET framework. It provides a set of immutable and thread-safe value types, such as LocalDate, LocalTime, and Instant. It also provides a time zone abstraction, the DateTimeZone class, that allows you to work with time zones in a more accurate and consistent way, and a set of helper classes and extension methods that make it easy to perform common date and time-related operations.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@official@Intro to Noda Time](https://nodatime.org/)
|
||||
- [@official@What's the use of Noda Time?](https://nodatime.org/3.1.x/userguide/rationale)
|
||||
@@ -1,7 +0,0 @@
|
||||
# GenFu
|
||||
|
||||
GenFu is an open-source library for .NET that provides a simple and flexible way to generate test data for use in automated testing and development. It uses a fluent API to define the types of data that should be generated, and provides a set of built-in generators for common types, such as strings, numbers, and dates. It allows you to easily generate large quantities of test data quickly, and it is especially useful when working with complex object graphs and scenarios that require large amounts of data. It also has built-in support for some of the common libraries like AutoFixture, NBuilder and more, so that you can use it seamlessly with those libraries.
|
||||
|
||||
To learn more, visit the following links:
|
||||
|
||||
- [@opensource@Overview of GenFu in ASP.NET](https://github.com/MisterJames/GenFu)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Swashbuckle
|
||||
|
||||
Swashbuckle is an open-source library for .NET that provides a simple and easy-to-use API for generating Swagger (OpenAPI) documentation for web APIs. It automatically generates a Swagger specification for your API based on your existing .NET code, and provides a set of tools for customizing and displaying the documentation in a user-friendly format. It can be easily integrated into an ASP.NET Core web application and has a built-in web UI for developers and users to interact with the API and test its functionality. It also supports some of the advanced features like Authentication, Authorization, and more.
|
||||
|
||||
To learn more, visit the following resources:
|
||||
|
||||
- [@article@Get started with Swashbuckle and ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-7.0&tabs=visual-studio)
|
||||
- [@article@How to Setup Swagger in ASP.NET with Swashbuckle?](https://www.andrewhoefling.com/Blog/Post/web-api-swagger-swashbuckle)
|
||||
- [@article@How to use Swagger/Swashbuckle in ASP.NET?](https://www.pragimtech.com/blog/azure/how-to-use-swagger-in-asp.net-core-web-api/)
|
||||
@@ -1,14 +0,0 @@
|
||||
# Good To Know Libraries
|
||||
|
||||
There are many libraries available for .NET and ASP.NET that can help you with various tasks and improve your development workflow. Some of the libraries that are commonly used and considered to be "good to know" include:
|
||||
|
||||
- Entity Framework
|
||||
- AutoMapper
|
||||
- MediatR
|
||||
- FluentValidation
|
||||
- Newtonsoft.Json
|
||||
|
||||
To learn more, visit the following resources:
|
||||
|
||||
- [@article@Top ASP.NET Libraries – Every Developer Should Know](https://procodeguide.com/programming/top-12-aspnet-core-libraries/)
|
||||
- [@article@Top 10 .NET Libraries Every Developer Should Know](https://www.syncfusion.com/blogs/post/top-10-net-core-libraries-every-web-developers-should-know.aspx)
|
||||
@@ -0,0 +1 @@
|
||||
# AutoFixture
|
||||
@@ -0,0 +1 @@
|
||||
# Bogus
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user