38 | You will have to adapt commands and paths based on your environment (including digging into file ".gitpod.yml") and install the dependencies by yourself. We won't provide support to keep on track with schedule. However, we will do our best to give you the info you need to be successful. This is considered a more advanced path to take.
39 |
40 |
41 |
42 | 2️⃣ What other prerequisites are there?
43 |
44 |
45 |
You will need a github account
46 |
You should use Chrome or Firefox (other browsers might have trouble displaying Gitpod correctly)
47 |
You will need an Astra DB account, but we'll cover that in the exercises
48 |
49 |
50 |
51 |
52 |
53 | 3️⃣ Do I need to pay for anything for this workshop?
54 |
55 | No. All tools and services we provide here are FREE. FREE not only during the session but also afterwards.
56 |
57 |
58 |
59 | 4️⃣ Will I get a certificate if I attend this workshop?
60 |
61 | Attending the session is not enough. You need to complete the homework detailed below and you will get a nice badge that you can share on linkedin or anywhere else (open badge specification).
62 |
63 |
64 |
65 |
66 | ## Materials for the Session
67 |
68 | It doesn't matter if you join our workshop live or you prefer to do at your own pace, we have you covered. In this repository, you'll find everything you need for this workshop:
69 |
70 | - [Slide deck](./slides/slides.pdf)
71 | - [Discord chat](https://bit.ly/cassandra-workshop)
72 | - ["cassandra" on StackOverflow](https://stackoverflow.com/questions/tagged/cassandra)
73 | - ["cassandra" on DBA StackExchange](https://dba.stackexchange.com/questions/tagged/cassandra)
74 |
75 | ## Homework
76 |
77 |
78 |
79 | Don't forget to complete your upgrade and get your verified skill badge! Finish and submit your homework!
80 |
81 | 1. Complete the practice steps from this repository, as described below, to the end;
82 | 2. Insert (mutate) a **new show** or a **new genre** of your choice in the database;
83 | 3. Take a single **screenshot** of the React app with all of the working Astra DB sections and showing the entry you just added;
84 | 4. Submit your homework [here](https://dtsx.io/homework-intro-graphql).
85 |
86 | That's it, done.
87 | We will then grade the submissions: expect an email in a few days!
88 |
89 | # Let's start
90 |
91 |
92 | ### Extra resources
93 | [graphql.org](https://graphql.org/) - The first place to learn about GraphQL
94 |
95 | [The Netflix DGS framework Tutorial](https://netflix.github.io/dgs/getting-started/) - Java/Spring GraphQL backend (used to generate this code)
96 |
97 | [Spring Initializr](https://start.spring.io/) - Used in the ^above tutorial to generate the Java/Spring backend starter
98 |
99 | [GraphiQL](https://www.gatsbyjs.com/docs/how-to/querying-data/running-queries-with-graphiql/) - GraphQL IDE included with The Netflix DGS Framework
100 |
101 | [Apollo client](https://www.apollographql.com/docs/react/) - Awesome GraphQL client for React/JS (not used here, but really solid, Netflix uses this)
102 |
103 | [Top 7 GraphQL IDEs](https://hasura.io/blog/top-7-graphql-ides-you-should-know-about-in-2021/) - A nice collection of cool GraphQL IDEs to use
104 |
105 | [create-react-app tutorial](https://create-react-app.dev/) - Create a React app from scratch (used to generate this code)
106 |
107 | [A Beginner's Guide to GraphQL](https://www.youtube.com/watch?v=c2fJ7T0N1Sk) - Ali Spittel's really awesome GraphQL starter video
108 |
109 |
110 |
111 | ## 1. Login or Register to Astra DB and create database
112 |
113 | _**`ASTRA DB`** is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. No credit card required, 40M read/write operations and about 80GB storage monthly for free - sufficient to run small production workloads. If you use up your credits the databases will pause, no charge, and you will be given the option to upgrade to a higher tier._
114 |
115 | Leveraging [Database creation guide](https://awesome-astra.github.io/docs/pages/astra/create-instance/#c-procedure) create a database. *Right-Click the following button* with *Open in a new TAB.*
116 |
117 |
118 |
119 | |Field|Value|
120 | |---|---|
121 | |**Database Name**| `workshops`|
122 | |**Keyspace Name**| `intrographql`|
123 | |**Regions**| Select `GOOGLE CLOUD`, then an Area close to you, then a region with no LOCK 🔒 icons: the LOCKed regions are the region not accessible to the Free Tier.
124 |
125 | > **ℹ️ Note:** If you already have a database `workshops`, simply add a keyspace `intrographql` using the `Add Keyspace` button on the bottom right hand corner of the DB Dashboard page. You may have to "Resume" the database first in case it is in "hibernated" state.
126 |
127 | While the database is being created, you will also get a **Security token** (needed to authenticate with your database and start using it):
128 | **please IGNORE THIS ONE, as we will be soon creating a new, more powerful token for today**.
129 |
130 | The status will change from `Pending` to `Active` when the database is ready, this usually only takes 2-3 minutes.
131 |
132 |
133 |
134 | ## 2. Create a security token
135 |
136 | > Note: this step is very important, as the token generated automatically for you with
137 | > the database lacks some permissions we'll use in the workshop.
138 |
139 | [Create a token for your app](https://awesome-astra.github.io/docs/pages/astra/create-token/#c-procedure), _using the **"Database Administrator"** role_.
140 | Keep it handy for later use (best to download it in CSV format, as the values
141 | will not be visible afterward).
142 | This will provide authentication later when interacting with the database.
143 | Today, in particular, we will need the string labeled "token" (the one starting with `AstraCS:...`).
144 |
145 | > **⚠️ Important**
146 | > ```
147 | > The instructor will show the token creation on screen,
148 | > but will then destroy it immediately for security reasons.
149 | > ```
150 |
151 | ## 3. Launch Gitpod
152 |
153 | [Gitpod](https://www.gitpod.io/) is an 100% online IDE based on [Visual Studio Code](https://github.com/gitpod-io/vscode/blob/gp-code/LICENSE.txt?lang=en-US). To initialize your environment simply click on the button below _(CTRL + Click to open in new tab)_ You will be asked for you github account, as needed.
154 |
155 | **Warning**: for best results, open the link with Chrome or Firefox!
156 |
157 | [](https://gitpod.io/#https://github.com/datastaxdevs/workshop-intro-to-graphql)
158 |
159 |
160 | This will bootstrap your demo environment. Be patient, it will take a few minutes as everything loads up.
161 |
162 | > **Note**: during loading of the Gitpod environment, a new tab will be tentatively opened
163 | > with an URL such as `https://8080-datastaxdev-[...].gitpod.io/graphiql`.
164 | > Please **CHECK YOUR POPUP BLOCKER** and allow it before continuing: this will be your GraphiQL interface!
165 |
166 |
167 | Show me how Gitpod looks like for this workshop
168 |
169 | Gitpod starts with a file explorer on the left (1), an editor panel on the top (2),
170 | and - in the case of this specific environment - two consoles side by side,
171 | one to launch commands and later start the Node app (3) and one busy with
172 | running the Java backend (4). On the right you will find a console switcher to
173 | easily locate any console and make it active (but even just clicking
174 | on the desired console would do the trick).
175 |
176 | 
177 |
178 |
179 |
180 |
181 | ## 4. Experiment with GraphiQL
182 | It just so happens that [The Netflix DGS framework](https://netflix.github.io/dgs/getting-started/) comes with GraphiQL already integrated and ready for use. This is a wonderful tool you can use to explore graphQL queries and mutations. Let's experiment with this now!
183 |
184 | > _Note:_ the GraphiQL should be open already in a new tab for you; in case it isn't for some reason,
185 | > run this command in a Gitpod console and manually point a new tab to the URL it prints:```echo `gp url 8080`/graphiql```.
186 |
187 | Something to point out here is there is no database just yet. We are powering the graphQL schema via the back-end Java application and the graphQL data is completely hardcoded.
188 | Take a look at both **`ShowsDatafetcher.java`** and **`GenresDatafetcher.java`** located in **`graphql-backend-examples/src/main/java/com/example/demo`**
189 | to find the simple implementations using DGS annotations `@DgsComponent` and `@DgsQuery`.
190 |
191 |
192 | Show me how to open these files in Gitpod
193 |
194 | In the left toolbar, choose the first tool ("Explorer") and navigate the directory to the desired directory; then, clicking on the files will open them in the editor (topmost panel on the right).
195 |
196 | 
197 |
198 |
199 |
200 |
201 | #### Now, let's try out some graphQL queries
202 | Plug these into the GraphiQL IDE that launched into a new tab from GitPod.
203 |
204 | ```GraphQL
205 | query justTitle {
206 | shows {
207 | title
208 | }
209 | }
210 | ```
211 |
212 | ```GraphQL
213 | query withReleaseYear {
214 | shows {
215 | title
216 | releaseYear
217 | }
218 | }
219 | ```
220 |
221 | ```GraphQL
222 | query getOneShow {
223 | shows (titleFilter: "Ozark") {
224 | title
225 | releaseYear
226 | }
227 | }
228 | ```
229 |
230 | ```GraphQL
231 | query ShowsAndGenres {
232 | shows {
233 | title
234 | releaseYear
235 | }
236 | genres {
237 | value
238 | }
239 | }
240 | ```
241 |
242 | 
243 |
244 | #### Compare the GraphQL schema
245 |
246 | The objects known to a GraphQL API are defined starting from its "Schema".
247 | In the case of our DGS Java application, the schema is found in
248 | `graphql-backend-examples/src/main/resources/schema/schema.graphqls`.
249 | Take a look at its contents: notice the special `Query` item that defines
250 | the possible queries and, after that, the user-defined types available to
251 | the API:
252 |
253 | ```GraphQL
254 | type Query {
255 | shows(titleFilter: String): [Show]
256 | genres(labelFilter: String): [Genre]
257 | }
258 |
259 | type Show {
260 | title: String
261 | releaseYear: Int
262 | }
263 |
264 | type Genre {
265 | value: String!
266 | }
267 | ```
268 |
269 | ## 5. Experiment with Astra DB's GraphQL Playground
270 |
271 | Ok, let's take this a step further and prepare the data layer for our app.
272 | At this point you should have already [created your Astra DB database](#1-login-or-register-to-astradb-and-create-database).
273 | Follow the instructions below to launch the **GraphQL Playground** provided in **Astra DB**:
274 |
275 | #### ✅ Step 5a: Open GraphQL Playground:
276 |
277 | 0. Ensure you are logged on to your [Astra](https://astra.datastax.com) account
278 | 1. Click on the "workshops" database on the left (expanding the list if needed)
279 | 2. Click `Connect` TAB
280 | 3. Click the `APIs` connection method
281 | 4. Make sure `GraphQL API` is selected
282 | 5. Locate the link to your GraphQL Playground in the text
283 |
284 | 
285 |
286 |
287 | Click here if you are using the "New Astra Experience" UI
288 |
289 | 
290 |
291 |
292 |
293 | **Note**: in the following, we will refer to "playground tabs". These are _not_ the tabs
294 | in your browser, rather they are tabs _within_ the Playground application,
295 | to switch between the (logically distinct) realms of "managing schema" and "managing data in the tables"
296 | (more on that later).
297 |
298 | 
299 |
300 | #### ✅ Step 5b: Insert the Astra DB Token to run schema queries
301 |
302 | In the GraphQL Playground, **Populate HTTP HEADER** variable `x-cassandra-token` on the bottom of the page with your token (including the `AstraCS:` part).
303 | _This is the "Database Administrator" token you created earlier on the Astra DB dashboard (Step 2 above)._
304 |
305 | **Note**: make sure you are on the **`graphql-schema`** playground tab in this step, as this image illustrates:
306 |
307 | 
308 |
309 | > Note: the GraphQL Playground starts with a ready-to-use _temporary token_ as the `x-cassandra-token` header. But we want the queries run in the Playground
310 | > to be identical to those that the Netlify functions will run from code, so **please replace the token with your DB token as instructed**.
311 |
312 | #### ✅ Step 5c: Create a table in the GraphQL Playground
313 |
314 | Run the following mutation in the `graphql-schema` playground tab, making sure to replace `intrographql` in the URL if you used a different keyspace name:
315 |
316 | - Copy the following mutation on the left panel
317 |
318 | ```GraphQL
319 | mutation {
320 | reference_list: createTable(
321 | keyspaceName:"intrographql",
322 | tableName:"reference_list",
323 | ifNotExists:true
324 | partitionKeys: [
325 | { name: "label", type: {basic: TEXT} }
326 | ]
327 | clusteringKeys: [
328 | { name: "value", type: {basic: TEXT}, order: "ASC" }
329 | ]
330 | )
331 | }
332 | ```
333 |
334 | Click on the arrow in the middle of the screen to execute the query.
335 |
336 | 
337 |
338 | ## 6. Insert data to DB using the GraphQL Playground
339 |
340 | #### ✅ Step 6a: Adjust the second playground tab to your keyspace
341 |
342 | In the GraphQL playground, switch to the second Playground tab (`graphql`). Edit the ending of the URL _shown within the Playground page_ from `system` to the keyspace name `intrographql`:
343 |
344 | 
345 |
346 | #### ✅ Step 6b: Set the token to run data queries
347 |
348 | Populate the **HTTP HEADER** variable `x-cassandra-token` on the bottom of the page with your DB token as shown below _(Note: you did this for the `graphql-schema` playground tab, now repeat for the `graphql` playground tab!)_
349 |
350 | 
351 |
352 | #### ✅ Step 6c: Insert genre names with the Playground
353 |
354 | In the GraphQL Playground, populate the `reference_list` table with all values:
355 | copy the following mutation on the left panel
356 |
357 | ```GraphQL
358 | mutation insertGenres {
359 | action: insertreference_list(value: {label:"genre", value:"Action"}) {
360 | value{value}
361 | }
362 | anime: insertreference_list(value: {label:"genre", value:"Anime"}) {
363 | value{value}
364 | }
365 | award: insertreference_list(value: {label:"genre", value:"Award-Winning"}) {
366 | value{value}
367 | }
368 | children: insertreference_list(value: {label:"genre", value:"Children & Family"}) {
369 | value{value}
370 | }
371 | comedies: insertreference_list(value: {label:"genre", value:"Comedies"}) {
372 | value{value}
373 | }
374 | documentaries: insertreference_list(value: {label:"genre", value:"Documentaries"}) {
375 | value{value}
376 | }
377 | drama: insertreference_list(value: {label:"genre", value:"Dramas"}) {
378 | value{value}
379 | }
380 | fantasy: insertreference_list(value: {label:"genre", value:"Fantasy"}) {
381 | value{value}
382 | }
383 | french: insertreference_list(value: {label:"genre", value:"French"}) {
384 | value{value}
385 | }
386 | horror: insertreference_list(value: {label:"genre", value:"Horror"}) {
387 | value{value}
388 | }
389 | independent: insertreference_list(value: {label:"genre", value:"Independent"}) {
390 | value{value}
391 | }
392 | music: insertreference_list(value: {label:"genre", value:"Music & Musicals"}) {
393 | value{value}
394 | }
395 | romance: insertreference_list(value: {label:"genre", value:"Romance"}) {
396 | value{value}
397 | }
398 | scifi: insertreference_list(value: {label:"genre", value:"Sci-Fi"}) {
399 | value{value}
400 | }
401 | thriller: insertreference_list(value: {label:"genre", value:"Thriller"}) {
402 | value{value}
403 | }
404 | }
405 | ```
406 |
407 | Click on the arrow in the middle of the screen to execute the query.
408 |
409 | ## 7. Retrieve values from DB using the GraphQL Playground
410 |
411 | #### ✅ Step 7a: Read genres with a query in the Playground
412 |
413 | In the GraphQL Playground (staying on the `graphql` playground tab), list values from the table with the following query:
414 |
415 | ```yaml
416 | query getAllGenre {
417 | reference_list (value: {label:"genre"}) {
418 | values {
419 | value
420 | }
421 | }
422 | }
423 | ```
424 |
425 | *👁️ Expected output*
426 | 
427 |
428 | ## 8. Start up React
429 |
430 | So far we have executed GraphQL queries and mutations by hand from specific UIs.
431 | Now it's time to start the React client app and query the GraphQL endpoints from it!
432 |
433 | > "Endpoints", two of them. Each GraphQL server exposes a single endpoint for everything,
434 | > but remember this app will query both the local DGS app and the Astra DB server!
435 |
436 | First you need to run a couple commands to get things set up:
437 | in your **`GitPod`** IDE navigate to the "Client" terminal
438 | *(it should already be open for you on the bottom left)*
439 | and make sure you are in the **`workshop-intro-to-graphql/graphql-client-examples`** directory.
440 | **This is where you'll be running the nodejs/React app.**
441 |
442 |
443 | Remind me what is this "client terminal" ...
444 |
445 | It is the block labeled as "3". Click on it, or use the switcher (5):
446 |
447 | 
448 |
449 |
450 |
451 | #### ✅ Step 8a: Execute the following command
452 | ```shell
453 | npm install -g netlify-cli
454 | ```
455 |
456 | This will install the **Netlify CLI** (command line interface) which our **React/JS** app uses in conjunction with the serverless functions we've setup to talk to our **graphQL** endpoints.
457 |
458 | #### ✅ Step 8b: Then, execute
459 | ```shell
460 | netlify dev
461 | ```
462 |
463 | This will start the **React/JS** application and display results from both the **`Shows`** and **`Genres`** **graphQL** queries and endpoints we were just experimenting with.
464 |
465 | You should see Gitpod's mini-browser opening up by itself and showing the client application wihtin Gitpod.
466 |
467 | > _Note:_ the client, at this point, should be opened in the mini-browser within Gitpod; to open it manually,
468 | > run this command in a Gitpod console and point a new tab to the URL it prints:```echo `gp url 8888` ```.
469 |
470 | #### Compare javascript code to our graphQL queries from above
471 | If you take a look at both **`getShowsBackend.js`** and **`getGenresBackend.js`** located in **`graphql-client-examples/functions`** you should notice that both use the **same exact** **graphQL** queries that we used above.
472 |
473 | ```javascript
474 | const query = `
475 | query getAllShows {
476 | shows {
477 | title
478 | releaseYear
479 | }
480 | }
481 | `
482 | ```
483 |
484 | ```javascript
485 | const query = `
486 | query getAllGenres {
487 | genres {
488 | value
489 | }
490 | }
491 | `
492 | ```
493 |
494 | All of the javascript wrapped around these is simply there to call the **graphQL** endpoint with the given query and pass the responseBody back to the calling function.
495 |
496 | #### Now for the cool part
497 |
498 | Take a look at **`Shows.js`** and **`Genres.js`** located in **`graphql-client-examples/src/components/`**. In both cases they use **React** state, `gqlResult`
499 |
500 | ```javascript
501 | const [gqlResult, setGqlResult] = useState(null)
502 | ```
503 |
504 | to receive the responseBody from from our **graphQL** queries, set the **React** state, and inject the values dynamically into the DOM. Check out the following javascript snippet from **`Shows.js`**.
505 |
506 | ```javascript
507 | // Asynchronously fetch any "shows" graphQL data from the Java backend
508 | // using the getShowsBackend serverless function to call out to the
509 | // Netflix DGS Java graphQL endpoint
510 | const response = await fetch("/.netlify/functions/getShowsBackend", {
511 | method: "POST",
512 | })
513 | const responseBody = await response.json()
514 | setGqlResult(responseBody) // on response set our graphQL result state
515 | ```
516 |
517 | Notice how the fields (title, releaseYear) match our **graphQL** `Shows` schema exactly.
518 |
519 | ```javascript
520 | // Finally, if all other checks pass get the data
521 | // from the payload via gqlResult state and inject it into the DOM
522 | // Notice how the payload example below and the fields "title" and "releaseYear" match exactly
523 | // {"data":{"shows":[{"title":"Stranger Things","releaseYear":2016},{"title":"Ozark","releaseYear":2017}...
524 | return gqlResult.data.shows.map(({ title, releaseYear }) => (
525 |
526 |
527 | {title}: {releaseYear}
528 |
529 |
530 | ));
531 | ```
532 |
533 | Notice how the field (value) matches our **graphQL** `Genres` schema exactly.
534 |
535 | ```javascript
536 | // Finally, if all other checks pass get the data
537 | // from the payload via gqlResult state and inject it into the DOM
538 | // Notice how the payload example below and the field "value" match exactly
539 | // {"data":{"genres":[{"value":"Action"},{"value":"Anime"}...
540 | return gqlResult.data.genres.map(({ value }) => (
541 |
542 |
543 | {value}
544 |
545 |
546 | ));
547 | ```
548 |
549 | ## 9. Hook the database up to the React client app
550 |
551 | The next step is to make the client able to retrieve the genres and the shows
552 | from the database, by querying Astra DB's GraphQL API. To achieve this,
553 | it's time to provide connection details (addresses, secrets) to the
554 | serverless Netlify functions which will back the React client.
555 |
556 | #### ✅ Step 9a: Initialize Astra CLI
557 | In the **`GitPod`** IDE, click on the "Client" terminal to make it active, hit `Ctrl-C` to stop the running client, if any, and make sure you are in the **`workshop-intro-to-graphql/graphql-client-examples`** directory.
558 |
559 | Now you will create a `.env` file with connection info (addresses and secrets) for the Netlify function to be able to reach both the local backend and your Astra DB's GraphQL endpoint.
560 | You will use the Astra command-line interface to prepare a dot-env file for you; then you will complete it by adding a line defining the address of the local backend (i.e. the DGS locally-running GraphQL API).
561 |
562 | Run the following command and provide your **DB Administrator** token string (starting with `AstraCS:...`) when prompted:
563 |
564 | ```
565 | astra setup
566 | ```
567 |
568 | #### ✅ Step 9b: Configure database credentials
569 |
570 | Once you get a "Configuration has been saved" confirmation, proceed with:
571 |
572 | ```
573 | astra db create-dotenv workshops -k intrographql
574 | cat .local-backend.env >> .env
575 | gp open .env
576 | ```
577 |
578 | The credentials are now all set up: your dot-env file should be now shown in the editor for you to check its contents.
579 | You will see several lines pertaining to Astra DB (not all of which will be used by today's client)
580 | and, at the end, a single setting about the Java GraphQL API you tested earlier.
581 |
582 | Here is how the `.env` might look like (as a reference, check out the provided `.env.sample`):
583 |
584 | 
585 |
586 | > If you are preparing the file manually (i.e. as opposed to using the `astra-cli` tool), be aware that the only
587 | > variables needed by the React client are: `ASTRA_DB_APPLICATION_TOKEN`, `ASTRA_DB_GRAPHQL_URL`
588 | > and `JAVA_GRAPHQL_ENDPOINT`.
589 |
590 | #### ✅ Step 9c: Start your React app again
591 |
592 | Launch the following command once more:
593 | ```shell
594 | netlify dev
595 | ```
596 |
597 | #### ✅ Step 9d: Verify data load
598 | At this point your app should be running with a bunch of data displayed in the **`Shows`**, **`Genres,`** and **`ReferenceList`** sections, but notice the **`ShowsByName`** section displays **"Error :("**
599 |
600 | 
601 |
602 | #### Can you figure out what's going on here?
603 | Let's break this down.
604 |
605 | * We just added the database configuration and the **`ReferenceList`** section is populated which tells us our DB config and graphQL endpoints are configured properly
606 |
607 | * In the GraphQL Playground we added a schema for the **`reference_list`** table and added some data to the table, but we never created a schema for the **`ShowsByName`** section
608 |
609 | * If you take a look at the **`getShowsAstra.js`** script in **`graphql-client-examples/functions`** you can see the graphQL being used to query for data
610 | ```javascript
611 | exports.handler = async function (event) {
612 | const query = `
613 | query getAllShows {
614 | show_by_name {
615 | values {
616 | title
617 | releaseYear
618 | }
619 | }
620 | }
621 | `
622 | ```
623 |
624 | #### ✅ Step 9e: Test this query
625 |
626 | Go back to the GraphQL **`graphQL`** playground tab.
627 |
628 | Copy this into the playground and press the _"play"_ button to execute the query. **NOTE, you can simply append the query to the end of the list and then choose the query you wish to execute when you hit the "play" button.**
629 |
630 | ```GraphQL
631 | query getAllShows {
632 | show_by_name {
633 | values {
634 | title
635 | releaseYear
636 | }
637 | }
638 | }
639 | ```
640 |
641 | 
642 |
643 | #### View Results
644 | Notice what happened here. We have a validation error because there is no schema associated with the query we just executed. GraphQL uses a typed validation system so this is something to expect if a query is malformed, missing a schema, or something along those lines. You will want to control for this in your code.
645 |
646 | 
647 |
648 | #### ✅ Step 9f: Create the missing table
649 |
650 | To fix up the schema issue, and resolve the error,
651 | create the **`ShowsByName`** table with a graphQL mutation to fix the app.
652 | Execute the following mutation in the **`graph-schema`** Playground tab
653 |
654 | ```GraphQL
655 | mutation CreateShowsTable {
656 | createTable(
657 | keyspaceName: "intrographql"
658 | tableName: "show_by_name"
659 | partitionKeys: [{
660 | name: "title", type: {basic:TEXT}
661 | }]
662 | values:[{
663 | name: "releaseYear", type: {basic:INT}
664 | }]
665 | )
666 | }
667 | ```
668 |
669 | 
670 |
671 | #### ✅ Verify result
672 | Once executed you should see a result like this
673 |
674 | 
675 |
676 | #### ✅ Step 9g: Add some data
677 | Now, go back to the **`graphql`** playground tab and add the following mutation
678 | ```GraphQL
679 | mutation insertShows {
680 | stranger: insertshow_by_name (
681 | value: {
682 | title: "Stranger Things",
683 | releaseYear: 2016}) {
684 | value{title}
685 | }
686 | ozark: insertshow_by_name (
687 | value: {
688 | title: "Ozark",
689 | releaseYear: 2017}) {
690 | value{title}
691 | }
692 | }
693 | ```
694 |
695 | 
696 |
697 | #### ✅ Check the result
698 |
699 | 
700 |
701 | #### ✅ Step 9h: Check the client again
702 |
703 | Finally, refresh your React app.
704 |
705 | Notice this no longer displays an error. Now it correctly displays the data you just inserted (mutated). It might be fun to add some of your own data to this schema and refresh your page.
706 |
707 | 
708 |
709 | #### Play a bit more!
710 |
711 | Feel free to experiment with a couple more graphQL queries now that you have some data in the table
712 |
713 | Queries usually offer some way to restrict the results returned,
714 | in the form of parameters passed to queries. Recall the original `getAllShows`, repeated here for convenience:
715 |
716 |
717 | ```GraphQL
718 | query getAllShows {
719 | show_by_name {
720 | values {
721 | title
722 | releaseYear
723 | }
724 | }
725 | }
726 | ```
727 |
728 | Now let's see a way to pass a `title` parameter to the query and just get
729 | matching values (a single entry, in this case):
730 |
731 | ```GraphQL
732 | query getOneShow {
733 | show_by_name (value: {title: "Ozark"}) {
734 | values {
735 | title
736 | releaseYear
737 | }
738 | }
739 | }
740 | ```
741 |
742 | The following query, which uses the [more general `filter` syntax](https://docs.datastax.com/en/astra/docs/develop/dev-with-graphql-cql-first.html#_retrieve_data),
743 | is completely equivalent to the previous one:
744 |
745 | ```GraphQL
746 | query getOneShowF {
747 | show_by_name(filter: {title: {eq: "Ozark"}}){
748 | values {
749 | title
750 | releaseYear
751 | }
752 | }
753 | }
754 | ```
755 |
756 |
757 | ### That's it, you did it! Nice job!
758 |
759 | We hope this workshop gave you enough information on GraphQL to be dangerous and start you on a journey to using GraphQL in your own apps.
760 | Also, don't forget your [HOMEWORK](#homework).
761 |
--------------------------------------------------------------------------------
/astra.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Introduction to GraphQL",
3 | "description": "Learn the basics of GraphQL. Plenty of hands-on experience covering: a React client interacting with Astra DB using the Netlify stack; usage of playgrounds and tooling; running your own Java GraphQL backend based on the Netflix DGS framework.",
4 | "duration": "120 minutes",
5 | "skillLevel": "Beginner",
6 | "language":["java", "javascript", "nodejs"],
7 | "stack": ["cassandra", "jamstack", "netlify"],
8 | "githubUrl": "https://github.com/datastaxdevs/workshop-intro-to-graphql",
9 | "badge": "https://media.badgr.com/uploads/badges/6d9d20f0-8a8e-4cc9-9cf2-dc4e1baa97d6.png",
10 | "youTubeUrl": ["https://www.youtube.com/watch?v=Z83XTWhav7g"],
11 | "tags": [
12 | { "name": "workshop" },
13 | { "name": "nosql" },
14 | { "name": "cassandra" },
15 | { "name": "graphql" },
16 | { "name": "gitpod" },
17 | { "name": "astradb" }
18 | ],
19 | "category": "workshop",
20 | "usecases": []
21 | }
--------------------------------------------------------------------------------
/graphql-backend-examples/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2007-present the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import java.net.*;
17 | import java.io.*;
18 | import java.nio.channels.*;
19 | import java.util.Properties;
20 |
21 | public class MavenWrapperDownloader {
22 |
23 | private static final String WRAPPER_VERSION = "0.5.6";
24 | /**
25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
26 | */
27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
29 |
30 | /**
31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
32 | * use instead of the default one.
33 | */
34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
35 | ".mvn/wrapper/maven-wrapper.properties";
36 |
37 | /**
38 | * Path where the maven-wrapper.jar will be saved to.
39 | */
40 | private static final String MAVEN_WRAPPER_JAR_PATH =
41 | ".mvn/wrapper/maven-wrapper.jar";
42 |
43 | /**
44 | * Name of the property which should be used to override the default download url for the wrapper.
45 | */
46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
47 |
48 | public static void main(String args[]) {
49 | System.out.println("- Downloader started");
50 | File baseDirectory = new File(args[0]);
51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
52 |
53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom
54 | // wrapperUrl parameter.
55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
56 | String url = DEFAULT_DOWNLOAD_URL;
57 | if(mavenWrapperPropertyFile.exists()) {
58 | FileInputStream mavenWrapperPropertyFileInputStream = null;
59 | try {
60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
61 | Properties mavenWrapperProperties = new Properties();
62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
64 | } catch (IOException e) {
65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
66 | } finally {
67 | try {
68 | if(mavenWrapperPropertyFileInputStream != null) {
69 | mavenWrapperPropertyFileInputStream.close();
70 | }
71 | } catch (IOException e) {
72 | // Ignore ...
73 | }
74 | }
75 | }
76 | System.out.println("- Downloading from: " + url);
77 |
78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
79 | if(!outputFile.getParentFile().exists()) {
80 | if(!outputFile.getParentFile().mkdirs()) {
81 | System.out.println(
82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
83 | }
84 | }
85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
86 | try {
87 | downloadFileFromURL(url, outputFile);
88 | System.out.println("Done");
89 | System.exit(0);
90 | } catch (Throwable e) {
91 | System.out.println("- Error downloading");
92 | e.printStackTrace();
93 | System.exit(1);
94 | }
95 | }
96 |
97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception {
98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
99 | String username = System.getenv("MVNW_USERNAME");
100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
101 | Authenticator.setDefault(new Authenticator() {
102 | @Override
103 | protected PasswordAuthentication getPasswordAuthentication() {
104 | return new PasswordAuthentication(username, password);
105 | }
106 | });
107 | }
108 | URL website = new URL(urlString);
109 | ReadableByteChannel rbc;
110 | rbc = Channels.newChannel(website.openStream());
111 | FileOutputStream fos = new FileOutputStream(destination);
112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
113 | fos.close();
114 | rbc.close();
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/graphql-backend-examples/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-graphql/5e472012272d3a73a3d9d6a3679c3ab24f85fdb3/graphql-backend-examples/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/graphql-backend-examples/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/graphql-backend-examples/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | demo
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.m2e.core.maven2Builder
15 |
16 |
17 |
18 |
19 | org.springframework.ide.eclipse.boot.validation.springbootbuilder
20 |
21 |
22 |
23 |
24 |
25 | org.eclipse.jdt.core.javanature
26 | org.eclipse.m2e.core.maven2Nature
27 |
28 |
29 |
30 | 1631126904356
31 |
32 | 30
33 |
34 | org.eclipse.core.resources.regexFilterMatcher
35 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/graphql-backend-examples/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "type": "java",
5 | "name": "Spring Boot-DemoApplication",
6 | "request": "launch",
7 | "cwd": "${workspaceFolder}",
8 | "console": "internalConsole",
9 | "mainClass": "com.example.demo.DemoApplication",
10 | "projectName": "demo",
11 | "args": ""
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/graphql-backend-examples/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "java.configuration.updateBuildConfiguration": "interactive"
3 | }
--------------------------------------------------------------------------------
/graphql-backend-examples/HELP.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ### Reference Documentation
4 | For further reference, please consider the following sections:
5 |
6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.5.4/maven-plugin/reference/html/)
8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.5.4/maven-plugin/reference/html/#build-image)
9 | * [Spring Web](https://docs.spring.io/spring-boot/docs/2.5.4/reference/htmlsingle/#boot-features-developing-web-applications)
10 |
11 | ### Guides
12 | The following guides illustrate how to use some features concretely:
13 |
14 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
15 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
16 | * [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/)
17 |
18 |
--------------------------------------------------------------------------------
/graphql-backend-examples/bin/main/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/graphql-backend-examples/bin/main/com/example/demo/DemoApplication.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-graphql/5e472012272d3a73a3d9d6a3679c3ab24f85fdb3/graphql-backend-examples/bin/main/com/example/demo/DemoApplication.class
--------------------------------------------------------------------------------
/graphql-backend-examples/demo.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-graphql/5e472012272d3a73a3d9d6a3679c3ab24f85fdb3/graphql-backend-examples/demo.zip
--------------------------------------------------------------------------------
/graphql-backend-examples/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # https://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Mingw, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | fi
118 |
119 | if [ -z "$JAVA_HOME" ]; then
120 | javaExecutable="`which javac`"
121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
122 | # readlink(1) is not available as standard on Solaris 10.
123 | readLink=`which readlink`
124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
125 | if $darwin ; then
126 | javaHome="`dirname \"$javaExecutable\"`"
127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
128 | else
129 | javaExecutable="`readlink -f \"$javaExecutable\"`"
130 | fi
131 | javaHome="`dirname \"$javaExecutable\"`"
132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
133 | JAVA_HOME="$javaHome"
134 | export JAVA_HOME
135 | fi
136 | fi
137 | fi
138 |
139 | if [ -z "$JAVACMD" ] ; then
140 | if [ -n "$JAVA_HOME" ] ; then
141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
142 | # IBM's JDK on AIX uses strange locations for the executables
143 | JAVACMD="$JAVA_HOME/jre/sh/java"
144 | else
145 | JAVACMD="$JAVA_HOME/bin/java"
146 | fi
147 | else
148 | JAVACMD="`which java`"
149 | fi
150 | fi
151 |
152 | if [ ! -x "$JAVACMD" ] ; then
153 | echo "Error: JAVA_HOME is not defined correctly." >&2
154 | echo " We cannot execute $JAVACMD" >&2
155 | exit 1
156 | fi
157 |
158 | if [ -z "$JAVA_HOME" ] ; then
159 | echo "Warning: JAVA_HOME environment variable is not set."
160 | fi
161 |
162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
163 |
164 | # traverses directory structure from process work directory to filesystem root
165 | # first directory with .mvn subdirectory is considered project base directory
166 | find_maven_basedir() {
167 |
168 | if [ -z "$1" ]
169 | then
170 | echo "Path not specified to find_maven_basedir"
171 | return 1
172 | fi
173 |
174 | basedir="$1"
175 | wdir="$1"
176 | while [ "$wdir" != '/' ] ; do
177 | if [ -d "$wdir"/.mvn ] ; then
178 | basedir=$wdir
179 | break
180 | fi
181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
182 | if [ -d "${wdir}" ]; then
183 | wdir=`cd "$wdir/.."; pwd`
184 | fi
185 | # end of workaround
186 | done
187 | echo "${basedir}"
188 | }
189 |
190 | # concatenates all lines of a file
191 | concat_lines() {
192 | if [ -f "$1" ]; then
193 | echo "$(tr -s '\n' ' ' < "$1")"
194 | fi
195 | }
196 |
197 | BASE_DIR=`find_maven_basedir "$(pwd)"`
198 | if [ -z "$BASE_DIR" ]; then
199 | exit 1;
200 | fi
201 |
202 | ##########################################################################################
203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
204 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
205 | ##########################################################################################
206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
207 | if [ "$MVNW_VERBOSE" = true ]; then
208 | echo "Found .mvn/wrapper/maven-wrapper.jar"
209 | fi
210 | else
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
213 | fi
214 | if [ -n "$MVNW_REPOURL" ]; then
215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
216 | else
217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
218 | fi
219 | while IFS="=" read key value; do
220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
221 | esac
222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
223 | if [ "$MVNW_VERBOSE" = true ]; then
224 | echo "Downloading from: $jarUrl"
225 | fi
226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
227 | if $cygwin; then
228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
229 | fi
230 |
231 | if command -v wget > /dev/null; then
232 | if [ "$MVNW_VERBOSE" = true ]; then
233 | echo "Found wget ... using wget"
234 | fi
235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
236 | wget "$jarUrl" -O "$wrapperJarPath"
237 | else
238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
239 | fi
240 | elif command -v curl > /dev/null; then
241 | if [ "$MVNW_VERBOSE" = true ]; then
242 | echo "Found curl ... using curl"
243 | fi
244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
245 | curl -o "$wrapperJarPath" "$jarUrl" -f
246 | else
247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
248 | fi
249 |
250 | else
251 | if [ "$MVNW_VERBOSE" = true ]; then
252 | echo "Falling back to using Java to download"
253 | fi
254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
255 | # For Cygwin, switch paths to Windows format before running javac
256 | if $cygwin; then
257 | javaClass=`cygpath --path --windows "$javaClass"`
258 | fi
259 | if [ -e "$javaClass" ]; then
260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
261 | if [ "$MVNW_VERBOSE" = true ]; then
262 | echo " - Compiling MavenWrapperDownloader.java ..."
263 | fi
264 | # Compiling the Java class
265 | ("$JAVA_HOME/bin/javac" "$javaClass")
266 | fi
267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
268 | # Running the downloader
269 | if [ "$MVNW_VERBOSE" = true ]; then
270 | echo " - Running MavenWrapperDownloader.java ..."
271 | fi
272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
273 | fi
274 | fi
275 | fi
276 | fi
277 | ##########################################################################################
278 | # End of extension
279 | ##########################################################################################
280 |
281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
282 | if [ "$MVNW_VERBOSE" = true ]; then
283 | echo $MAVEN_PROJECTBASEDIR
284 | fi
285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
286 |
287 | # For Cygwin, switch paths to Windows format before running java
288 | if $cygwin; then
289 | [ -n "$M2_HOME" ] &&
290 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
291 | [ -n "$JAVA_HOME" ] &&
292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
293 | [ -n "$CLASSPATH" ] &&
294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
295 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
297 | fi
298 |
299 | # Provide a "standardized" way to retrieve the CLI args that will
300 | # work with both Windows and non-Windows executions.
301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
302 | export MAVEN_CMD_LINE_ARGS
303 |
304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
305 |
306 | exec "$JAVACMD" \
307 | $MAVEN_OPTS \
308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
311 |
--------------------------------------------------------------------------------
/graphql-backend-examples/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM https://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
124 |
125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
162 | if ERRORLEVEL 1 goto error
163 | goto end
164 |
165 | :error
166 | set ERROR_CODE=1
167 |
168 | :end
169 | @endlocal & set ERROR_CODE=%ERROR_CODE%
170 |
171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
175 | :skipRcPost
176 |
177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
179 |
180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
181 |
182 | exit /B %ERROR_CODE%
183 |
--------------------------------------------------------------------------------
/graphql-backend-examples/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.5.4
9 |
10 |
11 | com.example
12 | graphql-backend-examples
13 | 0.0.1-SNAPSHOT
14 | graphql-backend-examples
15 | Demo project for Spring Boot
16 |
17 | 11
18 |
19 |
20 |
21 |
22 |
23 | com.netflix.graphql.dgs
24 | graphql-dgs-platform-dependencies
25 |
26 | 4.1.0
27 | pom
28 | import
29 |
30 |
31 |
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-web
37 |
38 |
39 |
40 | com.netflix.graphql.dgs
41 | graphql-dgs-spring-boot-starter
42 |
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-test
47 | test
48 |
49 |
50 |
51 |
52 |
53 |
54 | org.springframework.boot
55 | spring-boot-maven-plugin
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/graphql-backend-examples/src/main/java/com/example/demo/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
2 |
3 | import java.util.logging.Logger;
4 |
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
10 | import org.springframework.web.servlet.config.annotation.CorsRegistry;
11 |
12 | @SpringBootApplication
13 | public class DemoApplication {
14 | Logger logger = Logger.getLogger(DemoApplication.class.getName());
15 |
16 | @Value("${JAVA_GRAPHQL_ENDPOINT}")
17 | private String graphQLEndpoint;
18 |
19 | public static void main(String[] args) {
20 | SpringApplication.run(DemoApplication.class, args);
21 | }
22 |
23 | /*
24 | * No cors origin global setting.
25 | * This is needed to allow clients from a different origin
26 | * other than where this server is running to connect without
27 | * being denied via a CORS error
28 | */
29 | @Bean
30 | public WebMvcConfigurer corsConfigurer() {
31 |
32 | return new WebMvcConfigurer() {
33 | @Override
34 | public void addCorsMappings(CorsRegistry registry) {
35 | final String[] endpoint = graphQLEndpoint.split("/graphql");
36 | final String origin = endpoint[0];
37 |
38 | logger.info("GraphQL endpoint is: " + origin);
39 | registry.addMapping("/graphql").allowedOrigins("" + origin + "");
40 | }
41 | };
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/graphql-backend-examples/src/main/java/com/example/demo/Genre.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
2 |
3 | public class Genre {
4 | private final String value;
5 |
6 | public Genre(String value) {
7 | this.value = value;
8 | }
9 |
10 | public String getValue() {
11 | return value;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/graphql-backend-examples/src/main/java/com/example/demo/GenresDatafetcher.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
2 |
3 | import com.netflix.graphql.dgs.DgsComponent;
4 | import com.netflix.graphql.dgs.DgsQuery;
5 | import com.netflix.graphql.dgs.InputArgument;
6 |
7 | import java.util.List;
8 | import java.util.stream.Collectors;
9 |
10 | import java.util.logging.*;
11 |
12 | @DgsComponent
13 | public class GenresDatafetcher {
14 | Logger logger = Logger.getLogger(GenresDatafetcher.class.getName());
15 |
16 | private final List genres = List.of(
17 | new Genre("Action"),
18 | new Genre("Anime"),
19 | new Genre("Award-Winning"),
20 | new Genre("Children & Family"),
21 | new Genre("Comedies")
22 | );
23 |
24 | @DgsQuery
25 | public List genres(@InputArgument String labelFilter) {
26 | logger.info("QUERY executed - Genres value is: " + genres.get(0).getValue());
27 |
28 | if(labelFilter == null) {
29 | return genres;
30 | }
31 |
32 | return genres.stream().filter(s -> s.getValue().contains(labelFilter)).collect(Collectors.toList());
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/graphql-backend-examples/src/main/java/com/example/demo/Show.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
2 |
3 | public class Show {
4 | private final String title;
5 | private final Integer releaseYear ;
6 |
7 | public Show(String title, Integer releaseYear) {
8 | this.title = title;
9 | this.releaseYear = releaseYear;
10 | }
11 |
12 | public String getTitle() {
13 | return title;
14 | }
15 |
16 | public Integer getReleaseYear() {
17 | return releaseYear;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/graphql-backend-examples/src/main/java/com/example/demo/ShowsDatafetcher.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
2 |
3 | import com.netflix.graphql.dgs.DgsComponent;
4 | import com.netflix.graphql.dgs.DgsQuery;
5 | import com.netflix.graphql.dgs.InputArgument;
6 |
7 | import java.util.List;
8 | import java.util.stream.Collectors;
9 |
10 | import java.util.logging.*;
11 |
12 | @DgsComponent
13 | public class ShowsDatafetcher {
14 | Logger logger = Logger.getLogger(ShowsDatafetcher.class.getName());
15 |
16 | private final List shows = List.of(
17 | new Show("Stranger Things", 2016),
18 | new Show("Ozark", 2017),
19 | new Show("The Crown", 2016),
20 | new Show("Dead to Me", 2019),
21 | new Show("Orange is the New Black", 2013)
22 | );
23 |
24 | @DgsQuery
25 | public List shows(@InputArgument String titleFilter) {
26 | logger.info("QUERY executed - Shows title is: " + shows.get(0).getTitle());
27 |
28 | if(titleFilter == null) {
29 | return shows;
30 | }
31 |
32 | return shows.stream().filter(s -> s.getTitle().contains(titleFilter)).collect(Collectors.toList());
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/graphql-backend-examples/src/main/java/com/example/demo/ZRedirectGraphIQlController.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 |
6 | import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
7 | import static org.springframework.web.servlet.view.UrlBasedViewResolver.REDIRECT_URL_PREFIX;
8 |
9 | @Controller
10 | public class ZRedirectGraphIQlController {
11 |
12 | @GetMapping(DEFAULT_PATH_SEPARATOR)
13 | public String index() {
14 | return REDIRECT_URL_PREFIX + "/graphiql";
15 | }
16 |
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/graphql-backend-examples/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/graphql-backend-examples/src/main/resources/schema/schema.graphqls:
--------------------------------------------------------------------------------
1 | type Query {
2 | shows(titleFilter: String): [Show]
3 | genres(labelFilter: String): [Genre]
4 | }
5 |
6 | type Show {
7 | title: String
8 | releaseYear: Int
9 | }
10 |
11 | type Genre {
12 | value: String!
13 | }
--------------------------------------------------------------------------------
/graphql-backend-examples/src/test/java/com/example/demo/DemoApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class DemoApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/graphql-client-examples/.env.sample:
--------------------------------------------------------------------------------
1 | ASTRA_DB_APPLICATION_TOKEN="AstraCS:..."
2 | ASTRA_DB_GRAPHQL_URL="https://....apps.astra.datastax.com/api/graphql/intrographql"
3 | ASTRA_DB_GRAPHQL_URL_ADMIN="https://......-.......apps.astra.datastax.com/api/graphql-admin"
4 | ASTRA_DB_GRAPHQL_URL_PLAYGROUND="https://......-.......apps.astra.datastax.com/api/playground"
5 | ASTRA_DB_GRAPHQL_URL_SCHEMA="https://......-.......apps.astra.datastax.com/api/graphql-schema"
6 | ASTRA_DB_ID="......"
7 | ASTRA_DB_KEYSPACE="intrographql"
8 | ASTRA_DB_REGION="......"
9 | ASTRA_DB_REST_URL="https://......-.......apps.astra.datastax.com/api/rest"
10 | ASTRA_DB_REST_URL_SWAGGER="https://......-.......apps.astra.datastax.com/api/rest/swagger-ui/"
11 | ASTRA_DB_SECURE_BUNDLE_PATH="/home/gitpod/.astra/scb/scb_......_.......zip"
12 | ASTRA_DB_SECURE_BUNDLE_URL="https://datastax-cluster-config.....amazonaws.com/....../secure-connect-workshops.zip?......"
13 | ASTRA_ORG_ID="......"
14 | ASTRA_ORG_NAME="user@domain.com"
15 | ASTRA_ORG_TOKEN="AstraCS:..."
16 |
17 | # Non-Astra-DB additional setting (local backend URL):
18 | JAVA_GRAPHQL_ENDPOINT="https://8080-datastaxdev-......gitpod.io/graphql"
19 |
--------------------------------------------------------------------------------
/graphql-client-examples/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/graphql-client-examples/functions/getGenresAstra.js:
--------------------------------------------------------------------------------
1 | const fetch = require('node-fetch')
2 |
3 | exports.handler = async function (event) {
4 | const query = `
5 | query getAllGenres {
6 | reference_list (value: { label: "genre"}) {
7 | values {
8 | value
9 | }
10 | }
11 | }
12 | `
13 | const url = process.env.ASTRA_DB_GRAPHQL_URL
14 | const response = await fetch(url, {
15 | method: 'POST',
16 | headers: {
17 | "Content-Type": "application/json",
18 | "x-cassandra-token": process.env.ASTRA_DB_APPLICATION_TOKEN
19 | },
20 | body: JSON.stringify({ query })
21 | })
22 |
23 | try {
24 | const responseBody = await response.json()
25 | return {
26 | statusCode: 200,
27 | body: JSON.stringify(responseBody)
28 | }
29 | } catch (e) {
30 | console.log(e)
31 | return {
32 | statusCode: 500,
33 | body: JSON.stringify(e)
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/graphql-client-examples/functions/getGenresBackend.js:
--------------------------------------------------------------------------------
1 | const fetch = require('node-fetch')
2 |
3 | exports.handler = async function (event) {
4 | const query = `
5 | query getAllGenres {
6 | genres {
7 | value
8 | }
9 | }
10 | `
11 | const url = process.env.JAVA_GRAPHQL_ENDPOINT
12 | const response = await fetch(url, {
13 | method: 'POST',
14 | headers: {
15 | "Content-Type": "application/json"
16 | },
17 | body: JSON.stringify({ query })
18 | })
19 |
20 | try {
21 | const responseBody = await response.json()
22 | return {
23 | statusCode: 200,
24 | body: JSON.stringify(responseBody)
25 | }
26 | } catch (e) {
27 | console.log(e)
28 | return {
29 | statusCode: 500,
30 | body: JSON.stringify(e)
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/graphql-client-examples/functions/getShowsAstra.js:
--------------------------------------------------------------------------------
1 | const fetch = require('node-fetch')
2 |
3 | exports.handler = async function (event) {
4 | const query = `
5 | query getAllShows {
6 | show_by_name {
7 | values {
8 | title
9 | releaseYear
10 | }
11 | }
12 | }
13 | `
14 | const url = process.env.ASTRA_DB_GRAPHQL_URL
15 | const response = await fetch(url, {
16 | method: 'POST',
17 | headers: {
18 | "Content-Type": "application/json",
19 | "x-cassandra-token": process.env.ASTRA_DB_APPLICATION_TOKEN
20 | },
21 | body: JSON.stringify({ query })
22 | })
23 |
24 | try {
25 | const responseBody = await response.json()
26 | return {
27 | statusCode: 200,
28 | body: JSON.stringify(responseBody)
29 | }
30 | } catch (e) {
31 | console.log(e)
32 | return {
33 | statusCode: 500,
34 | body: JSON.stringify(e)
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/graphql-client-examples/functions/getShowsBackend.js:
--------------------------------------------------------------------------------
1 | const fetch = require('node-fetch')
2 |
3 | exports.handler = async function (event) {
4 | const query = `
5 | query getAllShows {
6 | shows {
7 | title
8 | releaseYear
9 | }
10 | }
11 | `
12 | const url = process.env.JAVA_GRAPHQL_ENDPOINT
13 | const response = await fetch(url, {
14 | method: 'POST',
15 | headers: {
16 | "Content-Type": "application/json"
17 | },
18 | body: JSON.stringify({ query })
19 | })
20 |
21 | try {
22 | const responseBody = await response.json()
23 | return {
24 | statusCode: 200,
25 | body: JSON.stringify(responseBody)
26 | }
27 | } catch (e) {
28 | console.log(e)
29 | return {
30 | statusCode: 500,
31 | body: JSON.stringify(e)
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/graphql-client-examples/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | command = "npm run build"
3 | functions = "functions"
4 | publish = "build"
--------------------------------------------------------------------------------
/graphql-client-examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@apollo/client": "^3.4.10",
7 | "@testing-library/jest-dom": "^5.14.1",
8 | "@testing-library/react": "^11.2.7",
9 | "@testing-library/user-event": "^12.8.3",
10 | "node-fetch": "^2.6.1",
11 | "netlify-cli": "^3.31.14",
12 | "graphql": "^15.5.2",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test",
21 | "eject": "react-scripts eject"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/graphql-client-examples/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-graphql/5e472012272d3a73a3d9d6a3679c3ab24f85fdb3/graphql-client-examples/public/favicon.ico
--------------------------------------------------------------------------------
/graphql-client-examples/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/graphql-client-examples/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-graphql/5e472012272d3a73a3d9d6a3679c3ab24f85fdb3/graphql-client-examples/public/logo192.png
--------------------------------------------------------------------------------
/graphql-client-examples/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-graphql/5e472012272d3a73a3d9d6a3679c3ab24f85fdb3/graphql-client-examples/public/logo512.png
--------------------------------------------------------------------------------
/graphql-client-examples/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/graphql-client-examples/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/graphql-client-examples/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | margin-top:25px;
7 | height: 10vmin;
8 | pointer-events: none;
9 | }
10 |
11 | @media (prefers-reduced-motion: no-preference) {
12 | .App-logo {
13 | animation: App-logo-spin infinite 50s linear;
14 | }
15 | }
16 |
17 | .App-header {
18 | background-color: #282c34;
19 | min-height: 100vh;
20 | display: flex;
21 | flex-direction: column;
22 | align-items: center;
23 | justify-content: center;
24 | font-size: calc(10px + 2vmin);
25 | color: white;
26 | }
27 |
28 | .App-subtitle a {
29 | color: gold;
30 | font-style: italic;
31 | }
32 |
33 | .App-link {
34 | color: #61dafb;
35 | }
36 |
37 | @keyframes App-logo-spin {
38 | from {
39 | transform: rotate(0deg);
40 | }
41 | to {
42 | transform: rotate(360deg);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/graphql-client-examples/src/App.js:
--------------------------------------------------------------------------------
1 | import graphql_logo from './graphql_logo.svg';
2 | import './App.css';
3 | import Shows from './components/Shows';
4 | import Genres from './components/Genres';
5 | import ShowsByName from './components/ShowsByName';
6 | import ReferenceList from './components/ReferenceList';
7 |
8 | function App() {
9 | return (
10 |
30 | );
31 | }
32 |
33 | export default App;
34 |
--------------------------------------------------------------------------------
/graphql-client-examples/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/graphql-client-examples/src/components/Genres.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react"
2 |
3 | function Genres() {
4 | const [gqlResult, setGqlResult] = useState(null) // State to hold graphQL result data
5 | const [isLoading, setIsLoading] = useState(true) // State to determine when the async graphQL call is complete
6 | const [isError, setIsError] = useState(true) // State to determine if the graphQL payload contains an error object
7 |
8 | const fetchData = async () => {
9 |
10 | // Asynchronously fetch any "genres" graphQL data from the Java backend
11 | // using the getGenresBackend serverless function to call out to the
12 | // Netflix DGS Java graphQL endpoint
13 | const response = await fetch("/.netlify/functions/getGenresBackend", {
14 | method: "POST",
15 | })
16 | const responseBody = await response.json()
17 | setGqlResult(responseBody) // on response set our graphQL result state
18 | }
19 |
20 | useEffect(() => {
21 | fetchData()
22 | }, [])
23 |
24 | // Watch gqlResult for any state changes and determine if we
25 | // are finishled loading the payload or if there are
26 | // any errors
27 | useEffect(() => {
28 | if (gqlResult !== null) {
29 | setIsLoading(false)
30 |
31 | // Check the payload for any errors https://graphql.org/learn/validation/
32 | // and if any exist set error state and dump the message to the console
33 | if ('errors' in gqlResult) {
34 | setIsError(true)
35 | console.log("genres ERROR IS: ", gqlResult.errors)
36 | } else {
37 | setIsError(false)
38 | }
39 | }
40 | }, [gqlResult]) // <- watch me for state changes
41 |
42 | // If no result yet display loading text and return
43 | // This will exit the function and "skip" conditions below it
44 | if (isLoading) return
Is Loading...
;
45 |
46 | // If there is an error state display error text and return
47 | // This will exit the function and "skip" conditions below it
48 | if (isError) return
Error :(
;
49 |
50 | // If payload loading is complete and there are no errors
51 | // now check to see there is any data returned
52 | // (If this triggers it essentially means there are no rows returned from the data layer)
53 | // This will exit the function and "skip" conditions below it
54 | if (!gqlResult.data.genres.length) return
No Data
;
55 |
56 | // Finally, if all other checks pass get the data
57 | // from the payload via gqlResult state and inject it into the DOM
58 | // Notice how the payload example below and the field "value" match exactly
59 | // {"data":{"genres":[{"value":"Action"},{"value":"Anime"}...
60 | return gqlResult.data.genres.map(({ value }) => (
61 |
62 |
63 | {value}
64 |
65 |
66 | ));
67 |
68 | }
69 |
70 | export default Genres;
--------------------------------------------------------------------------------
/graphql-client-examples/src/components/ReferenceList.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react"
2 |
3 | function ReferenceList() {
4 | const [gqlResult, setGqlResult] = useState(null) // State to hold graphQL result data
5 | const [isLoading, setIsLoading] = useState(true) // State to determine when the async graphQL call is complete
6 | const [isError, setIsError] = useState(true) // State to determine if the graphQL payload contains an error object
7 |
8 | const fetchData = async () => {
9 |
10 | // Asynchronously fetch any "reference_list" graphQL data from the Astra DB GraphQL API
11 | // using the getGenresAstra serverless function to call out to the
12 | // Astra/Stargate graphQL endpoint
13 | // https://stargate.io/docs/stargate/1.0/developers-guide/graphql.html
14 | const response = await fetch("/.netlify/functions/getGenresAstra", {
15 | method: "POST",
16 | })
17 | const responseBody = await response.json()
18 | setGqlResult(responseBody) // on response set our graphQL result state
19 | }
20 |
21 | useEffect(() => {
22 | fetchData()
23 | }, [])
24 |
25 | // Watch gqlResult for any state changes and determine if we
26 | // are finishled loading the payload or if there are
27 | // any errors
28 | useEffect(() => {
29 | if (gqlResult !== null) {
30 | setIsLoading(false)
31 |
32 | // Check the payload for any errors https://graphql.org/learn/validation/
33 | // and if any exist set error state and dump the message to the console
34 | if ('errors' in gqlResult) {
35 | setIsError(true)
36 | console.log("reference_list ERROR IS: ", gqlResult.errors)
37 | } else {
38 | setIsError(false)
39 | }
40 | }
41 | }, [gqlResult]) // <- watch me for state changes
42 |
43 | // If no result yet display loading text and return
44 | // This will exit the function and "skip" conditions below it
45 | if (isLoading) return
Is Loading...
;
46 |
47 | // If there is an error state display error text and return
48 | // This will exit the function and "skip" conditions below it
49 | if (isError) return
Error :(
;
50 |
51 | // If payload loading is complete and there are no errors
52 | // now check to see there is any data returned
53 | // (If this triggers it essentially means there are no rows returned from the data layer)
54 | // This will exit the function and "skip" conditions below it
55 | if (!gqlResult.data.reference_list.values.length) return
No Data
;
56 |
57 | // Finally, if all other checks pass get the data
58 | // from the payload via gqlResult state and inject it into the DOM
59 | // Notice how the payload example below and the field "value" match exactly
60 | // {"data":{"reference_list":{"values":[{"value":"Action"},{"value":"Anime"}...
61 | return gqlResult.data.reference_list.values.map(({ value }) => (
62 |
63 |
64 | {value}
65 |
66 |
67 | ));
68 |
69 | }
70 |
71 | export default ReferenceList;
--------------------------------------------------------------------------------
/graphql-client-examples/src/components/Shows.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react"
2 |
3 | function Shows() {
4 | const [gqlResult, setGqlResult] = useState(null) // State to hold graphQL result data
5 | const [isLoading, setIsLoading] = useState(true) // State to determine when the async graphQL call is complete
6 | const [isError, setIsError] = useState(true) // State to determine if the graphQL payload contains an error object
7 |
8 | const fetchData = async () => {
9 |
10 | // Asynchronously fetch any "shows" graphQL data from the Java backend
11 | // using the getShowsBackend serverless function to call out to the
12 | // Netflix DGS Java graphQL endpoint
13 | const response = await fetch("/.netlify/functions/getShowsBackend", {
14 | method: "POST",
15 | })
16 | const responseBody = await response.json()
17 | setGqlResult(responseBody) // on response set our graphQL result state
18 | }
19 |
20 | useEffect(() => {
21 | fetchData()
22 | }, [])
23 |
24 | // Watch gqlResult for any state changes and determine if we
25 | // are finishled loading the payload or if there are
26 | // any errors
27 | useEffect(() => {
28 | if (gqlResult !== null) {
29 | setIsLoading(false)
30 |
31 | // Check the payload for any errors https://graphql.org/learn/validation/
32 | // and if any exist set error state and dump the message to the console
33 | if ('errors' in gqlResult) {
34 | setIsError(true)
35 | console.log("shows ERROR IS: ", gqlResult.errors)
36 | } else {
37 | setIsError(false)
38 | }
39 | }
40 | }, [gqlResult]) // <- watch me for state changes
41 |
42 | // If no result yet display loading text and return
43 | // This will exit the function and "skip" conditions below it
44 | if (isLoading) return
Is Loading...
;
45 |
46 | // If there is an error state display error text and return
47 | // This will exit the function and "skip" conditions below it
48 | if (isError) return
Error :(
;
49 |
50 | // If payload loading is complete and there are no errors
51 | // now check to see there is any data returned
52 | // (If this triggers it essentially means there are no rows returned from the data layer)
53 | // This will exit the function and "skip" conditions below it
54 | if (!gqlResult.data.shows.length) return
No Data
;
55 |
56 | // Finally, if all other checks pass get the data
57 | // from the payload via gqlResult state and inject it into the DOM
58 | // Notice how the payload example below and the fields "title" and "releaseYear" match exactly
59 | // {"data":{"shows":[{"title":"Stranger Things","releaseYear":2016},{"title":"Ozark","releaseYear":2017}...
60 | return gqlResult.data.shows.map(({ title, releaseYear }) => (
61 |
62 |
63 | {title}: {releaseYear}
64 |
65 |
66 | ));
67 |
68 | }
69 |
70 | export default Shows;
--------------------------------------------------------------------------------
/graphql-client-examples/src/components/ShowsByName.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react"
2 |
3 | function ShowsByName() {
4 | const [gqlResult, setGqlResult] = useState(null) // State to hold graphQL result data
5 | const [isLoading, setIsLoading] = useState(true) // State to determine when the async graphQL call is complete
6 | const [isError, setIsError] = useState(true) // State to determine if the graphQL payload contains an error object
7 | const [errorString, setErrorString] = useState("") // State to keep the error message(s) for display if necessary
8 |
9 | const fetchData = async () => {
10 |
11 | // Asynchronously fetch any "shows_by_name" graphQL data from the Astra DB GraphQL API
12 | // using the getShowsAstra serverless function to call out to the
13 | // Astra/Stargate graphQL endpoint
14 | // https://stargate.io/docs/stargate/1.0/developers-guide/graphql.html
15 | const response = await fetch("/.netlify/functions/getShowsAstra", {
16 | method: "POST",
17 | })
18 | const responseBody = await response.json()
19 | setGqlResult(responseBody) // on response set our graphQL result state
20 | }
21 |
22 | useEffect(() => {
23 | fetchData()
24 | }, [])
25 |
26 | // Watch gqlResult for any state changes and determine if we
27 | // are finishled loading the payload or if there are
28 | // any errors
29 | useEffect(() => {
30 | if (gqlResult !== null) {
31 | setIsLoading(false)
32 |
33 | // Check the payload for any errors https://graphql.org/learn/validation/
34 | // and if any exist set error state and dump the message to the console
35 | if ('errors' in gqlResult) {
36 | setIsError(true)
37 | setErrorString(JSON.stringify(gqlResult.errors))
38 | console.log("show_by_name ERROR IS: ", gqlResult.errors)
39 | } else {
40 | setIsError(false)
41 | }
42 | }
43 | }, [gqlResult]) // <- watch me for state changes
44 |
45 | // If no result yet display loading text and return
46 | // This will exit the function and "skip" conditions below it
47 | if (isLoading) return
Is Loading...
;
48 |
49 | // If there is an error state display error text and return
50 | // This will exit the function and "skip" conditions below it
51 | if (isError) return
Error :( { errorString }
;
52 |
53 | // If payload loading is complete and there are no errors
54 | // now check to see there is any data returned
55 | // (If this triggers it essentially means there are no rows returned from the data layer)
56 | // This will exit the function and "skip" conditions below it
57 | if (!gqlResult.data.show_by_name.values.length) return
No Data
;
58 |
59 | // Finally, if all other checks pass get the data
60 | // from the payload via gqlResult state and inject it into the DOM
61 | // Notice how the payload example below and the fields "title" and "releaseYear" match exactly
62 | // {"data":{"show_by_name":{"values":[{"title":"Stranger Things","releaseYear":2016},{"title":"Ozark","releaseYear":2017}...
63 | return gqlResult.data.show_by_name.values.map(({ title, releaseYear }) => (
64 |