In Jhipster, what is the easiest way to allow a user with role ROLE_USE to access the embedded swagger-ui (and perhaps also allow them to use the usual authorize button to automatically add their access token to all requests)
If you really need to open up this route to regular users, in my opinion the best approach is to move it out of /admin.
First open admin-routing.module.ts and remove the following lines:
{
path: 'docs',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule),
},
Now edit app-routing.module.ts and add the /docs route:
// ...
},
{
path: 'login',
loadChildren: () => import('./login/login.module').then(m => m.LoginModule),
},
{
path: 'docs',
data: {
authorities: [Authority.ADMIN, Authority.USER],
},
canActivate: [UserRouteAccessService],
loadChildren: () => import('./admin/docs/docs.module').then(m => m.DocsModule),
},
...LAYOUT_ROUTES,
],
{ enableTracing: DEBUG_INFO_ENABLED }
// ...
Change the old routerLink for the docs menu entry in your navbar.component.html:
<li *ngIf="openAPIEnabled">
<a class="dropdown-item" routerLink="docs" routerLinkActive="active" (click)="collapseNavbar()">
<fa-icon icon="book" [fixedWidth]="true"></fa-icon>
<span>API</span>
</a>
</li>
Now if you access localhost:8080/docs you should be able to use the swagger-ui as a regular user.
This is just the minimal amount of changes you require, some things to keep in mind:
You could move the folder \src\main\webapp\app\admin\docs into \src\main\webapp\app\docs, just to keep things tidy and coherent. If you do this remember to update the reference in your app-routing.module.ts.
You should add a new menu entry in your navbar for regular users to navigate into swagger, this is trivial so I leave it to you.
Related
I'm using react-router v 6.4 with createBrowserRouter to support the new data API.
I have routes that have a loader, and this loader can take 1-2 sec to get the data from the server, and I want to show a loading animation at that time.
See the following as a simple example of what I have, and a comment pointing to what I was expecting to do/find in the docs:
const router = createBrowserRouter([
{
path: '/',
element: <Layout/>,
children: [
{
index: true,
element: <Screen title="Home"/>,
},
{
path: 'materials',
loader: async () => {
return (await fetch('/api/materials')).json()
},
fallbackElement: <Loading />, // <<--- THIS IS WHAT I WAS EXPECTING TO DO
element: <Materials/>,
},
{
path: 'projects',
loader: async () => {
return (await fetch('/api/projects')).json()
},
element: <Projects/>,
},
],
},
])
Could not find how to place a "fallback" element on a route to show while the loader is waiting for the data, only to place a fallbackElement on the RouterProvider component, but that is not what I want (it shows the fallback element only on the mount of RouterProvider, not when changing between routes).
Seems kinda weird that such a thing is not supported, and cannot really find answers through the search here as well.
As per the documentation, on the component consuming the loader data you have to use React.Suspense and Await components to show the fallback, something like this:
import { Await, useLoaderData } from "react-router-dom";
function Book() {
const { book, reviews } = useLoaderData();
return (
<div>
<h1>{book.title}</h1>
<p>{book.description}</p>
<React.Suspense fallback={<ReviewsSkeleton />}>
<Await
resolve={reviews}
errorElement={
<div>Could not load reviews 😬</div>
}
children={(resolvedReviews) => (
<Reviews items={resolvedReviews} />
)}
/>
</React.Suspense>
</div>
);
}
https://reactrouter.com/en/main/components/await#await
That's in theory, because I've done that and my loaders are not showing either.
So I'm very new to Gatsby, react, GraphQL, etc. In the past I've used pure CSS, HTML, and javascript to make my sites. Although, I was interested in Gatsby and the capabilities of it, so I decided to challenge myself and learn it.
I'm putting together a portfolio site for myself and for ease of updating, I would like to be able to add new projects through creating new folders, running a build script, and dropping the built site into my FTP.
This is how my folder structure for projects is set up:
-src
--projects
---1-daido-moriyama
----1-dm-frontcover.jpg
----2-dm-spread.jpg
----3-dm-backcover.jpg
----project-metadata.md
[...]
---2-lunch-from-a-care-package
----1-lf-wordmark.png
----2-lf-logo.png
----3-lf-poster.jpg
----project-metadata.md
[...]
The site is a single page, so no need to create new pages for each project. I just have them sorted into numbered folders because that would be the easiest to update for myself.
Ideally I would want to take the title and description from each project's markdown file, and put the title in an h3, the description in a p, and then display the images in a div, which when styled will become a carousel.
Mockup of the design
My current progress
I've been running some tests and have been able to access the markdown files using allMarkdownRemark, and the images using allImageSharp. It was hacky, but it worked, the only problem is that it was displaying all of the images, and not just the images needed for each project. Say, I have 8 images in a project, and 5 in another, it would display all 13 images.
Is there a way to do what I'm trying to do with Gatsby? Or should I just give up and move back to Jekyll…
gatsby-config.js:
module.exports = {
siteMetadata: {
title: 'J.C.R.'
},
plugins: [
'gatsby-plugin-react-helmet',
'gatsby-plugin-sass',
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'projects',
path: `${__dirname}/src/projects/`
}
},
'gatsby-transformer-remark',
'gatsby-transformer-sharp',
'gatsby-plugin-sharp',
`#dream-bit-de/gatsby-plugin-better-page-tree`
]
}
gatsby-node.js:
const path = require('path')
module.exports.onCreateNode = ({ node, actions}) => {
const {createNodeField} = actions
if (node.internal.type === 'MarkdownRemark') {
const slug = path.basename(path.dirname(node.fileAbsolutePath, '.md'))
createNodeField({
node,
name: 'slug',
value: slug
})
}
}
Work component:
import React from 'react'
import { graphql, useStaticQuery } from 'gatsby'
import Img from 'gatsby-image'
const Work = () => {
const data = useStaticQuery(graphql`
query {
allMarkdownRemark(
sort: { order: ASC, fields: [frontmatter___position]}
) {
edges {
node {
frontmatter {
title
description
}
fields {
slug
}
}
}
}
allFile (
filter: {
ext: {eq: ".jpg"}
},
sort: {
order: ASC,
fields: [relativePath]
}
) {
edges {
node {
relativePath
relativeDirectory
name
ext
id
base
}
}
}
}
`)
console.log(data)
return (
<div id="work">
<ol>
{data.allMarkdownRemark.edges.map((edge) => {
return (
<li class={edge.node.fields.slug}>
<h3>{edge.node.frontmatter.title}</h3>
<p>{edge.node.frontmatter.description}</p>
{data.allFile.edges.map((edge) => {
return (
<img src={`../projects${edge.node.relativeDirectory}/${edge.node.name}-${edge.node.base}${edge.node.ext}`}></img>
)
})}
</li>
)
})}
</ol>
</div>
)
}
export default Work
index.js:
import React from 'react'
import Head from '../components/head'
import Info from '../components/info'
import Work from '../components/work'
import '../styles/index.scss'
const indexPage = () => {
return (
<div>
<Head/>
<Info/>
<Work/>
</div>
)
}
export default indexPage
I should dive deeper in the project to fully understand how you could handle that directly with the graphql query, but a short fix would be to filter out the images not related to the project.
{data.allFile.edge0s.filter((item) => item.name.includes(edge.node.frontmatter.id).map((edge) => {
return (
<img src={`../projects${edge.node.relativeDirectory}/${edge.node.name}-${edge.node.base}${edge.node.ext}`}></img>
)
})}
You will need to add a specific id on the name of your files related to the project so when you fetch them you filter out the one not related.
To be better, you could maybe format your imageSchema and add a specific property to handle the case so you won't need to format the image name and then instead of .includes() you could do item.myProperty === myCustomSchemaProperty.
It's much easier than you've tried so far. I would recommend using gatsby-image since all the images belong to the Gatsby ecosystem when you add the following:
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'projects',
path: `${__dirname}/src/projects/`
}
},
Gatsby internally will parse everything inside /src/projects/ and will create nodes to make the schema available for GraphQL, so:
{
allFile(filter: {extension: {eq: "jpg"}}) {
edges {
node {
childImageSharp{
fluid{
...GatsbyImageSharpFluid
}
}
}
}
}
}
Then in your component just:
{data.allFile.edges.map((edge) => {
return <Img fluid={edge.childImageSharp.fluid} />
})}
Forgive me for the English of the Translator :)
I created a basic form to see if I get data in my API using vuetify however, when submitting the data the v-select data is not sent and I can not understand the reason, since in general the examples of these forms do not really make a request POST, follows snippets of the code I'm using:
<v-form method="post" action="http://127.0.0.1:3000/produtos">
<v-text-field name="escola" v-model="name" required :rules="nameRules"></v-text-field>
<v-select
v-model="selectPessoa"
:items="pessoas"
:rules="[v => !!v || 'Item is required']"
item-value="id"
item-text="nome"
label="itens"
required
name="pessoa"
return-object
value="id"
></v-select>
<v-btn color="warning" type="submit">Submit</v-btn>
</v-form>
Excerpt from javascript code:
data(){
return { pessoas: [{ id: 1, nome: "sandro" },
{ id: 2, nome: "haiden" }],
name: '',
selectPessoa: null,
}
}
The information I type in the v-text-field I get in the API node, but the one in the v-select does not:
Form screen:
API log screen:
On the<v-select> component you have defined the return-object and item-value="id" props. Using the return-object is overriding the item-value by returning the entire object from the v-select component instead of just the id. In this case you could just remove the return-object prop from the <v-select> component and that will fix your issue.
<v-select
v-model="selectPessoa"
:items="pessoas"
:rules="[v => !!v || 'Item is required']"
item-value="id"
item-text="nome"
label="itens"
required
name="pessoa"
return-object <------REMOVE THIS!!!
value="id"
></v-select>
Vuetify v-select docs: https://vuetifyjs.com/en/components/selects
Another option instead of removing the return-object prop could be to modify your API endpoint to expect an object rather than an int.
Also, I would not recommend using the "method" and "action" attributes on the <v-form> component. Instead, put a click event handler on the submit button of the form that calls a method. The method should then grab the data and send it to the API endpoint via an AJAX call.
On the Form Component
Before: <v-form method="post" action="http://127.0.0.1:3000/produtos">
After: <form #submit.prevent>
On the Button Component
Before: <v-btn color="warning" type="submit">Submit</v-btn>
After: <v-btn color="warning" #click="submit">Submit</v-btn>
In the methods have a function do something like this (used axios in my example, not sure what your project is using):
methods: {
submit () {
let data = { name: this.name, selectPessoa: this.selectPessoa }
axios.post('http://127.0.0.1:3000/produtos', data)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
I can't make inline editing of rich text save back to the db in some cases.
Please bear with me, there will be some code pasted here, as it is the only way I can describe what I'm doing.
I have two kinds of custom widgets in my project - the ones where there is only one instance of the widget, typically defined like this in the lib\modules directory:
article-widgets
- views
- - widget.html
- index.js
And then the kind of widgets that are repeated, and can be used in several places around the site, typically defined like this:
employees
- index.js
employees-widgets
- views
- - widget.html
This one I can make work:
In the first kind I define a rich text are in article-widgets\index.js
{
name: 'ingress',
label: 'Ingress',
type: 'area',
required: true,
options: {
widgets: {
'apostrophe-rich-text': {
toolbar: ['Bold', 'Italic', 'Link', 'Unlink' ]
}
}
}
}
And then in article-widgets\views\widget.html
{{ apos.singleton(data.widget, 'ingress', 'apostrophe-rich-text',{
toolbar: [ 'Bold', 'Italic', 'Link', 'Unlink' ]
}) }}
This one is not working for me:
In the second type - I can edit inline, but changes are not saved.
In employees\index.js
{
name: 'body',
label: 'Beskrivelse',
type: 'area',
options: {
widgets: {
'apostrophe-rich-text': {
toolbar: ['Bold', 'Italic', 'Link', 'Unlink']
}
}
}
}
And then in employees-widgets\views\widget.html
{% for piece in data.widget._pieces %}
<div>
{{
apos.singleton(piece, 'body', 'apostrophe-rich-text', {
toolbar: [ 'Bold', 'Italic', 'Link', 'Unlink' ]
})
}}
</div>
{% endfor %}
I can't understand why the later is not working. Am I doing something wrong - or is inline editing of repeated items not possible?
One of Apostrophe's subtleties is that any property beginning with an underscore (_) (except _id) is not actually part of the parent object and changes don't go back to the database
These are properties attached for convenience by Apostrophe somewhere in the retrieval process.
I don't thinkg modifying the piece from the widget is something baked in by default (typically piece-widgets are single-serving views for content) but it is possible to set up custom backend functionality.
Not a total 1:1 of your issue but if you look at the comments and comments-widgets module in this (rough) example, you can see we're modifying the piece's content via the widget.
https://github.com/stuartromanek/apostrophe-comment-system/tree/master/lib/modules
When using routes, like (http://localhost:port/foo/bar), in the view, in my case an ejs, do i need to declare the full path in the view, to load css/js/images ?
Vide: https://github.com/poeticninja/hapi-ninja
hapi-ninja/server/base/index.js
...
{
method: 'GET',
path: '/foo/bar',
config: {
handler: function(request, reply){
reply.view('about', {
title: 'Super Informative About Page'
});
},
id: 'about'
}
},
...
ex:
foot.ejs
<!-- Include the JS -->
<% if (assets.js.length) { %>
<% assets.js.forEach(function(js){ %>
<script src="<%=js%><%=version.cache%>"></script>
<% }) %>
<% } %>
Cause, every time i try to load, it gets the relative path (http://localhost:port/foo/bar/js/script.js).
As described by the creator himself:
First solution:
#poeticninja commented 4 hours ago
In your assets file make the paths /js/script.js and not js/script.js. That will fix your problem.
Other 'solution':
In the assets.js
// assets to be used by the 'hapi-assets' module based on process.env.NODE_ENV
module.exports = {
development: {
js: ['js/jquery-2.1.4.js', 'js/bootstrap.js'],
css: ['css/bootstrap.css', 'css/bootstrap-theme.css', 'css/3-col-portfolio.css'],
host: 'http://development:PORT/'
},
production: {
// TODO: Add the resources minified.
js: ['js/jquery-2.1.4.js', 'js/bootstrap.js'],
css: ['css/bootstrap.css', 'css/bootstrap-theme.css', 'css/3-col-portfolio.css'],
host: 'http://production:PORT/'
}
}
and in the view:
<script src="<%=assets.host%><%=js%><%=version.cache%>"></script>
But, with the initial '/', is better.